Merge tag 'v3.9.0a3'
Python 3.9.0a3
diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst
index 8394304..515bdd0 100644
--- a/Doc/library/unittest.mock.rst
+++ b/Doc/library/unittest.mock.rst
@@ -1401,7 +1401,8 @@
"as"; very useful if :func:`patch` is creating a mock object for you.
:func:`patch` takes arbitrary keyword arguments. These will be passed to
- the :class:`Mock` (or *new_callable*) on construction.
+ :class:`AsyncMock` if the patched object is asynchronous, to
+ :class:`MagicMock` otherwise or to *new_callable* if specified.
``patch.dict(...)``, ``patch.multiple(...)`` and ``patch.object(...)`` are
available for alternate use-cases.
diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt
index 708292e..eda7c27 100644
--- a/Lib/idlelib/NEWS.txt
+++ b/Lib/idlelib/NEWS.txt
@@ -3,7 +3,9 @@
======================================
-bpo-39050: Make Settings dialog Help button work again.
+bpo-39388: Settings dialog Cancel button cancels pending changes.
+
+bpo-39050: Settings dialog Help button again displays help text.
bpo-32989: Add tests for editor newline_and_indent_event method.
Remove unneeded arguments and dead code from pyparse
diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py
index 0e007b5..2f95c9c 100644
--- a/Lib/idlelib/configdialog.py
+++ b/Lib/idlelib/configdialog.py
@@ -191,6 +191,7 @@
Methods:
destroy: inherited
"""
+ changes.clear()
self.destroy()
def destroy(self):
diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py
index 7c575d0..817a3521 100644
--- a/Lib/idlelib/idle_test/test_configdialog.py
+++ b/Lib/idlelib/idle_test/test_configdialog.py
@@ -47,17 +47,24 @@
root.destroy()
root = dialog = None
-class ConfigDialogTest(unittest.TestCase):
- def test_help(self):
+class DialogTest(unittest.TestCase):
+
+ @mock.patch(__name__+'.dialog.destroy', new_callable=Func)
+ def test_cancel(self, destroy):
+ changes['main']['something'] = 1
+ dialog.cancel()
+ self.assertEqual(changes['main'], {})
+ self.assertEqual(destroy.called, 1)
+
+ @mock.patch('idlelib.configdialog.view_text', new_callable=Func)
+ def test_help(self, view):
dialog.note.select(dialog.keyspage)
- saved = configdialog.view_text
- view = configdialog.view_text = Func()
dialog.help()
s = view.kwds['contents']
- self.assertTrue(s.startswith('When you click'))
- self.assertTrue(s.endswith('a different name.\n'))
- configdialog.view_text = saved
+ self.assertTrue(s.startswith('When you click') and
+ s.endswith('a different name.\n'))
+
class FontPageTest(unittest.TestCase):
"""Test that font widgets enable users to make font changes.
diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index 92b596f..a97542a 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -46,6 +46,8 @@
def _is_async_obj(obj):
if _is_instance_mock(obj) and not isinstance(obj, AsyncMock):
return False
+ if hasattr(obj, '__func__'):
+ obj = getattr(obj, '__func__')
return asyncio.iscoroutinefunction(obj) or inspect.isawaitable(obj)
@@ -1728,7 +1730,8 @@
"as"; very useful if `patch` is creating a mock object for you.
`patch` takes arbitrary keyword arguments. These will be passed to
- the `Mock` (or `new_callable`) on construction.
+ `AsyncMock` if the patched object is asynchronous, to `MagicMock`
+ otherwise or to `new_callable` if specified.
`patch.dict(...)`, `patch.multiple(...)` and `patch.object(...)` are
available for alternate use-cases.
diff --git a/Lib/unittest/test/testmock/testasync.py b/Lib/unittest/test/testmock/testasync.py
index 73d31a2..43b8749 100644
--- a/Lib/unittest/test/testmock/testasync.py
+++ b/Lib/unittest/test/testmock/testasync.py
@@ -19,6 +19,15 @@
def normal_method(self):
pass
+ @classmethod
+ async def async_class_method(cls):
+ pass
+
+ @staticmethod
+ async def async_static_method():
+ pass
+
+
class AwaitableClass:
def __await__(self):
yield
@@ -71,6 +80,20 @@
test_async()
+ def test_is_AsyncMock_patch_staticmethod(self):
+ @patch.object(AsyncClass, 'async_static_method')
+ def test_async(mock_method):
+ self.assertIsInstance(mock_method, AsyncMock)
+
+ test_async()
+
+ def test_is_AsyncMock_patch_classmethod(self):
+ @patch.object(AsyncClass, 'async_class_method')
+ def test_async(mock_method):
+ self.assertIsInstance(mock_method, AsyncMock)
+
+ test_async()
+
def test_async_def_patch(self):
@patch(f"{__name__}.async_func", return_value=1)
@patch(f"{__name__}.async_func_args", return_value=2)
diff --git a/Misc/NEWS.d/next/IDLE/2020-01-25-02-26-45.bpo-39388.x4TQNh.rst b/Misc/NEWS.d/next/IDLE/2020-01-25-02-26-45.bpo-39388.x4TQNh.rst
new file mode 100644
index 0000000..42bbfb1
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2020-01-25-02-26-45.bpo-39388.x4TQNh.rst
@@ -0,0 +1 @@
+IDLE Settings Cancel button now cancels pending changes
diff --git a/Misc/NEWS.d/next/Library/2020-01-24-13-24-35.bpo-39082.qKgrq_.rst b/Misc/NEWS.d/next/Library/2020-01-24-13-24-35.bpo-39082.qKgrq_.rst
new file mode 100644
index 0000000..52c4ee1
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-01-24-13-24-35.bpo-39082.qKgrq_.rst
@@ -0,0 +1 @@
+Allow AsyncMock to correctly patch static/class methods