Repository: spyder-ide/spyder

Issue:
sig_new_file error when converting notebook to script
## Description

**What steps will reproduce the problem?**

1. Right-Click on an .ipynb file > Click on "Convert To Python Script"

**What is the expected output? What do you see instead?**

I expected to see a .py file generated based on the notebook, but instead received the following error in the internal console:

```
Traceback (most recent call last):
  File "C:\Program Files\Anaconda2\lib\site-packages\spyder\widgets\explorer.py", line 568, in convert_notebooks
    self.convert_notebook(fname)
  File "C:\Program Files\Anaconda2\lib\site-packages\spyder\widgets\explorer.py", line 559, in convert_notebook
    self.parent_widget.sig_new_file.emit(script)
AttributeError: 'Projects' object has no attribute 'sig_new_file'
```

## Version and main components

* Spyder Version: 3.0.0 
* Python Version: 2.7.12
* Qt Versions: 5.6.0, PyQt5 5.6 on Windows

## Dependencies
```
pyflakes >=0.5.0 :  1.3.0 (OK)
pep8 >=0.6       :  1.7.0 (OK)
pygments >=2.0   :  2.1.3 (OK)
qtconsole >=4.2.0:  4.2.1 (OK)
nbconvert >=4.0  :  4.2.0 (OK)
pandas >=0.13.1  :  0.18.1 (OK)
numpy >=1.7      :  1.11.1 (OK)
sphinx >=0.6.6   :  1.4.6 (OK)
rope >=0.9.4     :  0.9.4 (OK)
jedi >=0.8.1     :  0.9.0 (OK)
matplotlib >=1.0 :  1.5.3 (OK)
sympy >=0.7.3    :  1.0 (OK)
pylint >=0.25    :  1.5.4 (OK)
```


Discussion:
@andfoy, this is a simple one for you :-)
@ccordoba12 I tried to reproduce the error, but the conversion worked successfully, I don't know if the error is related to Qt 5.x
@andfoy, did you do the conversion from the Project explorer instead of the File Explorer?

Resolving patch:
diff --git a/spyder/plugins/projects.py b/spyder/plugins/projects.py
--- a/spyder/plugins/projects.py
+++ b/spyder/plugins/projects.py
@@ -51,6 +51,7 @@ class Projects(ProjectExplorerWidget, SpyderPluginMixin):
     sig_project_created = Signal(object, object, object)
     sig_project_loaded = Signal(object)
     sig_project_closed = Signal(object)
+    sig_new_file = Signal(str)
 
     def __init__(self, parent=None):
         ProjectExplorerWidget.__init__(self, parent=parent,
@@ -136,6 +137,8 @@ def register_plugin(self):
 
         self.sig_open_file.connect(self.main.open_file)
 
+        self.sig_new_file.connect(lambda x: self.editor.new(text=x))
+
         # New project connections. Order matters!
         self.sig_project_loaded.connect(
             lambda v: self.workingdirectory.chdir(v))


Test patch:
diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py
--- a/spyder/app/tests/test_mainwindow.py
+++ b/spyder/app/tests/test_mainwindow.py
@@ -10,8 +10,10 @@
 
 import os
 import os.path as osp
+import tempfile
 
 from flaky import flaky
+import nbformat
 import numpy as np
 from numpy.testing import assert_array_equal
 import pytest
@@ -79,6 +81,38 @@ def close_widget():
 #==============================================================================
 # Tests
 #==============================================================================
+@flaky(max_runs=10)
+@pytest.mark.skipif(os.name == 'nt', reason="It times out sometimes on Windows")
+def test_open_notebooks_from_project_explorer(main_window, qtbot):
+    """Test that new breakpoints are set in the IPython console."""
+    projects = main_window.projects
+    editorstack = main_window.editor.get_current_editorstack()
+
+    # Create a temp project directory
+    project_dir = tempfile.mkdtemp()
+
+    # Create an empty notebook in the project dir
+    nb_contents = nbformat.v4.new_notebook()
+    nbformat.write(nb_contents, osp.join(project_dir, 'notebook.ipynb'))
+
+    # Create project
+    with qtbot.waitSignal(projects.sig_project_loaded):
+        projects._create_project(project_dir)
+
+    # Select notebook in the project explorer
+    idx = projects.treewidget.get_index('notebook.ipynb')
+    projects.treewidget.setCurrentIndex(idx)
+
+    # Prese Enter there
+    qtbot.keyClick(projects.treewidget, Qt.Key_Enter)
+
+    # Assert that notebook was open
+    assert 'notebook.ipynb' in editorstack.get_current_filename()
+
+    # Close project
+    projects.close_project()
+
+
 @flaky(max_runs=10)
 @pytest.mark.skipif(os.name == 'nt', reason="It times out sometimes on Windows")
 def test_set_new_breakpoints(main_window, qtbot):
diff --git a/spyder/utils/tests/test_dochelpers.py b/spyder/utils/tests/test_dochelpers.py
--- a/spyder/utils/tests/test_dochelpers.py
+++ b/spyder/utils/tests/test_dochelpers.py
@@ -9,6 +9,7 @@
 """
 # Standard library imports
 import os
+import sys
 
 # Test library imports
 import pytest
@@ -21,6 +22,8 @@ class Test(object):
     def method(self, x, y=2):
         pass
 
+@pytest.mark.skipif(not 'Continuum' in sys.version,
+                    reason="It fails when not run in Anaconda")
 def test_dochelpers():
     """Test dochelpers."""
     assert not getargtxt(Test.__init__)
@@ -51,7 +54,7 @@ def test_dochelpers():
                                                    'the\nreverse flag can be '
                                                    'set to request the result '
                                                    'in descending order.',
-                                                   'name': 'sorted'}
+                                      'name': 'sorted'}
         else:
             assert getdoc(sorted) == {'note': 'Function of builtins module',
                                       'argspec': '(...)',
@@ -66,7 +69,7 @@ def test_dochelpers():
                                                    'the\nreverse flag can be '
                                                    'set to request the result '
                                                    'in descending order.',
-                                                   'name': 'sorted'}          
+                                      'name': 'sorted'}
         assert not getargtxt(sorted)
     assert isdefined('numpy.take', force_import=True)
     assert isdefined('__import__')