diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 0b76020eacc1da2..acccc9da416827f 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -355,11 +355,11 @@ ABC hierarchy:: See :pep:`302` for the exact definition for a loader. Loaders that wish to support resource reading should implement a - :meth:`get_resource_reader` method as specified by + :meth:`!get_resource_reader` method as specified by :class:`importlib.resources.abc.ResourceReader`. .. versionchanged:: 3.7 - Introduced the optional :meth:`get_resource_reader` method. + Introduced the optional :meth:`!get_resource_reader` method. .. versionchanged:: 3.15 Removed the ``load_module()`` method. @@ -717,7 +717,8 @@ find and load modules. .. versionchanged:: 3.5 As part of :pep:`489`, the builtin importer now implements - :meth:`Loader.create_module` and :meth:`Loader.exec_module` + :meth:`Loader.create_module ` + and :meth:`Loader.exec_module ` .. class:: FrozenImporter @@ -730,7 +731,8 @@ find and load modules. instantiation. .. versionchanged:: 3.4 - Gained :meth:`~Loader.create_module` and :meth:`~Loader.exec_module` + Gained :meth:`~importlib.abc.Loader.create_module` and + :meth:`~importlib.abc.Loader.exec_module` methods. @@ -1180,8 +1182,8 @@ an :term:`importer`. with the source *path*. For example, if *path* is ``/foo/bar/baz.py`` the return value would be ``/foo/bar/__pycache__/baz.cpython-32.pyc`` for Python 3.2. The ``cpython-32`` string comes from the current magic tag (see - :func:`get_tag`; if :attr:`sys.implementation.cache_tag` is not defined then - :exc:`NotImplementedError` will be raised). + :attr:`sys.implementation.cache_tag `; if it is not + defined then :exc:`NotImplementedError` will be raised). The *optimization* parameter is used to specify the optimization level of the bytecode file. An empty string represents no optimization, so @@ -1213,7 +1215,7 @@ an :term:`importer`. ``/foo/bar/__pycache__/baz.cpython-32.pyc`` the returned path would be ``/foo/bar/baz.py``. *path* need not exist, however if it does not conform to :pep:`3147` or :pep:`488` format, a :exc:`ValueError` is raised. If - :attr:`sys.implementation.cache_tag` is not defined, + :attr:`sys.implementation.cache_tag ` is not defined, :exc:`NotImplementedError` is raised. .. versionadded:: 3.4 @@ -1263,7 +1265,8 @@ an :term:`importer`. If **name** is for a submodule (contains a dot), the parent module is automatically imported. - **name** and **package** work the same as for :func:`import_module`. + **name** and **package** work the same as for + :func:`importlib.import_module`. .. versionadded:: 3.4 @@ -1293,7 +1296,8 @@ an :term:`importer`. A factory function for creating a :class:`~importlib.machinery.ModuleSpec` instance based on a loader. The parameters have the same meaning as they do for ModuleSpec. The function uses available :term:`loader` APIs, such as - :meth:`InspectLoader.is_package`, to fill in any missing + :meth:`InspectLoader.is_package + `, to fill in any missing information on the spec. .. versionadded:: 3.4 diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 3ce7c2f22e12b0f..38f082ca37e48d4 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -6,7 +6,6 @@ Doc/library/ast.rst Doc/library/asyncio-extending.rst Doc/library/email.charset.rst Doc/library/email.parser.rst -Doc/library/importlib.rst Doc/library/logging.config.rst Doc/library/logging.handlers.rst Doc/library/mmap.rst diff --git a/Lib/copy.py b/Lib/copy.py index 6149301ad1389e2..f7fd680657b85a5 100644 --- a/Lib/copy.py +++ b/Lib/copy.py @@ -122,9 +122,8 @@ def deepcopy(x, memo=None): if memo is None: memo = {} else: - y = memo.get(d, None) - if y is not None: - return y + if d in memo: + return memo[d] copier = _deepcopy_dispatch.get(cls) if copier is not None: diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index 13b7ce29565deed..2a12a6737df1631 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -951,6 +951,17 @@ def m(self): self.assertIs(g.b.__self__, g) g.b() + def test_deepcopy_memo_none_result(self): + # Objects whose deepcopy result is None must still be memoized. + class C: + call_count = 0 + def __deepcopy__(self, memo): + C.call_count += 1 + return None + obj = C() + copy.deepcopy([obj, obj, obj]) + self.assertEqual(C.call_count, 1) + class TestReplace(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2026-07-24-00-02-00.gh-issue-154594.Xk7mQ2.rst b/Misc/NEWS.d/next/Library/2026-07-24-00-02-00.gh-issue-154594.Xk7mQ2.rst new file mode 100644 index 000000000000000..5f96bba97aa19f4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-24-00-02-00.gh-issue-154594.Xk7mQ2.rst @@ -0,0 +1,2 @@ +Fix :func:`copy.deepcopy` so that an object whose deep copy is ``None`` is +still memoized. Patch by tonghuaroot. diff --git a/Modules/_ctypes/malloc_closure.c b/Modules/_ctypes/malloc_closure.c index 62c7aa5d6affbfb..3abab8873acbd01 100644 --- a/Modules/_ctypes/malloc_closure.c +++ b/Modules/_ctypes/malloc_closure.c @@ -68,10 +68,25 @@ static void more_core(void) /* allocate a memory block */ #ifdef MS_WIN32 +#ifdef MS_WINDOWS_DESKTOP item = (ITEM *)VirtualAlloc(NULL, count * sizeof(ITEM), MEM_COMMIT, PAGE_EXECUTE_READWRITE); +#else // UWP + /* Due security restrictions, UWP not allows request Read-Write-Execute permissions at once. + The correct flow in UWP for execute dynamic code in memmory is: + 1. Alloate as Read-Write (PAGE_READWRITE) and write dynamic code to memmory. + 2. Change to Executable (PAGE_EXECUTE_READ) with 'VirtualProtectFromApp'. + 3. Flush cache with 'FlushInstructionCache' to ensure CPU instruction cache coherency + before executing the generated code. + TODO: Implement 2 and 3 in the appropriate places. For now, this defers + the error from import time to time of use (or never, if an app avoids it). */ + item = (ITEM*)VirtualAllocFromApp(NULL, + count * sizeof(ITEM), + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); +#endif // !MS_WINDOWS_DESKTOP if (item == NULL) return; #else