mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
Merge ac80f2d978 into 3db7bf2d18
This commit is contained in:
commit
c8f14db7c8
133 changed files with 4733 additions and 174 deletions
|
|
@ -341,6 +341,58 @@ Importing Modules
|
||||||
|
|
||||||
.. versionadded:: 3.14
|
.. versionadded:: 3.14
|
||||||
|
|
||||||
|
.. c:function:: PyImport_LazyImportsMode PyImport_GetLazyImportsMode()
|
||||||
|
|
||||||
|
Gets the current lazy imports mode.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
|
.. c:function:: PyObject* PyImport_GetLazyImportsFilter()
|
||||||
|
|
||||||
|
Return a :term:`strong reference` to the current lazy imports filter,
|
||||||
|
or ``NULL`` if none exists. This function always succeeds.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
|
.. c:function:: int PyImport_SetLazyImportsMode(PyImport_LazyImportsMode mode)
|
||||||
|
|
||||||
|
Similar to :c:func:`PyImport_ImportModuleAttr`, but names are UTF-8 encoded
|
||||||
|
strings instead of Python :class:`str` objects.
|
||||||
|
|
||||||
|
This function always returns ``0``.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
|
.. c:function:: int PyImport_SetLazyImportsFilter(PyObject *filter)
|
||||||
|
|
||||||
|
Sets the current lazy imports filter. The *filter* should be a callable that
|
||||||
|
will receive ``(importing_module_name, imported_module_name, [fromlist])``
|
||||||
|
when an import can potentially be lazy and that must return ``True`` if
|
||||||
|
the import should be lazy and ``False`` otherwise.
|
||||||
|
|
||||||
|
Return ``0`` on success and ``-1`` with an exception set otherwise.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
|
.. c:type:: PyImport_LazyImportsMode
|
||||||
|
|
||||||
|
Enumeration of possible lazy import modes.
|
||||||
|
|
||||||
|
.. c:enumerator:: PyImport_LAZY_NORMAL
|
||||||
|
|
||||||
|
Respect the ``lazy`` keyword in source code. This is the default mode.
|
||||||
|
|
||||||
|
.. c:enumerator:: PyImport_LAZY_ALL
|
||||||
|
|
||||||
|
Make all imports lazy by default.
|
||||||
|
|
||||||
|
.. c:enumerator:: PyImport_LAZY_NONE
|
||||||
|
|
||||||
|
Disable lazy imports entirely. Even explicit ``lazy`` statements become
|
||||||
|
eager imports.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
.. c:function:: PyObject* PyImport_CreateModuleFromInitfunc(PyObject *spec, PyObject* (*initfunc)(void))
|
.. c:function:: PyObject* PyImport_CreateModuleFromInitfunc(PyObject *spec, PyObject* (*initfunc)(void))
|
||||||
|
|
||||||
This function is a building block that enables embedders to implement
|
This function is a building block that enables embedders to implement
|
||||||
|
|
|
||||||
|
|
@ -1113,7 +1113,8 @@ Imports
|
||||||
names=[
|
names=[
|
||||||
alias(name='x'),
|
alias(name='x'),
|
||||||
alias(name='y'),
|
alias(name='y'),
|
||||||
alias(name='z')])])
|
alias(name='z')],
|
||||||
|
is_lazy=0)])
|
||||||
|
|
||||||
|
|
||||||
.. class:: ImportFrom(module, names, level)
|
.. class:: ImportFrom(module, names, level)
|
||||||
|
|
@ -1134,7 +1135,8 @@ Imports
|
||||||
alias(name='x'),
|
alias(name='x'),
|
||||||
alias(name='y'),
|
alias(name='y'),
|
||||||
alias(name='z')],
|
alias(name='z')],
|
||||||
level=0)])
|
level=0,
|
||||||
|
is_lazy=0)])
|
||||||
|
|
||||||
|
|
||||||
.. class:: alias(name, asname)
|
.. class:: alias(name, asname)
|
||||||
|
|
@ -1152,7 +1154,8 @@ Imports
|
||||||
names=[
|
names=[
|
||||||
alias(name='a', asname='b'),
|
alias(name='a', asname='b'),
|
||||||
alias(name='c')],
|
alias(name='c')],
|
||||||
level=2)])
|
level=2,
|
||||||
|
is_lazy=0)])
|
||||||
|
|
||||||
Control flow
|
Control flow
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
|
@ -911,6 +911,43 @@ always available. Unless explicitly noted otherwise, all variables are read-only
|
||||||
|
|
||||||
.. versionadded:: 3.11
|
.. versionadded:: 3.11
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: get_lazy_imports()
|
||||||
|
|
||||||
|
Returns the current lazy imports mode as a string.
|
||||||
|
|
||||||
|
* ``"normal"``: Only imports explicitly marked with the ``lazy`` keyword are lazy
|
||||||
|
* ``"all"``: All top-level imports are potentially lazy
|
||||||
|
* ``"none"``: All lazy imports are suppressed (even explicitly marked ones)
|
||||||
|
|
||||||
|
See also :func:`set_lazy_imports` and :pep:`810`.
|
||||||
|
|
||||||
|
.. versionadded:: 3.15
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: get_lazy_imports_filter()
|
||||||
|
|
||||||
|
Returns the current lazy imports filter function, or ``None`` if no filter
|
||||||
|
is set.
|
||||||
|
|
||||||
|
The filter function is called for every potentially lazy import to determine
|
||||||
|
whether it should actually be lazy. See :func:`set_lazy_imports_filter` for
|
||||||
|
details on the filter function signature.
|
||||||
|
|
||||||
|
.. versionadded:: 3.15
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: get_lazy_modules()
|
||||||
|
|
||||||
|
Returns a set of fully-qualified module names that have been lazily imported.
|
||||||
|
This is primarily useful for diagnostics and introspection.
|
||||||
|
|
||||||
|
Note that modules are removed from this set when they are reified (actually
|
||||||
|
loaded on first use).
|
||||||
|
|
||||||
|
.. versionadded:: 3.15
|
||||||
|
|
||||||
|
|
||||||
.. function:: getrefcount(object)
|
.. function:: getrefcount(object)
|
||||||
|
|
||||||
Return the reference count of the *object*. The count returned is generally one
|
Return the reference count of the *object*. The count returned is generally one
|
||||||
|
|
@ -1719,6 +1756,57 @@ always available. Unless explicitly noted otherwise, all variables are read-only
|
||||||
|
|
||||||
.. versionadded:: 3.11
|
.. versionadded:: 3.11
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: set_lazy_imports(mode)
|
||||||
|
|
||||||
|
Sets the global lazy imports mode. The *mode* parameter must be one of the
|
||||||
|
following strings:
|
||||||
|
|
||||||
|
* ``"normal"``: Only imports explicitly marked with the ``lazy`` keyword are lazy
|
||||||
|
* ``"all"``: All top-level imports become potentially lazy
|
||||||
|
* ``"none"``: All lazy imports are suppressed (even explicitly marked ones)
|
||||||
|
|
||||||
|
This function is intended for advanced users who need to control lazy imports
|
||||||
|
across their entire application. Library developers should generally not use
|
||||||
|
this function as it affects the runtime execution of applications.
|
||||||
|
|
||||||
|
In addition to the mode, lazy imports can be controlled via the filter
|
||||||
|
provided by :func:`set_lazy_imports_filter`.
|
||||||
|
|
||||||
|
See also :func:`get_lazy_imports` and :pep:`810`.
|
||||||
|
|
||||||
|
.. versionadded:: 3.15
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: set_lazy_imports_filter(filter)
|
||||||
|
|
||||||
|
Sets the lazy imports filter callback. The *filter* parameter must be a
|
||||||
|
callable or ``None`` to clear the filter.
|
||||||
|
|
||||||
|
The filter function is called for every potentially lazy import to determine
|
||||||
|
whether it should actually be lazy. It must have the following signature::
|
||||||
|
|
||||||
|
def filter(importing_module: str, imported_module: str,
|
||||||
|
fromlist: tuple[str, ...] | None) -> bool
|
||||||
|
|
||||||
|
Where:
|
||||||
|
|
||||||
|
* *importing_module* is the name of the module doing the import
|
||||||
|
* *imported_module* is the name of the module being imported
|
||||||
|
* *fromlist* is the tuple of names being imported (for ``from ... import``
|
||||||
|
statements), or ``None`` for regular imports
|
||||||
|
|
||||||
|
The filter should return ``True`` to allow the import to be lazy, or
|
||||||
|
``False`` to force an eager import.
|
||||||
|
|
||||||
|
This is an advanced feature intended for specialized users who need
|
||||||
|
fine-grained control over lazy import behavior.
|
||||||
|
|
||||||
|
See also :func:`get_lazy_imports_filter` and :pep:`810`.
|
||||||
|
|
||||||
|
.. versionadded:: 3.15
|
||||||
|
|
||||||
|
|
||||||
.. function:: setprofile(profilefunc)
|
.. function:: setprofile(profilefunc)
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
|
|
|
||||||
|
|
@ -343,6 +343,18 @@ Standard names are defined for the following types:
|
||||||
.. seealso:: :pep:`667`
|
.. seealso:: :pep:`667`
|
||||||
|
|
||||||
|
|
||||||
|
.. data:: LazyImportType
|
||||||
|
|
||||||
|
The type of lazy import proxy objects. These objects are created when a
|
||||||
|
module is lazily imported and serve as placeholders until the module is
|
||||||
|
actually accessed. This type can be used to detect lazy imports
|
||||||
|
programmatically.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
|
.. seealso:: :pep:`810`
|
||||||
|
|
||||||
|
|
||||||
.. data:: GetSetDescriptorType
|
.. data:: GetSetDescriptorType
|
||||||
|
|
||||||
The type of objects defined in extension modules with ``PyGetSetDef``, such
|
The type of objects defined in extension modules with ``PyGetSetDef``, such
|
||||||
|
|
|
||||||
|
|
@ -457,6 +457,7 @@ Some names are only reserved under specific contexts. These are known as
|
||||||
|
|
||||||
- ``match``, ``case``, and ``_``, when used in the :keyword:`match` statement.
|
- ``match``, ``case``, and ``_``, when used in the :keyword:`match` statement.
|
||||||
- ``type``, when used in the :keyword:`type` statement.
|
- ``type``, when used in the :keyword:`type` statement.
|
||||||
|
- ``lazy``, when used before an :keyword:`import` statement.
|
||||||
|
|
||||||
These syntactically act as keywords in their specific contexts,
|
These syntactically act as keywords in their specific contexts,
|
||||||
but this distinction is done at the parser level, not when tokenizing.
|
but this distinction is done at the parser level, not when tokenizing.
|
||||||
|
|
@ -468,6 +469,9 @@ identifier names.
|
||||||
.. versionchanged:: 3.12
|
.. versionchanged:: 3.12
|
||||||
``type`` is now a soft keyword.
|
``type`` is now a soft keyword.
|
||||||
|
|
||||||
|
.. versionchanged:: next
|
||||||
|
``lazy`` is now a soft keyword.
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
single: _, identifiers
|
single: _, identifiers
|
||||||
single: __, identifiers
|
single: __, identifiers
|
||||||
|
|
|
||||||
|
|
@ -748,14 +748,15 @@ The :keyword:`!import` statement
|
||||||
pair: name; binding
|
pair: name; binding
|
||||||
pair: keyword; from
|
pair: keyword; from
|
||||||
pair: keyword; as
|
pair: keyword; as
|
||||||
|
pair: keyword; lazy
|
||||||
pair: exception; ImportError
|
pair: exception; ImportError
|
||||||
single: , (comma); import statement
|
single: , (comma); import statement
|
||||||
|
|
||||||
.. productionlist:: python-grammar
|
.. productionlist:: python-grammar
|
||||||
import_stmt: "import" `module` ["as" `identifier`] ("," `module` ["as" `identifier`])*
|
import_stmt: ["lazy"] "import" `module` ["as" `identifier`] ("," `module` ["as" `identifier`])*
|
||||||
: | "from" `relative_module` "import" `identifier` ["as" `identifier`]
|
: | ["lazy"] "from" `relative_module` "import" `identifier` ["as" `identifier`]
|
||||||
: ("," `identifier` ["as" `identifier`])*
|
: ("," `identifier` ["as" `identifier`])*
|
||||||
: | "from" `relative_module` "import" "(" `identifier` ["as" `identifier`]
|
: | ["lazy"] "from" `relative_module` "import" "(" `identifier` ["as" `identifier`]
|
||||||
: ("," `identifier` ["as" `identifier`])* [","] ")"
|
: ("," `identifier` ["as" `identifier`])* [","] ")"
|
||||||
: | "from" `relative_module` "import" "*"
|
: | "from" `relative_module` "import" "*"
|
||||||
module: (`identifier` ".")* `identifier`
|
module: (`identifier` ".")* `identifier`
|
||||||
|
|
@ -870,6 +871,56 @@ determine dynamically the modules to be loaded.
|
||||||
|
|
||||||
.. audit-event:: import module,filename,sys.path,sys.meta_path,sys.path_hooks import
|
.. audit-event:: import module,filename,sys.path,sys.meta_path,sys.path_hooks import
|
||||||
|
|
||||||
|
|
||||||
|
.. _lazy-imports:
|
||||||
|
.. _lazy:
|
||||||
|
|
||||||
|
Lazy imports
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. index::
|
||||||
|
pair: lazy; import
|
||||||
|
single: lazy import
|
||||||
|
|
||||||
|
The :keyword:`lazy` keyword marks an import as lazy. It is a :ref:`soft keyword
|
||||||
|
<soft-keywords>` that only has special meaning when it appears immediately
|
||||||
|
before an :keyword:`import` or :keyword:`from` statement.
|
||||||
|
|
||||||
|
When an import statement is preceded by the :keyword:`lazy` keyword,
|
||||||
|
the import becomes *lazy*: the module is not loaded immediately at the import
|
||||||
|
statement. Instead, a lazy proxy object is created and bound to the name. The
|
||||||
|
actual module is loaded on first use of that name.
|
||||||
|
|
||||||
|
Lazy imports are only permitted at module scope. Using ``lazy`` inside a
|
||||||
|
function, class body, or :keyword:`try`/:keyword:`except`/:keyword:`finally`
|
||||||
|
block raises a :exc:`SyntaxError`. Star imports cannot be lazy (``lazy from
|
||||||
|
module import *`` is a syntax error), and :ref:`future statements <future>`
|
||||||
|
cannot be lazy.
|
||||||
|
|
||||||
|
When using ``lazy from ... import``, each imported name is bound to a lazy
|
||||||
|
proxy object. The first access to any of these names triggers loading of the
|
||||||
|
entire module and resolves only that specific name to its actual value. Other
|
||||||
|
names remain as lazy proxies until they are accessed.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
lazy import json
|
||||||
|
|
||||||
|
print('json' in sys.modules) # False - module not loaded yet
|
||||||
|
|
||||||
|
# First use triggers loading
|
||||||
|
result = json.dumps({"hello": "world"})
|
||||||
|
|
||||||
|
print('json' in sys.modules) # True - now loaded
|
||||||
|
|
||||||
|
If an error occurs during module loading (such as :exc:`ImportError` or
|
||||||
|
:exc:`SyntaxError`), it is raised at the point where the lazy import is first
|
||||||
|
used, not at the import statement itself.
|
||||||
|
|
||||||
|
See :pep:`810` for the full specification of lazy imports.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
.. _future:
|
.. _future:
|
||||||
|
|
||||||
Future statements
|
Future statements
|
||||||
|
|
|
||||||
|
|
@ -694,6 +694,14 @@ Miscellaneous options
|
||||||
|
|
||||||
.. versionadded:: 3.14
|
.. versionadded:: 3.14
|
||||||
|
|
||||||
|
* :samp:`-X lazy_imports={all,none,normal}` controls lazy import behavior.
|
||||||
|
``all`` makes all imports lazy by default, ``none`` disables lazy imports
|
||||||
|
entirely (even explicit ``lazy`` statements become eager), and ``normal``
|
||||||
|
(the default) respects the ``lazy`` keyword in source code.
|
||||||
|
See also :envvar:`PYTHON_LAZY_IMPORTS`.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
It also allows passing arbitrary values and retrieving them through the
|
It also allows passing arbitrary values and retrieving them through the
|
||||||
:data:`sys._xoptions` dictionary.
|
:data:`sys._xoptions` dictionary.
|
||||||
|
|
||||||
|
|
@ -1339,6 +1347,17 @@ conflict.
|
||||||
|
|
||||||
.. versionadded:: 3.14
|
.. versionadded:: 3.14
|
||||||
|
|
||||||
|
.. envvar:: PYTHON_LAZY_IMPORTS
|
||||||
|
|
||||||
|
Controls lazy import behavior. Accepts three values: ``all`` makes all
|
||||||
|
imports lazy by default, ``none`` disables lazy imports entirely (even
|
||||||
|
explicit ``lazy`` statements become eager), and ``normal`` (the default)
|
||||||
|
respects the ``lazy`` keyword in source code.
|
||||||
|
|
||||||
|
See also the :option:`-X lazy_imports <-X>` command-line option.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
Debug-mode variables
|
Debug-mode variables
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,8 @@ Summary -- Release highlights
|
||||||
|
|
||||||
.. PEP-sized items next.
|
.. PEP-sized items next.
|
||||||
|
|
||||||
|
* :pep:`810`: :ref:`Explicit lazy imports for faster startup times
|
||||||
|
<whatsnew315-pep810>`
|
||||||
* :pep:`799`: :ref:`A dedicated profiling package for organizing Python
|
* :pep:`799`: :ref:`A dedicated profiling package for organizing Python
|
||||||
profiling tools <whatsnew315-profiling-package>`
|
profiling tools <whatsnew315-profiling-package>`
|
||||||
* :pep:`686`: :ref:`Python now uses UTF-8 as the default encoding
|
* :pep:`686`: :ref:`Python now uses UTF-8 as the default encoding
|
||||||
|
|
@ -77,6 +79,98 @@ Summary -- Release highlights
|
||||||
New features
|
New features
|
||||||
============
|
============
|
||||||
|
|
||||||
|
.. _whatsnew315-pep810:
|
||||||
|
|
||||||
|
:pep:`810`: Explicit lazy imports
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
Large Python applications often suffer from slow startup times. A significant
|
||||||
|
contributor to this problem is the import system: when a module is imported,
|
||||||
|
Python must locate the file, read it from disk, compile it to bytecode, and
|
||||||
|
execute all top-level code. For applications with deep dependency trees, this
|
||||||
|
process can take seconds, even when most of the imported code is never actually
|
||||||
|
used during a particular run.
|
||||||
|
|
||||||
|
Developers have worked around this by moving imports inside functions, using
|
||||||
|
:mod:`importlib` to load modules on demand, or restructuring code to avoid
|
||||||
|
unnecessary dependencies. These approaches work but make code harder to read
|
||||||
|
and maintain, scatter import statements throughout the codebase, and require
|
||||||
|
discipline to apply consistently.
|
||||||
|
|
||||||
|
Python now provides a cleaner solution through explicit lazy imports using the
|
||||||
|
new ``lazy`` soft keyword. When you mark an import as lazy, Python defers the
|
||||||
|
actual module loading until the imported name is first used. This gives you
|
||||||
|
the organizational benefits of declaring all imports at the top of the file
|
||||||
|
while only paying the loading cost for modules you actually use.
|
||||||
|
|
||||||
|
The ``lazy`` keyword works with both ``import`` and ``from ... import`` statements.
|
||||||
|
When you write ``lazy import heavy_module``, Python does not immediately load the
|
||||||
|
module. Instead, it creates a lightweight proxy object. The actual module loading
|
||||||
|
happens transparently when you first access the name:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
lazy import json
|
||||||
|
lazy from datetime import datetime
|
||||||
|
|
||||||
|
print("Starting up...") # json and datetime not loaded yet
|
||||||
|
|
||||||
|
data = json.loads('{"key": "value"}') # json loads here
|
||||||
|
now = datetime() # datetime loads here
|
||||||
|
|
||||||
|
This mechanism is particularly useful for applications that import many modules
|
||||||
|
at the top level but may only use a subset of them in any given run. The deferred
|
||||||
|
loading reduces startup latency without requiring code restructuring or conditional
|
||||||
|
imports scattered throughout the codebase.
|
||||||
|
|
||||||
|
When a lazy import eventually fails (for example, if the module does not exist),
|
||||||
|
Python raises the exception at the point of first use rather than at import time.
|
||||||
|
The traceback includes both the location where the name was accessed and the
|
||||||
|
original import statement, making it straightforward to diagnose the problem.
|
||||||
|
|
||||||
|
For cases where you want to enable lazy loading globally without modifying source
|
||||||
|
code, Python provides the :option:`-X lazy_imports <-X>` command-line option and
|
||||||
|
the :envvar:`PYTHON_LAZY_IMPORTS` environment variable. Both accept three values:
|
||||||
|
``all`` makes all imports lazy by default, ``none`` disables lazy imports entirely
|
||||||
|
(even explicit ``lazy`` statements become eager), and ``normal`` (the default)
|
||||||
|
respects the ``lazy`` keyword in source code. The :func:`sys.set_lazy_imports` and
|
||||||
|
:func:`sys.get_lazy_imports` functions allow changing and querying this mode at
|
||||||
|
runtime.
|
||||||
|
|
||||||
|
For more selective control, :func:`sys.set_lazy_imports_filter` accepts a callable
|
||||||
|
that determines whether a specific module should be loaded lazily. The filter
|
||||||
|
receives three arguments: the importing module's name (or ``None``), the imported
|
||||||
|
module's name, and the fromlist (or ``None`` for regular imports). It should
|
||||||
|
return ``True`` to allow the import to be lazy, or ``False`` to force eager loading.
|
||||||
|
This allows patterns like making only your own application's modules lazy while
|
||||||
|
keeping third-party dependencies eager:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.set_lazy_imports_filter(lambda importing, imported, fromlist: imported.startswith("myapp."))
|
||||||
|
sys.set_lazy_imports("all")
|
||||||
|
|
||||||
|
import myapp.slow_module # lazy (matches filter)
|
||||||
|
import json # eager (does not match filter)
|
||||||
|
|
||||||
|
For debugging and introspection, :func:`sys.get_lazy_modules` returns a set
|
||||||
|
containing the names of all modules that have been lazily imported but not yet
|
||||||
|
loaded. The proxy type itself is available as :data:`types.LazyImportType` for
|
||||||
|
code that needs to detect lazy imports programmatically.
|
||||||
|
|
||||||
|
There are some restrictions on where ``lazy`` can appear. Lazy imports are only
|
||||||
|
permitted at module scope; using ``lazy`` inside a function, class body, or
|
||||||
|
``try``/``except``/``finally`` block raises a :exc:`SyntaxError`. Star imports
|
||||||
|
cannot be lazy (``lazy from module import *`` is a syntax error), and future
|
||||||
|
imports cannot be lazy either (``lazy from __future__ import ...`` raises
|
||||||
|
:exc:`SyntaxError`).
|
||||||
|
|
||||||
|
.. seealso:: :pep:`810` for the full specification and rationale.
|
||||||
|
|
||||||
|
(Contributed by Pablo Galindo Salgado and Dino Viehland in :gh:`142349`.)
|
||||||
|
|
||||||
.. _whatsnew315-profiling-package:
|
.. _whatsnew315-profiling-package:
|
||||||
|
|
||||||
:pep:`799`: A dedicated profiling package
|
:pep:`799`: A dedicated profiling package
|
||||||
|
|
|
||||||
|
|
@ -121,9 +121,9 @@ simple_stmts[asdl_stmt_seq*]:
|
||||||
simple_stmt[stmt_ty] (memo):
|
simple_stmt[stmt_ty] (memo):
|
||||||
| assignment
|
| assignment
|
||||||
| &"type" type_alias
|
| &"type" type_alias
|
||||||
|
| &('import' | 'from' | "lazy") import_stmt
|
||||||
| e=star_expressions { _PyAST_Expr(e, EXTRA) }
|
| e=star_expressions { _PyAST_Expr(e, EXTRA) }
|
||||||
| &'return' return_stmt
|
| &'return' return_stmt
|
||||||
| &('import' | 'from') import_stmt
|
|
||||||
| &'raise' raise_stmt
|
| &'raise' raise_stmt
|
||||||
| &'pass' pass_stmt
|
| &'pass' pass_stmt
|
||||||
| &'del' del_stmt
|
| &'del' del_stmt
|
||||||
|
|
@ -216,7 +216,7 @@ assert_stmt[stmt_ty]:
|
||||||
| invalid_assert_stmt
|
| invalid_assert_stmt
|
||||||
| 'assert' a=expression b=[',' z=expression { z }] { _PyAST_Assert(a, b, EXTRA) }
|
| 'assert' a=expression b=[',' z=expression { z }] { _PyAST_Assert(a, b, EXTRA) }
|
||||||
|
|
||||||
import_stmt[stmt_ty]:
|
import_stmt[stmt_ty](memo):
|
||||||
| invalid_import
|
| invalid_import
|
||||||
| import_name
|
| import_name
|
||||||
| import_from
|
| import_from
|
||||||
|
|
@ -224,13 +224,15 @@ import_stmt[stmt_ty]:
|
||||||
# Import statements
|
# Import statements
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|
||||||
import_name[stmt_ty]: 'import' a=dotted_as_names { _PyAST_Import(a, EXTRA) }
|
import_name[stmt_ty]:
|
||||||
|
| lazy="lazy"? 'import' a=dotted_as_names { _PyAST_Import(a, lazy ? 1 : 0, EXTRA) }
|
||||||
|
|
||||||
# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS
|
# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS
|
||||||
import_from[stmt_ty]:
|
import_from[stmt_ty]:
|
||||||
| 'from' a=('.' | '...')* b=dotted_name 'import' c=import_from_targets {
|
| lazy="lazy"? 'from' a=('.' | '...')* b=dotted_name 'import' c=import_from_targets {
|
||||||
_PyPegen_checked_future_import(p, b->v.Name.id, c, _PyPegen_seq_count_dots(a), EXTRA) }
|
_PyPegen_checked_future_import(p, b->v.Name.id, c, _PyPegen_seq_count_dots(a), lazy, EXTRA) }
|
||||||
| 'from' a=('.' | '...')+ 'import' b=import_from_targets {
|
| lazy="lazy"? 'from' a=('.' | '...')+ 'import' b=import_from_targets {
|
||||||
_PyAST_ImportFrom(NULL, b, _PyPegen_seq_count_dots(a), EXTRA) }
|
_PyAST_ImportFrom(NULL, b, _PyPegen_seq_count_dots(a), lazy ? 1 : 0, EXTRA) }
|
||||||
import_from_targets[asdl_alias_seq*]:
|
import_from_targets[asdl_alias_seq*]:
|
||||||
| '(' a=import_from_as_names [','] ')' { a }
|
| '(' a=import_from_as_names [','] ')' { a }
|
||||||
| import_from_as_names !','
|
| import_from_as_names !','
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,7 @@ typedef struct PyConfig {
|
||||||
int enable_gil;
|
int enable_gil;
|
||||||
int tlbc_enabled;
|
int tlbc_enabled;
|
||||||
#endif
|
#endif
|
||||||
|
int lazy_imports;
|
||||||
|
|
||||||
/* --- Path configuration inputs ------------ */
|
/* --- Path configuration inputs ------------ */
|
||||||
int pathconfig_warnings;
|
int pathconfig_warnings;
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,20 @@ PyAPI_FUNC(int) PyImport_AppendInittab(
|
||||||
PyObject* (*initfunc)(void)
|
PyObject* (*initfunc)(void)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PyImport_LAZY_NORMAL,
|
||||||
|
PyImport_LAZY_ALL,
|
||||||
|
PyImport_LAZY_NONE,
|
||||||
|
} PyImport_LazyImportsMode;
|
||||||
|
|
||||||
|
#ifndef Py_LIMITED_API
|
||||||
|
PyAPI_FUNC(int) PyImport_SetLazyImportsMode(PyImport_LazyImportsMode mode);
|
||||||
|
PyAPI_FUNC(int) PyImport_SetLazyImportsFilter(PyObject *filter);
|
||||||
|
|
||||||
|
PyAPI_FUNC(PyImport_LazyImportsMode) PyImport_GetLazyImportsMode(void);
|
||||||
|
PyAPI_FUNC(PyObject *) PyImport_GetLazyImportsFilter(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef Py_LIMITED_API
|
#ifndef Py_LIMITED_API
|
||||||
# define Py_CPYTHON_IMPORT_H
|
# define Py_CPYTHON_IMPORT_H
|
||||||
# include "cpython/import.h"
|
# include "cpython/import.h"
|
||||||
|
|
|
||||||
11
Include/internal/pycore_ast.h
generated
11
Include/internal/pycore_ast.h
generated
|
|
@ -329,12 +329,14 @@ struct _stmt {
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
asdl_alias_seq *names;
|
asdl_alias_seq *names;
|
||||||
|
int is_lazy;
|
||||||
} Import;
|
} Import;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
identifier module;
|
identifier module;
|
||||||
asdl_alias_seq *names;
|
asdl_alias_seq *names;
|
||||||
int level;
|
int level;
|
||||||
|
int is_lazy;
|
||||||
} ImportFrom;
|
} ImportFrom;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
@ -764,11 +766,12 @@ stmt_ty _PyAST_TryStar(asdl_stmt_seq * body, asdl_excepthandler_seq * handlers,
|
||||||
end_col_offset, PyArena *arena);
|
end_col_offset, PyArena *arena);
|
||||||
stmt_ty _PyAST_Assert(expr_ty test, expr_ty msg, int lineno, int col_offset,
|
stmt_ty _PyAST_Assert(expr_ty test, expr_ty msg, int lineno, int col_offset,
|
||||||
int end_lineno, int end_col_offset, PyArena *arena);
|
int end_lineno, int end_col_offset, PyArena *arena);
|
||||||
stmt_ty _PyAST_Import(asdl_alias_seq * names, int lineno, int col_offset, int
|
stmt_ty _PyAST_Import(asdl_alias_seq * names, int is_lazy, int lineno, int
|
||||||
end_lineno, int end_col_offset, PyArena *arena);
|
col_offset, int end_lineno, int end_col_offset, PyArena
|
||||||
|
*arena);
|
||||||
stmt_ty _PyAST_ImportFrom(identifier module, asdl_alias_seq * names, int level,
|
stmt_ty _PyAST_ImportFrom(identifier module, asdl_alias_seq * names, int level,
|
||||||
int lineno, int col_offset, int end_lineno, int
|
int is_lazy, int lineno, int col_offset, int
|
||||||
end_col_offset, PyArena *arena);
|
end_lineno, int end_col_offset, PyArena *arena);
|
||||||
stmt_ty _PyAST_Global(asdl_identifier_seq * names, int lineno, int col_offset,
|
stmt_ty _PyAST_Global(asdl_identifier_seq * names, int lineno, int col_offset,
|
||||||
int end_lineno, int end_col_offset, PyArena *arena);
|
int end_lineno, int end_col_offset, PyArena *arena);
|
||||||
stmt_ty _PyAST_Nonlocal(asdl_identifier_seq * names, int lineno, int
|
stmt_ty _PyAST_Nonlocal(asdl_identifier_seq * names, int lineno, int
|
||||||
|
|
|
||||||
1
Include/internal/pycore_ast_state.h
generated
1
Include/internal/pycore_ast_state.h
generated
|
|
@ -205,6 +205,7 @@ struct ast_state {
|
||||||
PyObject *id;
|
PyObject *id;
|
||||||
PyObject *ifs;
|
PyObject *ifs;
|
||||||
PyObject *is_async;
|
PyObject *is_async;
|
||||||
|
PyObject *is_lazy;
|
||||||
PyObject *items;
|
PyObject *items;
|
||||||
PyObject *iter;
|
PyObject *iter;
|
||||||
PyObject *key;
|
PyObject *key;
|
||||||
|
|
|
||||||
|
|
@ -311,7 +311,14 @@ PyAPI_FUNC(void) _PyEval_FormatExcCheckArg(PyThreadState *tstate, PyObject *exc,
|
||||||
PyAPI_FUNC(void) _PyEval_FormatExcUnbound(PyThreadState *tstate, PyCodeObject *co, int oparg);
|
PyAPI_FUNC(void) _PyEval_FormatExcUnbound(PyThreadState *tstate, PyCodeObject *co, int oparg);
|
||||||
PyAPI_FUNC(void) _PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs);
|
PyAPI_FUNC(void) _PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs);
|
||||||
PyAPI_FUNC(PyObject *) _PyEval_ImportFrom(PyThreadState *, PyObject *, PyObject *);
|
PyAPI_FUNC(PyObject *) _PyEval_ImportFrom(PyThreadState *, PyObject *, PyObject *);
|
||||||
PyAPI_FUNC(PyObject *) _PyEval_ImportName(PyThreadState *, _PyInterpreterFrame *, PyObject *, PyObject *, PyObject *);
|
PyAPI_FUNC(PyObject *) _PyEval_LazyImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals,
|
||||||
|
PyObject *locals, PyObject *name, PyObject *fromlist, PyObject *level, int lazy);
|
||||||
|
PyAPI_FUNC(PyObject *) _PyEval_LazyImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name);
|
||||||
|
PyAPI_FUNC(PyObject *) _PyEval_ImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals, PyObject *locals,
|
||||||
|
PyObject *name, PyObject *fromlist, PyObject *level);
|
||||||
|
PyObject *
|
||||||
|
_PyEval_ImportNameWithImport(PyThreadState *tstate, PyObject *import_func, PyObject *globals, PyObject *locals,
|
||||||
|
PyObject *name, PyObject *fromlist, PyObject *level);
|
||||||
PyAPI_FUNC(PyObject *)_PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, Py_ssize_t nargs, PyObject *kwargs);
|
PyAPI_FUNC(PyObject *)_PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, Py_ssize_t nargs, PyObject *kwargs);
|
||||||
PyAPI_FUNC(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys);
|
PyAPI_FUNC(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys);
|
||||||
PyAPI_FUNC(void) _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
|
PyAPI_FUNC(void) _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,7 @@ int _PyCompile_PushFBlock(struct _PyCompiler *c, _Py_SourceLocation loc,
|
||||||
void _PyCompile_PopFBlock(struct _PyCompiler *c, enum _PyCompile_FBlockType t,
|
void _PyCompile_PopFBlock(struct _PyCompiler *c, enum _PyCompile_FBlockType t,
|
||||||
_PyJumpTargetLabel block_label);
|
_PyJumpTargetLabel block_label);
|
||||||
_PyCompile_FBlockInfo *_PyCompile_TopFBlock(struct _PyCompiler *c);
|
_PyCompile_FBlockInfo *_PyCompile_TopFBlock(struct _PyCompiler *c);
|
||||||
|
bool _PyCompile_InExceptionHandler(struct _PyCompiler *c);
|
||||||
|
|
||||||
int _PyCompile_EnterScope(struct _PyCompiler *c, identifier name, int scope_type,
|
int _PyCompile_EnterScope(struct _PyCompiler *c, identifier name, int scope_type,
|
||||||
void *key, int lineno, PyObject *private,
|
void *key, int lineno, PyObject *private,
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,15 @@ extern int _PyDict_DelItem_KnownHash_LockHeld(PyObject *mp, PyObject *key,
|
||||||
|
|
||||||
extern int _PyDict_Contains_KnownHash(PyObject *, PyObject *, Py_hash_t);
|
extern int _PyDict_Contains_KnownHash(PyObject *, PyObject *, Py_hash_t);
|
||||||
|
|
||||||
|
// "Id" variants
|
||||||
|
extern PyObject* _PyDict_GetItemIdWithError(PyObject *dp,
|
||||||
|
_Py_Identifier *key);
|
||||||
|
extern int _PyDict_ContainsId(PyObject *, _Py_Identifier *);
|
||||||
|
extern int _PyDict_SetItemId(PyObject *dp, _Py_Identifier *key, PyObject *item);
|
||||||
|
extern int _PyDict_DelItemId(PyObject *mp, _Py_Identifier *key);
|
||||||
|
extern void _PyDict_ClearKeysVersion(PyObject *mp);
|
||||||
|
|
||||||
|
|
||||||
extern int _PyDict_Next(
|
extern int _PyDict_Next(
|
||||||
PyObject *mp, Py_ssize_t *pos, PyObject **key, PyObject **value, Py_hash_t *hash);
|
PyObject *mp, Py_ssize_t *pos, PyObject **key, PyObject **value, Py_hash_t *hash);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1434,6 +1434,8 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__iter__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__iter__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__itruediv__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__itruediv__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__ixor__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__ixor__));
|
||||||
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__lazy_import__));
|
||||||
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__lazy_modules__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__le__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__le__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__len__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__len__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__length_hint__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__length_hint__));
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,8 @@ struct _Py_global_strings {
|
||||||
STRUCT_FOR_ID(__iter__)
|
STRUCT_FOR_ID(__iter__)
|
||||||
STRUCT_FOR_ID(__itruediv__)
|
STRUCT_FOR_ID(__itruediv__)
|
||||||
STRUCT_FOR_ID(__ixor__)
|
STRUCT_FOR_ID(__ixor__)
|
||||||
|
STRUCT_FOR_ID(__lazy_import__)
|
||||||
|
STRUCT_FOR_ID(__lazy_modules__)
|
||||||
STRUCT_FOR_ID(__le__)
|
STRUCT_FOR_ID(__le__)
|
||||||
STRUCT_FOR_ID(__len__)
|
STRUCT_FOR_ID(__len__)
|
||||||
STRUCT_FOR_ID(__length_hint__)
|
STRUCT_FOR_ID(__length_hint__)
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,19 @@ extern int _PyImport_FixupBuiltin(
|
||||||
PyObject *modules
|
PyObject *modules
|
||||||
);
|
);
|
||||||
|
|
||||||
|
extern PyObject *
|
||||||
|
_PyImport_ResolveName(PyThreadState *tstate, PyObject *name, PyObject *globals, int level);
|
||||||
|
extern PyObject *
|
||||||
|
_PyImport_GetAbsName(PyThreadState *tstate, PyObject *name, PyObject *globals, int level);
|
||||||
|
// Symbol is exported for the JIT on Windows builds.
|
||||||
|
PyAPI_FUNC(PyObject *)
|
||||||
|
_PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import);
|
||||||
|
extern PyObject *
|
||||||
|
_PyImport_LazyImportModuleLevelObject(PyThreadState *tstate, PyObject *name, PyObject *builtins, PyObject *globals,
|
||||||
|
PyObject *locals, PyObject *fromlist,
|
||||||
|
int level);
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_DLOPEN
|
#ifdef HAVE_DLOPEN
|
||||||
# include <dlfcn.h> // RTLD_NOW, RTLD_LAZY
|
# include <dlfcn.h> // RTLD_NOW, RTLD_LAZY
|
||||||
# if HAVE_DECL_RTLD_NOW
|
# if HAVE_DECL_RTLD_NOW
|
||||||
|
|
@ -74,6 +87,10 @@ extern int _PyImport_IsDefaultImportFunc(
|
||||||
PyInterpreterState *interp,
|
PyInterpreterState *interp,
|
||||||
PyObject *func);
|
PyObject *func);
|
||||||
|
|
||||||
|
extern int _PyImport_IsDefaultLazyImportFunc(
|
||||||
|
PyInterpreterState *interp,
|
||||||
|
PyObject *func);
|
||||||
|
|
||||||
extern PyObject * _PyImport_GetImportlibLoader(
|
extern PyObject * _PyImport_GetImportlibLoader(
|
||||||
PyInterpreterState *interp,
|
PyInterpreterState *interp,
|
||||||
const char *loader_name);
|
const char *loader_name);
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,12 @@ struct _import_state {
|
||||||
int dlopenflags;
|
int dlopenflags;
|
||||||
#endif
|
#endif
|
||||||
PyObject *import_func;
|
PyObject *import_func;
|
||||||
|
PyObject *lazy_import_func;
|
||||||
|
int lazy_imports_mode;
|
||||||
|
PyObject *lazy_imports_filter;
|
||||||
|
PyObject *lazy_importing_modules;
|
||||||
|
PyObject *lazy_modules;
|
||||||
|
PyObject *lazy_modules_set; /* Set of fully-qualified module names lazily imported (PEP 810) */
|
||||||
/* The global import lock. */
|
/* The global import lock. */
|
||||||
_PyRecursiveMutex lock;
|
_PyRecursiveMutex lock;
|
||||||
/* diagnostic info in PyImport_ImportModuleLevelObject() */
|
/* diagnostic info in PyImport_ImportModuleLevelObject() */
|
||||||
|
|
|
||||||
36
Include/internal/pycore_lazyimportobject.h
Normal file
36
Include/internal/pycore_lazyimportobject.h
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* File added for Lazy Imports */
|
||||||
|
|
||||||
|
/* Lazy object interface */
|
||||||
|
|
||||||
|
#ifndef Py_INTERNAL_LAZYIMPORTOBJECT_H
|
||||||
|
#define Py_INTERNAL_LAZYIMPORTOBJECT_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef Py_BUILD_CORE
|
||||||
|
# error "this header requires Py_BUILD_CORE define"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PyAPI_DATA(PyTypeObject) PyLazyImport_Type;
|
||||||
|
#define PyLazyImport_CheckExact(op) Py_IS_TYPE((op), &PyLazyImport_Type)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
PyObject *lz_builtins;
|
||||||
|
PyObject *lz_from;
|
||||||
|
PyObject *lz_attr;
|
||||||
|
/* Frame information for the original import location */
|
||||||
|
PyCodeObject *lz_code; /* code object where the lazy import was created */
|
||||||
|
int lz_instr_offset; /* instruction offset where the lazy import was created */
|
||||||
|
} PyLazyImportObject;
|
||||||
|
|
||||||
|
|
||||||
|
PyAPI_FUNC(PyObject *) _PyLazyImport_GetName(PyObject *lazy_import);
|
||||||
|
PyAPI_FUNC(PyObject *) _PyLazyImport_New(PyObject *import_func, PyObject *from, PyObject *attr);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif /* !Py_INTERNAL_LAZYIMPORTOBJECT_H */
|
||||||
|
|
@ -287,6 +287,7 @@ Known values:
|
||||||
Python 3.15a1 3654 (Fix missing exception handlers in logical expression)
|
Python 3.15a1 3654 (Fix missing exception handlers in logical expression)
|
||||||
Python 3.15a1 3655 (Fix miscompilation of some module-level annotations)
|
Python 3.15a1 3655 (Fix miscompilation of some module-level annotations)
|
||||||
Python 3.15a1 3656 (Add TRACE_RECORD instruction, for platforms with switch based interpreter)
|
Python 3.15a1 3656 (Add TRACE_RECORD instruction, for platforms with switch based interpreter)
|
||||||
|
Python 3.15a3 3657 (Lazy imports IMPORT_NAME opcode changes)
|
||||||
|
|
||||||
|
|
||||||
Python 3.16 will start with 3700
|
Python 3.16 will start with 3700
|
||||||
|
|
@ -300,7 +301,7 @@ PC/launcher.c must also be updated.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define PYC_MAGIC_NUMBER 3656
|
#define PYC_MAGIC_NUMBER 3657
|
||||||
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
|
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
|
||||||
(little-endian) and then appending b'\r\n'. */
|
(little-endian) and then appending b'\r\n'. */
|
||||||
#define PYC_MAGIC_NUMBER_TOKEN \
|
#define PYC_MAGIC_NUMBER_TOKEN \
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ typedef struct {
|
||||||
PyObject *md_weaklist;
|
PyObject *md_weaklist;
|
||||||
// for logging purposes after md_dict is cleared
|
// for logging purposes after md_dict is cleared
|
||||||
PyObject *md_name;
|
PyObject *md_name;
|
||||||
|
// module version we last checked for lazy values
|
||||||
|
uint32_t m_dict_version;
|
||||||
bool md_token_is_def; /* if true, `md_token` is the PyModuleDef */
|
bool md_token_is_def; /* if true, `md_token` is the PyModuleDef */
|
||||||
#ifdef Py_GIL_DISABLED
|
#ifdef Py_GIL_DISABLED
|
||||||
bool md_requires_gil;
|
bool md_requires_gil;
|
||||||
|
|
@ -76,6 +78,8 @@ extern Py_ssize_t _PyModule_GetFilenameUTF8(
|
||||||
PyObject* _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress);
|
PyObject* _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress);
|
||||||
PyObject* _Py_module_getattro(PyObject *m, PyObject *name);
|
PyObject* _Py_module_getattro(PyObject *m, PyObject *name);
|
||||||
|
|
||||||
|
PyAPI_FUNC(int) _PyModule_ReplaceLazyValue(PyObject *dict, PyObject *name, PyObject *value);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
2
Include/internal/pycore_runtime_init_generated.h
generated
2
Include/internal/pycore_runtime_init_generated.h
generated
|
|
@ -1432,6 +1432,8 @@ extern "C" {
|
||||||
INIT_ID(__iter__), \
|
INIT_ID(__iter__), \
|
||||||
INIT_ID(__itruediv__), \
|
INIT_ID(__itruediv__), \
|
||||||
INIT_ID(__ixor__), \
|
INIT_ID(__ixor__), \
|
||||||
|
INIT_ID(__lazy_import__), \
|
||||||
|
INIT_ID(__lazy_modules__), \
|
||||||
INIT_ID(__le__), \
|
INIT_ID(__le__), \
|
||||||
INIT_ID(__len__), \
|
INIT_ID(__len__), \
|
||||||
INIT_ID(__length_hint__), \
|
INIT_ID(__length_hint__), \
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ typedef struct _symtable_entry {
|
||||||
unsigned ste_method : 1; /* true if block is a function block defined in class scope */
|
unsigned ste_method : 1; /* true if block is a function block defined in class scope */
|
||||||
unsigned ste_has_conditional_annotations : 1; /* true if block has conditionally executed annotations */
|
unsigned ste_has_conditional_annotations : 1; /* true if block has conditionally executed annotations */
|
||||||
unsigned ste_in_conditional_block : 1; /* set while we are inside a conditionally executed block */
|
unsigned ste_in_conditional_block : 1; /* set while we are inside a conditionally executed block */
|
||||||
|
unsigned ste_in_try_block : 1; /* set while we are inside a try/except block */
|
||||||
unsigned ste_in_unevaluated_annotation : 1; /* set while we are processing an annotation that will not be evaluated */
|
unsigned ste_in_unevaluated_annotation : 1; /* set while we are processing an annotation that will not be evaluated */
|
||||||
int ste_comp_iter_expr; /* non-zero if visiting a comprehension range expression */
|
int ste_comp_iter_expr; /* non-zero if visiting a comprehension range expression */
|
||||||
_Py_SourceLocation ste_loc; /* source location of block */
|
_Py_SourceLocation ste_loc; /* source location of block */
|
||||||
|
|
|
||||||
|
|
@ -408,6 +408,14 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
||||||
_PyUnicode_InternStatic(interp, &string);
|
_PyUnicode_InternStatic(interp, &string);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
assert(PyUnicode_GET_LENGTH(string) != 1);
|
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||||
|
string = &_Py_ID(__lazy_import__);
|
||||||
|
_PyUnicode_InternStatic(interp, &string);
|
||||||
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
|
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||||
|
string = &_Py_ID(__lazy_modules__);
|
||||||
|
_PyUnicode_InternStatic(interp, &string);
|
||||||
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
|
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||||
string = &_Py_ID(__le__);
|
string = &_Py_ID(__le__);
|
||||||
_PyUnicode_InternStatic(interp, &string);
|
_PyUnicode_InternStatic(interp, &string);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,9 @@ PyAPI_DATA(PyObject *) PyExc_EOFError;
|
||||||
PyAPI_DATA(PyObject *) PyExc_FloatingPointError;
|
PyAPI_DATA(PyObject *) PyExc_FloatingPointError;
|
||||||
PyAPI_DATA(PyObject *) PyExc_OSError;
|
PyAPI_DATA(PyObject *) PyExc_OSError;
|
||||||
PyAPI_DATA(PyObject *) PyExc_ImportError;
|
PyAPI_DATA(PyObject *) PyExc_ImportError;
|
||||||
|
#if !defined(Py_LIMITED_API)
|
||||||
|
PyAPI_DATA(PyObject *) PyExc_ImportCycleError;
|
||||||
|
#endif
|
||||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000
|
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000
|
||||||
PyAPI_DATA(PyObject *) PyExc_ModuleNotFoundError;
|
PyAPI_DATA(PyObject *) PyExc_ModuleNotFoundError;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -240,6 +240,7 @@
|
||||||
REVERSE_NAME_MAPPING[('builtins', excname)] = ('exceptions', 'OSError')
|
REVERSE_NAME_MAPPING[('builtins', excname)] = ('exceptions', 'OSError')
|
||||||
|
|
||||||
PYTHON3_IMPORTERROR_EXCEPTIONS = (
|
PYTHON3_IMPORTERROR_EXCEPTIONS = (
|
||||||
|
'ImportCycleError',
|
||||||
'ModuleNotFoundError',
|
'ModuleNotFoundError',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -276,6 +276,8 @@ def is_soft_keyword_used(*tokens: TI | None) -> bool:
|
||||||
TI(T.NAME, string=s)
|
TI(T.NAME, string=s)
|
||||||
):
|
):
|
||||||
return not keyword.iskeyword(s)
|
return not keyword.iskeyword(s)
|
||||||
|
case (None | TI(T.NEWLINE) | TI(T.INDENT) | TI(T.DEDENT), TI(string="lazy"), TI(string="import") | TI(string="from")):
|
||||||
|
return True
|
||||||
case _:
|
case _:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
||||||
10
Lib/dis.py
10
Lib/dis.py
|
|
@ -35,6 +35,7 @@
|
||||||
FUNCTION_ATTR_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure', 'annotate')
|
FUNCTION_ATTR_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure', 'annotate')
|
||||||
|
|
||||||
ENTER_EXECUTOR = opmap['ENTER_EXECUTOR']
|
ENTER_EXECUTOR = opmap['ENTER_EXECUTOR']
|
||||||
|
IMPORT_NAME = opmap['IMPORT_NAME']
|
||||||
LOAD_GLOBAL = opmap['LOAD_GLOBAL']
|
LOAD_GLOBAL = opmap['LOAD_GLOBAL']
|
||||||
LOAD_SMALL_INT = opmap['LOAD_SMALL_INT']
|
LOAD_SMALL_INT = opmap['LOAD_SMALL_INT']
|
||||||
BINARY_OP = opmap['BINARY_OP']
|
BINARY_OP = opmap['BINARY_OP']
|
||||||
|
|
@ -601,6 +602,12 @@ def get_argval_argrepr(self, op, arg, offset):
|
||||||
argval, argrepr = _get_name_info(arg//4, get_name)
|
argval, argrepr = _get_name_info(arg//4, get_name)
|
||||||
if (arg & 1) and argrepr:
|
if (arg & 1) and argrepr:
|
||||||
argrepr = f"{argrepr} + NULL|self"
|
argrepr = f"{argrepr} + NULL|self"
|
||||||
|
elif deop == IMPORT_NAME:
|
||||||
|
argval, argrepr = _get_name_info(arg//4, get_name)
|
||||||
|
if (arg & 1) and argrepr:
|
||||||
|
argrepr = f"{argrepr} + lazy"
|
||||||
|
elif (arg & 2) and argrepr:
|
||||||
|
argrepr = f"{argrepr} + eager"
|
||||||
else:
|
else:
|
||||||
argval, argrepr = _get_name_info(arg, get_name)
|
argval, argrepr = _get_name_info(arg, get_name)
|
||||||
elif deop in hasjump or deop in hasexc:
|
elif deop in hasjump or deop in hasexc:
|
||||||
|
|
@ -1013,7 +1020,8 @@ def _find_imports(co):
|
||||||
(level_op[0] in hasconst or level_op[0] == LOAD_SMALL_INT)):
|
(level_op[0] in hasconst or level_op[0] == LOAD_SMALL_INT)):
|
||||||
level = _get_const_value(level_op[0], level_op[1], consts)
|
level = _get_const_value(level_op[0], level_op[1], consts)
|
||||||
fromlist = _get_const_value(from_op[0], from_op[1], consts)
|
fromlist = _get_const_value(from_op[0], from_op[1], consts)
|
||||||
yield (names[oparg], level, fromlist)
|
# IMPORT_NAME encodes lazy/eager flags in bits 0-1, name index in bits 2+
|
||||||
|
yield (names[oparg >> 2], level, fromlist)
|
||||||
|
|
||||||
def _find_store_names(co):
|
def _find_store_names(co):
|
||||||
"""Find names of variables which are written in the code
|
"""Find names of variables which are written in the code
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,11 @@ def make_pat():
|
||||||
]) +
|
]) +
|
||||||
r"))"
|
r"))"
|
||||||
)
|
)
|
||||||
|
lazy_softkw = (
|
||||||
|
r"^[ \t]*" + # at beginning of line + possible indentation
|
||||||
|
r"(?P<LAZY_SOFTKW>lazy)" +
|
||||||
|
r"(?=[ \t]+(?:import|from)\b)" # followed by 'import' or 'from'
|
||||||
|
)
|
||||||
builtinlist = [str(name) for name in dir(builtins)
|
builtinlist = [str(name) for name in dir(builtins)
|
||||||
if not name.startswith('_') and
|
if not name.startswith('_') and
|
||||||
name not in keyword.kwlist]
|
name not in keyword.kwlist]
|
||||||
|
|
@ -56,7 +61,7 @@ def make_pat():
|
||||||
prog = re.compile("|".join([
|
prog = re.compile("|".join([
|
||||||
builtin, comment, string, kw,
|
builtin, comment, string, kw,
|
||||||
match_softkw, case_default,
|
match_softkw, case_default,
|
||||||
case_softkw_and_pattern,
|
case_softkw_and_pattern, lazy_softkw,
|
||||||
any("SYNC", [r"\n"]),
|
any("SYNC", [r"\n"]),
|
||||||
]),
|
]),
|
||||||
re.DOTALL | re.MULTILINE)
|
re.DOTALL | re.MULTILINE)
|
||||||
|
|
@ -70,6 +75,7 @@ def make_pat():
|
||||||
"CASE_SOFTKW": "KEYWORD",
|
"CASE_SOFTKW": "KEYWORD",
|
||||||
"CASE_DEFAULT_UNDERSCORE": "KEYWORD",
|
"CASE_DEFAULT_UNDERSCORE": "KEYWORD",
|
||||||
"CASE_SOFTKW2": "KEYWORD",
|
"CASE_SOFTKW2": "KEYWORD",
|
||||||
|
"LAZY_SOFTKW": "KEYWORD",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -542,6 +542,22 @@ def test_case_soft_keyword(self):
|
||||||
self._assert_highlighting('case _:', {'KEYWORD': [('1.0', '1.4'),
|
self._assert_highlighting('case _:', {'KEYWORD': [('1.0', '1.4'),
|
||||||
('1.5', '1.6')]})
|
('1.5', '1.6')]})
|
||||||
|
|
||||||
|
def test_lazy_soft_keyword(self):
|
||||||
|
# lazy followed by import
|
||||||
|
self._assert_highlighting('lazy import foo',
|
||||||
|
{'KEYWORD': [('1.0', '1.4'), ('1.5', '1.11')]})
|
||||||
|
self._assert_highlighting(' lazy import foo',
|
||||||
|
{'KEYWORD': [('1.4', '1.8'), ('1.9', '1.15')]})
|
||||||
|
|
||||||
|
# lazy followed by from
|
||||||
|
self._assert_highlighting('lazy from foo import bar',
|
||||||
|
{'KEYWORD': [('1.0', '1.4'), ('1.5', '1.9'),
|
||||||
|
('1.14', '1.20')]})
|
||||||
|
|
||||||
|
# lazy not followed by import/from (not highlighted)
|
||||||
|
self._assert_highlighting('lazy = 1', {})
|
||||||
|
self._assert_highlighting('lazy foo', {})
|
||||||
|
|
||||||
def test_long_multiline_string(self):
|
def test_long_multiline_string(self):
|
||||||
source = textwrap.dedent('''\
|
source = textwrap.dedent('''\
|
||||||
"""a
|
"""a
|
||||||
|
|
|
||||||
|
|
@ -1359,6 +1359,12 @@ def _find_and_load_unlocked(name, import_):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
msg = f"Cannot set an attribute on {parent!r} for child module {child!r}"
|
msg = f"Cannot set an attribute on {parent!r} for child module {child!r}"
|
||||||
_warnings.warn(msg, ImportWarning)
|
_warnings.warn(msg, ImportWarning)
|
||||||
|
# Set attributes to lazy submodules on the module.
|
||||||
|
try:
|
||||||
|
_imp._set_lazy_attributes(module, name)
|
||||||
|
except Exception as e:
|
||||||
|
msg = f"Cannot set lazy attributes on {name!r}: {e!r}"
|
||||||
|
_warnings.warn(msg, ImportWarning)
|
||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
1
Lib/keyword.py
generated
1
Lib/keyword.py
generated
|
|
@ -56,6 +56,7 @@ kwlist = [
|
||||||
softkwlist = [
|
softkwlist = [
|
||||||
'_',
|
'_',
|
||||||
'case',
|
'case',
|
||||||
|
'lazy',
|
||||||
'match',
|
'match',
|
||||||
'type'
|
'type'
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
import re
|
import re
|
||||||
import __main__
|
import __main__
|
||||||
import warnings
|
import warnings
|
||||||
|
import types
|
||||||
|
|
||||||
__all__ = ["Completer"]
|
__all__ = ["Completer"]
|
||||||
|
|
||||||
|
|
@ -188,7 +189,16 @@ def attr_matches(self, text):
|
||||||
# property method, which is not desirable.
|
# property method, which is not desirable.
|
||||||
matches.append(match)
|
matches.append(match)
|
||||||
continue
|
continue
|
||||||
if (value := getattr(thisobject, word, None)) is not None:
|
|
||||||
|
if (isinstance(thisobject, types.ModuleType)
|
||||||
|
and
|
||||||
|
isinstance(thisobject.__dict__.get(word), types.LazyImportType)
|
||||||
|
):
|
||||||
|
value = thisobject.__dict__.get(word)
|
||||||
|
else:
|
||||||
|
value = getattr(thisobject, word, None)
|
||||||
|
|
||||||
|
if value is not None:
|
||||||
matches.append(self._callable_postfix(value, match))
|
matches.append(self._callable_postfix(value, match))
|
||||||
else:
|
else:
|
||||||
matches.append(match)
|
matches.append(match)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ extend-exclude = [
|
||||||
# New grammar constructions may not yet be recognized by Ruff,
|
# New grammar constructions may not yet be recognized by Ruff,
|
||||||
# and tests re-use the same names as only the grammar is being checked.
|
# and tests re-use the same names as only the grammar is being checked.
|
||||||
"test_grammar.py",
|
"test_grammar.py",
|
||||||
|
# Lazy import syntax (PEP 810) not yet supported by Ruff
|
||||||
|
"test_import/data/lazy_imports/*.py",
|
||||||
|
"test_import/data/lazy_imports/**/*.py",
|
||||||
]
|
]
|
||||||
|
|
||||||
[per-file-target-version]
|
[per-file-target-version]
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ BaseException
|
||||||
├── EOFError
|
├── EOFError
|
||||||
├── ExceptionGroup [BaseExceptionGroup]
|
├── ExceptionGroup [BaseExceptionGroup]
|
||||||
├── ImportError
|
├── ImportError
|
||||||
|
│ └── ImportCycleError
|
||||||
│ └── ModuleNotFoundError
|
│ └── ModuleNotFoundError
|
||||||
├── LookupError
|
├── LookupError
|
||||||
│ ├── IndexError
|
│ ├── IndexError
|
||||||
|
|
|
||||||
|
|
@ -69,10 +69,14 @@ Module(body=[Try(body=[Pass()], handlers=[ExceptHandler(type=Name(...), name='ex
|
||||||
Module(body=[TryStar(body=[Pass()], handlers=[ExceptHandler(type=Name(...), name='exc', body=[Pass(...)])], orelse=[Pass()], finalbody=[Pass()])], type_ignores=[])
|
Module(body=[TryStar(body=[Pass()], handlers=[ExceptHandler(type=Name(...), name='exc', body=[Pass(...)])], orelse=[Pass()], finalbody=[Pass()])], type_ignores=[])
|
||||||
Module(body=[Assert(test=Name(id='v', ctx=Load(...)), msg=None)], type_ignores=[])
|
Module(body=[Assert(test=Name(id='v', ctx=Load(...)), msg=None)], type_ignores=[])
|
||||||
Module(body=[Assert(test=Name(id='v', ctx=Load(...)), msg=Constant(value='message', kind=None))], type_ignores=[])
|
Module(body=[Assert(test=Name(id='v', ctx=Load(...)), msg=Constant(value='message', kind=None))], type_ignores=[])
|
||||||
Module(body=[Import(names=[alias(name='sys', asname=None)])], type_ignores=[])
|
Module(body=[Import(names=[alias(name='sys', asname=None)], is_lazy=0)], type_ignores=[])
|
||||||
Module(body=[Import(names=[alias(name='foo', asname='bar')])], type_ignores=[])
|
Module(body=[Import(names=[alias(name='foo', asname='bar')], is_lazy=0)], type_ignores=[])
|
||||||
Module(body=[ImportFrom(module='sys', names=[alias(name='x', asname='y')], level=0)], type_ignores=[])
|
Module(body=[ImportFrom(module='sys', names=[alias(name='x', asname='y')], level=0, is_lazy=0)], type_ignores=[])
|
||||||
Module(body=[ImportFrom(module='sys', names=[alias(name='v', asname=None)], level=0)], type_ignores=[])
|
Module(body=[ImportFrom(module='sys', names=[alias(name='v', asname=None)], level=0, is_lazy=0)], type_ignores=[])
|
||||||
|
Module(body=[Import(names=[alias(name='sys', asname=None)], is_lazy=1)], type_ignores=[])
|
||||||
|
Module(body=[Import(names=[alias(name='foo', asname='bar')], is_lazy=1)], type_ignores=[])
|
||||||
|
Module(body=[ImportFrom(module='sys', names=[alias(name='x', asname='y')], level=0, is_lazy=1)], type_ignores=[])
|
||||||
|
Module(body=[ImportFrom(module='sys', names=[alias(name='v', asname=None)], level=0, is_lazy=1)], type_ignores=[])
|
||||||
Module(body=[Global(names=['v'])], type_ignores=[])
|
Module(body=[Global(names=['v'])], type_ignores=[])
|
||||||
Module(body=[Expr(value=Constant(value=1, kind=None))], type_ignores=[])
|
Module(body=[Expr(value=Constant(value=1, kind=None))], type_ignores=[])
|
||||||
Module(body=[Pass()], type_ignores=[])
|
Module(body=[Pass()], type_ignores=[])
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,12 @@
|
||||||
# ImportFrom
|
# ImportFrom
|
||||||
"from sys import x as y",
|
"from sys import x as y",
|
||||||
"from sys import v",
|
"from sys import v",
|
||||||
|
# Lazy Import
|
||||||
|
"lazy import sys",
|
||||||
|
"lazy import foo as bar",
|
||||||
|
# Lazy ImportFrom
|
||||||
|
"lazy from sys import x as y",
|
||||||
|
"lazy from sys import v",
|
||||||
# Global
|
# Global
|
||||||
"global v",
|
"global v",
|
||||||
# Expr
|
# Expr
|
||||||
|
|
@ -460,10 +466,14 @@ def main():
|
||||||
('Module', [('TryStar', (1, 0, 7, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 8, 3, 17), 'Exception', ('Load',)), 'exc', [('Pass', (4, 2, 4, 6))])], [('Pass', (5, 7, 5, 11))], [('Pass', (7, 2, 7, 6))])], []),
|
('Module', [('TryStar', (1, 0, 7, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 8, 3, 17), 'Exception', ('Load',)), 'exc', [('Pass', (4, 2, 4, 6))])], [('Pass', (5, 7, 5, 11))], [('Pass', (7, 2, 7, 6))])], []),
|
||||||
('Module', [('Assert', (1, 0, 1, 8), ('Name', (1, 7, 1, 8), 'v', ('Load',)), None)], []),
|
('Module', [('Assert', (1, 0, 1, 8), ('Name', (1, 7, 1, 8), 'v', ('Load',)), None)], []),
|
||||||
('Module', [('Assert', (1, 0, 1, 19), ('Name', (1, 7, 1, 8), 'v', ('Load',)), ('Constant', (1, 10, 1, 19), 'message', None))], []),
|
('Module', [('Assert', (1, 0, 1, 19), ('Name', (1, 7, 1, 8), 'v', ('Load',)), ('Constant', (1, 10, 1, 19), 'message', None))], []),
|
||||||
('Module', [('Import', (1, 0, 1, 10), [('alias', (1, 7, 1, 10), 'sys', None)])], []),
|
('Module', [('Import', (1, 0, 1, 10), [('alias', (1, 7, 1, 10), 'sys', None)], 0)], []),
|
||||||
('Module', [('Import', (1, 0, 1, 17), [('alias', (1, 7, 1, 17), 'foo', 'bar')])], []),
|
('Module', [('Import', (1, 0, 1, 17), [('alias', (1, 7, 1, 17), 'foo', 'bar')], 0)], []),
|
||||||
('Module', [('ImportFrom', (1, 0, 1, 22), 'sys', [('alias', (1, 16, 1, 22), 'x', 'y')], 0)], []),
|
('Module', [('ImportFrom', (1, 0, 1, 22), 'sys', [('alias', (1, 16, 1, 22), 'x', 'y')], 0, 0)], []),
|
||||||
('Module', [('ImportFrom', (1, 0, 1, 17), 'sys', [('alias', (1, 16, 1, 17), 'v', None)], 0)], []),
|
('Module', [('ImportFrom', (1, 0, 1, 17), 'sys', [('alias', (1, 16, 1, 17), 'v', None)], 0, 0)], []),
|
||||||
|
('Module', [('Import', (1, 0, 1, 15), [('alias', (1, 12, 1, 15), 'sys', None)], 1)], []),
|
||||||
|
('Module', [('Import', (1, 0, 1, 22), [('alias', (1, 12, 1, 22), 'foo', 'bar')], 1)], []),
|
||||||
|
('Module', [('ImportFrom', (1, 0, 1, 27), 'sys', [('alias', (1, 21, 1, 27), 'x', 'y')], 0, 1)], []),
|
||||||
|
('Module', [('ImportFrom', (1, 0, 1, 22), 'sys', [('alias', (1, 21, 1, 22), 'v', None)], 0, 1)], []),
|
||||||
('Module', [('Global', (1, 0, 1, 8), ['v'])], []),
|
('Module', [('Global', (1, 0, 1, 8), ['v'])], []),
|
||||||
('Module', [('Expr', (1, 0, 1, 1), ('Constant', (1, 0, 1, 1), 1, None))], []),
|
('Module', [('Expr', (1, 0, 1, 1), ('Constant', (1, 0, 1, 1), 1, None))], []),
|
||||||
('Module', [('Pass', (1, 0, 1, 4))], []),
|
('Module', [('Pass', (1, 0, 1, 4))], []),
|
||||||
|
|
|
||||||
|
|
@ -1692,8 +1692,8 @@ def check_text(code, empty, full, **kwargs):
|
||||||
|
|
||||||
check_text(
|
check_text(
|
||||||
"import _ast as ast; from module import sub",
|
"import _ast as ast; from module import sub",
|
||||||
empty="Module(body=[Import(names=[alias(name='_ast', asname='ast')]), ImportFrom(module='module', names=[alias(name='sub')], level=0)])",
|
empty="Module(body=[Import(names=[alias(name='_ast', asname='ast')], is_lazy=0), ImportFrom(module='module', names=[alias(name='sub')], level=0, is_lazy=0)])",
|
||||||
full="Module(body=[Import(names=[alias(name='_ast', asname='ast')]), ImportFrom(module='module', names=[alias(name='sub')], level=0)], type_ignores=[])",
|
full="Module(body=[Import(names=[alias(name='_ast', asname='ast')], is_lazy=0), ImportFrom(module='module', names=[alias(name='sub')], level=0, is_lazy=0)], type_ignores=[])",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_copy_location(self):
|
def test_copy_location(self):
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ def test_config_get(self):
|
||||||
("int_max_str_digits", int, None),
|
("int_max_str_digits", int, None),
|
||||||
("interactive", bool, None),
|
("interactive", bool, None),
|
||||||
("isolated", bool, None),
|
("isolated", bool, None),
|
||||||
|
("lazy_imports", int, None),
|
||||||
("malloc_stats", bool, None),
|
("malloc_stats", bool, None),
|
||||||
("module_search_paths", list[str], "path"),
|
("module_search_paths", list[str], "path"),
|
||||||
("optimization_level", int, None),
|
("optimization_level", int, None),
|
||||||
|
|
|
||||||
|
|
@ -292,7 +292,7 @@ def wrap_func_w_kwargs():
|
||||||
|
|
||||||
1 LOAD_SMALL_INT 0
|
1 LOAD_SMALL_INT 0
|
||||||
LOAD_CONST 1 (('*',))
|
LOAD_CONST 1 (('*',))
|
||||||
IMPORT_NAME 0 (math)
|
IMPORT_NAME 2 (math + eager)
|
||||||
CALL_INTRINSIC_1 2 (INTRINSIC_IMPORT_STAR)
|
CALL_INTRINSIC_1 2 (INTRINSIC_IMPORT_STAR)
|
||||||
POP_TOP
|
POP_TOP
|
||||||
LOAD_CONST 2 (None)
|
LOAD_CONST 2 (None)
|
||||||
|
|
|
||||||
|
|
@ -635,6 +635,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
'tracemalloc': 0,
|
'tracemalloc': 0,
|
||||||
'perf_profiling': 0,
|
'perf_profiling': 0,
|
||||||
'import_time': 0,
|
'import_time': 0,
|
||||||
|
'lazy_imports': -1,
|
||||||
'thread_inherit_context': DEFAULT_THREAD_INHERIT_CONTEXT,
|
'thread_inherit_context': DEFAULT_THREAD_INHERIT_CONTEXT,
|
||||||
'context_aware_warnings': DEFAULT_CONTEXT_AWARE_WARNINGS,
|
'context_aware_warnings': DEFAULT_CONTEXT_AWARE_WARNINGS,
|
||||||
'code_debug_ranges': True,
|
'code_debug_ranges': True,
|
||||||
|
|
|
||||||
4
Lib/test/test_import/data/lazy_imports/basic2.py
Normal file
4
Lib/test/test_import/data/lazy_imports/basic2.py
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
def f():
|
||||||
|
pass
|
||||||
|
|
||||||
|
x = 42
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
__lazy_modules__ = ['test.test_import.data.lazy_imports.basic2']
|
||||||
|
import test.test_import.data.lazy_imports.basic2
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
__lazy_modules__ = ['test.test_import.data.lazy_imports.basic2']
|
||||||
|
lazy from .basic2 import f
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
__lazy_modules__ = ['test.test_import.data.lazy_imports.basic2']
|
||||||
|
import test.test_import.data.lazy_imports.basic2
|
||||||
|
test.test_import.data.lazy_imports.basic2.f()
|
||||||
2
Lib/test/test_import/data/lazy_imports/basic_dir.py
Normal file
2
Lib/test/test_import/data/lazy_imports/basic_dir.py
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
lazy import test.test_import.data.lazy_imports.basic2
|
||||||
|
x = dir()
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
lazy from test.test_import.data.lazy_imports import basic2
|
||||||
1
Lib/test/test_import/data/lazy_imports/basic_unused.py
Normal file
1
Lib/test/test_import/data/lazy_imports/basic_unused.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
lazy import test.test_import.data.lazy_imports.basic2
|
||||||
3
Lib/test/test_import/data/lazy_imports/basic_used.py
Normal file
3
Lib/test/test_import/data/lazy_imports/basic_used.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
lazy import test.test_import.data.lazy_imports.basic2 as basic2
|
||||||
|
|
||||||
|
basic2.f()
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Module that exists but doesn't have expected attributes
|
||||||
|
x = 42
|
||||||
|
# No 'nonexistent_attr' here
|
||||||
2
Lib/test/test_import/data/lazy_imports/broken_module.py
Normal file
2
Lib/test/test_import/data/lazy_imports/broken_module.py
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Module that raises an error during import
|
||||||
|
raise ValueError("This module always fails to import")
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
__lazy_modules__ = ['test.test_import.data.lazy_imports.basic2']
|
||||||
|
def f():
|
||||||
|
import test.test_import.data.lazy_imports.basic2
|
||||||
|
|
||||||
|
f()
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
__lazy_modules__ = ['test.test_import.data.lazy_imports.basic2']
|
||||||
|
try:
|
||||||
|
import test.test_import.data.lazy_imports.basic2
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
basic = __lazy_import__('test.test_import.data.lazy_imports.basic2')
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def myimport(*args):
|
||||||
|
return sys.modules[__name__]
|
||||||
|
|
||||||
|
|
||||||
|
new_globals = dict(globals())
|
||||||
|
new_globals["__builtins__"] = {
|
||||||
|
"__import__": myimport,
|
||||||
|
}
|
||||||
|
basic2 = 42
|
||||||
|
basic = __lazy_import__("test.test_import.data.lazy_imports", fromlist="basic2", globals=new_globals)
|
||||||
|
basic
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
basic = __lazy_import__('test.test_import.data.lazy_imports', fromlist="basic2")
|
||||||
|
basic
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
def f():
|
||||||
|
import test.test_import.data.lazy_imports.basic2 as basic2
|
||||||
|
return basic2
|
||||||
10
Lib/test/test_import/data/lazy_imports/global_filter.py
Normal file
10
Lib/test/test_import/data/lazy_imports/global_filter.py
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def filter(module_name, imported_name, from_list):
|
||||||
|
assert module_name == __name__
|
||||||
|
assert imported_name == "test.test_import.data.lazy_imports.basic2"
|
||||||
|
return False
|
||||||
|
|
||||||
|
sys.set_lazy_imports_filter(filter)
|
||||||
|
|
||||||
|
lazy import test.test_import.data.lazy_imports.basic2 as basic2
|
||||||
11
Lib/test/test_import/data/lazy_imports/global_filter_from.py
Normal file
11
Lib/test/test_import/data/lazy_imports/global_filter_from.py
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
def filter(module_name, imported_name, from_list):
|
||||||
|
assert module_name == __name__
|
||||||
|
assert imported_name == "test.test_import.data.lazy_imports.basic2"
|
||||||
|
assert from_list == ['f']
|
||||||
|
return False
|
||||||
|
|
||||||
|
importlib.set_lazy_imports(None, filter)
|
||||||
|
|
||||||
|
lazy from import test.test_import.data.lazy_imports.basic2 import f
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
def filter(module_name, imported_name, from_list):
|
||||||
|
assert module_name == __name__
|
||||||
|
assert imported_name == "test.test_import.data.lazy_imports.basic2"
|
||||||
|
assert from_list == ['f']
|
||||||
|
return True
|
||||||
|
|
||||||
|
importlib.set_lazy_imports(None, filter)
|
||||||
|
|
||||||
|
lazy from import test.test_import.data.lazy_imports.basic2 import f
|
||||||
11
Lib/test/test_import/data/lazy_imports/global_filter_true.py
Normal file
11
Lib/test/test_import/data/lazy_imports/global_filter_true.py
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def filter(module_name, imported_name, from_list):
|
||||||
|
assert module_name == __name__
|
||||||
|
assert imported_name == "test.test_import.data.lazy_imports.basic2"
|
||||||
|
return True
|
||||||
|
|
||||||
|
sys.set_lazy_imports("normal")
|
||||||
|
sys.set_lazy_imports_filter(filter)
|
||||||
|
|
||||||
|
lazy import test.test_import.data.lazy_imports.basic2 as basic2
|
||||||
5
Lib/test/test_import/data/lazy_imports/global_off.py
Normal file
5
Lib/test/test_import/data/lazy_imports/global_off.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.set_lazy_imports("none")
|
||||||
|
|
||||||
|
lazy import test.test_import.data.lazy_imports.basic2 as basic2
|
||||||
5
Lib/test/test_import/data/lazy_imports/global_on.py
Normal file
5
Lib/test/test_import/data/lazy_imports/global_on.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.set_lazy_imports("all")
|
||||||
|
|
||||||
|
import test.test_import.data.lazy_imports.basic2 as basic2
|
||||||
9
Lib/test/test_import/data/lazy_imports/globals_access.py
Normal file
9
Lib/test/test_import/data/lazy_imports/globals_access.py
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Test that globals() returns lazy proxy objects without reifying
|
||||||
|
lazy import test.test_import.data.lazy_imports.basic2 as basic2
|
||||||
|
|
||||||
|
def get_from_globals():
|
||||||
|
g = globals()
|
||||||
|
return g['basic2']
|
||||||
|
|
||||||
|
def get_direct():
|
||||||
|
return basic2
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# SyntaxError: lazy import inside class body is not allowed
|
||||||
|
class Foo:
|
||||||
|
lazy import json
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Test __lazy_modules__ with from imports
|
||||||
|
__lazy_modules__ = ['test.test_import.data.lazy_imports.basic2']
|
||||||
|
from test.test_import.data.lazy_imports.basic2 import x, f
|
||||||
|
|
||||||
|
def get_x():
|
||||||
|
return x
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
lazy from __future__ import annotations
|
||||||
7
Lib/test/test_import/data/lazy_imports/lazy_get_value.py
Normal file
7
Lib/test/test_import/data/lazy_imports/lazy_get_value.py
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
lazy import test.test_import.data.lazy_imports.basic2 as basic2
|
||||||
|
|
||||||
|
def f():
|
||||||
|
x = globals()
|
||||||
|
return x['basic2'].resolve()
|
||||||
|
|
||||||
|
f()
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
def f():
|
||||||
|
lazy import foo
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
lazy import test.test_import.data.lazy_imports.pkg.bar
|
||||||
|
x = test.test_import.data.lazy_imports.pkg.bar.f
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
try:
|
||||||
|
lazy import foo
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
try:
|
||||||
|
lazy from foo import bar
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
lazy from foo import *
|
||||||
3
Lib/test/test_import/data/lazy_imports/lazy_with.py
Normal file
3
Lib/test/test_import/data/lazy_imports/lazy_with.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import contextlib
|
||||||
|
with contextlib.nullcontext():
|
||||||
|
lazy import test.test_import.data.lazy_imports.basic2
|
||||||
3
Lib/test/test_import/data/lazy_imports/lazy_with_from.py
Normal file
3
Lib/test/test_import/data/lazy_imports/lazy_with_from.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import contextlib
|
||||||
|
with contextlib.nullcontext():
|
||||||
|
lazy import test.test_import.data.lazy_imports.basic2 as basic2
|
||||||
5
Lib/test/test_import/data/lazy_imports/modules_dict.py
Normal file
5
Lib/test/test_import/data/lazy_imports/modules_dict.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
lazy import test.test_import.data.lazy_imports.basic2 as basic2
|
||||||
|
|
||||||
|
import sys
|
||||||
|
mod = sys.modules[__name__]
|
||||||
|
x = mod.__dict__
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
lazy import test.test_import.data.lazy_imports.basic2 as basic2
|
||||||
|
|
||||||
|
import sys
|
||||||
|
mod = sys.modules[__name__]
|
||||||
|
x = mod.basic2
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
lazy import test.test_import.data.lazy_imports.basic2 as basic2
|
||||||
|
|
||||||
|
import sys
|
||||||
|
mod = sys.modules[__name__]
|
||||||
|
x = mod.__name__
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Test that lazy from import with multiple names only reifies accessed names
|
||||||
|
lazy from test.test_import.data.lazy_imports.basic2 import f, x
|
||||||
|
|
||||||
|
def get_globals():
|
||||||
|
return globals()
|
||||||
1
Lib/test/test_import/data/lazy_imports/pkg/__init__.py
Normal file
1
Lib/test/test_import/data/lazy_imports/pkg/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
x = 42
|
||||||
2
Lib/test/test_import/data/lazy_imports/pkg/b.py
Normal file
2
Lib/test/test_import/data/lazy_imports/pkg/b.py
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
def foo():
|
||||||
|
return 'foo'
|
||||||
1
Lib/test/test_import/data/lazy_imports/pkg/bar.py
Normal file
1
Lib/test/test_import/data/lazy_imports/pkg/bar.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
def f(): pass
|
||||||
4
Lib/test/test_import/data/lazy_imports/pkg/c.py
Normal file
4
Lib/test/test_import/data/lazy_imports/pkg/c.py
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
lazy from . import b, x
|
||||||
|
|
||||||
|
def get_globals():
|
||||||
|
return globals()
|
||||||
5
Lib/test/test_import/data/lazy_imports/relative_lazy.py
Normal file
5
Lib/test/test_import/data/lazy_imports/relative_lazy.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Test relative imports with lazy keyword
|
||||||
|
lazy from . import basic2
|
||||||
|
|
||||||
|
def get_basic2():
|
||||||
|
return basic2
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Test relative from imports with lazy keyword
|
||||||
|
lazy from .basic2 import x, f
|
||||||
|
|
||||||
|
def get_x():
|
||||||
|
return x
|
||||||
|
|
||||||
|
def get_f():
|
||||||
|
return f
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
try:
|
||||||
|
import test.test_import.data.lazy_imports.basic2
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
try:
|
||||||
|
from test.test_import.data.lazy_imports.basic2 import f
|
||||||
|
except:
|
||||||
|
pass
|
||||||
1596
Lib/test/test_import/test_lazy_imports.py
Normal file
1596
Lib/test/test_import/test_lazy_imports.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -89,10 +89,14 @@ def test_gen_colors_keyword_highlighting(self):
|
||||||
("obj.list", [(".", "op")]),
|
("obj.list", [(".", "op")]),
|
||||||
("obj.match", [(".", "op")]),
|
("obj.match", [(".", "op")]),
|
||||||
("b. \\\n format", [(".", "op")]),
|
("b. \\\n format", [(".", "op")]),
|
||||||
|
("lazy", []),
|
||||||
|
("lazy()", [('(', 'op'), (')', 'op')]),
|
||||||
# highlights
|
# highlights
|
||||||
("set", [("set", "builtin")]),
|
("set", [("set", "builtin")]),
|
||||||
("list", [("list", "builtin")]),
|
("list", [("list", "builtin")]),
|
||||||
(" \n dict", [("dict", "builtin")]),
|
(" \n dict", [("dict", "builtin")]),
|
||||||
|
(" lazy import", [("lazy", "soft_keyword"), ("import", "keyword")]),
|
||||||
|
("lazy from cool_people import pablo", [('lazy', 'soft_keyword'), ('from', 'keyword'), ('import', 'keyword')])
|
||||||
]
|
]
|
||||||
for code, expected_highlights in cases:
|
for code, expected_highlights in cases:
|
||||||
with self.subTest(code=code):
|
with self.subTest(code=code):
|
||||||
|
|
|
||||||
|
|
@ -3461,6 +3461,119 @@ def test_ifexp_body_stmt_else_stmt(self):
|
||||||
]:
|
]:
|
||||||
self._check_error(f"x = {lhs_stmt} if 1 else {rhs_stmt}", msg)
|
self._check_error(f"x = {lhs_stmt} if 1 else {rhs_stmt}", msg)
|
||||||
|
|
||||||
|
|
||||||
|
class LazyImportRestrictionTestCase(SyntaxErrorTestCase):
|
||||||
|
"""Test syntax restrictions for lazy imports."""
|
||||||
|
|
||||||
|
def test_lazy_import_in_try_block(self):
|
||||||
|
"""Test that lazy imports are not allowed inside try blocks."""
|
||||||
|
self._check_error("""\
|
||||||
|
try:
|
||||||
|
lazy import os
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
""", "lazy import not allowed inside try/except blocks")
|
||||||
|
|
||||||
|
self._check_error("""\
|
||||||
|
try:
|
||||||
|
lazy from sys import path
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
""", "lazy from ... import not allowed inside try/except blocks")
|
||||||
|
|
||||||
|
def test_lazy_import_in_trystar_block(self):
|
||||||
|
"""Test that lazy imports are not allowed inside try* blocks."""
|
||||||
|
self._check_error("""\
|
||||||
|
try:
|
||||||
|
lazy import json
|
||||||
|
except* Exception:
|
||||||
|
pass
|
||||||
|
""", "lazy import not allowed inside try/except blocks")
|
||||||
|
|
||||||
|
self._check_error("""\
|
||||||
|
try:
|
||||||
|
lazy from collections import defaultdict
|
||||||
|
except* ImportError:
|
||||||
|
pass
|
||||||
|
""", "lazy from ... import not allowed inside try/except blocks")
|
||||||
|
|
||||||
|
def test_lazy_import_in_function(self):
|
||||||
|
"""Test that lazy imports are not allowed inside functions."""
|
||||||
|
self._check_error("""\
|
||||||
|
def func():
|
||||||
|
lazy import math
|
||||||
|
""", "lazy import not allowed inside functions")
|
||||||
|
|
||||||
|
self._check_error("""\
|
||||||
|
def func():
|
||||||
|
lazy from datetime import datetime
|
||||||
|
""", "lazy from ... import not allowed inside functions")
|
||||||
|
|
||||||
|
def test_lazy_import_in_async_function(self):
|
||||||
|
"""Test that lazy imports are not allowed inside async functions."""
|
||||||
|
self._check_error("""\
|
||||||
|
async def async_func():
|
||||||
|
lazy import asyncio
|
||||||
|
""", "lazy import not allowed inside functions")
|
||||||
|
|
||||||
|
self._check_error("""\
|
||||||
|
async def async_func():
|
||||||
|
lazy from json import loads
|
||||||
|
""", "lazy from ... import not allowed inside functions")
|
||||||
|
|
||||||
|
def test_lazy_import_in_class(self):
|
||||||
|
"""Test that lazy imports are not allowed inside classes."""
|
||||||
|
self._check_error("""\
|
||||||
|
class MyClass:
|
||||||
|
lazy import typing
|
||||||
|
""", "lazy import not allowed inside classes")
|
||||||
|
|
||||||
|
self._check_error("""\
|
||||||
|
class MyClass:
|
||||||
|
lazy from abc import ABC
|
||||||
|
""", "lazy from ... import not allowed inside classes")
|
||||||
|
|
||||||
|
def test_lazy_import_star_forbidden(self):
|
||||||
|
"""Test that 'lazy from ... import *' is forbidden everywhere."""
|
||||||
|
# At module level should also be forbidden
|
||||||
|
self._check_error("lazy from os import *",
|
||||||
|
"lazy from ... import \\* is not allowed")
|
||||||
|
|
||||||
|
# Inside function should give lazy function error first
|
||||||
|
self._check_error("""\
|
||||||
|
def func():
|
||||||
|
lazy from sys import *
|
||||||
|
""", "lazy from ... import not allowed inside functions")
|
||||||
|
|
||||||
|
def test_lazy_import_nested_scopes(self):
|
||||||
|
"""Test lazy imports in nested scopes."""
|
||||||
|
self._check_error("""\
|
||||||
|
class Outer:
|
||||||
|
def method(self):
|
||||||
|
lazy import sys
|
||||||
|
""", "lazy import not allowed inside functions")
|
||||||
|
|
||||||
|
self._check_error("""\
|
||||||
|
def outer():
|
||||||
|
class Inner:
|
||||||
|
lazy import json
|
||||||
|
""", "lazy import not allowed inside classes")
|
||||||
|
|
||||||
|
self._check_error("""\
|
||||||
|
def outer():
|
||||||
|
def inner():
|
||||||
|
lazy from collections import deque
|
||||||
|
""", "lazy from ... import not allowed inside functions")
|
||||||
|
|
||||||
|
def test_lazy_import_valid_cases(self):
|
||||||
|
"""Test that lazy imports work at module level."""
|
||||||
|
# These should compile without errors
|
||||||
|
compile("lazy import os", "<test>", "exec")
|
||||||
|
compile("lazy from sys import path", "<test>", "exec")
|
||||||
|
compile("lazy import json as j", "<test>", "exec")
|
||||||
|
compile("lazy from datetime import datetime as dt", "<test>", "exec")
|
||||||
|
|
||||||
|
|
||||||
def load_tests(loader, tests, pattern):
|
def load_tests(loader, tests, pattern):
|
||||||
tests.addTest(doctest.DocTestSuite())
|
tests.addTest(doctest.DocTestSuite())
|
||||||
return tests
|
return tests
|
||||||
|
|
|
||||||
|
|
@ -866,7 +866,8 @@ def test_sys_flags(self):
|
||||||
"dont_write_bytecode", "no_user_site", "no_site",
|
"dont_write_bytecode", "no_user_site", "no_site",
|
||||||
"ignore_environment", "verbose", "bytes_warning", "quiet",
|
"ignore_environment", "verbose", "bytes_warning", "quiet",
|
||||||
"hash_randomization", "isolated", "dev_mode", "utf8_mode",
|
"hash_randomization", "isolated", "dev_mode", "utf8_mode",
|
||||||
"warn_default_encoding", "safe_path", "int_max_str_digits")
|
"warn_default_encoding", "safe_path", "int_max_str_digits",
|
||||||
|
"lazy_imports")
|
||||||
for attr in attrs:
|
for attr in attrs:
|
||||||
self.assertHasAttr(sys.flags, attr)
|
self.assertHasAttr(sys.flags, attr)
|
||||||
attr_type = bool if attr in ("dev_mode", "safe_path") else int
|
attr_type = bool if attr in ("dev_mode", "safe_path") else int
|
||||||
|
|
@ -1726,7 +1727,7 @@ def get_gen(): yield 1
|
||||||
md_gil = '?'
|
md_gil = '?'
|
||||||
else:
|
else:
|
||||||
md_gil = ''
|
md_gil = ''
|
||||||
check(unittest, size('PPPP?' + md_gil + 'NPPPPP'))
|
check(unittest, size('PPPPI?' + md_gil + 'NPPPPP'))
|
||||||
# None
|
# None
|
||||||
check(None, size(''))
|
check(None, size(''))
|
||||||
# NotImplementedType
|
# NotImplementedType
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ def test_names(self):
|
||||||
'CoroutineType', 'EllipsisType', 'FrameType', 'FunctionType',
|
'CoroutineType', 'EllipsisType', 'FrameType', 'FunctionType',
|
||||||
'FrameLocalsProxyType',
|
'FrameLocalsProxyType',
|
||||||
'GeneratorType', 'GenericAlias', 'GetSetDescriptorType',
|
'GeneratorType', 'GenericAlias', 'GetSetDescriptorType',
|
||||||
'LambdaType', 'MappingProxyType', 'MemberDescriptorType',
|
'LambdaType', 'LazyImportType', 'MappingProxyType', 'MemberDescriptorType',
|
||||||
'MethodDescriptorType', 'MethodType', 'MethodWrapperType',
|
'MethodDescriptorType', 'MethodType', 'MethodWrapperType',
|
||||||
'ModuleType', 'NoneType', 'NotImplementedType', 'SimpleNamespace',
|
'ModuleType', 'NoneType', 'NotImplementedType', 'SimpleNamespace',
|
||||||
'TracebackType', 'UnionType', 'WrapperDescriptorType',
|
'TracebackType', 'UnionType', 'WrapperDescriptorType',
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,10 @@ def _m(self): pass
|
||||||
# CapsuleType cannot be accessed from pure Python,
|
# CapsuleType cannot be accessed from pure Python,
|
||||||
# so there is no fallback definition.
|
# so there is no fallback definition.
|
||||||
|
|
||||||
|
exec("lazy import sys as _lazy_sys", _lz := {})
|
||||||
|
LazyImportType = type(_lz['_lazy_sys'])
|
||||||
|
del _lz
|
||||||
|
|
||||||
del sys, _f, _g, _C, _c, _ag, _cell_factory # Not for export
|
del sys, _f, _g, _C, _c, _ag, _cell_factory # Not for export
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -539,6 +539,7 @@ OBJECT_OBJS= \
|
||||||
Objects/funcobject.o \
|
Objects/funcobject.o \
|
||||||
Objects/interpolationobject.o \
|
Objects/interpolationobject.o \
|
||||||
Objects/iterobject.o \
|
Objects/iterobject.o \
|
||||||
|
Objects/lazyimportobject.o \
|
||||||
Objects/listobject.o \
|
Objects/listobject.o \
|
||||||
Objects/longobject.o \
|
Objects/longobject.o \
|
||||||
Objects/dictobject.o \
|
Objects/dictobject.o \
|
||||||
|
|
@ -1372,6 +1373,7 @@ PYTHON_HEADERS= \
|
||||||
$(srcdir)/Include/internal/pycore_interpolation.h \
|
$(srcdir)/Include/internal/pycore_interpolation.h \
|
||||||
$(srcdir)/Include/internal/pycore_intrinsics.h \
|
$(srcdir)/Include/internal/pycore_intrinsics.h \
|
||||||
$(srcdir)/Include/internal/pycore_jit.h \
|
$(srcdir)/Include/internal/pycore_jit.h \
|
||||||
|
$(srcdir)/Include/internal/pycore_lazyimportobject.h \
|
||||||
$(srcdir)/Include/internal/pycore_list.h \
|
$(srcdir)/Include/internal/pycore_list.h \
|
||||||
$(srcdir)/Include/internal/pycore_llist.h \
|
$(srcdir)/Include/internal/pycore_llist.h \
|
||||||
$(srcdir)/Include/internal/pycore_lock.h \
|
$(srcdir)/Include/internal/pycore_lock.h \
|
||||||
|
|
@ -2652,6 +2654,8 @@ TESTSUBDIRS= idlelib/idle_test \
|
||||||
test/test_import/data/package3 \
|
test/test_import/data/package3 \
|
||||||
test/test_import/data/package4 \
|
test/test_import/data/package4 \
|
||||||
test/test_import/data/unwritable \
|
test/test_import/data/unwritable \
|
||||||
|
test/test_import/data/lazy_imports \
|
||||||
|
test/test_import/data/lazy_imports/pkg \
|
||||||
test/test_importlib \
|
test/test_importlib \
|
||||||
test/test_importlib/builtin \
|
test/test_importlib/builtin \
|
||||||
test/test_importlib/extension \
|
test/test_importlib/extension \
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Implement :pep:`810`. Patch by Pablo Galindo and Dino Viehland.
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#include "pycore_descrobject.h" // _PyMethodWrapper_Type
|
#include "pycore_descrobject.h" // _PyMethodWrapper_Type
|
||||||
|
#include "pycore_lazyimportobject.h" // PyLazyImport_Type
|
||||||
#include "pycore_namespace.h" // _PyNamespace_Type
|
#include "pycore_namespace.h" // _PyNamespace_Type
|
||||||
#include "pycore_object.h" // _PyNone_Type, _PyNotImplemented_Type
|
#include "pycore_object.h" // _PyNone_Type, _PyNotImplemented_Type
|
||||||
#include "pycore_unionobject.h" // _PyUnion_Type
|
#include "pycore_unionobject.h" // _PyUnion_Type
|
||||||
|
|
@ -35,6 +36,7 @@ _types_exec(PyObject *m)
|
||||||
EXPORT_STATIC_TYPE("GetSetDescriptorType", PyGetSetDescr_Type);
|
EXPORT_STATIC_TYPE("GetSetDescriptorType", PyGetSetDescr_Type);
|
||||||
// LambdaType is the same as FunctionType
|
// LambdaType is the same as FunctionType
|
||||||
EXPORT_STATIC_TYPE("LambdaType", PyFunction_Type);
|
EXPORT_STATIC_TYPE("LambdaType", PyFunction_Type);
|
||||||
|
EXPORT_STATIC_TYPE("LazyImportType", PyLazyImport_Type);
|
||||||
EXPORT_STATIC_TYPE("MappingProxyType", PyDictProxy_Type);
|
EXPORT_STATIC_TYPE("MappingProxyType", PyDictProxy_Type);
|
||||||
EXPORT_STATIC_TYPE("MemberDescriptorType", PyMemberDescr_Type);
|
EXPORT_STATIC_TYPE("MemberDescriptorType", PyMemberDescr_Type);
|
||||||
EXPORT_STATIC_TYPE("MethodDescriptorType", PyMethodDescr_Type);
|
EXPORT_STATIC_TYPE("MethodDescriptorType", PyMethodDescr_Type);
|
||||||
|
|
|
||||||
|
|
@ -4694,6 +4694,14 @@ _PyDict_SizeOf_LockHeld(PyDictObject *mp)
|
||||||
return (Py_ssize_t)res;
|
return (Py_ssize_t)res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_PyDict_ClearKeysVersion(PyObject *mp)
|
||||||
|
{
|
||||||
|
ASSERT_DICT_LOCKED(mp);
|
||||||
|
|
||||||
|
FT_ATOMIC_STORE_UINT32_RELAXED(((PyDictObject *)mp)->ma_keys->dk_version, 0);
|
||||||
|
}
|
||||||
|
|
||||||
Py_ssize_t
|
Py_ssize_t
|
||||||
_PyDict_SizeOf(PyDictObject *mp)
|
_PyDict_SizeOf(PyDictObject *mp)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2022,6 +2022,12 @@ static PyTypeObject _PyExc_ImportError = {
|
||||||
};
|
};
|
||||||
PyObject *PyExc_ImportError = (PyObject *)&_PyExc_ImportError;
|
PyObject *PyExc_ImportError = (PyObject *)&_PyExc_ImportError;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ImportCycleError extends ImportError
|
||||||
|
*/
|
||||||
|
|
||||||
|
MiddlingExtendsException(PyExc_ImportError, ImportCycleError, ImportError,
|
||||||
|
"Import produces a cycle.");
|
||||||
/*
|
/*
|
||||||
* ModuleNotFoundError extends ImportError
|
* ModuleNotFoundError extends ImportError
|
||||||
*/
|
*/
|
||||||
|
|
@ -4455,6 +4461,7 @@ static struct static_exception static_exceptions[] = {
|
||||||
{&_PyExc_IncompleteInputError, "_IncompleteInputError"}, // base: SyntaxError(Exception)
|
{&_PyExc_IncompleteInputError, "_IncompleteInputError"}, // base: SyntaxError(Exception)
|
||||||
ITEM(IndexError), // base: LookupError(Exception)
|
ITEM(IndexError), // base: LookupError(Exception)
|
||||||
ITEM(KeyError), // base: LookupError(Exception)
|
ITEM(KeyError), // base: LookupError(Exception)
|
||||||
|
ITEM(ImportCycleError), // base: ImportError(Exception)
|
||||||
ITEM(ModuleNotFoundError), // base: ImportError(Exception)
|
ITEM(ModuleNotFoundError), // base: ImportError(Exception)
|
||||||
ITEM(NotImplementedError), // base: RuntimeError(Exception)
|
ITEM(NotImplementedError), // base: RuntimeError(Exception)
|
||||||
ITEM(PythonFinalizationError), // base: RuntimeError(Exception)
|
ITEM(PythonFinalizationError), // base: RuntimeError(Exception)
|
||||||
|
|
@ -4650,4 +4657,3 @@ _PyException_AddNote(PyObject *exc, PyObject *note)
|
||||||
Py_XDECREF(r);
|
Py_XDECREF(r);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue