Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some Python 3.12 test errors #21688

Open
5 of 10 tasks
juliangilbey opened this issue Jan 9, 2024 · 3 comments
Open
5 of 10 tasks

Some Python 3.12 test errors #21688

juliangilbey opened this issue Jan 9, 2024 · 3 comments

Comments

@juliangilbey
Copy link
Contributor

juliangilbey commented Jan 9, 2024

Issue Report Checklist

  • Searched the issues page for similar reports
  • Read the relevant sections of the Spyder Troubleshooting Guide 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
    • Tried the other applicable steps from the Troubleshooting Guide
  • Completed the Problem Description, Steps to Reproduce and Version sections below

Problem Description

There are some test suite errors with Python 3.12 (and possibly more recent versions of some related modules) with Spyder 5.5.0.

spyder/plugins/editor/widgets/tests/test_warnings.py::test_update_warnings_after_closequotes (now moved to spyder/plugins/editor/widgets/codeeditor/tests/test_warnings.py in the master branch)

This fails with the error:

>       assert editor.get_current_warnings() == expected
E       AssertionError: assert [['unterminated string literal (detected at line 1)', 1], ['E901 TokenError: unterminated string literal (detected at line 1)', 1]] == [['unterminated string literal (detected at line 1)', 1]]
E         Left contains one more item: ['E901 TokenError: unterminated string literal (detected at line 1)', 1]

With Python 3.12, pycodestyle returns the message 1:8: E901 TokenError: unterminated string literal (detected at line 1) whereas with Python 3.11, it gives no output. I could patch this test by allowing the second result string, but it may be that it is necessary to change the editor warning code to avoid having a duplicated warning. (I haven't checked this yet - I'm still running Spyder under Python 3.11, but I could if you'd like me to.)

spyder/plugins/editor/widgets/tests/test_warnings.py::test_update_warnings_after_closebrackets

This fails with the error:

>       assert editor.get_current_warnings() == expected
E       assert [["'(' was never closed", 1], ['E901 TokenError: unexpected EOF in multi-line statement', 1]] == [["'(' was never closed", 1], ['E901 TokenError: EOF in multi-line statement', 2]]
E         At index 1 diff: ['E901 TokenError: unexpected EOF in multi-line statement', 1] != ['E901 TokenError: EOF in multi-line statement', 2]

Running pycodestyle with the two Python versions shows the cause:
Python 3.11: /tmp/pycodestyle_test2.py:2:1: E901 TokenError: EOF in multi-line statement
Python 3.12: /tmp/pycodestyle_test2.py:1:1: E901 TokenError: unexpected EOF in multi-line statement

spyder/plugins/editor/tests/test_plugin.py::test_renamed_tree and spyder/plugins/editor/tests/test_plugin.py::test_report_issue_url

The first fails with the error

E               AttributeError: 'called_with' is not a valid assertion. Use a spec for the mock if 'called_with' is meant to be an attribute.

and the second with a similar error; this is because called_with should be assert_called_with; there are five occurrences of this in the codebase:

spyder/widgets/tests/test_reporterror.py:106:    mockQDesktopServices_instance.openUrl.called_with(target_url)
spyder/widgets/tests/test_reporterror.py:113:    mockQDesktopServices_instance.openUrl.called_with(target_url)
spyder/plugins/editor/tests/test_plugin.py:146:    assert editor.renamed.called_with(source='/test/directory/file1.py',
spyder/plugins/editor/tests/test_plugin.py:148:    assert editor.renamed.called_with(source='/test/directory/file2.txt',
spyder/plugins/editor/tests/test_plugin.py:150:    assert editor.renamed.called_with(source='/test/directory/file4.rst',

Perhaps pytest-mock has got a bit stricter recently or this is a Python 3.12 change in unittest.mock? Anyway, this is easy to fix.

spyder/plugins/variableexplorer/widgets/objectexplorer/tests/test_objectexplorer.py::test_objectexplorer_collection_types[params4]

This seems like a minor update is needed for Python 3.12:

        # Root row with children
        # Since rowCount for python 3 and 2 varies on differents systems,
        # we use a range of values
        expected_output_range = list(range(min(row_count), max(row_count) + 1))
>       assert model.rowCount(model.index(0, 0)) in expected_output_range
E       assert 167 in [162, 163, 164, 165, 166]

Versions

  • Spyder version: 5.5.0
  • Python version: Python 3.12
  • Qt version: n/a
  • PyQt version: n/a
  • Operating System name/version: Debian unstable
@juliangilbey
Copy link
Contributor Author

Oh dear, I tried patching the assert_called_with errors and now two of the tests fail:

______________________________ test_renamed_tree _______________________________

self = <MagicMock name='renamed' id='140652547455312'>, args = ()
kwargs = {'dest': 'test/dir/file1.py', 'source': '/test/directory/file1.py'}
expected = call(source='/test/directory/file1.py', dest='test/dir/file1.py')
actual = call(source='/test/directory/file4.rst', dest='/test/dir/file4.rst')
_error_message = <function NonCallableMock.assert_called_with.<locals>._error_message at 0x7fec39378180>
cause = None

    def assert_called_with(self, /, *args, **kwargs):
        """assert that the last call was made with the specified arguments.
    
        Raises an AssertionError if the args and keyword args passed in are
        different to the last call to the mock."""
        if self.call_args is None:
            expected = self._format_mock_call_signature(args, kwargs)
            actual = 'not called.'
            error_message = ('expected call not found.\nExpected: %s\n  Actual: %s'
                    % (expected, actual))
            raise AssertionError(error_message)
    
        def _error_message():
            msg = self._format_mock_failure_message(args, kwargs)
            return msg
        expected = self._call_matcher(_Call((args, kwargs), two=True))
        actual = self._call_matcher(self.call_args)
        if actual != expected:
            cause = expected if isinstance(expected, Exception) else None
>           raise AssertionError(_error_message()) from cause
E           AssertionError: expected call not found.
E           Expected: renamed(source='/test/directory/file1.py', dest='test/dir/file1.py')
E             Actual: renamed(source='/test/directory/file4.rst', dest='/test/dir/file4.rst')

/usr/lib/python3.11/unittest/mock.py:939: AssertionError

During handling of the above exception, another exception occurred:

editor_plugin = <spyder.plugins.editor.plugin.Editor object at 0x7fec39576170>
mocker = <pytest_mock.plugin.MockerFixture object at 0x7fece8686cd0>

    def test_renamed_tree(editor_plugin, mocker):
        """Test editor.renamed_tree().
    
        This tests that the file renaming functions are called correctly,
        but does not test that all the renaming happens in File Explorer,
        Project Explorer, and Editor widget as those aren't part of the plugin.
        """
        editor = editor_plugin
        mocker.patch.object(editor, 'get_filenames')
        mocker.patch.object(editor, 'renamed')
        editor.get_filenames.return_value = ['/test/directory/file1.py',
                                             '/test/directory/file2.txt',
                                             '/home/spyder/testing/file3.py',
                                             '/test/directory/file4.rst']
    
        editor.renamed_tree('/test/directory', '/test/dir')
        assert editor.renamed.call_count == 3
>       assert editor.renamed.assert_called_with(source='/test/directory/file1.py',
                                                 dest='test/dir/file1.py')
E       AssertionError: expected call not found.
E       Expected: renamed(source='/test/directory/file1.py', dest='test/dir/file1.py')
E         Actual: renamed(source='/test/directory/file4.rst', dest='/test/dir/file4.rst')
E       
E       pytest introspection follows:
E       
E       Kwargs:

spyder/plugins/editor/tests/test_plugin.py:146: AssertionError

The first small thing to observe is that the assert_called_with() method will raise an AssertionError if there is a failure, so there is no need to say assert mocked.assert_called_with(); it is sufficient to say mocker.assert_called_with(). But the failure is that assert_called_with only checks the last call made to the method, which in this case is file4.rst. So the first two assertions must fail.

____________________________ test_report_issue_url _____________________________

self = <MagicMock name='mock().openUrl' id='140652455162384'>
args = (PyQt5.QtCore.QUrl('https://github.com/spyder-ide/spyder/issues/new?body=This is an example error report body text.'),)
kwargs = {}
expected = "openUrl(PyQt5.QtCore.QUrl('https://github.com/spyder-ide/spyder/issues/new?body=This is an example error report body text.'))"
actual = 'not called.'
error_message = "expected call not found.\nExpected: openUrl(PyQt5.QtCore.QUrl('https://github.com/spyder-ide/spyder/issues/new?body=This is an example error report body text.'))\n  Actual: not called."

    def assert_called_with(self, /, *args, **kwargs):
        """assert that the last call was made with the specified arguments.
    
        Raises an AssertionError if the args and keyword args passed in are
        different to the last call to the mock."""
        if self.call_args is None:
            expected = self._format_mock_call_signature(args, kwargs)
            actual = 'not called.'
            error_message = ('expected call not found.\nExpected: %s\n  Actual: %s'
                    % (expected, actual))
>           raise AssertionError(error_message)
E           AssertionError: expected call not found.
E           Expected: openUrl(PyQt5.QtCore.QUrl('https://github.com/spyder-ide/spyder/issues/new?body=This is an example error report body text.'))
E             Actual: not called.

/usr/lib/python3.11/unittest/mock.py:930: AssertionError

During handling of the above exception, another exception occurred:

monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fec33a04650>

    def test_report_issue_url(monkeypatch):
        """Test that report_issue sends the data, and to correct url."""
        body = 'This is an example error report body text.'
        title = 'Uncreative issue title here'
        target_url_base = __project_url__ + '/issues/new'
    
        MockQDesktopServices = MagicMock()
        mockQDesktopServices_instance = MockQDesktopServices()
        attr_to_patch = ('spyder.widgets.reporterror.QDesktopServices')
        monkeypatch.setattr(attr_to_patch, MockQDesktopServices)
    
        # Test when body != None, i.e. when auto-submitting error to Github
        target_url = QUrl(target_url_base + '?body=' + body)
        SpyderErrorDialog.open_web_report(body=body, title=None)
        assert MockQDesktopServices.openUrl.call_count == 1
>       mockQDesktopServices_instance.openUrl.assert_called_with(target_url)
E       AssertionError: expected call not found.
E       Expected: openUrl(PyQt5.QtCore.QUrl('https://github.com/spyder-ide/spyder/issues/new?body=This is an example error report body text.'))
E         Actual: not called.

spyder/widgets/tests/test_reporterror.py:106: AssertionError

It turns out that this test has multiple errors in it, which appear once called_with is replaced by assert_called_with. I've fixed it in a local patch which I'll submit shortly.

@juliangilbey
Copy link
Contributor Author

A final test that fails, which I missed earlier is spyder/app/tests/test_mainwindow.py::test_move_to_first_breakpoint:

_____________________ test_move_to_first_breakpoint[False] _____________________

main_window = <spyder.app.mainwindow.MainWindow object at 0x7f371079f920>
qtbot = <pytestqt.qtbot.QtBot object at 0x7f36d012f5f0>, debugcell = False

    @flaky(max_runs=3)
    @pytest.mark.skipif(not sys.platform.startswith('linux'),
                        reason="Fails sometimes on Windows and Mac")
    @pytest.mark.parametrize("debugcell", [True, False])
    def test_move_to_first_breakpoint(main_window, qtbot, debugcell):
        """Test that we move to the first breakpoint if there's one present."""
        # Wait until the window is fully up
        shell = main_window.ipyconsole.get_current_shellwidget()
        qtbot.waitUntil(
            lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT)
    
        # Main variables
        control = shell._control
        debug_action = main_window.debug_toolbar_actions[0]
        debug_button = main_window.debug_toolbar.widgetForAction(debug_action)
    
        # Clear all breakpoints
        main_window.editor.clear_all_breakpoints()
    
        # Load test file
        test_file = osp.join(LOCATION, 'script.py')
        main_window.editor.load(test_file)
        code_editor = main_window.editor.get_focus_widget()
    
        # Set breakpoint
        code_editor.debugger.toogle_breakpoint(line_number=10)
        qtbot.wait(500)
        cursor = code_editor.textCursor()
        cursor.setPosition(0)
        code_editor.setTextCursor(cursor)
    
        if debugcell:
            # Advance 2 cells
            for i in range(2):
                qtbot.keyClick(code_editor, Qt.Key_Return,
                               modifier=Qt.ShiftModifier)
                qtbot.wait(500)
    
            # Debug the cell
            with qtbot.waitSignal(shell.executed):
                qtbot.keyClick(code_editor, Qt.Key_Return,
                               modifier=Qt.AltModifier | Qt.ShiftModifier)
    
            # Make sure everything is ready
            assert shell.spyder_kernel_comm.is_open()
            assert shell.is_waiting_pdb_input()
    
            with qtbot.waitSignal(shell.executed):
                shell.pdb_execute('!b')
            assert 'script.py:10' in shell._control.toPlainText()
            # We need to press continue as we don't test yet if a breakpoint
            # is in the cell
            with qtbot.waitSignal(shell.executed):
                shell.pdb_execute('!c')
    
        else:
            # Click the debug button
            with qtbot.waitSignal(shell.executed):
                qtbot.mouseClick(debug_button, Qt.LeftButton)
    
        # Verify that we are at first breakpoint
        shell.clear_console()
        qtbot.wait(500)
        with qtbot.waitSignal(shell.executed):
            shell.pdb_execute("!list")
>       assert "1--> 10 arr = np.array(li)" in control.toPlainText()
E       AssertionError: assert '1--> 10 arr = np.array(li)' in '\nIPdb [3]: !list\n'
E        +  where '\nIPdb [3]: !list\n' = <built-in method toPlainText of ControlWidget object at 0x7f367072ecc0>()
E        +    where <built-in method toPlainText of ControlWidget object at 0x7f367072ecc0> = <spyder.plugins.ipythonconsole.widgets.control.ControlWidget object at 0x7f367072ecc0>.toPlainText

/tmp/autopkgtest-lxc.eoqy9y66/downtmp/build.tT1/src/spyder/app/tests/test_mainwindow.py:624: AssertionError
----------------------------- Captured Qt messages -----------------------------
QtWarningMsg: Attribute Qt::AA_UseSoftwareOpenGL must be set before QCoreApplication is created.
QtWarningMsg: Scenegraph already initialized, setBackend() request ignored
---------------------------- Captured stdout setup -----------------------------
Attribute Qt::AA_UseSoftwareOpenGL must be set before QCoreApplication is created.
--------------------------- Captured stdout teardown ---------------------------

IPdb [3]: !list
      5 # In[23]
      6 li = [1, 2, 3]
      7 
      8 # <codecell> Third Code Cell\'s name
      9 import numpy as np
1--> 10 arr = np.array(li)
     11 
     12 #%% Fourth Code/ Cell's % name\
     13 s = """Z:\\escape\\test\\string\n"""
     14 a


IPdb [4]: 
--------------------------- Captured stdout teardown ---------------------------

IPdb [3]: !list
      5 # In[23]
      6 li = [1, 2, 3]
      7 
      8 # <codecell> Third Code Cell\'s name
      9 import numpy as np
1--> 10 arr = np.array(li)
     11 
     12 #%% Fourth Code/ Cell's % name\
     13 s = """Z:\\escape\\test\\string\n"""
     14 a


IPdb [4]: 
--------------------------- Captured stdout teardown ---------------------------

IPdb [3]: !list
      5 # In[23]
      6 li = [1, 2, 3]
      7 
      8 # <codecell> Third Code Cell\'s name
      9 import numpy as np
1--> 10 arr = np.array(li)
     11 
     12 #%% Fourth Code/ Cell's % name\
     13 s = """Z:\\escape\\test\\string\n"""
     14 a


IPdb [4]: 
--------------------------- Captured stdout teardown ---------------------------

IPdb [3]: !list
      5 # In[23]
      6 li = [1, 2, 3]
      7 
      8 # <codecell> Third Code Cell\'s name
      9 import numpy as np
1--> 10 arr = np.array(li)
     11 
     12 #%% Fourth Code/ Cell's % name\
     13 s = """Z:\\escape\\test\\string\n"""
     14 a


IPdb [4]: 
--------------------------- Captured stdout teardown ---------------------------

IPdb [3]: !list
      5 # In[23]
      6 li = [1, 2, 3]
      7 
      8 # <codecell> Third Code Cell\'s name
      9 import numpy as np
1--> 10 arr = np.array(li)
     11 
     12 #%% Fourth Code/ Cell's % name\
     13 s = """Z:\\escape\\test\\string\n"""
     14 a


IPdb [4]: 
--------------------------- Captured stdout teardown ---------------------------

IPdb [3]: !list
      5 # In[23]
      6 li = [1, 2, 3]
      7 
      8 # <codecell> Third Code Cell\'s name
      9 import numpy as np
1--> 10 arr = np.array(li)
     11 
     12 #%% Fourth Code/ Cell's % name\
     13 s = """Z:\\escape\\test\\string\n"""
     14 a


IPdb [4]: 
--------------------------- Captured stdout teardown ---------------------------

IPdb [3]: !list
      5 # In[23]
      6 li = [1, 2, 3]
      7 
      8 # <codecell> Third Code Cell\'s name
      9 import numpy as np
1--> 10 arr = np.array(li)
     11 
     12 #%% Fourth Code/ Cell's % name\
     13 s = """Z:\\escape\\test\\string\n"""
     14 a


IPdb [4]: 
--------------------------- Captured stdout teardown ---------------------------

IPdb [3]: !list
      5 # In[23]
      6 li = [1, 2, 3]
      7 
      8 # <codecell> Third Code Cell\'s name
      9 import numpy as np
1--> 10 arr = np.array(li)
     11 
     12 #%% Fourth Code/ Cell's % name\
     13 s = """Z:\\escape\\test\\string\n"""
     14 a


IPdb [4]: 

It works fine with Python 3.11, but with Python 3.12, although the line wanted is present, for some reason, control.toPlainText() is only returning the first line of text rather than the whole test. This is with PyQt5 5.15.10, and I have no idea how to fix this.

juliangilbey pushed a commit to juliangilbey/spyder that referenced this issue Jan 10, 2024
juliangilbey pushed a commit to juliangilbey/spyder that referenced this issue Jan 10, 2024
juliangilbey pushed a commit to juliangilbey/spyder that referenced this issue Jan 10, 2024
@juliangilbey
Copy link
Contributor Author

So with the pull requests accepted, the only remaining newly failing test is, I think, spyder/app/tests/test_mainwindow.py::test_move_to_first_breakpoint. And I haven't got a clue about that one or how significant it is yet (I'm still primarily running under Python 3.11).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants