]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-65961: Stop setting `__cached__` on modules (GH-142165)
authorBrett Cannon <brett@python.org>
Thu, 11 Dec 2025 19:44:46 +0000 (11:44 -0800)
committerGitHub <noreply@github.com>
Thu, 11 Dec 2025 19:44:46 +0000 (11:44 -0800)
33 files changed:
Doc/c-api/import.rst
Doc/deprecations/pending-removal-in-3.15.rst
Doc/howto/gdb_helpers.rst
Doc/library/functions.rst
Doc/library/importlib.rst
Doc/library/runpy.rst
Doc/reference/datamodel.rst
Doc/whatsnew/3.12.rst
Doc/whatsnew/3.2.rst
Lib/importlib/_bootstrap.py
Lib/importlib/_bootstrap_external.py
Lib/inspect.py
Lib/profile.py
Lib/profiling/tracing/__init__.py
Lib/pydoc.py
Lib/runpy.py
Lib/site.py
Lib/test/test_cmd_line_script.py
Lib/test/test_import/__init__.py
Lib/test/test_import/data/unwritable/__init__.py
Lib/test/test_importlib/import_/test_helpers.py
Lib/test/test_importlib/test_abc.py
Lib/test/test_importlib/test_api.py
Lib/test/test_importlib/test_spec.py
Lib/test/test_importlib/test_util.py
Lib/test/test_inspect/test_inspect.py
Lib/test/test_pkg.py
Lib/test/test_pyrepl/test_pyrepl.py
Lib/test/test_runpy.py
Lib/test/test_site.py
Lib/trace.py
Misc/NEWS.d/next/Core_and_Builtins/2025-12-01-15-22-54.gh-issue-65961.hCJvRB.rst [new file with mode: 0644]
Python/pythonrun.c

index 1786ac6b5038953267476f6dec47c56b3988bc1f..a28c0713dd3b2fe8ed2b05595776af2f1b49fa0c 100644 (file)
@@ -129,8 +129,7 @@ Importing Modules
    of :class:`~importlib.machinery.SourceFileLoader` otherwise.
 
    The module's :attr:`~module.__file__` attribute will be set to the code
-   object's :attr:`~codeobject.co_filename`.  If applicable,
-   :attr:`~module.__cached__` will also be set.
+   object's :attr:`~codeobject.co_filename`.
 
    This function will reload the module if it was already imported.  See
    :c:func:`PyImport_ReloadModule` for the intended way to reload a module.
@@ -142,10 +141,13 @@ Importing Modules
    :c:func:`PyImport_ExecCodeModuleWithPathnames`.
 
    .. versionchanged:: 3.12
-      The setting of :attr:`~module.__cached__` and :attr:`~module.__loader__`
+      The setting of ``__cached__`` and :attr:`~module.__loader__`
       is deprecated. See :class:`~importlib.machinery.ModuleSpec` for
       alternatives.
 
+   .. versionchanged:: 3.15
+      ``__cached__`` is no longer set.
+
 
 .. c:function:: PyObject* PyImport_ExecCodeModuleEx(const char *name, PyObject *co, const char *pathname)
 
@@ -157,16 +159,19 @@ Importing Modules
 
 .. c:function:: PyObject* PyImport_ExecCodeModuleObject(PyObject *name, PyObject *co, PyObject *pathname, PyObject *cpathname)
 
-   Like :c:func:`PyImport_ExecCodeModuleEx`, but the :attr:`~module.__cached__`
-   attribute of the module object is set to *cpathname* if it is
-   non-``NULL``.  Of the three functions, this is the preferred one to use.
+   Like :c:func:`PyImport_ExecCodeModuleEx`, but the path to any compiled file
+   via *cpathname* is used appropriately when non-``NULL``.  Of the three
+   functions, this is the preferred one to use.
 
    .. versionadded:: 3.3
 
    .. versionchanged:: 3.12
-      Setting :attr:`~module.__cached__` is deprecated. See
+      Setting ``__cached__`` is deprecated. See
       :class:`~importlib.machinery.ModuleSpec` for alternatives.
 
+   .. versionchanged:: 3.15
+      ``__cached__`` no longer set.
+
 
 .. c:function:: PyObject* PyImport_ExecCodeModuleWithPathnames(const char *name, PyObject *co, const char *pathname, const char *cpathname)
 
index 09cbd6f01a0580f28a8235019824c1a1e5791fb1..3b9cf892fe913d8a9f48e27b8d8d996d23df996f 100644 (file)
@@ -3,9 +3,9 @@ Pending removal in Python 3.15
 
 * The import system:
 
-  * Setting :attr:`~module.__cached__` on a module while
+  * Setting ``__cached__`` on a module while
     failing to set :attr:`__spec__.cached <importlib.machinery.ModuleSpec.cached>`
-    is deprecated. In Python 3.15, :attr:`!__cached__` will cease to be set or
+    is deprecated. In Python 3.15, ``__cached__`` will cease to be set or
     take into consideration by the import system or standard library. (:gh:`97879`)
 
   * Setting :attr:`~module.__package__` on a module while
index 98ce813ca4ab024fb83319ceaed628c74ab8c568..33d1fbf8cd9e9eaef8beca37307d89d73666988d 100644 (file)
@@ -136,7 +136,7 @@ enabled::
        at Objects/unicodeobject.c:551
    #7  0x0000000000440d94 in PyUnicodeUCS2_FromString (u=0x5c2b8d "__lltrace__") at Objects/unicodeobject.c:569
    #8  0x0000000000584abd in PyDict_GetItemString (v=
-       {'Yuck': <type at remote 0xad4730>, '__builtins__': <module at remote 0x7ffff7fd5ee8>, '__file__': 'Lib/test/crashers/nasty_eq_vs_dict.py', '__package__': None, 'y': <Yuck(i=0) at remote 0xaacd80>, 'dict': {0: 0, 1: 1, 2: 2, 3: 3}, '__cached__': None, '__name__': '__main__', 'z': <Yuck(i=0) at remote 0xaace60>, '__doc__': None}, key=
+       {'Yuck': <type at remote 0xad4730>, '__builtins__': <module at remote 0x7ffff7fd5ee8>, '__file__': 'Lib/test/crashers/nasty_eq_vs_dict.py', '__package__': None, 'y': <Yuck(i=0) at remote 0xaacd80>, 'dict': {0: 0, 1: 1, 2: 2, 3: 3}, '__name__': '__main__', 'z': <Yuck(i=0) at remote 0xaace60>, '__doc__': None}, key=
        0x5c2b8d "__lltrace__") at Objects/dictobject.c:2171
 
 Notice how the dictionary argument to ``PyDict_GetItemString`` is displayed
index 8314fed80fa51259919d69ebdf4eb65ecabed409..601745a75780fce695dde578cff770f5d2fb9bb3 100644 (file)
@@ -526,7 +526,7 @@ are always available.  They are listed here in alphabetical order.
       >>> dir()   # show the names in the module namespace  # doctest: +SKIP
       ['__builtins__', '__name__', 'struct']
       >>> dir(struct)   # show the names in the struct module # doctest: +SKIP
-      ['Struct', '__all__', '__builtins__', '__cached__', '__doc__', '__file__',
+      ['Struct', '__all__', '__builtins__', '__doc__', '__file__',
        '__initializing__', '__loader__', '__name__', '__package__',
        '_clearcache', 'calcsize', 'error', 'pack', 'pack_into',
        'unpack', 'unpack_from']
index b851b929b7e2fbd778325d13a3e87d5bd6494b59..c5ea78c1683761ff2522283815becf48f9cd163f 100644 (file)
@@ -1197,8 +1197,7 @@ find and load modules.
 
    .. attribute:: cached
 
-      The filename of a compiled version of the module's code
-      (see :attr:`module.__cached__`).
+      The filename of a compiled version of the module's code.
       The :term:`finder` should always set this attribute but it may be ``None``
       for modules that do not need compiled code stored.
 
index b07ec6e93f80aba2b76085f7a5c61e0a4edc96bf..64735b5a109e6646b00077358eb46619569f03e7 100644 (file)
@@ -50,10 +50,10 @@ The :mod:`runpy` module provides two functions:
    overridden by :func:`run_module`.
 
    The special global variables ``__name__``, ``__spec__``, ``__file__``,
-   ``__cached__``, ``__loader__`` and ``__package__`` are set in the globals
-   dictionary before the module code is executed. (Note that this is a
-   minimal set of variables - other variables may be set implicitly as an
-   interpreter implementation detail.)
+   ``__loader__`` and ``__package__`` are set in the globals dictionary before
+   the module code is executed. (Note that this is a minimal set of variables -
+   other variables may be set implicitly as an interpreter implementation
+   detail.)
 
    ``__name__`` is set to *run_name* if this optional argument is not
    :const:`None`, to ``mod_name + '.__main__'`` if the named module is a
@@ -63,7 +63,7 @@ The :mod:`runpy` module provides two functions:
    module (that is, ``__spec__.name`` will always be *mod_name* or
    ``mod_name + '.__main__'``, never *run_name*).
 
-   ``__file__``, ``__cached__``, ``__loader__`` and ``__package__`` are
+   ``__file__``, ``__loader__`` and ``__package__`` are
    :ref:`set as normal <import-mod-attrs>` based on the module spec.
 
    If the argument *alter_sys* is supplied and evaluates to :const:`True`,
@@ -98,6 +98,9 @@ The :mod:`runpy` module provides two functions:
       ``__package__`` are deprecated. See
       :class:`~importlib.machinery.ModuleSpec` for alternatives.
 
+   .. versionchanged:: 3.15
+      ``__cached__`` is no longer set.
+
 .. function:: run_path(path_name, init_globals=None, run_name=None)
 
    .. index::
@@ -125,23 +128,23 @@ The :mod:`runpy` module provides two functions:
    overridden by :func:`run_path`.
 
    The special global variables ``__name__``, ``__spec__``, ``__file__``,
-   ``__cached__``, ``__loader__`` and ``__package__`` are set in the globals
-   dictionary before the module code is executed. (Note that this is a
-   minimal set of variables - other variables may be set implicitly as an
-   interpreter implementation detail.)
+   ``__loader__`` and ``__package__`` are set in the globals dictionary before
+   the module code is executed. (Note that this is a minimal set of variables -
+   other variables may be set implicitly as an interpreter implementation
+   detail.)
 
    ``__name__`` is set to *run_name* if this optional argument is not
    :const:`None` and to ``'<run_path>'`` otherwise.
 
    If *file_path* directly references a script file (whether as source
    or as precompiled byte code), then ``__file__`` will be set to
-   *file_path*, and ``__spec__``, ``__cached__``, ``__loader__`` and
+   *file_path*, and ``__spec__``, ``__loader__`` and
    ``__package__`` will all be set to :const:`None`.
 
    If *file_path* is a reference to a valid :data:`sys.path` entry, then
    ``__spec__`` will be set appropriately for the imported :mod:`__main__`
    module (that is, ``__spec__.name`` will always be ``__main__``).
-   ``__file__``, ``__cached__``, ``__loader__`` and ``__package__`` will be
+   ``__file__``, ``__loader__`` and ``__package__`` will be
    :ref:`set as normal <import-mod-attrs>` based on the module spec.
 
    A number of alterations are also made to the :mod:`sys` module. Firstly,
@@ -173,6 +176,9 @@ The :mod:`runpy` module provides two functions:
       The setting of ``__cached__``, ``__loader__``, and
       ``__package__`` are deprecated.
 
+   .. versionchanged:: 3.15
+      ``__cached__`` is no longer set.
+
 .. seealso::
 
    :pep:`338` -- Executing modules as scripts
index 5f79c6fe8f50ff73c411fb703ae744348260e5fe..97f55acf00d52130afd204ce946a59544c6fa873 100644 (file)
@@ -895,7 +895,6 @@ Attribute assignment updates the module's namespace dictionary, e.g.,
    single: __loader__ (module attribute)
    single: __path__ (module attribute)
    single: __file__ (module attribute)
-   single: __cached__ (module attribute)
    single: __doc__ (module attribute)
    single: __annotations__ (module attribute)
    single: __annotate__ (module attribute)
@@ -1044,43 +1043,28 @@ this approach.
    instead of :attr:`!module.__path__`.
 
 .. attribute:: module.__file__
-.. attribute:: module.__cached__
 
-   :attr:`!__file__` and :attr:`!__cached__` are both optional attributes that
+   :attr:`!__file__` is an optional attribute that
    may or may not be set. Both attributes should be a :class:`str` when they
    are available.
 
-   :attr:`!__file__` indicates the pathname of the file from which the module
-   was loaded (if loaded from a file), or the pathname of the shared library
-   file for extension modules loaded dynamically from a shared library.
-   It might be missing for certain types of modules, such as C modules that are
-   statically linked into the interpreter, and the
+   An optional attribute, :attr:`!__file__` indicates the pathname of the file
+   from which the module was loaded (if loaded from a file), or the pathname of
+   the shared library file for extension modules loaded dynamically from a
+   shared library. It might be missing for certain types of modules, such as C
+   modules that are statically linked into the interpreter, and the
    :ref:`import system <importsystem>` may opt to leave it unset if it
    has no semantic meaning (for example, a module loaded from a database).
 
-   If :attr:`!__file__` is set then the :attr:`!__cached__` attribute might
-   also be set,  which is the path to any compiled version of
-   the code (for example, a byte-compiled file). The file does not need to exist
-   to set this attribute; the path can simply point to where the
-   compiled file *would* exist (see :pep:`3147`).
-
-   Note that :attr:`!__cached__` may be set even if :attr:`!__file__` is not
-   set.  However, that scenario is quite atypical.  Ultimately, the
-   :term:`loader` is what makes use of the module spec provided by the
-   :term:`finder` (from which :attr:`!__file__` and :attr:`!__cached__` are
-   derived).  So if a loader can load from a cached module but otherwise does
-   not load from a file, that atypical scenario may be appropriate.
-
-   It is **strongly** recommended that you use
-   :attr:`module.__spec__.cached <importlib.machinery.ModuleSpec.cached>`
-   instead of :attr:`!module.__cached__`.
-
    .. deprecated-removed:: 3.13 3.15
-      Setting :attr:`!__cached__` on a module while failing to set
+      Setting ``__cached__`` on a module while failing to set
       :attr:`!__spec__.cached` is deprecated. In Python 3.15,
-      :attr:`!__cached__` will cease to be set or taken into consideration by
+      ``__cached__`` will cease to be set or taken into consideration by
       the import system or standard library.
 
+   .. versionchanged:: 3.15
+      ``__cached__`` is no longer set.
+
 Other writable attributes on module objects
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
index 8badfe9a6b49b95c4f46a82d7f78746b9204844d..221956f3dd38195d55fef9cec861551560952c4a 100644 (file)
@@ -1337,7 +1337,7 @@ Deprecated
   it was :exc:`ImportWarning`).
   (Contributed by Brett Cannon in :gh:`65961`.)
 
-* Setting :attr:`~module.__package__` or :attr:`~module.__cached__` on a
+* Setting :attr:`~module.__package__` or ``__cached__`` on a
   module is deprecated, and will cease to be set or taken into consideration by
   the import system in Python 3.14. (Contributed by Brett Cannon in :gh:`65961`.)
 
index 47c4d9acbc870e5001978303609fca6e097c321a..3b13d90f7692cd4632e31d0b530040c6dd848984 100644 (file)
@@ -312,7 +312,7 @@ cluttering source directories, the *pyc* files are now collected in a
 Aside from the filenames and target directories, the new scheme has a few
 aspects that are visible to the programmer:
 
-* Imported modules now have a :attr:`~module.__cached__` attribute which stores
+* Imported modules now have a ``__cached__`` attribute which stores
   the name of the actual file that was imported:
 
    >>> import collections
index 8cee9fda935050809a26e4d3d0f7d4599bdebc82..07d938b18fe7275d142d3697ccc5a64c0a0c09d1 100644 (file)
@@ -565,8 +565,7 @@ class ModuleSpec:
     `has_location` indicates that a spec's "origin" reflects a location.
     When this is True, `__file__` attribute of the module is set.
 
-    `cached` is the location of the cached bytecode file, if any.  It
-    corresponds to the `__cached__` attribute.
+    `cached` is the location of the cached bytecode file, if any.
 
     `submodule_search_locations` is the sequence of path entries to
     search when importing submodules.  If set, is_package should be
@@ -699,10 +698,6 @@ def _spec_from_module(module, loader=None, origin=None):
             origin = getattr(loader, '_ORIGIN', None)
         if not origin and location is not None:
             origin = location
-    try:
-        cached = module.__cached__
-    except AttributeError:
-        cached = None
     try:
         submodule_search_locations = list(module.__path__)
     except AttributeError:
@@ -710,7 +705,7 @@ def _spec_from_module(module, loader=None, origin=None):
 
     spec = ModuleSpec(name, loader, origin=origin)
     spec._set_fileattr = False if location is None else (origin == location)
-    spec.cached = cached
+    spec.cached = None
     spec.submodule_search_locations = submodule_search_locations
     return spec
 
@@ -770,7 +765,7 @@ def _init_module_attrs(spec, module, *, override=False):
                 module.__path__ = spec.submodule_search_locations
             except AttributeError:
                 pass
-    # __file__/__cached__
+    # __file__
     if spec.has_location:
         if override or getattr(module, '__file__', None) is None:
             try:
@@ -778,12 +773,6 @@ def _init_module_attrs(spec, module, *, override=False):
             except AttributeError:
                 pass
 
-        if override or getattr(module, '__cached__', None) is None:
-            if spec.cached is not None:
-                try:
-                    module.__cached__ = spec.cached
-                except AttributeError:
-                    pass
     return module
 
 
index 9d289674357b44cefebdcd9af2cdc88079d69381..b576ceb1ce9f6e9b1a0bf7e199fee3b57d328fc8 100644 (file)
@@ -1503,7 +1503,6 @@ def _fix_up_module(ns, name, pathname, cpathname=None):
         ns['__spec__'] = spec
         ns['__loader__'] = loader
         ns['__file__'] = pathname
-        ns['__cached__'] = cpathname
     except Exception:
         # Not important enough to report.
         pass
index ff462750888c8879c4473dc04f2ab05a2872cc02..07c4e28f0d9952f4bc5433d78260e5f237812203 100644 (file)
@@ -3407,20 +3407,20 @@ def _main():
         sys.exit(1)
 
     if args.details:
-        print('Target: {}'.format(target))
-        print('Origin: {}'.format(getsourcefile(module)))
-        print('Cached: {}'.format(module.__cached__))
+        print(f'Target: {target}')
+        print(f'Origin: {getsourcefile(module)}')
+        print(f'Cached: {module.__spec__.cached}')
         if obj is module:
-            print('Loader: {}'.format(repr(module.__loader__)))
+            print(f'Loader: {module.__loader__!r}')
             if hasattr(module, '__path__'):
-                print('Submodule search path: {}'.format(module.__path__))
+                print(f'Submodule search path: {module.__path__}')
         else:
             try:
                 __, lineno = findsource(obj)
             except Exception:
                 pass
             else:
-                print('Line: {}'.format(lineno))
+                print(f'Line: {lineno}')
 
         print()
     else:
index 20c500d28bc5b9c11ed2c8449f4ebd19cdcbbbd8..304284da42116328ba76c113af9b87187bb8685a 100644 (file)
@@ -607,7 +607,6 @@ def main():
                 '__file__': spec.origin,
                 '__name__': spec.name,
                 '__package__': None,
-                '__cached__': None,
             }
         try:
             runctx(code, globs, None, options.outfile, options.sort)
index a6b8edf721611fcd03fe5ee5792925c8e7dcca6b..bd3cbf299aab3b9c59b651353101a9fde10376ab 100644 (file)
@@ -201,7 +201,6 @@ def main():
                 '__file__': spec.origin,
                 '__name__': spec.name,
                 '__package__': None,
-                '__cached__': None,
             })
 
         try:
index 45ff5fca308c145da505842ca529a263cbdb1f14..ee4457d9d3a9329139f453da995037a33ac0ba35 100644 (file)
@@ -241,12 +241,12 @@ def visiblename(name, all=None, obj=None):
     """Decide whether to show documentation on a variable."""
     # Certain special names are redundant or internal.
     # XXX Remove __initializing__?
-    if name in {'__author__', '__builtins__', '__cached__', '__credits__',
-                '__date__', '__doc__', '__file__', '__spec__',
-                '__loader__', '__module__', '__name__', '__package__',
-                '__path__', '__qualname__', '__slots__', '__version__',
-                '__static_attributes__', '__firstlineno__',
-                '__annotate_func__', '__annotations_cache__'}:
+    if name in {'__author__', '__builtins__', '__credits__', '__date__',
+                '__doc__', '__file__', '__spec__', '__loader__', '__module__',
+                '__name__', '__package__', '__path__', '__qualname__',
+                '__slots__', '__version__', '__static_attributes__',
+                '__firstlineno__', '__annotate_func__',
+                '__annotations_cache__'}:
         return 0
     # Private names are hidden, but special names are displayed.
     if name.startswith('__') and name.endswith('__'): return 1
index f072498f6cb4054579faa634b0f84891446aceff..9f62d20e9a2322d3d6f027df14d704ce623ffc0c 100644 (file)
@@ -80,7 +80,6 @@ def _run_code(code, run_globals, init_globals=None,
             pkg_name = mod_spec.parent
     run_globals.update(__name__ = mod_name,
                        __file__ = fname,
-                       __cached__ = cached,
                        __doc__ = None,
                        __loader__ = loader,
                        __package__ = pkg_name,
@@ -180,7 +179,6 @@ def _run_module_as_main(mod_name, alter_argv=True):
        At the very least, these variables in __main__ will be overwritten:
            __name__
            __file__
-           __cached__
            __loader__
            __package__
     """
index 7c6810792cfa7ee86f2146bbe9277077ffbe4b27..1b7a656551b853f3c4708fef696c2790eff3ed7e 100644 (file)
@@ -111,7 +111,7 @@ def makepath(*paths):
 
 
 def abs_paths():
-    """Set all module __file__ and __cached__ attributes to an absolute path"""
+    """Set __file__ to an absolute path."""
     for m in set(sys.modules.values()):
         loader_module = None
         try:
@@ -127,10 +127,6 @@ def abs_paths():
             m.__file__ = os.path.abspath(m.__file__)
         except (AttributeError, OSError, TypeError):
             pass
-        try:
-            m.__cached__ = os.path.abspath(m.__cached__)
-        except (AttributeError, OSError, TypeError):
-            pass
 
 
 def removeduppaths():
@@ -699,7 +695,7 @@ def main():
     known_paths = removeduppaths()
     if orig_path != sys.path:
         # removeduppaths() might make sys.path absolute.
-        # fix __file__ and __cached__ of already imported modules too.
+        # Fix __file__ of already imported modules too.
         abs_paths()
 
     known_paths = venv(known_paths)
index cc1a625a5097d88d0fa5da11520bb9cfc76b6144..8695df9eb0c294bffc27f6327e038913124678f5 100644 (file)
@@ -44,7 +44,6 @@ from importlib.machinery import BuiltinImporter
 _loader = __loader__ if __loader__ is BuiltinImporter else type(__loader__)
 print('__loader__==%a' % _loader)
 print('__file__==%a' % __file__)
-print('__cached__==%a' % __cached__)
 print('__package__==%r' % __package__)
 # Check PEP 451 details
 import os.path
@@ -58,8 +57,6 @@ if __package__ is not None:
     assertEqual(__spec__.parent, __package__)
     assertIdentical(__spec__.submodule_search_locations, None)
     assertEqual(__spec__.origin, __file__)
-    if __spec__.cached is not None:
-        assertEqual(__spec__.cached, __cached__)
 # Check the sys module
 import sys
 assertIdentical(globals(), sys.modules[__name__].__dict__)
index bc61ddc4f03675aaf6805d9f1b7e03cd3d43e60f..c5cabc6477c8e6b0c49c6773ac1cf591dd090a18 100644 (file)
@@ -1713,78 +1713,6 @@ class PycacheTests(unittest.TestCase):
         finally:
             os.remove(pyc_file)
 
-    def test___cached__(self):
-        # Modules now also have an __cached__ that points to the pyc file.
-        m = __import__(TESTFN)
-        pyc_file = importlib.util.cache_from_source(TESTFN + '.py')
-        self.assertEqual(m.__cached__, os.path.join(os.getcwd(), pyc_file))
-
-    @skip_if_dont_write_bytecode
-    def test___cached___legacy_pyc(self):
-        # Like test___cached__() except that for backward compatibility,
-        # when the pyc file lives where the py file would have been (and named
-        # without the tag), it is importable.  The __cached__ of the imported
-        # module is the pyc location.
-        __import__(TESTFN)
-        # pyc_file gets removed in _clean() via tearDown().
-        pyc_file = make_legacy_pyc(self.source)
-        os.remove(self.source)
-        unload(TESTFN)
-        importlib.invalidate_caches()
-        m = __import__(TESTFN)
-        self.assertEqual(m.__cached__,
-                         os.path.join(os.getcwd(), os.path.relpath(pyc_file)))
-
-    @skip_if_dont_write_bytecode
-    def test_package___cached__(self):
-        # Like test___cached__ but for packages.
-        def cleanup():
-            rmtree('pep3147')
-            unload('pep3147.foo')
-            unload('pep3147')
-        os.mkdir('pep3147')
-        self.addCleanup(cleanup)
-        # Touch the __init__.py
-        with open(os.path.join('pep3147', '__init__.py'), 'wb'):
-            pass
-        with open(os.path.join('pep3147', 'foo.py'), 'wb'):
-            pass
-        importlib.invalidate_caches()
-        m = __import__('pep3147.foo')
-        init_pyc = importlib.util.cache_from_source(
-            os.path.join('pep3147', '__init__.py'))
-        self.assertEqual(m.__cached__, os.path.join(os.getcwd(), init_pyc))
-        foo_pyc = importlib.util.cache_from_source(os.path.join('pep3147', 'foo.py'))
-        self.assertEqual(sys.modules['pep3147.foo'].__cached__,
-                         os.path.join(os.getcwd(), foo_pyc))
-
-    def test_package___cached___from_pyc(self):
-        # Like test___cached__ but ensuring __cached__ when imported from a
-        # PEP 3147 pyc file.
-        def cleanup():
-            rmtree('pep3147')
-            unload('pep3147.foo')
-            unload('pep3147')
-        os.mkdir('pep3147')
-        self.addCleanup(cleanup)
-        # Touch the __init__.py
-        with open(os.path.join('pep3147', '__init__.py'), 'wb'):
-            pass
-        with open(os.path.join('pep3147', 'foo.py'), 'wb'):
-            pass
-        importlib.invalidate_caches()
-        m = __import__('pep3147.foo')
-        unload('pep3147.foo')
-        unload('pep3147')
-        importlib.invalidate_caches()
-        m = __import__('pep3147.foo')
-        init_pyc = importlib.util.cache_from_source(
-            os.path.join('pep3147', '__init__.py'))
-        self.assertEqual(m.__cached__, os.path.join(os.getcwd(), init_pyc))
-        foo_pyc = importlib.util.cache_from_source(os.path.join('pep3147', 'foo.py'))
-        self.assertEqual(sys.modules['pep3147.foo'].__cached__,
-                         os.path.join(os.getcwd(), foo_pyc))
-
     def test_recompute_pyc_same_second(self):
         # Even when the source file doesn't change timestamp, a change in
         # source size is enough to trigger recomputation of the pyc file.
index da4ddb3d027c34a6caf800cc2bedd2aeaa218a5b..1d61ff348d8d155321e5019b9f127923325d107b 100644 (file)
@@ -1,9 +1,9 @@
 import sys
 
 class MyMod(object):
-    __slots__ = ['__builtins__', '__cached__', '__doc__',
-                 '__file__', '__loader__', '__name__',
-                 '__package__', '__path__', '__spec__']
+    __slots__ = ['__builtins__', '__doc__', '__file__',
+                 '__loader__', '__name__', '__package__',
+                 '__path__', '__spec__']
     def __init__(self):
         for attr in self.__slots__:
             setattr(self, attr, globals()[attr])
index 550f88d1d7a6516691fe2a583a9f1ec4ad94a256..7587276a41e9539762668fde6d4e4f81d1f602c2 100644 (file)
@@ -19,8 +19,7 @@ class FixUpModuleTests:
         ns = {"__spec__": spec}
         _bootstrap_external._fix_up_module(ns, name, path)
 
-        expected = {"__spec__": spec, "__loader__": loader, "__file__": path,
-                    "__cached__": None}
+        expected = {"__spec__": spec, "__loader__": loader, "__file__": path}
         self.assertEqual(ns, expected)
 
     def test_no_loader_no_spec_but_sourceless(self):
@@ -29,7 +28,7 @@ class FixUpModuleTests:
         ns = {}
         _bootstrap_external._fix_up_module(ns, name, path, path)
 
-        expected = {"__file__": path, "__cached__": path}
+        expected = {"__file__": path}
 
         for key, val in expected.items():
             with self.subTest(f"{key}: {val}"):
@@ -51,7 +50,7 @@ class FixUpModuleTests:
         ns = {}
         _bootstrap_external._fix_up_module(ns, name, path)
 
-        expected = {"__file__": path, "__cached__": None}
+        expected = {"__file__": path}
 
         for key, val in expected.items():
             with self.subTest(f"{key}: {val}"):
index 8132a69d8f4e8922eb7aa92304d5321d82e554b3..7c146ea853b0d9a11731f760b533a3cceff5de77 100644 (file)
@@ -510,8 +510,7 @@ class ExecutionLoaderGetCodeTests:
 class SourceOnlyLoader:
 
     # Globals that should be defined for all modules.
-    source = (b"_ = '::'.join([__name__, __file__, __cached__, __package__, "
-              b"repr(__loader__)])")
+    source = (b"_ = '::'.join([__name__, __file__, __package__, repr(__loader__)])")
 
     def __init__(self, path):
         self.path = path
@@ -586,20 +585,17 @@ class SourceLoaderTestHarness:
     def verify_module(self, module):
         self.assertEqual(module.__name__, self.name)
         self.assertEqual(module.__file__, self.path)
-        self.assertEqual(module.__cached__, self.cached)
         self.assertEqual(module.__package__, self.package)
         self.assertEqual(module.__loader__, self.loader)
         values = module._.split('::')
         self.assertEqual(values[0], self.name)
         self.assertEqual(values[1], self.path)
-        self.assertEqual(values[2], self.cached)
-        self.assertEqual(values[3], self.package)
-        self.assertEqual(values[4], repr(self.loader))
+        self.assertEqual(values[2], self.package)
+        self.assertEqual(values[3], repr(self.loader))
 
     def verify_code(self, code_object):
         module = types.ModuleType(self.name)
         module.__file__ = self.path
-        module.__cached__ = self.cached
         module.__package__ = self.package
         module.__loader__ = self.loader
         module.__path__ = []
index 1bc531a2fe34e74589680700c81610d557121446..4de0cf029a81e098b25f1d9cc5d82b4aa9418594 100644 (file)
@@ -235,7 +235,6 @@ class ReloadTests:
                     expected = {'__name__': name,
                                 '__package__': '',
                                 '__file__': path,
-                                '__cached__': cached,
                                 '__doc__': None,
                                 }
                     os_helper.create_empty_file(path)
@@ -256,7 +255,6 @@ class ReloadTests:
                     expected = {'__name__': name,
                                 '__package__': name,
                                 '__file__': init_path,
-                                '__cached__': cached,
                                 '__path__': [os.path.dirname(init_path)],
                                 '__doc__': None,
                                 }
@@ -316,7 +314,6 @@ class ReloadTests:
                     expected = {'__name__': name,
                                 '__package__': name,
                                 '__file__': init_path,
-                                '__cached__': cached,
                                 '__path__': [os.path.dirname(init_path)],
                                 '__doc__': None,
                                 'eggs': None,
index fef0fda101e46d9e38945bd99afff0270ed4b261..b48d0a101ca9e77ae646911b9ae0be569b1a77b1 100644 (file)
@@ -336,7 +336,6 @@ class ModuleSpecMethodsTests:
         self.assertIs(loaded.__spec__, self.spec)
         self.assertNotHasAttr(loaded, '__path__')
         self.assertNotHasAttr(loaded, '__file__')
-        self.assertNotHasAttr(loaded, '__cached__')
 
 
 (Frozen_ModuleSpecMethodsTests,
index a49e360d10fb7c01759f9f590a2b3160c0a2709c..17a211f10fa0ac45d31e6abf0a179f5bf48e3d95 100644 (file)
@@ -124,12 +124,6 @@ class ModuleFromSpecTests:
         module = self.util.module_from_spec(spec)
         self.assertEqual(module.__file__, spec.origin)
 
-    def test___cached__(self):
-        spec = self.machinery.ModuleSpec('test', object())
-        spec.cached = 'some/path'
-        spec.has_location = True
-        module = self.util.module_from_spec(spec)
-        self.assertEqual(module.__cached__, spec.cached)
 
 (Frozen_ModuleFromSpecTests,
  Source_ModuleFromSpecTests
index dd3b7d9c5b4b5b41314347bc3dd8ae5d127e8b0a..075e1802bebc3e5c74074e20b05f211a0e43dd25 100644 (file)
@@ -6494,13 +6494,12 @@ class TestMain(unittest.TestCase):
         rc, out, err = assert_python_ok(*args, '-m', 'inspect',
                                         'unittest', '--details')
         output = out.decode()
-        # Just a quick sanity check on the output
+        # Just a quick safety check on the output
         self.assertIn(module.__spec__.name, output)
         self.assertIn(module.__name__, output)
         self.assertIn(module.__spec__.origin, output)
         self.assertIn(module.__file__, output)
         self.assertIn(module.__spec__.cached, output)
-        self.assertIn(module.__cached__, output)
         self.assertEqual(err, b'')
 
 
index d2b724db40d3e9b6e640c8f511505ad04e6c9476..0a366e2a5bb2d1c75cc59b8dad286e30e24d69a8 100644 (file)
@@ -198,15 +198,15 @@ class TestPkg(unittest.TestCase):
 
         import t5
         self.assertEqual(fixdir(dir(t5)),
-                         ['__cached__', '__doc__', '__file__', '__loader__',
-                          '__name__', '__package__', '__path__', '__spec__',
-                          'foo', 'string', 't5'])
+                         ['__doc__', '__file__', '__loader__', '__name__',
+                         '__package__', '__path__', '__spec__', 'foo',
+                         'string', 't5'])
         self.assertEqual(fixdir(dir(t5.foo)),
-                         ['__cached__', '__doc__', '__file__', '__loader__',
-                          '__name__', '__package__', '__spec__', 'string'])
+                         ['__doc__', '__file__', '__loader__', '__name__',
+                         '__package__', '__spec__', 'string'])
         self.assertEqual(fixdir(dir(t5.string)),
-                         ['__cached__', '__doc__', '__file__', '__loader__',
-                          '__name__', '__package__', '__spec__', 'spam'])
+                         ['__doc__', '__file__', '__loader__', '__name__',
+                         '__package__', '__spec__', 'spam'])
 
     def test_6(self):
         hier = [
@@ -221,14 +221,13 @@ class TestPkg(unittest.TestCase):
 
         import t6
         self.assertEqual(fixdir(dir(t6)),
-                         ['__all__', '__cached__', '__doc__', '__file__',
-                          '__loader__', '__name__', '__package__', '__path__',
-                          '__spec__'])
+                         ['__all__', '__doc__', '__file__', '__loader__',
+                         '__name__', '__package__', '__path__', '__spec__'])
         s = """
             import t6
             from t6 import *
             self.assertEqual(fixdir(dir(t6)),
-                             ['__all__', '__cached__', '__doc__', '__file__',
+                             ['__all__', '__doc__', '__file__',
                               '__loader__', '__name__', '__package__',
                               '__path__', '__spec__', 'eggs', 'ham', 'spam'])
             self.assertEqual(dir(), ['eggs', 'ham', 'self', 'spam', 't6'])
@@ -256,20 +255,19 @@ class TestPkg(unittest.TestCase):
         t7, sub, subsub = None, None, None
         import t7 as tas
         self.assertEqual(fixdir(dir(tas)),
-                         ['__cached__', '__doc__', '__file__', '__loader__',
-                          '__name__', '__package__', '__path__', '__spec__'])
+                         ['__doc__', '__file__', '__loader__', '__name__',
+                          '__package__', '__path__', '__spec__'])
         self.assertFalse(t7)
         from t7 import sub as subpar
         self.assertEqual(fixdir(dir(subpar)),
-                         ['__cached__', '__doc__', '__file__', '__loader__',
-                          '__name__', '__package__', '__path__', '__spec__'])
+                         ['__doc__', '__file__', '__loader__', '__name__',
+                          '__package__', '__path__', '__spec__'])
         self.assertFalse(t7)
         self.assertFalse(sub)
         from t7.sub import subsub as subsubsub
         self.assertEqual(fixdir(dir(subsubsub)),
-                         ['__cached__', '__doc__', '__file__', '__loader__',
-                          '__name__', '__package__', '__path__', '__spec__',
-                          'spam'])
+                         ['__doc__', '__file__', '__loader__', '__name__',
+                          '__package__', '__path__', '__spec__', 'spam'])
         self.assertFalse(t7)
         self.assertFalse(sub)
         self.assertFalse(subsub)
index e298b2add52c3e97a1c431c0551dd656c1276c7e..ddcaafc9b7dbe836431c5f0c4d8aad1ba4079371 100644 (file)
@@ -1443,10 +1443,10 @@ class TestMain(ReplTestCase):
         case2 = f"{pre}, '__doc__', '__file__', {post}" in output
 
         # if `__main__` is a cached .pyc file and the .py source exists
-        case3 = f"{pre}, '__cached__', '__doc__', '__file__', {post}" in output
+        case3 = f"{pre}, '__doc__', '__file__', {post}" in output
 
         # if `__main__` is a cached .pyc file but there's no .py source file
-        case4 = f"{pre}, '__cached__', '__doc__', {post}" in output
+        case4 = f"{pre}, '__doc__', {post}" in output
 
         self.assertTrue(case1 or case2 or case3 or case4, output)
 
index cc76b72b9639ebcebec62b90c8cd3cb900092126..254a009a69718b61722f9d12787dd5465941c938 100644 (file)
@@ -57,7 +57,6 @@ nested = runpy._run_module_code('x=1\\n', mod_name='<run>')
 implicit_namespace = {
     "__name__": None,
     "__file__": None,
-    "__cached__": None,
     "__package__": None,
     "__doc__": None,
     "__spec__": None
@@ -286,7 +285,6 @@ class RunModuleTestCase(unittest.TestCase, CodeExecutionMixin):
     def _fix_ns_for_legacy_pyc(self, ns, alter_sys):
         char_to_add = "c"
         ns["__file__"] += char_to_add
-        ns["__cached__"] = ns["__file__"]
         spec = ns["__spec__"]
         new_spec = importlib.util.spec_from_file_location(spec.name,
                                                           ns["__file__"])
@@ -306,7 +304,6 @@ class RunModuleTestCase(unittest.TestCase, CodeExecutionMixin):
         expected_ns.update({
             "__name__": mod_name,
             "__file__": mod_fname,
-            "__cached__": mod_spec.cached,
             "__package__": mod_name.rpartition(".")[0],
             "__spec__": mod_spec,
         })
@@ -347,7 +344,6 @@ class RunModuleTestCase(unittest.TestCase, CodeExecutionMixin):
         expected_ns.update({
             "__name__": mod_name,
             "__file__": mod_fname,
-            "__cached__": importlib.util.cache_from_source(mod_fname),
             "__package__": pkg_name,
             "__spec__": mod_spec,
         })
@@ -552,7 +548,6 @@ from ..uncle.cousin import nephew
         expected_ns.update({
             "__name__": run_name,
             "__file__": mod_fname,
-            "__cached__": importlib.util.cache_from_source(mod_fname),
             "__package__": mod_name.rpartition(".")[0],
             "__spec__": mod_spec,
         })
@@ -632,7 +627,6 @@ class RunPathTestCase(unittest.TestCase, CodeExecutionMixin):
         expected_ns.update({
             "__name__": expected_name,
             "__file__": expected_file,
-            "__cached__": mod_cached,
             "__package__": "",
             "__spec__": mod_spec,
             "run_argv0": expected_argv0,
index 27ae3539b554ef54afb89b924fd8526525b64794..e7dc5e2611c2decd8dd1814d3c9761d0dc32d889 100644 (file)
@@ -466,17 +466,6 @@ class ImportSideEffectTests(unittest.TestCase):
         """Restore sys.path"""
         sys.path[:] = self.sys_path
 
-    def test_abs_paths_cached_None(self):
-        """Test for __cached__ is None.
-
-        Regarding to PEP 3147, __cached__ can be None.
-
-        See also: https://bugs.python.org/issue30167
-        """
-        sys.modules['test'].__cached__ = None
-        site.abs_paths()
-        self.assertIsNone(sys.modules['test'].__cached__)
-
     def test_no_duplicate_paths(self):
         # No duplicate paths should exist in sys.path
         # Handled by removeduppaths()
index cf8817f4383fc1d513bc54dc977bc5b3e255f52e..cd3a6d30661da3979bd24c7f8c3c9240f9b2c7d9 100644 (file)
@@ -721,7 +721,6 @@ def main():
                 '__package__': mod_spec.parent,
                 '__loader__': mod_spec.loader,
                 '__spec__': mod_spec,
-                '__cached__': None,
             }
         else:
             sys.argv = [opts.progname, *opts.arguments]
@@ -734,7 +733,6 @@ def main():
                 '__file__': opts.progname,
                 '__name__': '__main__',
                 '__package__': None,
-                '__cached__': None,
             }
         t.runctx(code, globs, globs)
     except OSError as err:
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-01-15-22-54.gh-issue-65961.hCJvRB.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-01-15-22-54.gh-issue-65961.hCJvRB.rst
new file mode 100644 (file)
index 0000000..59ab00a
--- /dev/null
@@ -0,0 +1 @@
+Stop setting ``__cached__`` on modules.
index 272be504a68fa18c19966cc2f258829fd78d43c5..f2c402eb1a03b58e88bdcf805e0db79678e013ed 100644 (file)
@@ -478,9 +478,6 @@ _PyRun_SimpleFileObject(FILE *fp, PyObject *filename, int closeit,
         if (PyDict_SetItemString(dict, "__file__", filename) < 0) {
             goto done;
         }
-        if (PyDict_SetItemString(dict, "__cached__", Py_None) < 0) {
-            goto done;
-        }
         set_file_name = 1;
     }
 
@@ -535,9 +532,6 @@ _PyRun_SimpleFileObject(FILE *fp, PyObject *filename, int closeit,
         if (PyDict_PopString(dict, "__file__", NULL) < 0) {
             PyErr_Print();
         }
-        if (PyDict_PopString(dict, "__cached__", NULL) < 0) {
-            PyErr_Print();
-        }
     }
     Py_XDECREF(main_module);
     return ret;