Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/reusable-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ permissions:

env:
FORCE_COLOR: 1
IncludeUwp: >-
true

jobs:
build:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/stale.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ jobs:
uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-pr-message: 'This PR is stale because it has been open for 30 days with no activity.'
stale-pr-message: 'This PR is stale because it has been open for 90 days with no activity.'
stale-pr-label: 'stale'
days-before-issue-stale: -1
days-before-pr-stale: 30
days-before-pr-stale: 90
days-before-close: -1
ascending: true
operations-per-run: 120
5 changes: 0 additions & 5 deletions Doc/c-api/import.rst
Original file line number Diff line number Diff line change
Expand Up @@ -393,11 +393,6 @@ Importing Modules

Make all imports lazy by default.

.. c:enumerator:: PyImport_LAZY_NONE

Disable lazy imports entirely. Even explicit ``lazy`` statements become
eager imports.

.. versionadded:: 3.15

.. c:function:: PyObject* PyImport_CreateModuleFromInitfunc(PyObject *spec, PyObject* (*initfunc)(void))
Expand Down
19 changes: 19 additions & 0 deletions Doc/library/importlib.metadata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ You can also get a :ref:`distribution's version number <version>`, list its
current Python environment.


.. exception:: MetadataNotFound

Subclass of :class:`FileNotFoundError` raised when attempting to load metadata
from a distribution folder that is empty or otherwise does not contain a
metadata file.


Functional API
==============

Expand Down Expand Up @@ -224,6 +231,9 @@ Distribution metadata
Raises :exc:`PackageNotFoundError` if the named distribution
package is not installed in the current Python environment.

Raises :exc:`MetadataNotFound` if a distribution package is
present but no METADATA file is present.

.. class:: PackageMetadata

A concrete implementation of the
Expand Down Expand Up @@ -252,6 +262,12 @@ all the metadata in a JSON-compatible form per :PEP:`566`::
The full set of available metadata is not described here.
See the PyPA `Core metadata specification <https://packaging.python.org/en/latest/specifications/core-metadata/#core-metadata>`_ for additional details.

.. versionchanged:: 3.15
Previously and incidentally, if a METADATA file was missing from a distribution, an
empty ``PackageMetadata`` would be returned, indistinguishable from
an empty METADATA file. Now, a missing METADATA file triggers a
``MetadataNotFound`` exception.

.. versionchanged:: 3.10
The ``Description`` is now included in the metadata when presented
through the payload. Line continuation characters have been removed.
Expand Down Expand Up @@ -465,6 +481,9 @@ The same applies for :func:`entry_points` and :func:`files`.
.. attribute:: metadata
:type: PackageMetadata

Raises :exc:`MetadataNotFound` if the METADATA file is not present in
the distribution.

There are all kinds of additional metadata available on :class:`!Distribution`
instances as a :class:`PackageMetadata` instance::

Expand Down
24 changes: 18 additions & 6 deletions Doc/library/string.rst
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,9 @@ of a number respectively. It can be one of the following:
| | this option is not supported. |
+---------+----------------------------------------------------------+

For a locale aware separator, use the ``'n'`` presentation type instead.
For a locale-aware separator, use the ``'n'``
:ref:`float presentation type <n-format-float>` or
:ref:`integer presentation type <n-format-integer>` instead.

.. versionchanged:: 3.1
Added the ``','`` option (see also :pep:`378`).
Expand Down Expand Up @@ -518,9 +520,14 @@ The available integer presentation types are:
| | In case ``'#'`` is specified, the prefix ``'0x'`` will |
| | be upper-cased to ``'0X'`` as well. |
+---------+----------------------------------------------------------+
| ``'n'`` | Number. This is the same as ``'d'``, except that it uses |
| ``'n'`` | .. _n-format-integer: |
| | |
| | Number. This is the same as ``'d'``, except that it uses |
| | the current locale setting to insert the appropriate |
| | digit group separators. |
| | digit group separators. Note that the default locale is |
| | not the system locale. Depending on your use case, you |
| | may wish to set :const:`~locale.LC_NUMERIC` with |
| | :func:`locale.setlocale` before using ``'n'``. |
+---------+----------------------------------------------------------+
| None | The same as ``'d'``. |
+---------+----------------------------------------------------------+
Expand Down Expand Up @@ -603,10 +610,15 @@ The available presentation types for :class:`float` and
| | ``'E'`` if the number gets too large. The |
| | representations of infinity and NaN are uppercased, too. |
+---------+----------------------------------------------------------+
| ``'n'`` | Number. This is the same as ``'g'``, except that it uses |
| ``'n'`` | .. _n-format-float: |
| | |
| | Number. This is the same as ``'g'``, except that it uses |
| | the current locale setting to insert the appropriate |
| | digit group separators |
| | for the integral part of a number. |
| | digit group separators for the integral part of a |
| | number. Note that the default locale is not the system |
| | locale. Depending on your use case, you may wish to set |
| | :const:`~locale.LC_NUMERIC` with |
| | :func:`locale.setlocale` before using ``'n'``. |
+---------+----------------------------------------------------------+
| ``'%'`` | Percentage. Multiplies the number by 100 and displays |
| | in fixed (``'f'``) format, followed by a percent sign. |
Expand Down
4 changes: 0 additions & 4 deletions Doc/library/sys.rst
Original file line number Diff line number Diff line change
Expand Up @@ -919,8 +919,6 @@ always available. Unless explicitly noted otherwise, all variables are read-only
* ``"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`.

Expand Down Expand Up @@ -1757,8 +1755,6 @@ always available. Unless explicitly noted otherwise, all variables are read-only
* ``"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
Expand Down
4 changes: 0 additions & 4 deletions Doc/reference/simple_stmts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -965,10 +965,6 @@ Imports inside functions, class bodies, or
:keyword:`try`/:keyword:`except`/:keyword:`finally` blocks are always eager,
regardless of :attr:`!__lazy_modules__`.

Setting ``-X lazy_imports=none`` (or the :envvar:`PYTHON_LAZY_IMPORTS`
environment variable to ``none``) overrides :attr:`!__lazy_modules__` and
forces all imports to be eager.

.. versionadded:: 3.15

.. _future:
Expand Down
1 change: 1 addition & 0 deletions Doc/tools/removed-ids.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ using/configure.html: cmdoption-with-system-libmpdec
# Removed APIs
library/symtable.html: symtable.Class.get_methods
library/sys.html: sys._enablelegacywindowsfsencoding
c-api/import.html: c.PyImport_LazyImportsMode.PyImport_LAZY_NONE
14 changes: 6 additions & 8 deletions Doc/using/cmdline.rst
Original file line number Diff line number Diff line change
Expand Up @@ -705,10 +705,9 @@ Miscellaneous options

.. 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.
* :samp:`-X lazy_imports={all,normal}` controls lazy import behavior.
``all`` makes all imports lazy by default, and ``normal`` (the default)
respects the ``lazy`` keyword in source code.
See also :envvar:`PYTHON_LAZY_IMPORTS`.

.. versionadded:: 3.15
Expand Down Expand Up @@ -1413,10 +1412,9 @@ conflict.

.. 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.
Controls lazy import behavior. Accepts two values: ``all`` makes all
imports lazy by default, and ``normal`` (the default) respects the
``lazy`` keyword in source code.

See also the :option:`-X lazy_imports <-X>` command-line option.

Expand Down
9 changes: 4 additions & 5 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,10 @@ making it straightforward to diagnose and debug the failure.
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.
accept two values: ``all`` makes all imports lazy by default, 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.
Expand Down
3 changes: 1 addition & 2 deletions Include/import.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,7 @@ PyAPI_FUNC(int) PyImport_AppendInittab(

typedef enum {
PyImport_LAZY_NORMAL,
PyImport_LAZY_ALL,
PyImport_LAZY_NONE
PyImport_LAZY_ALL
} PyImport_LazyImportsMode;

#ifndef Py_LIMITED_API
Expand Down
3 changes: 2 additions & 1 deletion Include/internal/pycore_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
#define INTRINSIC_TYPEVARTUPLE 9
#define INTRINSIC_SUBSCRIPT_GENERIC 10
#define INTRINSIC_TYPEALIAS 11
#define INTRINSIC_BUILD_FROZENSET 12

#define MAX_INTRINSIC_1 11
#define MAX_INTRINSIC_1 12


/* Binary Functions: */
Expand Down
3 changes: 2 additions & 1 deletion Include/internal/pycore_opcode_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ extern "C" {
#define CONSTANT_TRUE 9
#define CONSTANT_FALSE 10
#define CONSTANT_MINUS_ONE 11
#define NUM_COMMON_CONSTANTS 12
#define CONSTANT_BUILTIN_FROZENSET 12
#define NUM_COMMON_CONSTANTS 13

/* Values used in the oparg for RESUME */
#define RESUME_AT_FUNC_START 0
Expand Down
3 changes: 3 additions & 0 deletions Include/internal/pycore_setobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ extern void _PySet_ClearInternal(PySetObject *so);

PyAPI_FUNC(int) _PySet_AddTakeRef(PySetObject *so, PyObject *key);

PyObject *
_PySet_Freeze(PyObject *set);

#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 1 addition & 1 deletion Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
builtins.set,
# Append-only — must match CONSTANT_* in
# Include/internal/pycore_opcode_utils.h.
None, "", True, False, -1]
None, "", True, False, -1, builtins.frozenset]
_nb_ops = _opcode.get_nb_ops()

hascompare = [opmap["COMPARE_OP"]]
Expand Down
23 changes: 14 additions & 9 deletions Lib/test/test_builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,10 @@ def f_list():
def f_set():
return set(2*x for x in [1,2,3])

funcs = [f_all, f_any, f_tuple, f_list, f_set]
def f_frozenset():
return frozenset(2*x for x in [1,2,3])

funcs = [f_all, f_any, f_tuple, f_list, f_set, f_frozenset]

for f in funcs:
# check that generator code object is not duplicated
Expand All @@ -278,35 +281,37 @@ def f_set():

# check the overriding the builtins works

global all, any, tuple, list, set
saved = all, any, tuple, list, set
global all, any, tuple, list, set, frozenset
saved = all, any, tuple, list, set, frozenset
try:
all = lambda x : "all"
any = lambda x : "any"
tuple = lambda x : "tuple"
list = lambda x : "list"
set = lambda x : "set"
frozenset = lambda x : "frozenset"

overridden_outputs = [f() for f in funcs]
finally:
all, any, tuple, list, set = saved
all, any, tuple, list, set, frozenset = saved

self.assertEqual(overridden_outputs, ['all', 'any', 'tuple', 'list', 'set'])
self.assertEqual(overridden_outputs, ['all', 'any', 'tuple', 'list', 'set', 'frozenset'])
# Now repeat, overriding the builtins module as well
saved = all, any, tuple, list, set
saved = all, any, tuple, list, set, frozenset
try:
builtins.all = all = lambda x : "all"
builtins.any = any = lambda x : "any"
builtins.tuple = tuple = lambda x : "tuple"
builtins.list = list = lambda x : "list"
builtins.set = set = lambda x : "set"
builtins.frozenset = frozenset = lambda x : "frozenset"

overridden_outputs = [f() for f in funcs]
finally:
all, any, tuple, list, set = saved
builtins.all, builtins.any, builtins.tuple, builtins.list, builtins.set = saved
all, any, tuple, list, set, frozenset = saved
builtins.all, builtins.any, builtins.tuple, builtins.list, builtins.set, builtins.frozenset = saved

self.assertEqual(overridden_outputs, ['all', 'any', 'tuple', 'list', 'set'])
self.assertEqual(overridden_outputs, ['all', 'any', 'tuple', 'list', 'set', 'frozenset'])

def test_builtin_call_async_genexpr_no_crash(self):
async def f_all():
Expand Down
31 changes: 31 additions & 0 deletions Lib/test/test_compiler_codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,34 @@ def test_syntax_error__return_not_in_function(self):
self.assertIsNone(cm.exception.text)
self.assertEqual(cm.exception.offset, 1)
self.assertEqual(cm.exception.end_offset, 10)

def test_frozenset_optimization(self):
l1 = self.Label()
snippet = "frozenset({1, 2, 3})"
expected = [
('RESUME', 0),
('ANNOTATIONS_PLACEHOLDER', None),
('LOAD_NAME', 0),
('COPY', 1),
('LOAD_COMMON_CONSTANT', 12),
('IS_OP', 0),
('POP_JUMP_IF_FALSE', l1),
('POP_TOP', None),
('LOAD_CONST', 1),
('LOAD_CONST', 2),
('LOAD_CONST', 3),
('BUILD_SET', 3),
('CALL_INTRINSIC_1', 12),
('JUMP', 0),
l1,
('PUSH_NULL', None),
('LOAD_CONST', 1),
('LOAD_CONST', 2),
('LOAD_CONST', 3),
('BUILD_SET', 3),
('CALL', 1),
('POP_TOP', None),
('LOAD_CONST', 0),
('RETURN_VALUE', None)
]
self.codegen_test(snippet, expected)
2 changes: 1 addition & 1 deletion Lib/test/test_free_threading/test_iteration.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
NUMITEMS = 1000
NUMTHREADS = 2
else:
NUMITEMS = 100000
NUMITEMS = 5000
NUMTHREADS = 5
NUMMUTATORS = 2

Expand Down
34 changes: 34 additions & 0 deletions Lib/test/test_free_threading/test_pickle.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,39 @@ def mutator():
with threading_helper.start_threads(threads):
pass

def test_pickle_dumps_with_concurrent_list_mutations(self):
# gh-149816: Pickling a list while another thread mutates it
# used to be a UAF in free-threaded mode. batch_list_exact()
# used PyList_GET_ITEM (borrowed) followed by Py_INCREF, and a
# concurrent replace/pop could free the item between those two
# operations.
shared = [list(range(20)) for _ in range(50)]

def dumper():
for _ in range(1000):
try:
pickle.dumps(shared)
except (RuntimeError, IndexError):
pass

def mutator():
for i in range(1000):
idx = i % 50
shared[idx] = list(range(i % 20))
if i % 10 == 0:
try:
shared.pop()
except IndexError:
pass
shared.append([i])

threads = []
for _ in range(10):
threads.append(threading.Thread(target=dumper))
threads.append(threading.Thread(target=mutator))

with threading_helper.start_threads(threads):
pass

if __name__ == "__main__":
unittest.main()
Loading
Loading