Repository: spyder-ide/spyder

Issue:
Recursing problem with new plugin API on master
<!--- **PLEASE READ:** When submitting here, please ensure you've completed the following checklist and checked the boxes to confirm. Issue reports without it may be closed. Thanks! --->

### Issue Report Checklist

* [ ] Searched the [issues page](https://github.com/spyder-ide/spyder/issues?q=is%3Aissue) for similar reports
* [ ] Read the relevant sections of the [Spyder Troubleshooting Guide](https://github.com/spyder-ide/spyder/wiki/Troubleshooting-Guide-and-FAQ) and followed its advice
* [ ] Reproduced the issue after updating with ``conda update spyder`` (or ``pip``, if not using Anaconda)
* [ ] Could not reproduce inside ``jupyter qtconsole`` (if console-related)
* [ ] Tried basic troubleshooting (if a bug/error)
    * [ ] Restarted Spyder
    * [ ] Reset preferences with ``spyder --reset``
    * [ ] Reinstalled the latest version of [Anaconda](https://www.anaconda.com/download/)
    * [ ] Tried the other applicable steps from the Troubleshooting Guide
* [X] Completed the **Problem Description**, **Steps to Reproduce** and **Version** sections below

## Problem Description
@goanpeca

open_project function project plugin doesn't behave as expected, as [spyder/plugins/projects/plugin.py#L373](https://github.com/spyder-ide/spyder/blob/f04e1d45d31988e6518fc49404f3fc5801eaa10c/spyder/plugins/projects/plugin.py#L373) is called recursively ...

## Solution/Work-around

initialization of project instance should not take "self" as argument, this has the consequence that the
project will call itself (it's open_project function) each time the Semi-ATE Plugin called it's open_project 
function, and then we stuck in a circle relationship in which Semi-ATE Plugin called open_project function
of the project plugin and vise versa !!!

![image](https://user-images.githubusercontent.com/3516972/89291548-07d52a80-d65b-11ea-9a48-12552e2bc3a8.png)

```python
        if project is None:
            project_type = self._load_project_type(path)
            project_types = self.get_project_types()
            if project_type in project_types:
                # project = project_types[project_type](path, self)  !!
                project_type_class = project_types[project_type]
                project = project_type_class(path, project_type_class._PARENT_PLUGIN
            else:
                project = EmptyProject(path, self)
```

### What steps reproduce the problem?

1. Open an existing Semi-ATE project or Create a Semi-ATE project then restart spyder
 
### What is the expected output? What do you see instead?

N/A

### Paste Traceback/Error Below (if applicable)
<!--- Copy from error dialog or View > Panes > Internal Console --->

```python-traceback

N/A

```

## Versions
<!--- You can get this information from Help > About Spyder...
or (if Spyder won't launch) the "conda list" command
from the Anaconda Prompt/Terminal/command line. --->

* Spyder version: master branch
* Python version: 3.7
* Qt version: N/A
* PyQt version:  N/A
* Operating System name/version: observed on Linux&Windows 

### Dependencies
<!--- Please go to the menu entry Help > Dependencies,
press the Copy to clipboard button and paste below --->

```
N/A
```



Discussion:

Resolving patch:
diff --git a/spyder/plugins/projects/plugin.py b/spyder/plugins/projects/plugin.py
--- a/spyder/plugins/projects/plugin.py
+++ b/spyder/plugins/projects/plugin.py
@@ -313,19 +313,21 @@ def create_new_project(self):
                 # multiple workspaces.
                 self.sig_project_closed.emit(active_project.root_path)
 
-            self._create_project(root_path, project_type=project_type)
+            self._create_project(root_path, project_type_id=project_type)
             self.sig_pythonpath_changed.emit()
             self.restart_consoles()
             dlg.close()
 
-    def _create_project(self, root_path, project_type=EmptyProject.ID,
+    def _create_project(self, root_path, project_type_id=EmptyProject.ID,
                         packages=None):
         """Create a new project."""
         project_types = self.get_project_types()
-        if project_type in project_types:
-            project_type_class = project_types[project_type]
+        if project_type_id in project_types:
+            project_type_class = project_types[project_type_id]
             project = project_type_class(
-                root_path, project_type_class._PARENT_PLUGIN)
+                root_path=root_path,
+                parent_plugin=project_type_class._PARENT_PLUGIN,
+            )
 
             created_succesfully, message = project.create_project()
             if not created_succesfully:
@@ -334,7 +336,7 @@ def _create_project(self, root_path, project_type=EmptyProject.ID,
                 return
 
             # TODO: In a subsequent PR return a value and emit based on that
-            self.sig_project_created.emit(root_path, project_type, packages)
+            self.sig_project_created.emit(root_path, project_type_id, packages)
             self.open_project(path=root_path, project=project)
         else:
             if not running_under_pytest():
@@ -367,12 +369,11 @@ def open_project(self, path=None, project=None, restart_consoles=True,
             path = encoding.to_unicode_from_fs(path)
 
         if project is None:
-            project_type = self._load_project_type(path)
-            project_types = self.get_project_types()
-            if project_type in project_types:
-                project = project_types[project_type](path, self)
-            else:
-                project = EmptyProject(path, self)
+            project_type_class = self._load_project_type_class(path)
+            project = project_type_class(
+                root_path=path,
+                parent_plugin=project_type_class._PARENT_PLUGIN,
+            )
 
         # A project was not open before
         if self.current_active_project is None:
@@ -754,25 +755,36 @@ def handle_workspace_edit(self, params):
 
     # --- New API:
     # ------------------------------------------------------------------------
-    def _load_project_type(self, path):
+    def _load_project_type_class(self, path):
         """
-        Load a project type from the config project folder directly.
+        Load a project type class from the config project folder directly.
 
         Notes
         -----
         This is done directly, since using the EmptyProject would rewrite the
-        value in the constructor.
+        value in the constructor. If the project found has not been registered
+        as a valid project type, the EmptyProject type will be returned.
+
+        Returns
+        -------
+        spyder.plugins.projects.api.BaseProjectType
+            Loaded project type class.
         """
         fpath = osp.join(
             path, get_project_config_folder(), 'config', WORKSPACE + ".ini")
 
-        project_type = EmptyProject.ID
+        project_type_id = EmptyProject.ID
         if osp.isfile(fpath):
             config = configparser.ConfigParser()
             config.read(fpath)
-            project_type = config[WORKSPACE].get("project_type", EmptyProject.ID)
+            project_type_id = config[WORKSPACE].get(
+                "project_type", EmptyProject.ID)
+
 
-        return project_type
+        EmptyProject._PARENT_PLUGIN = self
+        project_types = self.get_project_types()
+        project_type_class = project_types.get(project_type_id, EmptyProject)
+        return project_type_class
 
     def register_project_type(self, parent_plugin, project_type):
         """
@@ -804,7 +816,8 @@ def get_project_types(self):
         Returns
         -------
         dict
-            Project types dictionary.
+            Project types dictionary. Keys are project type IDs and values
+            are project type classes.
         """
         return self._project_types
 


Test patch: