Skip to content

Commit 1429c9e

Browse files
Merge branch 'main' into urllib-parse-allow-none
2 parents ef79d2d + 4d21297 commit 1429c9e

20 files changed

Lines changed: 290 additions & 39 deletions

Doc/library/compression.zstd.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ Reading and writing compressed files
7373
argument is not None, a :exc:`!TypeError` will be raised.
7474

7575
When writing, the *options* argument can be a dictionary
76-
providing advanced decompression parameters; see
76+
providing advanced compression parameters; see
7777
:class:`CompressionParameter` for detailed information about supported
7878
parameters. The *level* argument is the compression level to use when
7979
writing compressed data. Only one of *level* or *options* may be non-None.
@@ -117,7 +117,7 @@ Reading and writing compressed files
117117
argument is not None, a :exc:`!TypeError` will be raised.
118118

119119
When writing, the *options* argument can be a dictionary
120-
providing advanced decompression parameters; see
120+
providing advanced compression parameters; see
121121
:class:`CompressionParameter` for detailed information about supported
122122
parameters. The *level* argument is the compression level to use when
123123
writing compressed data. Only one of *level* or *options* may be passed. The

Doc/library/importlib.metadata.rst

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,16 @@ Distributions
418418
equal, even if they relate to the same installed distribution and
419419
accordingly have the same attributes.
420420

421+
.. method:: discover(cls, *, context=None, **kwargs)
422+
423+
Returns an iterable of :class:`Distribution` instances for all packages.
424+
425+
The optional argument *context* is a :class:`DistributionFinder.Context`
426+
instance, used to modify the search for distributions. Alternatively,
427+
*kwargs* may contain keyword arguments for constructing a new
428+
:class:`!DistributionFinder.Context`.
429+
430+
421431
While the module level API described above is the most common and convenient usage,
422432
you can get all of that information from the :class:`!Distribution` class.
423433
:class:`!Distribution` is an abstract object that represents the metadata for
@@ -466,6 +476,61 @@ This metadata finder search defaults to ``sys.path``, but varies slightly in how
466476
- ``importlib.metadata`` does not honor :class:`bytes` objects on ``sys.path``.
467477
- ``importlib.metadata`` will incidentally honor :py:class:`pathlib.Path` objects on ``sys.path`` even though such values will be ignored for imports.
468478

479+
.. class:: DistributionFinder
480+
481+
A :class:`~importlib.abc.MetaPathFinder` subclass capable of discovering
482+
installed distributions.
483+
484+
Custom providers should implement this interface in order to
485+
supply metadata.
486+
487+
.. class:: Context(**kwargs)
488+
489+
A :class:`!Context` gives a custom provider a means to
490+
solicit additional details from the callers of distribution discovery
491+
functions like :func:`distributions` or :meth:`Distribution.discover`
492+
beyond :attr:`!.name` and :attr:`!.path` when searching
493+
for distributions.
494+
495+
For example, a provider could expose suites of packages in either a
496+
"public" or "private" ``realm``. A caller of distribution discovery
497+
functions may wish to query only for distributions in a particular realm
498+
and could call ``distributions(realm="private")`` to signal to the
499+
custom provider to only include distributions from that
500+
realm.
501+
502+
Each :class:`!DistributionFinder` must expect any parameters and should
503+
attempt to honor the canonical parameters defined below when
504+
appropriate.
505+
506+
See the section on :ref:`implementing-custom-providers` for more details.
507+
508+
.. attribute:: name
509+
510+
Specific name for which a distribution finder should match.
511+
512+
A :attr:`!.name` of ``None`` matches all distributions.
513+
514+
.. attribute:: path
515+
516+
A property providing the sequence of directory paths that a
517+
distribution finder should search.
518+
519+
Typically refers to Python installed package paths such as
520+
"site-packages" directories and defaults to :attr:`sys.path`.
521+
522+
523+
.. function:: distributions(**kwargs)
524+
525+
Returns an iterable of :class:`Distribution` instances for all packages.
526+
527+
The *kwargs* argument may contain either a keyword argument ``context``, a
528+
:class:`DistributionFinder.Context` instance, or pass keyword arguments for
529+
constructing a new :class:`!DistributionFinder.Context`. The
530+
:class:`!DistributionFinder.Context` is used to modify the search for
531+
distributions.
532+
533+
.. _implementing-custom-providers:
469534

470535
Implementing Custom Providers
471536
=============================
@@ -493,7 +558,7 @@ interface expected of finders by Python's import system.
493558
``importlib.metadata`` extends this protocol by looking for an optional
494559
``find_distributions`` callable on the finders from
495560
:data:`sys.meta_path` and presents this extended interface as the
496-
``DistributionFinder`` abstract base class, which defines this abstract
561+
:class:`DistributionFinder` abstract base class, which defines this abstract
497562
method::
498563

499564
@abc.abstractmethod
@@ -502,9 +567,11 @@ method::
502567
loading the metadata for packages for the indicated ``context``.
503568
"""
504569

505-
The ``DistributionFinder.Context`` object provides ``.path`` and ``.name``
506-
properties indicating the path to search and name to match and may
507-
supply other relevant context sought by the consumer.
570+
The :class:`DistributionFinder.Context` object provides
571+
:attr:`~DistributionFinder.Context.path` and
572+
:attr:`~DistributionFinder.Context.name` properties indicating the path to
573+
search and name to match and may supply other relevant context sought by the
574+
consumer.
508575

509576
In practice, to support finding distribution package
510577
metadata in locations other than the file system, subclass
@@ -529,7 +596,7 @@ Imagine a custom finder that loads Python modules from a database::
529596
That importer now presumably provides importable modules from a
530597
database, but it provides no metadata or entry points. For this
531598
custom importer to provide metadata, it would also need to implement
532-
``DistributionFinder``::
599+
:class:`DistributionFinder`::
533600

534601
from importlib.metadata import DistributionFinder
535602

Doc/reference/datamodel.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2617,7 +2617,7 @@ Notes on using *__slots__*:
26172617
* :exc:`TypeError` will be raised if *__slots__* other than *__dict__* and
26182618
*__weakref__* are defined for a class derived from a
26192619
:c:member:`"variable-length" built-in type <PyTypeObject.tp_itemsize>` such as
2620-
:class:`int`, :class:`bytes`, and :class:`tuple`.
2620+
:class:`int`, :class:`bytes`, and :class:`type`, except :class:`tuple`.
26212621

26222622
* Any non-string :term:`iterable` may be assigned to *__slots__*.
26232623

@@ -2642,6 +2642,7 @@ Notes on using *__slots__*:
26422642

26432643
.. versionchanged:: 3.15
26442644
Allowed defining the *__dict__* and *__weakref__* *__slots__* for any class.
2645+
Allowed defining any *__slots__* for a class derived from :class:`tuple`.
26452646

26462647

26472648
.. _class-customization:

Doc/whatsnew/3.15.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,10 @@ Other language changes
398398
for any class.
399399
(Contributed by Serhiy Storchaka in :gh:`41779`.)
400400

401+
* Allowed defining any :ref:`__slots__ <slots>` for a class derived from
402+
:class:`tuple` (including classes created by :func:`collections.namedtuple`).
403+
(Contributed by Serhiy Storchaka in :gh:`41779`.)
404+
401405

402406
New modules
403407
===========
@@ -1424,7 +1428,7 @@ that may require changes to your code.
14241428
:func:`resource.setrlimit` and :func:`resource.prlimit` is now deprecated.
14251429
(Contributed by Serhiy Storchaka in :gh:`137044`.)
14261430

1427-
* :meth:`~mmap.mmap.resize` has been removed on platforms that don't support the
1431+
* :meth:`mmap.mmap.resize` has been removed on platforms that don't support the
14281432
underlying syscall, instead of raising a :exc:`SystemError`.
14291433

14301434
* A resource warning is now emitted for an unclosed

Include/descrobject.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,14 @@ struct PyMemberDef {
8080
#define _Py_T_NONE 20 // Deprecated. Value is always None.
8181

8282
/* Flags */
83-
#define Py_READONLY 1
84-
#define Py_AUDIT_READ 2 // Added in 3.10, harmless no-op before that
85-
#define _Py_WRITE_RESTRICTED 4 // Deprecated, no-op. Do not reuse the value.
86-
#define Py_RELATIVE_OFFSET 8
83+
#define Py_READONLY (1 << 0)
84+
#define Py_AUDIT_READ (1 << 1) // Added in 3.10, harmless no-op before that
85+
#define _Py_WRITE_RESTRICTED (1 << 2) // Deprecated, no-op. Do not reuse the value.
86+
#define Py_RELATIVE_OFFSET (1 << 3)
87+
88+
#ifndef Py_LIMITED_API
89+
# define _Py_AFTER_ITEMS (1 << 4) // For internal use.
90+
#endif
8791

8892
PyAPI_FUNC(PyObject *) PyMember_GetOne(const char *, PyMemberDef *);
8993
PyAPI_FUNC(int) PyMember_SetOne(char *, PyMemberDef *, PyObject *);

Include/internal/pycore_descrobject.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ typedef propertyobject _PyPropertyObject;
2222

2323
extern PyTypeObject _PyMethodWrapper_Type;
2424

25+
extern void *_PyMember_GetOffset(PyObject *, PyMemberDef *);
26+
2527
#ifdef __cplusplus
2628
}
2729
#endif

Lib/_pyrepl/_module_completer.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,18 @@ def _find_modules(self, path: str, prefix: str) -> list[str]:
108108
return []
109109

110110
modules: Iterable[pkgutil.ModuleInfo] = self.global_cache
111+
imported_module = sys.modules.get(path.split('.')[0])
112+
if imported_module:
113+
# Filter modules to those who name and specs match the
114+
# imported module to avoid invalid suggestions
115+
spec = imported_module.__spec__
116+
if spec:
117+
modules = [mod for mod in modules
118+
if mod.name == spec.name
119+
and mod.module_finder.find_spec(mod.name, None) == spec]
120+
else:
121+
modules = []
122+
111123
is_stdlib_import: bool | None = None
112124
for segment in path.split('.'):
113125
modules = [mod_info for mod_info in modules
@@ -196,7 +208,6 @@ def global_cache(self) -> list[pkgutil.ModuleInfo]:
196208
"""Global module cache"""
197209
if not self._global_cache or self._curr_sys_path != sys.path:
198210
self._curr_sys_path = sys.path[:]
199-
# print('getting packages')
200211
self._global_cache = list(pkgutil.iter_modules())
201212
return self._global_cache
202213

Lib/pdb.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None,
364364
bdb.Bdb.__init__(self, skip=skip, backend=backend if backend else get_default_backend())
365365
cmd.Cmd.__init__(self, completekey, stdin, stdout)
366366
sys.audit("pdb.Pdb")
367-
if stdin:
367+
if stdin is not None and stdin is not sys.stdin:
368368
self.use_rawinput = False
369369
self.prompt = '(Pdb) '
370370
self.aliases = {}

Lib/test/test_descr.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,18 @@ class X(object):
13201320
with self.assertRaisesRegex(AttributeError, "'X' object has no attribute 'a'"):
13211321
X().a
13221322

1323+
def test_slots_after_items(self):
1324+
class C(tuple):
1325+
__slots__ = ['a']
1326+
x = C((1, 2, 3))
1327+
self.assertNotHasAttr(x, "__dict__")
1328+
self.assertNotHasAttr(x, "a")
1329+
x.a = 42
1330+
self.assertEqual(x.a, 42)
1331+
del x.a
1332+
self.assertNotHasAttr(x, "a")
1333+
self.assertEqual(x, (1, 2, 3))
1334+
13231335
def test_slots_special(self):
13241336
# Testing __dict__ and __weakref__ in __slots__...
13251337
class D(object):
@@ -1422,6 +1434,9 @@ class W(base):
14221434
self.assertIs(weakref.ref(a)(), a)
14231435
self.assertEqual(a, base(arg))
14241436

1437+
@support.subTests('base', [int, bytes] +
1438+
([_testcapi.HeapCCollection] if _testcapi else []))
1439+
def test_unsupported_slots(self, base):
14251440
with self.assertRaises(TypeError):
14261441
class X(base):
14271442
__slots__ = ['x']

Lib/test/test_os/test_os.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2627,6 +2627,8 @@ def test_execve_invalid_env(self):
26272627
# See https://github.com/python/cpython/issues/137934 and the other
26282628
# related issues for the reason why we cannot test this on Windows.
26292629
@unittest.skipIf(os.name == "nt", "POSIX-specific test")
2630+
@unittest.skipUnless(unix_shell and os.path.exists(unix_shell),
2631+
"requires a shell")
26302632
def test_execve_env_concurrent_mutation_with_fspath_posix(self):
26312633
# Prevent crash when mutating environment during parsing.
26322634
# Regression test for https://github.com/python/cpython/issues/143309.
@@ -2648,14 +2650,17 @@ def __len__(self): return 1
26482650
def keys(self): return KEYS
26492651
def values(self): return VALUES
26502652
2651-
args = [sys.executable, '-c', "print({message!r})"]
2653+
args = [{unix_shell!r}, '-c', 'echo \"{message!s}\"']
26522654
os.execve(args[0], args, MyEnv())
2653-
""".format(message=message)
2655+
""".format(unix_shell=unix_shell, message=message)
26542656

2655-
# Use '__cleanenv' to signal to assert_python_ok() not
2656-
# to do a copy of os.environ on its own.
2657-
rc, out, _ = assert_python_ok('-c', code, __cleanenv=True)
2658-
self.assertEqual(rc, 0)
2657+
# Make sure to forward "LD_*" variables so that assert_python_ok()
2658+
# can run correctly.
2659+
minimal = {k: v for k, v in os.environ.items() if k.startswith("LD_")}
2660+
with os_helper.EnvironmentVarGuard() as env:
2661+
env.clear()
2662+
env.update(minimal)
2663+
_, out, _ = assert_python_ok('-c', code, **env)
26592664
self.assertIn(bytes(message, "ascii"), out)
26602665

26612666
@unittest.skipUnless(sys.platform == "win32", "Win32-specific test")

0 commit comments

Comments
 (0)