We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Relative paths in workspace.ini to make projects moveable
## Description of your problem

**What steps will reproduce the problem?**

1. Add a new project `Projects > New project...`
2. Open some files.
3. Close Spyder.
4. Copy the whole project's directory to another computer to a different path (or have it synced with other means to different directories, e.g. `/home/mm/Software/Project` and `/home/MickeyMouse/Work/Software/Project` for your home and work computers).
5. Open the project on the other computer.

**What is the expected output? What do you see instead?**

I would like to see the same files opened automatically on both computers when opening the project, but the `.spyproject/workspace.ini` file stores the full paths in `[main]recent_files`. If the paths were stored relative to `.spyproject/..` (the project's base directory), it would be possible to move the project to a different location.

**Please provide any additional information below**

There seems to be two related bugs: #1133 and #1343. Maybe more?

## Versions and main components

* Spyder Version: 3.2.3
* Python Version: 3.5.2 64bits
* Qt Version: 5.5.1
* PyQt Version: 5.5.1
* Operating system: Linux Mint 18.2 Cinnamon 64-bit


## Dependencies

Please go to the menu entry `Help > Optional Dependencies` (or
`Help > Dependencies`), press the button `Copy to clipboard`
and paste the contents below:

IPython >=4.0    :  6.1.0 (OK)
cython >=0.21    :  None (NOK)
jedi >=0.9.0     :  0.10.2 (OK)
nbconvert >=4.0  :  5.3.1 (OK)
numpy >=1.7      :  1.12.0 (OK)
pandas >=0.13.1  :  0.19.2 (OK)
psutil >=0.3     :  5.3.1 (OK)
pycodestyle >=2.3:  2.3.1 (OK)
pyflakes >=0.6.0 :  1.6.0 (OK)
pygments >=2.0   :  2.2.0 (OK)
pylint >=0.25    :  1.7.2 (OK)
qtconsole >=4.2.0:  4.3.1 (OK)
rope >=0.9.4     :  0.10.7 (OK)
sphinx >=0.6.6   :  1.6.3 (OK)
sympy >=0.7.3    :  1.0 (OK)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `spyder/plugins/projects/api.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 # -----------------------------------------------------------------------------
3 # Copyright © Spyder Project Contributors
4 #
5 # Licensed under the terms of the MIT License
6 # (see spyder/__init__.py for details)
7 # -----------------------------------------------------------------------------
8 """Project API"""
9 
10 import os
11 import os.path as osp
12 from collections import OrderedDict
13 
14 from spyder.config.base import _
15 from spyder.py3compat import to_text_string
16 from spyder.plugins.projects.utils.config import (ProjectConfig, CODESTYLE,
17                                             CODESTYLE_DEFAULTS,
18                                             CODESTYLE_VERSION, WORKSPACE,
19                                             WORKSPACE_DEFAULTS,
20                                             WORKSPACE_VERSION,
21                                             ENCODING, ENCODING_DEFAULTS,
22                                             ENCODING_VERSION,
23                                             VCS, VCS_DEFAULTS, VCS_VERSION)
24 
25 
26 class BaseProject(object):
27     """Spyder base project.
28 
29     This base class must not be used directly, but inherited from. It does not
30     assume that python is specific to this project.
31     """
32     PROJECT_FOLDER = '.spyproject'
33     PROJECT_TYPE_NAME = None
34     IGNORE_FILE = ""
35     CONFIG_SETUP = {WORKSPACE: {'filename': '{0}.ini'.format(WORKSPACE),
36                                 'defaults': WORKSPACE_DEFAULTS,
37                                 'version': WORKSPACE_VERSION},
38                     CODESTYLE: {'filename': '{0}.ini'.format(CODESTYLE),
39                                 'defaults': CODESTYLE_DEFAULTS,
40                                 'version': CODESTYLE_VERSION},
41                     ENCODING: {'filename': '{0}.ini'.format(ENCODING),
42                                'defaults': ENCODING_DEFAULTS,
43                                'version': ENCODING_VERSION},
44                     VCS: {'filename': '{0}.ini'.format(VCS),
45                           'defaults': VCS_DEFAULTS,
46                           'version': VCS_VERSION}
47                     }
48 
49     def __init__(self, root_path):
50         self.name = None
51         self.root_path = root_path
52         self.open_project_files = []
53         self.open_non_project_files = []
54         self.config_files = []
55         self.CONF = {}
56 
57         # Configuration files
58 
59         self.related_projects = []  # storing project path, not project objects
60 #        self.pythonpath = []
61         self.opened = True
62 
63         self.ioerror_flag = False
64         self.create_project_config_files()
65 
66     # --- Helpers
67     # -------------------------------------------------------------------------
68     def set_recent_files(self, recent_files):
69         """Set a list of files opened by the project."""
70         for recent_file in recent_files[:]:
71             if not os.path.isfile(recent_file):
72                 recent_files.remove(recent_file)
73         try:
74             self.CONF[WORKSPACE].set('main', 'recent_files',
75                                      list(OrderedDict.fromkeys(recent_files)))
76         except EnvironmentError:
77             pass
78 
79     def get_recent_files(self):
80         """Return a list of files opened by the project."""
81         try:
82             recent_files = self.CONF[WORKSPACE].get('main', 'recent_files',
83                                                     default=[])
84         except EnvironmentError:
85             return []
86 
87         for recent_file in recent_files[:]:
88             if not os.path.isfile(recent_file):
89                 recent_files.remove(recent_file)
90         return list(OrderedDict.fromkeys(recent_files))
91 
92     def create_project_config_files(self):
93         """ """
94         dic = self.CONFIG_SETUP
95         for key in dic:
96             name = key
97             filename = dic[key]['filename']
98             defaults = dic[key]['defaults']
99             version = dic[key]['version']
100             self.CONF[key] = ProjectConfig(name, self.root_path, filename,
101                                            defaults=defaults, load=True,
102                                            version=version)
103 
104     def get_conf_files(self):
105         """ """
106         return self.CONF
107 
108     def add_ignore_lines(self, lines):
109         """ """
110         text = self.IGNORE_FILE
111         for line in lines:
112             text += line
113         self.IGNORE_FILE = text
114 
115     def set_root_path(self, root_path):
116         """Set project root path."""
117         if self.name is None:
118             self.name = osp.basename(root_path)
119         self.root_path = to_text_string(root_path)
120         config_path = self.__get_project_config_path()
121         if osp.exists(config_path):
122             self.load()
123         else:
124             if not osp.isdir(self.root_path):
125                 os.mkdir(self.root_path)
126             self.save()
127 
128     def rename(self, new_name):
129         """Rename project and rename its root path accordingly."""
130         old_name = self.name
131         self.name = new_name
132         pypath = self.relative_pythonpath  # ??
133         self.root_path = self.root_path[:-len(old_name)]+new_name
134         self.relative_pythonpath = pypath  # ??
135         self.save()
136 
137     def __get_project_config_folder(self):
138         """Return project configuration folder."""
139         return osp.join(self.root_path, self.PROJECT_FOLDER)
140 
141     def __get_project_config_path(self):
142         """Return project configuration path"""
143         return osp.join(self.root_path, self.CONFIG_NAME)
144 
145     def load(self):
146         """Load project data"""
147 #        fname = self.__get_project_config_path()
148 #        try:
149 #            # Old format (Spyder 2.0-2.1 for Python 2)
150 #            with open(fname, 'U') as fdesc:
151 #                data = pickle.loads(fdesc.read())
152 #        except (pickle.PickleError, TypeError, UnicodeDecodeError,
153 #                AttributeError):
154 #            try:
155 #                # New format (Spyder >=2.2 for Python 2 and Python 3)
156 #                with open(fname, 'rb') as fdesc:
157 #                    data = pickle.loads(fdesc.read())
158 #            except (IOError, OSError, pickle.PickleError):
159 #                self.ioerror_flag = True
160 #                return
161         # Compatibilty with old project explorer file format:
162 #        if 'relative_pythonpath' not in data:
163 #            print("Warning: converting old configuration file "
164 #                  "for project '%s'" % data['name'], file=STDERR)
165 #            self.pythonpath = data['pythonpath']
166 #            data['relative_pythonpath'] = self.relative_pythonpath
167 #        for attr in self.CONFIG_ATTR:
168 #            setattr(self, attr, data[attr])
169 #        self.save()
170 
171     def save(self):
172         """Save project data"""
173 #        data = {}
174 #        for attr in self.PROJECT_ATTR:
175 #            data[attr] = getattr(self, attr)
176 #        try:
177 #            with open(self.__get_project_config_path(), 'wb') as fdesc:
178 #                pickle.dump(data, fdesc, 2)
179 #        except (IOError, OSError):
180 #            self.ioerror_flag = True
181 
182 #    def delete(self):
183 #        """Delete project"""
184 #        os.remove(self.__get_project_config_path())
185 #
186 #    # --- Misc.
187 #    def get_related_projects(self):
188 #        """Return related projects path list"""
189 #        return self.related_projects
190 #
191 #    def set_related_projects(self, related_projects):
192 #        """Set related projects"""
193 #        self.related_projects = related_projects
194 #        self.save()
195 #
196 #    def open(self):
197 #        """Open project"""
198 #        self.opened = True
199 #        self.save()
200 #
201 #    def close(self):
202 #        """Close project"""
203 #        self.opened = False
204 #        self.save()
205 #
206 #    def is_opened(self):
207 #        """Return True if project is opened"""
208 #        return self.opened
209 #
210 #    def is_file_in_project(self, fname):
211 #        """Return True if file *fname* is in one of the project subfolders"""
212 #        fixed_root = fixpath(self.root_path)
213 #        return fixpath(fname) == fixed_root or\
214 #            fixpath(osp.dirname(fname)).startswith(fixed_root)
215 #
216 #    def is_root_path(self, dirname):
217 #        """Return True if dirname is project's root path"""
218 #        return fixpath(dirname) == fixpath(self.root_path)
219 
220 
221 class EmptyProject(BaseProject):
222     """Empty Project"""
223     PROJECT_TYPE_NAME = _('Empty project')
224
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/spyder/plugins/projects/api.py b/spyder/plugins/projects/api.py
--- a/spyder/plugins/projects/api.py
+++ b/spyder/plugins/projects/api.py
@@ -70,6 +70,8 @@
         for recent_file in recent_files[:]:
             if not os.path.isfile(recent_file):
                 recent_files.remove(recent_file)
+        recent_files = [os.path.relpath(recent_file, self.root_path)
+                        for recent_file in recent_files]
         try:
             self.CONF[WORKSPACE].set('main', 'recent_files',
                                      list(OrderedDict.fromkeys(recent_files)))
@@ -84,6 +86,9 @@
         except EnvironmentError:
             return []
 
+        recent_files = [recent_file if os.path.isabs(recent_file)
+                        else os.path.join(self.root_path, recent_file)
+                        for recent_file in recent_files]
         for recent_file in recent_files[:]:
             if not os.path.isfile(recent_file):
                 recent_files.remove(recent_file)

{"golden_diff": "diff --git a/spyder/plugins/projects/api.py b/spyder/plugins/projects/api.py\n--- a/spyder/plugins/projects/api.py\n+++ b/spyder/plugins/projects/api.py\n@@ -70,6 +70,8 @@\n         for recent_file in recent_files[:]:\r\n             if not os.path.isfile(recent_file):\r\n                 recent_files.remove(recent_file)\r\n+        recent_files = [os.path.relpath(recent_file, self.root_path)\r\n+                        for recent_file in recent_files]\r\n         try:\r\n             self.CONF[WORKSPACE].set('main', 'recent_files',\r\n                                      list(OrderedDict.fromkeys(recent_files)))\r\n@@ -84,6 +86,9 @@\n         except EnvironmentError:\r\n             return []\r\n \r\n+        recent_files = [recent_file if os.path.isabs(recent_file)\r\n+                        else os.path.join(self.root_path, recent_file)\r\n+                        for recent_file in recent_files]\r\n         for recent_file in recent_files[:]:\r\n             if not os.path.isfile(recent_file):\r\n                 recent_files.remove(recent_file)\n", "issue": "Relative paths in workspace.ini to make projects moveable\n## Description of your problem\r\n\r\n**What steps will reproduce the problem?**\r\n\r\n1. Add a new project `Projects > New project...`\r\n2. Open some files.\r\n3. Close Spyder.\r\n4. Copy the whole project's directory to another computer to a different path (or have it synced with other means to different directories, e.g. `/home/mm/Software/Project` and `/home/MickeyMouse/Work/Software/Project` for your home and work computers).\r\n5. Open the project on the other computer.\r\n\r\n**What is the expected output? What do you see instead?**\r\n\r\nI would like to see the same files opened automatically on both computers when opening the project, but the `.spyproject/workspace.ini` file stores the full paths in `[main]recent_files`. If the paths were stored relative to `.spyproject/..` (the project's base directory), it would be possible to move the project to a different location.\r\n\r\n**Please provide any additional information below**\r\n\r\nThere seems to be two related bugs: #1133 and #1343. Maybe more?\r\n\r\n## Versions and main components\r\n\r\n* Spyder Version: 3.2.3\r\n* Python Version: 3.5.2 64bits\r\n* Qt Version: 5.5.1\r\n* PyQt Version: 5.5.1\r\n* Operating system: Linux Mint 18.2 Cinnamon 64-bit\r\n\r\n\r\n## Dependencies\r\n\r\nPlease go to the menu entry `Help > Optional Dependencies` (or\r\n`Help > Dependencies`), press the button `Copy to clipboard`\r\nand paste the contents below:\r\n\r\nIPython >=4.0    :  6.1.0 (OK)\r\ncython >=0.21    :  None (NOK)\r\njedi >=0.9.0     :  0.10.2 (OK)\r\nnbconvert >=4.0  :  5.3.1 (OK)\r\nnumpy >=1.7      :  1.12.0 (OK)\r\npandas >=0.13.1  :  0.19.2 (OK)\r\npsutil >=0.3     :  5.3.1 (OK)\r\npycodestyle >=2.3:  2.3.1 (OK)\r\npyflakes >=0.6.0 :  1.6.0 (OK)\r\npygments >=2.0   :  2.2.0 (OK)\r\npylint >=0.25    :  1.7.2 (OK)\r\nqtconsole >=4.2.0:  4.3.1 (OK)\r\nrope >=0.9.4     :  0.10.7 (OK)\r\nsphinx >=0.6.6   :  1.6.3 (OK)\r\nsympy >=0.7.3    :  1.0 (OK)\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\r\n# -----------------------------------------------------------------------------\r\n# Copyright \u00a9 Spyder Project Contributors\r\n#\r\n# Licensed under the terms of the MIT License\r\n# (see spyder/__init__.py for details)\r\n# -----------------------------------------------------------------------------\r\n\"\"\"Project API\"\"\"\r\n\r\nimport os\r\nimport os.path as osp\r\nfrom collections import OrderedDict\r\n\r\nfrom spyder.config.base import _\r\nfrom spyder.py3compat import to_text_string\r\nfrom spyder.plugins.projects.utils.config import (ProjectConfig, CODESTYLE,\r\n                                            CODESTYLE_DEFAULTS,\r\n                                            CODESTYLE_VERSION, WORKSPACE,\r\n                                            WORKSPACE_DEFAULTS,\r\n                                            WORKSPACE_VERSION,\r\n                                            ENCODING, ENCODING_DEFAULTS,\r\n                                            ENCODING_VERSION,\r\n                                            VCS, VCS_DEFAULTS, VCS_VERSION)\r\n\r\n\r\nclass BaseProject(object):\r\n    \"\"\"Spyder base project.\r\n\r\n    This base class must not be used directly, but inherited from. It does not\r\n    assume that python is specific to this project.\r\n    \"\"\"\r\n    PROJECT_FOLDER = '.spyproject'\r\n    PROJECT_TYPE_NAME = None\r\n    IGNORE_FILE = \"\"\r\n    CONFIG_SETUP = {WORKSPACE: {'filename': '{0}.ini'.format(WORKSPACE),\r\n                                'defaults': WORKSPACE_DEFAULTS,\r\n                                'version': WORKSPACE_VERSION},\r\n                    CODESTYLE: {'filename': '{0}.ini'.format(CODESTYLE),\r\n                                'defaults': CODESTYLE_DEFAULTS,\r\n                                'version': CODESTYLE_VERSION},\r\n                    ENCODING: {'filename': '{0}.ini'.format(ENCODING),\r\n                               'defaults': ENCODING_DEFAULTS,\r\n                               'version': ENCODING_VERSION},\r\n                    VCS: {'filename': '{0}.ini'.format(VCS),\r\n                          'defaults': VCS_DEFAULTS,\r\n                          'version': VCS_VERSION}\r\n                    }\r\n\r\n    def __init__(self, root_path):\r\n        self.name = None\r\n        self.root_path = root_path\r\n        self.open_project_files = []\r\n        self.open_non_project_files = []\r\n        self.config_files = []\r\n        self.CONF = {}\r\n\r\n        # Configuration files\r\n\r\n        self.related_projects = []  # storing project path, not project objects\r\n#        self.pythonpath = []\r\n        self.opened = True\r\n\r\n        self.ioerror_flag = False\r\n        self.create_project_config_files()\r\n\r\n    # --- Helpers\r\n    # -------------------------------------------------------------------------\r\n    def set_recent_files(self, recent_files):\r\n        \"\"\"Set a list of files opened by the project.\"\"\"\r\n        for recent_file in recent_files[:]:\r\n            if not os.path.isfile(recent_file):\r\n                recent_files.remove(recent_file)\r\n        try:\r\n            self.CONF[WORKSPACE].set('main', 'recent_files',\r\n                                     list(OrderedDict.fromkeys(recent_files)))\r\n        except EnvironmentError:\r\n            pass\r\n\r\n    def get_recent_files(self):\r\n        \"\"\"Return a list of files opened by the project.\"\"\"\r\n        try:\r\n            recent_files = self.CONF[WORKSPACE].get('main', 'recent_files',\r\n                                                    default=[])\r\n        except EnvironmentError:\r\n            return []\r\n\r\n        for recent_file in recent_files[:]:\r\n            if not os.path.isfile(recent_file):\r\n                recent_files.remove(recent_file)\r\n        return list(OrderedDict.fromkeys(recent_files))\r\n\r\n    def create_project_config_files(self):\r\n        \"\"\" \"\"\"\r\n        dic = self.CONFIG_SETUP\r\n        for key in dic:\r\n            name = key\r\n            filename = dic[key]['filename']\r\n            defaults = dic[key]['defaults']\r\n            version = dic[key]['version']\r\n            self.CONF[key] = ProjectConfig(name, self.root_path, filename,\r\n                                           defaults=defaults, load=True,\r\n                                           version=version)\r\n\r\n    def get_conf_files(self):\r\n        \"\"\" \"\"\"\r\n        return self.CONF\r\n\r\n    def add_ignore_lines(self, lines):\r\n        \"\"\" \"\"\"\r\n        text = self.IGNORE_FILE\r\n        for line in lines:\r\n            text += line\r\n        self.IGNORE_FILE = text\r\n\r\n    def set_root_path(self, root_path):\r\n        \"\"\"Set project root path.\"\"\"\r\n        if self.name is None:\r\n            self.name = osp.basename(root_path)\r\n        self.root_path = to_text_string(root_path)\r\n        config_path = self.__get_project_config_path()\r\n        if osp.exists(config_path):\r\n            self.load()\r\n        else:\r\n            if not osp.isdir(self.root_path):\r\n                os.mkdir(self.root_path)\r\n            self.save()\r\n\r\n    def rename(self, new_name):\r\n        \"\"\"Rename project and rename its root path accordingly.\"\"\"\r\n        old_name = self.name\r\n        self.name = new_name\r\n        pypath = self.relative_pythonpath  # ??\r\n        self.root_path = self.root_path[:-len(old_name)]+new_name\r\n        self.relative_pythonpath = pypath  # ??\r\n        self.save()\r\n\r\n    def __get_project_config_folder(self):\r\n        \"\"\"Return project configuration folder.\"\"\"\r\n        return osp.join(self.root_path, self.PROJECT_FOLDER)\r\n\r\n    def __get_project_config_path(self):\r\n        \"\"\"Return project configuration path\"\"\"\r\n        return osp.join(self.root_path, self.CONFIG_NAME)\r\n\r\n    def load(self):\r\n        \"\"\"Load project data\"\"\"\r\n#        fname = self.__get_project_config_path()\r\n#        try:\r\n#            # Old format (Spyder 2.0-2.1 for Python 2)\r\n#            with open(fname, 'U') as fdesc:\r\n#                data = pickle.loads(fdesc.read())\r\n#        except (pickle.PickleError, TypeError, UnicodeDecodeError,\r\n#                AttributeError):\r\n#            try:\r\n#                # New format (Spyder >=2.2 for Python 2 and Python 3)\r\n#                with open(fname, 'rb') as fdesc:\r\n#                    data = pickle.loads(fdesc.read())\r\n#            except (IOError, OSError, pickle.PickleError):\r\n#                self.ioerror_flag = True\r\n#                return\r\n        # Compatibilty with old project explorer file format:\r\n#        if 'relative_pythonpath' not in data:\r\n#            print(\"Warning: converting old configuration file \"\r\n#                  \"for project '%s'\" % data['name'], file=STDERR)\r\n#            self.pythonpath = data['pythonpath']\r\n#            data['relative_pythonpath'] = self.relative_pythonpath\r\n#        for attr in self.CONFIG_ATTR:\r\n#            setattr(self, attr, data[attr])\r\n#        self.save()\r\n\r\n    def save(self):\r\n        \"\"\"Save project data\"\"\"\r\n#        data = {}\r\n#        for attr in self.PROJECT_ATTR:\r\n#            data[attr] = getattr(self, attr)\r\n#        try:\r\n#            with open(self.__get_project_config_path(), 'wb') as fdesc:\r\n#                pickle.dump(data, fdesc, 2)\r\n#        except (IOError, OSError):\r\n#            self.ioerror_flag = True\r\n\r\n#    def delete(self):\r\n#        \"\"\"Delete project\"\"\"\r\n#        os.remove(self.__get_project_config_path())\r\n#\r\n#    # --- Misc.\r\n#    def get_related_projects(self):\r\n#        \"\"\"Return related projects path list\"\"\"\r\n#        return self.related_projects\r\n#\r\n#    def set_related_projects(self, related_projects):\r\n#        \"\"\"Set related projects\"\"\"\r\n#        self.related_projects = related_projects\r\n#        self.save()\r\n#\r\n#    def open(self):\r\n#        \"\"\"Open project\"\"\"\r\n#        self.opened = True\r\n#        self.save()\r\n#\r\n#    def close(self):\r\n#        \"\"\"Close project\"\"\"\r\n#        self.opened = False\r\n#        self.save()\r\n#\r\n#    def is_opened(self):\r\n#        \"\"\"Return True if project is opened\"\"\"\r\n#        return self.opened\r\n#\r\n#    def is_file_in_project(self, fname):\r\n#        \"\"\"Return True if file *fname* is in one of the project subfolders\"\"\"\r\n#        fixed_root = fixpath(self.root_path)\r\n#        return fixpath(fname) == fixed_root or\\\r\n#            fixpath(osp.dirname(fname)).startswith(fixed_root)\r\n#\r\n#    def is_root_path(self, dirname):\r\n#        \"\"\"Return True if dirname is project's root path\"\"\"\r\n#        return fixpath(dirname) == fixpath(self.root_path)\r\n\r\n\r\nclass EmptyProject(BaseProject):\r\n    \"\"\"Empty Project\"\"\"\r\n    PROJECT_TYPE_NAME = _('Empty project')\r\n", "path": "spyder/plugins/projects/api.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\r\n# -----------------------------------------------------------------------------\r\n# Copyright \u00a9 Spyder Project Contributors\r\n#\r\n# Licensed under the terms of the MIT License\r\n# (see spyder/__init__.py for details)\r\n# -----------------------------------------------------------------------------\r\n\"\"\"Project API\"\"\"\r\n\r\nimport os\r\nimport os.path as osp\r\nfrom collections import OrderedDict\r\n\r\nfrom spyder.config.base import _\r\nfrom spyder.py3compat import to_text_string\r\nfrom spyder.plugins.projects.utils.config import (ProjectConfig, CODESTYLE,\r\n                                            CODESTYLE_DEFAULTS,\r\n                                            CODESTYLE_VERSION, WORKSPACE,\r\n                                            WORKSPACE_DEFAULTS,\r\n                                            WORKSPACE_VERSION,\r\n                                            ENCODING, ENCODING_DEFAULTS,\r\n                                            ENCODING_VERSION,\r\n                                            VCS, VCS_DEFAULTS, VCS_VERSION)\r\n\r\n\r\nclass BaseProject(object):\r\n    \"\"\"Spyder base project.\r\n\r\n    This base class must not be used directly, but inherited from. It does not\r\n    assume that python is specific to this project.\r\n    \"\"\"\r\n    PROJECT_FOLDER = '.spyproject'\r\n    PROJECT_TYPE_NAME = None\r\n    IGNORE_FILE = \"\"\r\n    CONFIG_SETUP = {WORKSPACE: {'filename': '{0}.ini'.format(WORKSPACE),\r\n                                'defaults': WORKSPACE_DEFAULTS,\r\n                                'version': WORKSPACE_VERSION},\r\n                    CODESTYLE: {'filename': '{0}.ini'.format(CODESTYLE),\r\n                                'defaults': CODESTYLE_DEFAULTS,\r\n                                'version': CODESTYLE_VERSION},\r\n                    ENCODING: {'filename': '{0}.ini'.format(ENCODING),\r\n                               'defaults': ENCODING_DEFAULTS,\r\n                               'version': ENCODING_VERSION},\r\n                    VCS: {'filename': '{0}.ini'.format(VCS),\r\n                          'defaults': VCS_DEFAULTS,\r\n                          'version': VCS_VERSION}\r\n                    }\r\n\r\n    def __init__(self, root_path):\r\n        self.name = None\r\n        self.root_path = root_path\r\n        self.open_project_files = []\r\n        self.open_non_project_files = []\r\n        self.config_files = []\r\n        self.CONF = {}\r\n\r\n        # Configuration files\r\n\r\n        self.related_projects = []  # storing project path, not project objects\r\n#        self.pythonpath = []\r\n        self.opened = True\r\n\r\n        self.ioerror_flag = False\r\n        self.create_project_config_files()\r\n\r\n    # --- Helpers\r\n    # -------------------------------------------------------------------------\r\n    def set_recent_files(self, recent_files):\r\n        \"\"\"Set a list of files opened by the project.\"\"\"\r\n        for recent_file in recent_files[:]:\r\n            if not os.path.isfile(recent_file):\r\n                recent_files.remove(recent_file)\r\n        recent_files = [os.path.relpath(recent_file, self.root_path)\r\n                        for recent_file in recent_files]\r\n        try:\r\n            self.CONF[WORKSPACE].set('main', 'recent_files',\r\n                                     list(OrderedDict.fromkeys(recent_files)))\r\n        except EnvironmentError:\r\n            pass\r\n\r\n    def get_recent_files(self):\r\n        \"\"\"Return a list of files opened by the project.\"\"\"\r\n        try:\r\n            recent_files = self.CONF[WORKSPACE].get('main', 'recent_files',\r\n                                                    default=[])\r\n        except EnvironmentError:\r\n            return []\r\n\r\n        recent_files = [recent_file if os.path.isabs(recent_file)\r\n                        else os.path.join(self.root_path, recent_file)\r\n                        for recent_file in recent_files]\r\n        for recent_file in recent_files[:]:\r\n            if not os.path.isfile(recent_file):\r\n                recent_files.remove(recent_file)\r\n        return list(OrderedDict.fromkeys(recent_files))\r\n\r\n    def create_project_config_files(self):\r\n        \"\"\" \"\"\"\r\n        dic = self.CONFIG_SETUP\r\n        for key in dic:\r\n            name = key\r\n            filename = dic[key]['filename']\r\n            defaults = dic[key]['defaults']\r\n            version = dic[key]['version']\r\n            self.CONF[key] = ProjectConfig(name, self.root_path, filename,\r\n                                           defaults=defaults, load=True,\r\n                                           version=version)\r\n\r\n    def get_conf_files(self):\r\n        \"\"\" \"\"\"\r\n        return self.CONF\r\n\r\n    def add_ignore_lines(self, lines):\r\n        \"\"\" \"\"\"\r\n        text = self.IGNORE_FILE\r\n        for line in lines:\r\n            text += line\r\n        self.IGNORE_FILE = text\r\n\r\n    def set_root_path(self, root_path):\r\n        \"\"\"Set project root path.\"\"\"\r\n        if self.name is None:\r\n            self.name = osp.basename(root_path)\r\n        self.root_path = to_text_string(root_path)\r\n        config_path = self.__get_project_config_path()\r\n        if osp.exists(config_path):\r\n            self.load()\r\n        else:\r\n            if not osp.isdir(self.root_path):\r\n                os.mkdir(self.root_path)\r\n            self.save()\r\n\r\n    def rename(self, new_name):\r\n        \"\"\"Rename project and rename its root path accordingly.\"\"\"\r\n        old_name = self.name\r\n        self.name = new_name\r\n        pypath = self.relative_pythonpath  # ??\r\n        self.root_path = self.root_path[:-len(old_name)]+new_name\r\n        self.relative_pythonpath = pypath  # ??\r\n        self.save()\r\n\r\n    def __get_project_config_folder(self):\r\n        \"\"\"Return project configuration folder.\"\"\"\r\n        return osp.join(self.root_path, self.PROJECT_FOLDER)\r\n\r\n    def __get_project_config_path(self):\r\n        \"\"\"Return project configuration path\"\"\"\r\n        return osp.join(self.root_path, self.CONFIG_NAME)\r\n\r\n    def load(self):\r\n        \"\"\"Load project data\"\"\"\r\n#        fname = self.__get_project_config_path()\r\n#        try:\r\n#            # Old format (Spyder 2.0-2.1 for Python 2)\r\n#            with open(fname, 'U') as fdesc:\r\n#                data = pickle.loads(fdesc.read())\r\n#        except (pickle.PickleError, TypeError, UnicodeDecodeError,\r\n#                AttributeError):\r\n#            try:\r\n#                # New format (Spyder >=2.2 for Python 2 and Python 3)\r\n#                with open(fname, 'rb') as fdesc:\r\n#                    data = pickle.loads(fdesc.read())\r\n#            except (IOError, OSError, pickle.PickleError):\r\n#                self.ioerror_flag = True\r\n#                return\r\n        # Compatibilty with old project explorer file format:\r\n#        if 'relative_pythonpath' not in data:\r\n#            print(\"Warning: converting old configuration file \"\r\n#                  \"for project '%s'\" % data['name'], file=STDERR)\r\n#            self.pythonpath = data['pythonpath']\r\n#            data['relative_pythonpath'] = self.relative_pythonpath\r\n#        for attr in self.CONFIG_ATTR:\r\n#            setattr(self, attr, data[attr])\r\n#        self.save()\r\n\r\n    def save(self):\r\n        \"\"\"Save project data\"\"\"\r\n#        data = {}\r\n#        for attr in self.PROJECT_ATTR:\r\n#            data[attr] = getattr(self, attr)\r\n#        try:\r\n#            with open(self.__get_project_config_path(), 'wb') as fdesc:\r\n#                pickle.dump(data, fdesc, 2)\r\n#        except (IOError, OSError):\r\n#            self.ioerror_flag = True\r\n\r\n#    def delete(self):\r\n#        \"\"\"Delete project\"\"\"\r\n#        os.remove(self.__get_project_config_path())\r\n#\r\n#    # --- Misc.\r\n#    def get_related_projects(self):\r\n#        \"\"\"Return related projects path list\"\"\"\r\n#        return self.related_projects\r\n#\r\n#    def set_related_projects(self, related_projects):\r\n#        \"\"\"Set related projects\"\"\"\r\n#        self.related_projects = related_projects\r\n#        self.save()\r\n#\r\n#    def open(self):\r\n#        \"\"\"Open project\"\"\"\r\n#        self.opened = True\r\n#        self.save()\r\n#\r\n#    def close(self):\r\n#        \"\"\"Close project\"\"\"\r\n#        self.opened = False\r\n#        self.save()\r\n#\r\n#    def is_opened(self):\r\n#        \"\"\"Return True if project is opened\"\"\"\r\n#        return self.opened\r\n#\r\n#    def is_file_in_project(self, fname):\r\n#        \"\"\"Return True if file *fname* is in one of the project subfolders\"\"\"\r\n#        fixed_root = fixpath(self.root_path)\r\n#        return fixpath(fname) == fixed_root or\\\r\n#            fixpath(osp.dirname(fname)).startswith(fixed_root)\r\n#\r\n#    def is_root_path(self, dirname):\r\n#        \"\"\"Return True if dirname is project's root path\"\"\"\r\n#        return fixpath(dirname) == fixpath(self.root_path)\r\n\r\n\r\nclass EmptyProject(BaseProject):\r\n    \"\"\"Empty Project\"\"\"\r\n    PROJECT_TYPE_NAME = _('Empty project')\r\n", "path": "spyder/plugins/projects/api.py"}]}