diff --git a/Doc/library/unicodedata.rst b/Doc/library/unicodedata.rst
index 0aef597d064e0e..76f9a038181698 100644
--- a/Doc/library/unicodedata.rst
+++ b/Doc/library/unicodedata.rst
@@ -25,80 +25,133 @@ Standard Annex #44, `"Unicode Character Database"
`_. It defines the
following functions:
+.. seealso::
+
+ The :ref:`unicode-howto` for more information about Unicode and how to use
+ this module.
+
.. function:: lookup(name)
Look up character by name. If a character with the given name is found, return
the corresponding character. If not found, :exc:`KeyError` is raised.
+ For example::
+
+ >>> unicodedata.lookup('LEFT CURLY BRACKET')
+ '{'
+
+ The characters returned by this function are the same as those produced by
+ ``\N`` escape sequence in string literals. For example::
+
+ >>> unicodedata.lookup('MIDDLE DOT') == '\N{MIDDLE DOT}'
+ True
.. versionchanged:: 3.3
Support for name aliases [#]_ and named sequences [#]_ has been added.
-.. function:: name(chr[, default])
+.. function:: name(chr, default=None, /)
Returns the name assigned to the character *chr* as a string. If no
name is defined, *default* is returned, or, if not given, :exc:`ValueError` is
- raised.
+ raised. For example::
+
+ >>> unicodedata.name('½')
+ 'VULGAR FRACTION ONE HALF'
+ >>> unicodedata.name('\uFFFF', 'fallback')
+ 'fallback'
-.. function:: decimal(chr[, default])
+.. function:: decimal(chr, default=None, /)
Returns the decimal value assigned to the character *chr* as integer.
If no such value is defined, *default* is returned, or, if not given,
- :exc:`ValueError` is raised.
+ :exc:`ValueError` is raised. For example::
+ >>> unicodedata.decimal('\N{ARABIC-INDIC DIGIT NINE}')
+ 9
+ >>> unicodedata.decimal('\N{SUPERSCRIPT NINE}', -1)
+ -1
-.. function:: digit(chr[, default])
+
+.. function:: digit(chr, default=None, /)
Returns the digit value assigned to the character *chr* as integer.
If no such value is defined, *default* is returned, or, if not given,
- :exc:`ValueError` is raised.
+ :exc:`ValueError` is raised::
+
+ >>> unicodedata.digit('\N{SUPERSCRIPT NINE}')
+ 9
-.. function:: numeric(chr[, default])
+.. function:: numeric(chr, default=None, /)
Returns the numeric value assigned to the character *chr* as float.
If no such value is defined, *default* is returned, or, if not given,
- :exc:`ValueError` is raised.
+ :exc:`ValueError` is raised::
+
+ >>> unicodedata.numeric('½')
+ 0.5
.. function:: category(chr)
Returns the general category assigned to the character *chr* as
- string.
+ string. General category names consist of two letters.
+ See the `General Category Values section of the Unicode Character
+ Database documentation `_
+ for a list of category codes. For example::
+
+ >>> unicodedata.category('A') # 'L'etter, 'u'ppercase
+ 'Lu'
.. function:: bidirectional(chr)
Returns the bidirectional class assigned to the character *chr* as
string. If no such value is defined, an empty string is returned.
+ See the `Bidirectional Class Values section of the Unicode Character
+ Database `_
+ documentation for a list of bidirectional codes. For example::
+
+ >>> unicodedata.bidirectional('\N{ARABIC-INDIC DIGIT SEVEN}') # 'A'rabic, 'N'umber
+ 'AN'
.. function:: combining(chr)
Returns the canonical combining class assigned to the character *chr*
as integer. Returns ``0`` if no combining class is defined.
+ See the `Canonical Combining Class Values section of the Unicode Character
+ Database `_
+ for more information.
.. function:: east_asian_width(chr)
Returns the east asian width assigned to the character *chr* as
- string.
+ string. For a list of widths and or more information, see the
+ `Unicode Standard Annex #11 `_.
.. function:: mirrored(chr)
Returns the mirrored property assigned to the character *chr* as
integer. Returns ``1`` if the character has been identified as a "mirrored"
- character in bidirectional text, ``0`` otherwise.
+ character in bidirectional text, ``0`` otherwise. For example::
+
+ >>> unicodedata.mirrored('>')
+ 1
.. function:: decomposition(chr)
Returns the character decomposition mapping assigned to the character
*chr* as string. An empty string is returned in case no such mapping is
- defined.
+ defined. For example::
+
+ >>> unicodedata.decomposition('Ã')
+ '0041 0303'
.. function:: normalize(form, unistr)
@@ -122,9 +175,9 @@ following functions:
normally would be unified with other characters. For example, U+2160 (ROMAN
NUMERAL ONE) is really the same thing as U+0049 (LATIN CAPITAL LETTER I).
However, it is supported in Unicode for compatibility with existing character
- sets (e.g. gb2312).
+ sets (for example, gb2312).
- The normal form KD (NFKD) will apply the compatibility decomposition, i.e.
+ The normal form KD (NFKD) will apply the compatibility decomposition, that is,
replace all compatibility characters with their equivalents. The normal form KC
(NFKC) first applies the compatibility decomposition, followed by the canonical
composition.
@@ -133,6 +186,7 @@ following functions:
a human reader, if one has combining characters and the other
doesn't, they may not compare equal.
+
.. function:: is_normalized(form, unistr)
Return whether the Unicode string *unistr* is in the normal form *form*. Valid
@@ -154,24 +208,6 @@ In addition, the module exposes the following constant:
Unicode database version 3.2 instead, for applications that require this
specific version of the Unicode database (such as IDNA).
-Examples:
-
- >>> import unicodedata
- >>> unicodedata.lookup('LEFT CURLY BRACKET')
- '{'
- >>> unicodedata.name('/')
- 'SOLIDUS'
- >>> unicodedata.decimal('9')
- 9
- >>> unicodedata.decimal('a')
- Traceback (most recent call last):
- File "", line 1, in
- ValueError: not a decimal
- >>> unicodedata.category('A') # 'L'etter, 'u'ppercase
- 'Lu'
- >>> unicodedata.bidirectional('\u0660') # 'A'rabic, 'N'umber
- 'AN'
-
.. rubric:: Footnotes
diff --git a/Include/cpython/object.h b/Include/cpython/object.h
index 973d358ed8e4ec..b244c062c7679e 100644
--- a/Include/cpython/object.h
+++ b/Include/cpython/object.h
@@ -491,3 +491,7 @@ PyAPI_FUNC(int) PyUnstable_TryIncRef(PyObject *);
PyAPI_FUNC(void) PyUnstable_EnableTryIncRef(PyObject *);
PyAPI_FUNC(int) PyUnstable_Object_IsUniquelyReferenced(PyObject *);
+
+/* Utility for the tp_traverse slot of mutable heap types that have no other
+ * references. */
+PyAPI_FUNC(int) _PyObject_VisitType(PyObject *op, visitproc visit, void *arg);
diff --git a/Lib/importlib/metadata/__init__.py b/Lib/importlib/metadata/__init__.py
index b59587e80165e5..1e2cea4009482a 100644
--- a/Lib/importlib/metadata/__init__.py
+++ b/Lib/importlib/metadata/__init__.py
@@ -1,33 +1,40 @@
+"""
+APIs exposing metadata from third-party Python packages.
+
+This codebase is shared between importlib.metadata in the stdlib
+and importlib_metadata in PyPI. See
+https://github.com/python/importlib_metadata/wiki/Development-Methodology
+for more detail.
+"""
+
from __future__ import annotations
-import os
-import re
import abc
-import sys
-import json
+import collections
import email
-import types
-import inspect
-import pathlib
-import zipfile
-import operator
-import textwrap
import functools
import itertools
+import operator
+import os
+import pathlib
import posixpath
-import collections
+import re
+import sys
+import textwrap
+import types
+from collections.abc import Iterable, Mapping
+from contextlib import suppress
+from importlib import import_module
+from importlib.abc import MetaPathFinder
+from itertools import starmap
+from typing import Any
from . import _meta
from ._collections import FreezableDefaultDict, Pair
from ._functools import method_cache, pass_none
from ._itertools import always_iterable, bucket, unique_everseen
from ._meta import PackageMetadata, SimplePath
-
-from contextlib import suppress
-from importlib import import_module
-from importlib.abc import MetaPathFinder
-from itertools import starmap
-from typing import Any, Iterable, List, Mapping, Match, Optional, Set, cast
+from ._typing import md_none
__all__ = [
'Distribution',
@@ -53,7 +60,7 @@ def __str__(self) -> str:
return f"No package metadata was found for {self.name}"
@property
- def name(self) -> str: # type: ignore[override]
+ def name(self) -> str: # type: ignore[override] # make readonly
(name,) = self.args
return name
@@ -123,6 +130,12 @@ def valid(line: str):
return line and not line.startswith('#')
+class _EntryPointMatch(types.SimpleNamespace):
+ module: str
+ attr: str
+ extras: str
+
+
class EntryPoint:
"""An entry point as defined by Python packaging conventions.
@@ -138,6 +151,30 @@ class EntryPoint:
'attr'
>>> ep.extras
['extra1', 'extra2']
+
+ If the value package or module are not valid identifiers, a
+ ValueError is raised on access.
+
+ >>> EntryPoint(name=None, group=None, value='invalid-name').module
+ Traceback (most recent call last):
+ ...
+ ValueError: ('Invalid object reference...invalid-name...
+ >>> EntryPoint(name=None, group=None, value='invalid-name').attr
+ Traceback (most recent call last):
+ ...
+ ValueError: ('Invalid object reference...invalid-name...
+ >>> EntryPoint(name=None, group=None, value='invalid-name').extras
+ Traceback (most recent call last):
+ ...
+ ValueError: ('Invalid object reference...invalid-name...
+
+ The same thing happens on construction.
+
+ >>> EntryPoint(name=None, group=None, value='invalid-name')
+ Traceback (most recent call last):
+ ...
+ ValueError: ('Invalid object reference...invalid-name...
+
"""
pattern = re.compile(
@@ -165,38 +202,44 @@ class EntryPoint:
value: str
group: str
- dist: Optional[Distribution] = None
+ dist: Distribution | None = None
def __init__(self, name: str, value: str, group: str) -> None:
vars(self).update(name=name, value=value, group=group)
+ self.module
def load(self) -> Any:
"""Load the entry point from its definition. If only a module
is indicated by the value, return that module. Otherwise,
return the named object.
"""
- match = cast(Match, self.pattern.match(self.value))
- module = import_module(match.group('module'))
- attrs = filter(None, (match.group('attr') or '').split('.'))
+ module = import_module(self.module)
+ attrs = filter(None, (self.attr or '').split('.'))
return functools.reduce(getattr, attrs, module)
@property
def module(self) -> str:
- match = self.pattern.match(self.value)
- assert match is not None
- return match.group('module')
+ return self._match.module
@property
def attr(self) -> str:
- match = self.pattern.match(self.value)
- assert match is not None
- return match.group('attr')
+ return self._match.attr
@property
- def extras(self) -> List[str]:
+ def extras(self) -> list[str]:
+ return re.findall(r'\w+', self._match.extras or '')
+
+ @functools.cached_property
+ def _match(self) -> _EntryPointMatch:
match = self.pattern.match(self.value)
- assert match is not None
- return re.findall(r'\w+', match.group('extras') or '')
+ if not match:
+ raise ValueError(
+ 'Invalid object reference. '
+ 'See https://packaging.python.org'
+ '/en/latest/specifications/entry-points/#data-model',
+ self.value,
+ )
+ return _EntryPointMatch(**match.groupdict())
def _for(self, dist):
vars(self).update(dist=dist)
@@ -222,9 +265,26 @@ def matches(self, **params):
>>> ep.matches(attr='bong')
True
"""
+ self._disallow_dist(params)
attrs = (getattr(self, param) for param in params)
return all(map(operator.eq, params.values(), attrs))
+ @staticmethod
+ def _disallow_dist(params):
+ """
+ Querying by dist is not allowed (dist objects are not comparable).
+ >>> EntryPoint(name='fan', value='fav', group='fag').matches(dist='foo')
+ Traceback (most recent call last):
+ ...
+ ValueError: "dist" is not suitable for matching...
+ """
+ if "dist" in params:
+ raise ValueError(
+ '"dist" is not suitable for matching. '
+ "Instead, use Distribution.entry_points.select() on a "
+ "located distribution."
+ )
+
def _key(self):
return self.name, self.value, self.group
@@ -254,7 +314,7 @@ class EntryPoints(tuple):
__slots__ = ()
- def __getitem__(self, name: str) -> EntryPoint: # type: ignore[override]
+ def __getitem__(self, name: str) -> EntryPoint: # type: ignore[override] # Work with str instead of int
"""
Get the EntryPoint in self matching name.
"""
@@ -278,14 +338,14 @@ def select(self, **params) -> EntryPoints:
return EntryPoints(ep for ep in self if ep.matches(**params))
@property
- def names(self) -> Set[str]:
+ def names(self) -> set[str]:
"""
Return the set of all names of all entry points.
"""
return {ep.name for ep in self}
@property
- def groups(self) -> Set[str]:
+ def groups(self) -> set[str]:
"""
Return the set of all groups of all entry points.
"""
@@ -306,11 +366,11 @@ def _from_text(text):
class PackagePath(pathlib.PurePosixPath):
"""A reference to a path in a package"""
- hash: Optional[FileHash]
+ hash: FileHash | None
size: int
dist: Distribution
- def read_text(self, encoding: str = 'utf-8') -> str: # type: ignore[override]
+ def read_text(self, encoding: str = 'utf-8') -> str:
return self.locate().read_text(encoding=encoding)
def read_binary(self) -> bytes:
@@ -341,7 +401,7 @@ class Distribution(metaclass=abc.ABCMeta):
"""
@abc.abstractmethod
- def read_text(self, filename) -> Optional[str]:
+ def read_text(self, filename) -> str | None:
"""Attempt to load metadata file given by the name.
Python distribution metadata is organized by blobs of text
@@ -368,6 +428,17 @@ def locate_file(self, path: str | os.PathLike[str]) -> SimplePath:
"""
Given a path to a file in this distribution, return a SimplePath
to it.
+
+ This method is used by callers of ``Distribution.files()`` to
+ locate files within the distribution. If it's possible for a
+ Distribution to represent files in the distribution as
+ ``SimplePath`` objects, it should implement this method
+ to resolve such objects.
+
+ Some Distribution providers may elect not to resolve SimplePath
+ objects within the distribution by raising a
+ NotImplementedError, but consumers of such a Distribution would
+ be unable to invoke ``Distribution.files()``.
"""
@classmethod
@@ -390,7 +461,7 @@ def from_name(cls, name: str) -> Distribution:
@classmethod
def discover(
- cls, *, context: Optional[DistributionFinder.Context] = None, **kwargs
+ cls, *, context: DistributionFinder.Context | None = None, **kwargs
) -> Iterable[Distribution]:
"""Return an iterable of Distribution objects for all packages.
@@ -436,7 +507,7 @@ def _discover_resolvers():
return filter(None, declared)
@property
- def metadata(self) -> _meta.PackageMetadata:
+ def metadata(self) -> _meta.PackageMetadata | None:
"""Return the parsed metadata for this Distribution.
The returned object will have keys that name the various bits of
@@ -446,10 +517,8 @@ def metadata(self) -> _meta.PackageMetadata:
Custom providers may provide the METADATA file or override this
property.
"""
- # deferred for performance (python/cpython#109829)
- from . import _adapters
- opt_text = (
+ text = (
self.read_text('METADATA')
or self.read_text('PKG-INFO')
# This last clause is here to support old egg-info files. Its
@@ -457,13 +526,20 @@ def metadata(self) -> _meta.PackageMetadata:
# (which points to the egg-info file) attribute unchanged.
or self.read_text('')
)
- text = cast(str, opt_text)
+ return self._assemble_message(text)
+
+ @staticmethod
+ @pass_none
+ def _assemble_message(text: str) -> _meta.PackageMetadata:
+ # deferred for performance (python/cpython#109829)
+ from . import _adapters
+
return _adapters.Message(email.message_from_string(text))
@property
def name(self) -> str:
"""Return the 'Name' metadata for the distribution package."""
- return self.metadata['Name']
+ return md_none(self.metadata)['Name']
@property
def _normalized_name(self):
@@ -473,7 +549,7 @@ def _normalized_name(self):
@property
def version(self) -> str:
"""Return the 'Version' metadata for the distribution package."""
- return self.metadata['Version']
+ return md_none(self.metadata)['Version']
@property
def entry_points(self) -> EntryPoints:
@@ -486,7 +562,7 @@ def entry_points(self) -> EntryPoints:
return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self)
@property
- def files(self) -> Optional[List[PackagePath]]:
+ def files(self) -> list[PackagePath] | None:
"""Files in this distribution.
:return: List of PackagePath for this distribution or None
@@ -579,7 +655,7 @@ def _read_files_egginfo_sources(self):
return text and map('"{}"'.format, text.splitlines())
@property
- def requires(self) -> Optional[List[str]]:
+ def requires(self) -> list[str] | None:
"""Generated requirements specified for this Distribution"""
reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs()
return reqs and list(reqs)
@@ -635,6 +711,9 @@ def origin(self):
return self._load_json('direct_url.json')
def _load_json(self, filename):
+ # Deferred for performance (python/importlib_metadata#503)
+ import json
+
return pass_none(json.loads)(
self.read_text(filename),
object_hook=lambda data: types.SimpleNamespace(**data),
@@ -682,7 +761,7 @@ def __init__(self, **kwargs):
vars(self).update(kwargs)
@property
- def path(self) -> List[str]:
+ def path(self) -> list[str]:
"""
The sequence of directory path that a distribution finder
should search.
@@ -719,7 +798,7 @@ class FastPath:
True
"""
- @functools.lru_cache() # type: ignore
+ @functools.lru_cache() # type: ignore[misc]
def __new__(cls, root):
return super().__new__(cls)
@@ -737,6 +816,9 @@ def children(self):
return []
def zip_children(self):
+ # deferred for performance (python/importlib_metadata#502)
+ import zipfile
+
zip_path = zipfile.Path(self.root)
names = zip_path.root.namelist()
self.joinpath = zip_path.joinpath
@@ -831,7 +913,7 @@ class Prepared:
normalized = None
legacy_normalized = None
- def __init__(self, name: Optional[str]):
+ def __init__(self, name: str | None):
self.name = name
if name is None:
return
@@ -894,7 +976,7 @@ def __init__(self, path: SimplePath) -> None:
"""
self._path = path
- def read_text(self, filename: str | os.PathLike[str]) -> Optional[str]:
+ def read_text(self, filename: str | os.PathLike[str]) -> str | None:
with suppress(
FileNotFoundError,
IsADirectoryError,
@@ -958,7 +1040,7 @@ def distributions(**kwargs) -> Iterable[Distribution]:
return Distribution.discover(**kwargs)
-def metadata(distribution_name: str) -> _meta.PackageMetadata:
+def metadata(distribution_name: str) -> _meta.PackageMetadata | None:
"""Get the metadata for the named package.
:param distribution_name: The name of the distribution package to query.
@@ -1001,7 +1083,7 @@ def entry_points(**params) -> EntryPoints:
return EntryPoints(eps).select(**params)
-def files(distribution_name: str) -> Optional[List[PackagePath]]:
+def files(distribution_name: str) -> list[PackagePath] | None:
"""Return a list of files for the named package.
:param distribution_name: The name of the distribution package to query.
@@ -1010,7 +1092,7 @@ def files(distribution_name: str) -> Optional[List[PackagePath]]:
return distribution(distribution_name).files
-def requires(distribution_name: str) -> Optional[List[str]]:
+def requires(distribution_name: str) -> list[str] | None:
"""
Return a list of requirements for the named package.
@@ -1020,7 +1102,7 @@ def requires(distribution_name: str) -> Optional[List[str]]:
return distribution(distribution_name).requires
-def packages_distributions() -> Mapping[str, List[str]]:
+def packages_distributions() -> Mapping[str, list[str]]:
"""
Return a mapping of top-level packages to their
distributions.
@@ -1033,7 +1115,7 @@ def packages_distributions() -> Mapping[str, List[str]]:
pkg_to_dist = collections.defaultdict(list)
for dist in distributions():
for pkg in _top_level_declared(dist) or _top_level_inferred(dist):
- pkg_to_dist[pkg].append(dist.metadata['Name'])
+ pkg_to_dist[pkg].append(md_none(dist.metadata)['Name'])
return dict(pkg_to_dist)
@@ -1041,7 +1123,7 @@ def _top_level_declared(dist):
return (dist.read_text('top_level.txt') or '').split()
-def _topmost(name: PackagePath) -> Optional[str]:
+def _topmost(name: PackagePath) -> str | None:
"""
Return the top-most parent as long as there is a parent.
"""
@@ -1067,11 +1149,10 @@ def _get_toplevel_name(name: PackagePath) -> str:
>>> _get_toplevel_name(PackagePath('foo.dist-info'))
'foo.dist-info'
"""
- return _topmost(name) or (
- # python/typeshed#10328
- inspect.getmodulename(name) # type: ignore
- or str(name)
- )
+ # Defer import of inspect for performance (python/cpython#118761)
+ import inspect
+
+ return _topmost(name) or inspect.getmodulename(name) or str(name)
def _top_level_inferred(dist):
diff --git a/Lib/importlib/metadata/_adapters.py b/Lib/importlib/metadata/_adapters.py
index 6223263ed53f22..f5b30dd92cde69 100644
--- a/Lib/importlib/metadata/_adapters.py
+++ b/Lib/importlib/metadata/_adapters.py
@@ -1,11 +1,58 @@
+import email.message
+import email.policy
import re
import textwrap
-import email.message
from ._text import FoldedCase
+class RawPolicy(email.policy.EmailPolicy):
+ def fold(self, name, value):
+ folded = self.linesep.join(
+ textwrap.indent(value, prefix=' ' * 8, predicate=lambda line: True)
+ .lstrip()
+ .splitlines()
+ )
+ return f'{name}: {folded}{self.linesep}'
+
+
class Message(email.message.Message):
+ r"""
+ Specialized Message subclass to handle metadata naturally.
+
+ Reads values that may have newlines in them and converts the
+ payload to the Description.
+
+ >>> msg_text = textwrap.dedent('''
+ ... Name: Foo
+ ... Version: 3.0
+ ... License: blah
+ ... de-blah
+ ...
+ ... First line of description.
+ ... Second line of description.
+ ...
+ ... Fourth line!
+ ... ''').lstrip().replace('', '')
+ >>> msg = Message(email.message_from_string(msg_text))
+ >>> msg['Description']
+ 'First line of description.\nSecond line of description.\n\nFourth line!\n'
+
+ Message should render even if values contain newlines.
+
+ >>> print(msg)
+ Name: Foo
+ Version: 3.0
+ License: blah
+ de-blah
+ Description: First line of description.
+ Second line of description.
+
+ Fourth line!
+
+
+ """
+
multiple_use_keys = set(
map(
FoldedCase,
@@ -57,15 +104,20 @@ def __getitem__(self, item):
def _repair_headers(self):
def redent(value):
"Correct for RFC822 indentation"
- if not value or '\n' not in value:
+ indent = ' ' * 8
+ if not value or '\n' + indent not in value:
return value
- return textwrap.dedent(' ' * 8 + value)
+ return textwrap.dedent(indent + value)
headers = [(key, redent(value)) for key, value in vars(self)['_headers']]
if self._payload:
headers.append(('Description', self.get_payload()))
+ self.set_payload('')
return headers
+ def as_string(self):
+ return super().as_string(policy=RawPolicy())
+
@property
def json(self):
"""
diff --git a/Lib/importlib/metadata/_collections.py b/Lib/importlib/metadata/_collections.py
index cf0954e1a30546..fc5045d36be572 100644
--- a/Lib/importlib/metadata/_collections.py
+++ b/Lib/importlib/metadata/_collections.py
@@ -1,4 +1,5 @@
import collections
+import typing
# from jaraco.collections 3.3
@@ -24,7 +25,10 @@ def freeze(self):
self._frozen = lambda key: self.default_factory()
-class Pair(collections.namedtuple('Pair', 'name value')):
+class Pair(typing.NamedTuple):
+ name: str
+ value: str
+
@classmethod
def parse(cls, text):
return cls(*map(str.strip, text.split("=", 1)))
diff --git a/Lib/importlib/metadata/_functools.py b/Lib/importlib/metadata/_functools.py
index 71f66bd03cb713..5dda6a2199ad0b 100644
--- a/Lib/importlib/metadata/_functools.py
+++ b/Lib/importlib/metadata/_functools.py
@@ -1,5 +1,5 @@
-import types
import functools
+import types
# from jaraco.functools 3.3
diff --git a/Lib/importlib/metadata/_meta.py b/Lib/importlib/metadata/_meta.py
index 1927d0f624d82f..0c20eff3da7522 100644
--- a/Lib/importlib/metadata/_meta.py
+++ b/Lib/importlib/metadata/_meta.py
@@ -1,9 +1,13 @@
from __future__ import annotations
import os
-from typing import Protocol
-from typing import Any, Dict, Iterator, List, Optional, TypeVar, Union, overload
-
+from collections.abc import Iterator
+from typing import (
+ Any,
+ Protocol,
+ TypeVar,
+ overload,
+)
_T = TypeVar("_T")
@@ -20,25 +24,25 @@ def __iter__(self) -> Iterator[str]: ... # pragma: no cover
@overload
def get(
self, name: str, failobj: None = None
- ) -> Optional[str]: ... # pragma: no cover
+ ) -> str | None: ... # pragma: no cover
@overload
- def get(self, name: str, failobj: _T) -> Union[str, _T]: ... # pragma: no cover
+ def get(self, name: str, failobj: _T) -> str | _T: ... # pragma: no cover
# overload per python/importlib_metadata#435
@overload
def get_all(
self, name: str, failobj: None = None
- ) -> Optional[List[Any]]: ... # pragma: no cover
+ ) -> list[Any] | None: ... # pragma: no cover
@overload
- def get_all(self, name: str, failobj: _T) -> Union[List[Any], _T]:
+ def get_all(self, name: str, failobj: _T) -> list[Any] | _T:
"""
Return all values associated with a possibly multi-valued key.
"""
@property
- def json(self) -> Dict[str, Union[str, List[str]]]:
+ def json(self) -> dict[str, str | list[str]]:
"""
A JSON-compatible form of the metadata.
"""
@@ -50,11 +54,11 @@ class SimplePath(Protocol):
"""
def joinpath(
- self, other: Union[str, os.PathLike[str]]
+ self, other: str | os.PathLike[str]
) -> SimplePath: ... # pragma: no cover
def __truediv__(
- self, other: Union[str, os.PathLike[str]]
+ self, other: str | os.PathLike[str]
) -> SimplePath: ... # pragma: no cover
@property
diff --git a/Lib/importlib/metadata/_typing.py b/Lib/importlib/metadata/_typing.py
new file mode 100644
index 00000000000000..32b1d2b98ac987
--- /dev/null
+++ b/Lib/importlib/metadata/_typing.py
@@ -0,0 +1,15 @@
+import functools
+import typing
+
+from ._meta import PackageMetadata
+
+md_none = functools.partial(typing.cast, PackageMetadata)
+"""
+Suppress type errors for optional metadata.
+
+Although Distribution.metadata can return None when metadata is corrupt
+and thus None, allow callers to assume it's not None and crash if
+that's the case.
+
+# python/importlib_metadata#493
+"""
diff --git a/Lib/platform.py b/Lib/platform.py
index 762f5d06099cf0..4028012dc3ce6a 100644
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -199,6 +199,7 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384):
| (GLIBC_([0-9.]+))
| (libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)
| (musl-([0-9.]+))
+ | (libc.musl(?:-\w+)?.so(?:\.(\d[0-9.]*))?)
""",
re.ASCII | re.VERBOSE)
@@ -224,9 +225,10 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384):
continue
if not m:
break
- libcinit, glibc, glibcversion, so, threads, soversion, musl, muslversion = [
- s.decode('latin1') if s is not None else s
- for s in m.groups()]
+ decoded_groups = [s.decode('latin1') if s is not None else s
+ for s in m.groups()]
+ (libcinit, glibc, glibcversion, so, threads, soversion,
+ musl, muslversion, musl_so, musl_sover) = decoded_groups
if libcinit and not lib:
lib = 'libc'
elif glibc:
@@ -246,6 +248,10 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384):
lib = 'musl'
if not ver or V(muslversion) > V(ver):
ver = muslversion
+ elif musl_so:
+ lib = 'musl'
+ if musl_sover and (not ver or V(musl_sover) > V(ver)):
+ ver = musl_sover
pos = m.end()
return lib, version if ver is None else ver
diff --git a/Lib/test/test_free_threading/test_json.py b/Lib/test/test_free_threading/test_json.py
index 8a541e960a63ad..010eb322a15b84 100644
--- a/Lib/test/test_free_threading/test_json.py
+++ b/Lib/test/test_free_threading/test_json.py
@@ -56,7 +56,7 @@ def worker(barrier, data, index):
if len(d) > 5:
try:
key = list(d)[0]
- d.pop()
+ d.pop(key)
except (KeyError, IndexError):
pass
else:
diff --git a/Lib/test/test_importlib/metadata/_issue138313.py b/Lib/test/test_importlib/metadata/_issue138313.py
new file mode 100644
index 00000000000000..4e1c57e622d657
--- /dev/null
+++ b/Lib/test/test_importlib/metadata/_issue138313.py
@@ -0,0 +1,15 @@
+import os
+import unittest
+
+
+def skip_on_buildbot(func):
+ """
+ #132947 revealed that after applying some otherwise stable
+ changes, only on some buildbot runners, the tests will fail with
+ ResourceWarnings.
+ """
+ # detect "not github actions" as a proxy for BUILDBOT not being present yet.
+ is_buildbot = "GITHUB_ACTION" not in os.environ or "BUILDBOT" in os.environ
+ skipper = unittest.skip("Causes Resource Warnings (python/cpython#132947)")
+ wrapper = skipper if is_buildbot else lambda x: x
+ return wrapper(func)
diff --git a/Lib/test/test_importlib/metadata/_path.py b/Lib/test/test_importlib/metadata/_path.py
index b3cfb9cd549d6c..e63d889f96bf13 100644
--- a/Lib/test/test_importlib/metadata/_path.py
+++ b/Lib/test/test_importlib/metadata/_path.py
@@ -1,9 +1,14 @@
-# from jaraco.path 3.7
+# from jaraco.path 3.7.2
+
+from __future__ import annotations
import functools
import pathlib
-from typing import Dict, Protocol, Union
-from typing import runtime_checkable
+from collections.abc import Mapping
+from typing import TYPE_CHECKING, Protocol, Union, runtime_checkable
+
+if TYPE_CHECKING:
+ from typing_extensions import Self
class Symlink(str):
@@ -12,29 +17,25 @@ class Symlink(str):
"""
-FilesSpec = Dict[str, Union[str, bytes, Symlink, 'FilesSpec']] # type: ignore
+FilesSpec = Mapping[str, Union[str, bytes, Symlink, 'FilesSpec']]
@runtime_checkable
class TreeMaker(Protocol):
- def __truediv__(self, *args, **kwargs): ... # pragma: no cover
-
- def mkdir(self, **kwargs): ... # pragma: no cover
-
- def write_text(self, content, **kwargs): ... # pragma: no cover
-
- def write_bytes(self, content): ... # pragma: no cover
-
- def symlink_to(self, target): ... # pragma: no cover
+ def __truediv__(self, other, /) -> Self: ...
+ def mkdir(self, *, exist_ok) -> object: ...
+ def write_text(self, content, /, *, encoding) -> object: ...
+ def write_bytes(self, content, /) -> object: ...
+ def symlink_to(self, target, /) -> object: ...
-def _ensure_tree_maker(obj: Union[str, TreeMaker]) -> TreeMaker:
- return obj if isinstance(obj, TreeMaker) else pathlib.Path(obj) # type: ignore
+def _ensure_tree_maker(obj: str | TreeMaker) -> TreeMaker:
+ return obj if isinstance(obj, TreeMaker) else pathlib.Path(obj)
def build(
spec: FilesSpec,
- prefix: Union[str, TreeMaker] = pathlib.Path(), # type: ignore
+ prefix: str | TreeMaker = pathlib.Path(),
):
"""
Build a set of files/directories, as described by the spec.
@@ -66,23 +67,24 @@ def build(
@functools.singledispatch
-def create(content: Union[str, bytes, FilesSpec], path):
+def create(content: str | bytes | FilesSpec, path: TreeMaker) -> None:
path.mkdir(exist_ok=True)
- build(content, prefix=path) # type: ignore
+ # Mypy only looks at the signature of the main singledispatch method. So it must contain the complete Union
+ build(content, prefix=path) # type: ignore[arg-type] # python/mypy#11727
@create.register
-def _(content: bytes, path):
+def _(content: bytes, path: TreeMaker) -> None:
path.write_bytes(content)
@create.register
-def _(content: str, path):
+def _(content: str, path: TreeMaker) -> None:
path.write_text(content, encoding='utf-8')
@create.register
-def _(content: Symlink, path):
+def _(content: Symlink, path: TreeMaker) -> None:
path.symlink_to(content)
diff --git a/Lib/test/test_importlib/metadata/fixtures.py b/Lib/test/test_importlib/metadata/fixtures.py
index 826b1b3259b4cd..494047dc98f9b6 100644
--- a/Lib/test/test_importlib/metadata/fixtures.py
+++ b/Lib/test/test_importlib/metadata/fixtures.py
@@ -1,11 +1,11 @@
-import sys
+import contextlib
import copy
+import functools
import json
-import shutil
import pathlib
+import shutil
+import sys
import textwrap
-import functools
-import contextlib
from test.support import import_helper
from test.support import os_helper
@@ -14,14 +14,10 @@
from . import _path
from ._path import FilesSpec
-
-try:
- from importlib import resources # type: ignore
-
- getattr(resources, 'files')
- getattr(resources, 'as_file')
-except (ImportError, AttributeError):
- import importlib_resources as resources # type: ignore
+if sys.version_info >= (3, 9):
+ from importlib import resources
+else:
+ import importlib_resources as resources
@contextlib.contextmanager
diff --git a/Lib/test/test_importlib/metadata/test_api.py b/Lib/test/test_importlib/metadata/test_api.py
index 813febf269593b..9f6e12c87e859c 100644
--- a/Lib/test/test_importlib/metadata/test_api.py
+++ b/Lib/test/test_importlib/metadata/test_api.py
@@ -1,9 +1,8 @@
+import importlib
import re
import textwrap
import unittest
-import importlib
-from . import fixtures
from importlib.metadata import (
Distribution,
PackageNotFoundError,
@@ -15,6 +14,8 @@
version,
)
+from . import fixtures
+
class APITests(
fixtures.EggInfoPkg,
diff --git a/Lib/test/test_importlib/metadata/test_main.py b/Lib/test/test_importlib/metadata/test_main.py
index a0bc8222d5ba24..71bdd5f0d25943 100644
--- a/Lib/test/test_importlib/metadata/test_main.py
+++ b/Lib/test/test_importlib/metadata/test_main.py
@@ -1,8 +1,7 @@
-import re
+import importlib
import pickle
+import re
import unittest
-import importlib
-import importlib.metadata
from test.support import os_helper
try:
@@ -10,8 +9,6 @@
except ImportError:
from .stubs import fake_filesystem_unittest as ffs
-from . import fixtures
-from ._path import Symlink
from importlib.metadata import (
Distribution,
EntryPoint,
@@ -24,6 +21,10 @@
version,
)
+from . import fixtures
+from . import _issue138313
+from ._path import Symlink
+
class BasicTests(fixtures.DistInfoPkg, unittest.TestCase):
version_pattern = r'\d+\.\d+(\.\d)?'
@@ -157,6 +158,16 @@ def test_valid_dists_preferred(self):
dist = Distribution.from_name('foo')
assert dist.version == "1.0"
+ def test_missing_metadata(self):
+ """
+ Dists with a missing metadata file should return None.
+
+ Ref python/importlib_metadata#493.
+ """
+ fixtures.build_files(self.make_pkg('foo-4.3', files={}), self.site_dir)
+ assert Distribution.from_name('foo').metadata is None
+ assert metadata('foo') is None
+
class NonASCIITests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
@staticmethod
@@ -347,6 +358,7 @@ def test_packages_distributions_example(self):
self._fixture_on_path('example-21.12-py3-none-any.whl')
assert packages_distributions()['example'] == ['example']
+ @_issue138313.skip_on_buildbot
def test_packages_distributions_example2(self):
"""
Test packages_distributions on a wheel built
diff --git a/Lib/test/test_importlib/metadata/test_zip.py b/Lib/test/test_importlib/metadata/test_zip.py
index 276f6288c91598..fcb649f3736076 100644
--- a/Lib/test/test_importlib/metadata/test_zip.py
+++ b/Lib/test/test_importlib/metadata/test_zip.py
@@ -1,7 +1,6 @@
import sys
import unittest
-from . import fixtures
from importlib.metadata import (
PackageNotFoundError,
distribution,
@@ -11,6 +10,8 @@
version,
)
+from . import fixtures
+
class TestZip(fixtures.ZipFixtures, unittest.TestCase):
def setUp(self):
diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py
index 73d4f3cbdb689d..601d450b7de9eb 100644
--- a/Lib/test/test_platform.py
+++ b/Lib/test/test_platform.py
@@ -567,6 +567,8 @@ def test_libc_ver(self):
# musl uses semver, but we accept some variations anyway:
(b'/aports/main/musl/src/musl-12.5', ('musl', '12.5')),
(b'/aports/main/musl/src/musl-1.2.5.7', ('musl', '1.2.5.7')),
+ (b'libc.musl.so.1', ('musl', '1')),
+ (b'libc.musl-x86_64.so.1.2.5', ('musl', '1.2.5')),
(b'', ('', '')),
):
with open(filename, 'wb') as fp:
@@ -585,6 +587,10 @@ def test_libc_ver(self):
(b'GLIBC_1.23.4\0GLIBC_1.9\0GLIBC_1.21\0', ('glibc', '1.23.4')),
(b'libc.so.2.4\0libc.so.9\0libc.so.23.1\0', ('libc', '23.1')),
(b'musl-1.4.1\0musl-2.1.1\0musl-2.0.1\0', ('musl', '2.1.1')),
+ (
+ b'libc.musl-x86_64.so.1.4.1\0libc.musl-x86_64.so.2.1.1\0libc.musl-x86_64.so.2.0.1',
+ ('musl', '2.1.1'),
+ ),
(b'no match here, so defaults are used', ('test', '100.1.0')),
):
with open(filename, 'wb') as f:
diff --git a/Misc/NEWS.d/next/Library/2025-08-16-18-11-41.gh-issue-90548.q3aJUK.rst b/Misc/NEWS.d/next/Library/2025-08-16-18-11-41.gh-issue-90548.q3aJUK.rst
new file mode 100644
index 00000000000000..f6e24af30fec1c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-08-16-18-11-41.gh-issue-90548.q3aJUK.rst
@@ -0,0 +1,2 @@
+Fix ``musl`` detection for :func:`platform.libc_ver` on Alpine Linux if
+compiled with --strip-all.
diff --git a/Misc/NEWS.d/next/Library/2025-08-17-10-22-31.gh-issue-132947.XR4MJ8.rst b/Misc/NEWS.d/next/Library/2025-08-17-10-22-31.gh-issue-132947.XR4MJ8.rst
new file mode 100644
index 00000000000000..8a2b0a4981eae9
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-08-17-10-22-31.gh-issue-132947.XR4MJ8.rst
@@ -0,0 +1,6 @@
+Applied changes to ``importlib.metadata`` from `importlib_metadata 8.7
+`_,
+including ``dist`` now disallowed for ``EntryPoints.select``; deferred
+imports for faster import times; added support for metadata with newlines
+(python/cpython#119650); and ``metadata()`` function now returns ``None``
+when a metadata directory is present but no metadata is present.
diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c
index d988901933703e..914172684158a1 100644
--- a/Modules/_bz2module.c
+++ b/Modules/_bz2module.c
@@ -381,14 +381,13 @@ _bz2_BZ2Compressor_impl(PyTypeObject *type, int compresslevel)
static void
BZ2Compressor_dealloc(PyObject *op)
{
- PyTypeObject *tp = Py_TYPE(op);
- PyObject_GC_UnTrack(op);
BZ2Compressor *self = _BZ2Compressor_CAST(op);
BZ2_bzCompressEnd(&self->bzs);
if (self->lock != NULL) {
PyThread_free_lock(self->lock);
}
- tp->tp_free(self);
+ PyTypeObject *tp = Py_TYPE(self);
+ tp->tp_free((PyObject *)self);
Py_DECREF(tp);
}
@@ -421,7 +420,7 @@ static PyType_Spec bz2_compressor_type_spec = {
// bz2_compressor_type_spec does not have Py_TPFLAGS_BASETYPE flag
// which prevents to create a subclass.
// So calling PyType_GetModuleState() in this file is always safe.
- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC),
+ .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE),
.slots = bz2_compressor_type_slots,
};
@@ -688,11 +687,9 @@ _bz2_BZ2Decompressor_impl(PyTypeObject *type)
static void
BZ2Decompressor_dealloc(PyObject *op)
{
- PyTypeObject *tp = Py_TYPE(op);
- PyObject_GC_UnTrack(op);
BZ2Decompressor *self = _BZ2Decompressor_CAST(op);
- if (self->input_buffer != NULL) {
+ if(self->input_buffer != NULL) {
PyMem_Free(self->input_buffer);
}
BZ2_bzDecompressEnd(&self->bzs);
@@ -700,7 +697,9 @@ BZ2Decompressor_dealloc(PyObject *op)
if (self->lock != NULL) {
PyThread_free_lock(self->lock);
}
- tp->tp_free(self);
+
+ PyTypeObject *tp = Py_TYPE(self);
+ tp->tp_free((PyObject *)self);
Py_DECREF(tp);
}
@@ -752,7 +751,7 @@ static PyType_Spec bz2_decompressor_type_spec = {
// bz2_decompressor_type_spec does not have Py_TPFLAGS_BASETYPE flag
// which prevents to create a subclass.
// So calling PyType_GetModuleState() in this file is always safe.
- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC),
+ .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE),
.slots = bz2_decompressor_type_slots,
};
diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c
index 0cd0f043de453d..17aca2f00a13c0 100644
--- a/Modules/_dbmmodule.c
+++ b/Modules/_dbmmodule.c
@@ -96,12 +96,6 @@ newdbmobject(_dbm_state *state, const char *file, int flags, int mode)
}
/* Methods */
-static int
-dbm_traverse(PyObject *dp, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(dp));
- return 0;
-}
static void
dbm_dealloc(PyObject *self)
@@ -540,7 +534,7 @@ static PyMethodDef dbm_methods[] = {
static PyType_Slot dbmtype_spec_slots[] = {
{Py_tp_dealloc, dbm_dealloc},
- {Py_tp_traverse, dbm_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{Py_tp_methods, dbm_methods},
{Py_sq_contains, dbm_contains},
{Py_mp_length, dbm_length},
diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c
index de7200af8c158b..50994be2968d35 100644
--- a/Modules/_decimal/_decimal.c
+++ b/Modules/_decimal/_decimal.c
@@ -746,13 +746,6 @@ signaldict_setitem(PyObject *self, PyObject *key, PyObject *value)
return 0;
}
-static int
-signaldict_traverse(PyObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(self));
- return 0;
-}
-
static void
signaldict_dealloc(PyObject *self)
{
@@ -845,7 +838,7 @@ static PyMethodDef signaldict_methods[] = {
static PyType_Slot signaldict_slots[] = {
{Py_tp_dealloc, signaldict_dealloc},
- {Py_tp_traverse, signaldict_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{Py_tp_repr, signaldict_repr},
{Py_tp_hash, PyObject_HashNotImplemented},
{Py_tp_getattro, PyObject_GenericGetAttr},
@@ -2194,13 +2187,6 @@ PyDecType_New(decimal_state *state, PyTypeObject *type)
}
#define dec_alloc(st) PyDecType_New(st, (st)->PyDec_Type)
-static int
-dec_traverse(PyObject *dec, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(dec));
- return 0;
-}
-
static void
dec_dealloc(PyObject *dec)
{
@@ -6032,7 +6018,7 @@ static PyType_Slot dec_slots[] = {
{Py_tp_token, Py_TP_USE_SPEC},
{Py_tp_dealloc, dec_dealloc},
{Py_tp_getattro, PyObject_GenericGetAttr},
- {Py_tp_traverse, dec_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{Py_tp_repr, dec_repr},
{Py_tp_hash, dec_hash},
{Py_tp_str, dec_str},
diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c
index f077a0ed329516..257d5c6d53611c 100644
--- a/Modules/_functoolsmodule.c
+++ b/Modules/_functoolsmodule.c
@@ -108,20 +108,13 @@ placeholder_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
return placeholder;
}
-static int
-placeholder_traverse(PyObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(self));
- return 0;
-}
-
static PyType_Slot placeholder_type_slots[] = {
{Py_tp_dealloc, placeholder_dealloc},
{Py_tp_repr, placeholder_repr},
{Py_tp_doc, (void *)placeholder_doc},
{Py_tp_methods, placeholder_methods},
{Py_tp_new, placeholder_new},
- {Py_tp_traverse, placeholder_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{0, 0}
};
diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c
index 76072ca60cf6cd..7bef6ae7f0c43e 100644
--- a/Modules/_gdbmmodule.c
+++ b/Modules/_gdbmmodule.c
@@ -122,13 +122,6 @@ newgdbmobject(_gdbm_state *state, const char *file, int flags, int mode)
}
/* Methods */
-static int
-gdbm_traverse(PyObject *op, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(op));
- return 0;
-}
-
static void
gdbm_dealloc(PyObject *op)
{
@@ -714,7 +707,7 @@ static PyMethodDef gdbm_methods[] = {
static PyType_Slot gdbmtype_spec_slots[] = {
{Py_tp_dealloc, gdbm_dealloc},
- {Py_tp_traverse, gdbm_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{Py_tp_methods, gdbm_methods},
{Py_sq_contains, gdbm_contains},
{Py_mp_length, gdbm_length},
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
index 9d79fc08dcfcac..a6496d0f04f2d0 100644
--- a/Modules/_hashopenssl.c
+++ b/Modules/_hashopenssl.c
@@ -752,9 +752,7 @@ py_wrapper_EVP_MD_CTX_new(void)
static HASHobject *
new_hash_object(PyTypeObject *type)
{
- assert(type != NULL);
- assert(type->tp_alloc != NULL);
- HASHobject *retval = (HASHobject *)type->tp_alloc(type, 0);
+ HASHobject *retval = PyObject_New(HASHobject, type);
if (retval == NULL) {
return NULL;
}
@@ -794,21 +792,13 @@ _hashlib_HASH_hash(HASHobject *self, const void *vp, Py_ssize_t len)
static void
_hashlib_HASH_dealloc(PyObject *op)
{
- PyTypeObject *tp = Py_TYPE(op);
- PyObject_GC_UnTrack(op);
HASHobject *self = HASHobject_CAST(op);
+ PyTypeObject *tp = Py_TYPE(self);
EVP_MD_CTX_free(self->ctx);
- tp->tp_free(self);
+ PyObject_Free(self);
Py_DECREF(tp);
}
-static int
-_hashlib_HASH_traverse(PyObject *op, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(op));
- return 0;
-}
-
static int
_hashlib_HASH_copy_locked(HASHobject *self, EVP_MD_CTX *new_ctx_p)
{
@@ -1003,7 +993,6 @@ PyDoc_STRVAR(HASHobject_type_doc,
static PyType_Slot HASHobject_type_slots[] = {
{Py_tp_dealloc, _hashlib_HASH_dealloc},
- {Py_tp_traverse, _hashlib_HASH_traverse},
{Py_tp_repr, _hashlib_HASH_repr},
{Py_tp_doc, (char *)HASHobject_type_doc},
{Py_tp_methods, HASH_methods},
@@ -1019,7 +1008,6 @@ static PyType_Spec HASHobject_type_spec = {
| Py_TPFLAGS_BASETYPE
| Py_TPFLAGS_DISALLOW_INSTANTIATION
| Py_TPFLAGS_IMMUTABLETYPE
- | Py_TPFLAGS_HAVE_GC
),
.slots = HASHobject_type_slots
};
@@ -1177,8 +1165,6 @@ PyDoc_STRVAR(HASHXOFobject_type_doc,
"digest_size -- number of bytes in this hashes output");
static PyType_Slot HASHXOFobject_type_slots[] = {
- {Py_tp_dealloc, _hashlib_HASH_dealloc},
- {Py_tp_traverse, _hashlib_HASH_traverse},
{Py_tp_doc, (char *)HASHXOFobject_type_doc},
{Py_tp_methods, HASHXOFobject_methods},
{Py_tp_getset, HASHXOFobject_getsets},
@@ -1193,7 +1179,6 @@ static PyType_Spec HASHXOFobject_type_spec = {
| Py_TPFLAGS_BASETYPE
| Py_TPFLAGS_DISALLOW_INSTANTIATION
| Py_TPFLAGS_IMMUTABLETYPE
- | Py_TPFLAGS_HAVE_GC
),
.slots = HASHXOFobject_type_slots
};
@@ -1917,8 +1902,7 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj,
goto error;
}
- assert(state->HMAC_type != NULL);
- self = (HMACobject *)state->HMAC_type->tp_alloc(state->HMAC_type, 0);
+ self = PyObject_New(HMACobject, state->HMAC_type);
if (self == NULL) {
goto error;
}
@@ -2024,8 +2008,7 @@ _hashlib_HMAC_copy_impl(HMACobject *self)
return NULL;
}
- PyTypeObject *type = Py_TYPE(self);
- retval = (HMACobject *)type->tp_alloc(type, 0);
+ retval = PyObject_New(HMACobject, Py_TYPE(self));
if (retval == NULL) {
HMAC_CTX_free(ctx);
return NULL;
@@ -2039,24 +2022,16 @@ _hashlib_HMAC_copy_impl(HMACobject *self)
static void
_hmac_dealloc(PyObject *op)
{
- PyTypeObject *tp = Py_TYPE(op);
- PyObject_GC_UnTrack(op);
HMACobject *self = HMACobject_CAST(op);
+ PyTypeObject *tp = Py_TYPE(self);
if (self->ctx != NULL) {
HMAC_CTX_free(self->ctx);
self->ctx = NULL;
}
- tp->tp_free(self);
+ PyObject_Free(self);
Py_DECREF(tp);
}
-static int
-_hashlib_HMAC_traverse(PyObject *op, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(op));
- return 0;
-}
-
static PyObject *
_hmac_repr(PyObject *op)
{
@@ -2223,21 +2198,15 @@ static PyType_Slot HMACtype_slots[] = {
{Py_tp_doc, (char *)hmactype_doc},
{Py_tp_repr, _hmac_repr},
{Py_tp_dealloc, _hmac_dealloc},
- {Py_tp_traverse, _hashlib_HMAC_traverse},
{Py_tp_methods, HMAC_methods},
{Py_tp_getset, HMAC_getset},
{0, NULL}
};
PyType_Spec HMACtype_spec = {
- .name = "_hashlib.HMAC",
- .basicsize = sizeof(HMACobject),
- .flags = (
- Py_TPFLAGS_DEFAULT
- | Py_TPFLAGS_DISALLOW_INSTANTIATION
- | Py_TPFLAGS_IMMUTABLETYPE
- | Py_TPFLAGS_HAVE_GC
- ),
+ "_hashlib.HMAC", /* name */
+ sizeof(HMACobject), /* basicsize */
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE,
.slots = HMACtype_slots,
};
diff --git a/Modules/_lzmamodule.c b/Modules/_lzmamodule.c
index bcc9ea7a7bba68..0b0b1bc765bbc9 100644
--- a/Modules/_lzmamodule.c
+++ b/Modules/_lzmamodule.c
@@ -866,13 +866,12 @@ Compressor_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
static void
Compressor_dealloc(PyObject *op)
{
- PyTypeObject *tp = Py_TYPE(op);
- PyObject_GC_UnTrack(op);
Compressor *self = Compressor_CAST(op);
lzma_end(&self->lzs);
if (self->lock != NULL) {
PyThread_free_lock(self->lock);
}
+ PyTypeObject *tp = Py_TYPE(self);
tp->tp_free(self);
Py_DECREF(tp);
}
@@ -934,7 +933,7 @@ static PyType_Spec lzma_compressor_type_spec = {
// lzma_compressor_type_spec does not have Py_TPFLAGS_BASETYPE flag
// which prevents to create a subclass.
// So calling PyType_GetModuleState() in this file is always safe.
- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC),
+ .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE),
.slots = lzma_compressor_type_slots,
};
@@ -1315,8 +1314,6 @@ _lzma_LZMADecompressor_impl(PyTypeObject *type, int format,
static void
Decompressor_dealloc(PyObject *op)
{
- PyTypeObject *tp = Py_TYPE(op);
- PyObject_GC_UnTrack(op);
Decompressor *self = Decompressor_CAST(op);
if(self->input_buffer != NULL)
PyMem_Free(self->input_buffer);
@@ -1326,6 +1323,7 @@ Decompressor_dealloc(PyObject *op)
if (self->lock != NULL) {
PyThread_free_lock(self->lock);
}
+ PyTypeObject *tp = Py_TYPE(self);
tp->tp_free(self);
Py_DECREF(tp);
}
@@ -1383,7 +1381,7 @@ static PyType_Spec lzma_decompressor_type_spec = {
// lzma_decompressor_type_spec does not have Py_TPFLAGS_BASETYPE flag
// which prevents to create a subclass.
// So calling PyType_GetModuleState() in this file is always safe.
- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC),
+ .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE),
.slots = lzma_decompressor_type_slots,
};
diff --git a/Modules/_multiprocessing/semaphore.c b/Modules/_multiprocessing/semaphore.c
index a4a2a866ccbfce..d5a1f27e9ff4ff 100644
--- a/Modules/_multiprocessing/semaphore.c
+++ b/Modules/_multiprocessing/semaphore.c
@@ -720,13 +720,6 @@ _multiprocessing_SemLock___exit___impl(SemLockObject *self,
return _multiprocessing_SemLock_release_impl(self);
}
-static int
-semlock_traverse(PyObject *s, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(s));
- return 0;
-}
-
/*
* Semaphore methods
*/
@@ -773,7 +766,7 @@ static PyType_Slot _PyMp_SemLockType_slots[] = {
{Py_tp_members, semlock_members},
{Py_tp_alloc, PyType_GenericAlloc},
{Py_tp_new, _multiprocessing_SemLock},
- {Py_tp_traverse, semlock_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{Py_tp_free, PyObject_GC_Del},
{Py_tp_doc, (void *)PyDoc_STR("Semaphore/Mutex type")},
{0, 0},
diff --git a/Modules/_pickle.c b/Modules/_pickle.c
index bc06478799345a..0774e91039ec41 100644
--- a/Modules/_pickle.c
+++ b/Modules/_pickle.c
@@ -413,13 +413,6 @@ typedef struct {
#define Pdata_CAST(op) ((Pdata *)(op))
-static int
-Pdata_traverse(PyObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(self));
- return 0;
-}
-
static void
Pdata_dealloc(PyObject *op)
{
@@ -437,7 +430,7 @@ Pdata_dealloc(PyObject *op)
static PyType_Slot pdata_slots[] = {
{Py_tp_dealloc, Pdata_dealloc},
- {Py_tp_traverse, Pdata_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{0, NULL},
};
diff --git a/Modules/_sqlite/prepare_protocol.c b/Modules/_sqlite/prepare_protocol.c
index 31092417cb480d..d7ac09e3947a77 100644
--- a/Modules/_sqlite/prepare_protocol.c
+++ b/Modules/_sqlite/prepare_protocol.c
@@ -29,13 +29,6 @@ pysqlite_prepare_protocol_init(PyObject *self, PyObject *args, PyObject *kwargs)
return 0;
}
-static int
-pysqlite_prepare_protocol_traverse(PyObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(self));
- return 0;
-}
-
static void
pysqlite_prepare_protocol_dealloc(PyObject *self)
{
@@ -50,7 +43,7 @@ PyDoc_STRVAR(doc, "PEP 246 style object adaption protocol type.");
static PyType_Slot type_slots[] = {
{Py_tp_dealloc, pysqlite_prepare_protocol_dealloc},
{Py_tp_init, pysqlite_prepare_protocol_init},
- {Py_tp_traverse, pysqlite_prepare_protocol_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{Py_tp_doc, (void *)doc},
{0, NULL},
};
diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c
index 736e60fd778287..77181104eda10b 100644
--- a/Modules/_sqlite/statement.c
+++ b/Modules/_sqlite/statement.c
@@ -116,13 +116,6 @@ stmt_dealloc(PyObject *op)
Py_DECREF(tp);
}
-static int
-stmt_traverse(PyObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(self));
- return 0;
-}
-
/*
* Strip leading whitespace and comments from incoming SQL (null terminated C
* string) and return a pointer to the first non-whitespace, non-comment
@@ -183,7 +176,7 @@ lstrip_sql(const char *sql)
static PyType_Slot stmt_slots[] = {
{Py_tp_dealloc, stmt_dealloc},
- {Py_tp_traverse, stmt_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{0, NULL},
};
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 2388bbb3bae631..048e640283638b 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -5692,13 +5692,6 @@ _ssl_MemoryBIO_impl(PyTypeObject *type)
return (PyObject *) self;
}
-static int
-memory_bio_traverse(PyObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(self));
- return 0;
-}
-
static void
memory_bio_dealloc(PyObject *op)
{
@@ -5869,7 +5862,7 @@ static PyType_Slot PySSLMemoryBIO_slots[] = {
{Py_tp_getset, memory_bio_getsetlist},
{Py_tp_new, _ssl_MemoryBIO},
{Py_tp_dealloc, memory_bio_dealloc},
- {Py_tp_traverse, memory_bio_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{0, 0},
};
diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index 0a22907375b0dd..1a64289ea01fdb 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -655,13 +655,6 @@ PyThreadHandleObject_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return (PyObject *)PyThreadHandleObject_new(type);
}
-static int
-PyThreadHandleObject_traverse(PyObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(self));
- return 0;
-}
-
static void
PyThreadHandleObject_dealloc(PyObject *op)
{
@@ -751,7 +744,7 @@ static PyType_Slot ThreadHandle_Type_slots[] = {
{Py_tp_dealloc, PyThreadHandleObject_dealloc},
{Py_tp_repr, PyThreadHandleObject_repr},
{Py_tp_getset, ThreadHandle_getsetlist},
- {Py_tp_traverse, PyThreadHandleObject_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{Py_tp_methods, ThreadHandle_methods},
{Py_tp_new, PyThreadHandleObject_tp_new},
{0, 0}
@@ -767,13 +760,6 @@ static PyType_Spec ThreadHandle_Type_spec = {
/* Lock objects */
-static int
-lock_traverse(PyObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(self));
- return 0;
-}
-
static void
lock_dealloc(PyObject *self)
{
@@ -1045,7 +1031,7 @@ static PyType_Slot lock_type_slots[] = {
{Py_tp_repr, lock_repr},
{Py_tp_doc, (void *)lock_doc},
{Py_tp_methods, lock_methods},
- {Py_tp_traverse, lock_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{Py_tp_new, lock_new},
{0, 0}
};
@@ -1060,13 +1046,6 @@ static PyType_Spec lock_type_spec = {
/* Recursive lock objects */
-static int
-rlock_traverse(PyObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(self));
- return 0;
-}
-
static int
rlock_locked_impl(rlockobject *self)
{
@@ -1359,7 +1338,7 @@ static PyType_Slot rlock_type_slots[] = {
{Py_tp_methods, rlock_methods},
{Py_tp_alloc, PyType_GenericAlloc},
{Py_tp_new, rlock_new},
- {Py_tp_traverse, rlock_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{0, 0},
};
diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c
index 654e9445985d84..d9ac5b97f8258f 100644
--- a/Modules/arraymodule.c
+++ b/Modules/arraymodule.c
@@ -714,14 +714,6 @@ ins1(arrayobject *self, Py_ssize_t where, PyObject *v)
}
/* Methods */
-
-static int
-array_tp_traverse(PyObject *op, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(op));
- return 0;
-}
-
static void
array_dealloc(PyObject *op)
{
@@ -2968,7 +2960,7 @@ static PyType_Slot array_slots[] = {
{Py_tp_getset, array_getsets},
{Py_tp_alloc, PyType_GenericAlloc},
{Py_tp_new, array_new},
- {Py_tp_traverse, array_tp_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
/* as sequence */
{Py_sq_length, array_length},
diff --git a/Modules/blake2module.c b/Modules/blake2module.c
index 163f238a4268d0..4921e8f945ef37 100644
--- a/Modules/blake2module.c
+++ b/Modules/blake2module.c
@@ -1008,17 +1008,10 @@ py_blake2_dealloc(PyObject *self)
Py_DECREF(type);
}
-static int
-py_blake2_traverse(PyObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(self));
- return 0;
-}
-
static PyType_Slot blake2b_type_slots[] = {
{Py_tp_clear, py_blake2_clear},
{Py_tp_dealloc, py_blake2_dealloc},
- {Py_tp_traverse, py_blake2_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{Py_tp_doc, (char *)py_blake2b_new__doc__},
{Py_tp_methods, py_blake2b_methods},
{Py_tp_getset, py_blake2b_getsetters},
@@ -1029,7 +1022,7 @@ static PyType_Slot blake2b_type_slots[] = {
static PyType_Slot blake2s_type_slots[] = {
{Py_tp_clear, py_blake2_clear},
{Py_tp_dealloc, py_blake2_dealloc},
- {Py_tp_traverse, py_blake2_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{Py_tp_doc, (char *)py_blake2s_new__doc__},
{Py_tp_methods, py_blake2b_methods},
{Py_tp_getset, py_blake2b_getsetters},
diff --git a/Modules/hmacmodule.c b/Modules/hmacmodule.c
index 680c93a04cac17..b30dda501a0716 100644
--- a/Modules/hmacmodule.c
+++ b/Modules/hmacmodule.c
@@ -1032,13 +1032,6 @@ HMACObject_dealloc(PyObject *op)
Py_DECREF(type);
}
-static int
-HMACObject_traverse(PyObject *op, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(op));
- return 0;
-}
-
static PyMethodDef HMACObject_methods[] = {
_HMAC_HMAC_COPY_METHODDEF
_HMAC_HMAC_UPDATE_METHODDEF
@@ -1060,7 +1053,7 @@ static PyType_Slot HMACObject_Type_slots[] = {
{Py_tp_getset, HMACObject_getsets},
{Py_tp_clear, HMACObject_clear},
{Py_tp_dealloc, HMACObject_dealloc},
- {Py_tp_traverse, HMACObject_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{0, NULL} /* sentinel */
};
diff --git a/Modules/md5module.c b/Modules/md5module.c
index 8b6dd4a8195dfb..6b6457427b6b5e 100644
--- a/Modules/md5module.c
+++ b/Modules/md5module.c
@@ -82,13 +82,6 @@ newMD5object(MD5State * st)
}
/* Internal methods for a hash object */
-static int
-MD5_traverse(PyObject *ptr, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(ptr));
- return 0;
-}
-
static void
MD5_dealloc(PyObject *op)
{
@@ -246,7 +239,7 @@ static PyType_Slot md5_type_slots[] = {
{Py_tp_dealloc, MD5_dealloc},
{Py_tp_methods, MD5_methods},
{Py_tp_getset, MD5_getseters},
- {Py_tp_traverse, MD5_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{0,0}
};
diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c
index 0cb4b62d734550..8413ebe668dffe 100644
--- a/Modules/mmapmodule.c
+++ b/Modules/mmapmodule.c
@@ -128,13 +128,6 @@ typedef struct {
#define mmap_object_CAST(op) ((mmap_object *)(op))
-static int
-mmap_object_traverse(PyObject *op, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(op));
- return 0;
-}
-
static void
mmap_object_dealloc(PyObject *op)
{
@@ -1499,7 +1492,7 @@ static PyType_Slot mmap_object_slots[] = {
{Py_tp_members, mmap_object_members},
{Py_tp_getset, mmap_object_getset},
{Py_tp_getattro, PyObject_GenericGetAttr},
- {Py_tp_traverse, mmap_object_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
/* as sequence */
{Py_sq_length, mmap_length},
diff --git a/Modules/sha1module.c b/Modules/sha1module.c
index faa9dcccc5755b..d64eb91458cae8 100644
--- a/Modules/sha1module.c
+++ b/Modules/sha1module.c
@@ -81,13 +81,6 @@ newSHA1object(SHA1State *st)
/* Internal methods for a hash object */
-static int
-SHA1_traverse(PyObject *ptr, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(ptr));
- return 0;
-}
-
static void
SHA1_dealloc(PyObject *op)
{
@@ -247,7 +240,7 @@ static PyType_Slot sha1_type_slots[] = {
{Py_tp_dealloc, SHA1_dealloc},
{Py_tp_methods, SHA1_methods},
{Py_tp_getset, SHA1_getseters},
- {Py_tp_traverse, SHA1_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{0,0}
};
diff --git a/Modules/sha2module.c b/Modules/sha2module.c
index 36300ba899fd44..66259fe47a0fd3 100644
--- a/Modules/sha2module.c
+++ b/Modules/sha2module.c
@@ -164,14 +164,6 @@ newSHA512object(sha2_state *state)
}
/* Internal methods for our hash objects. */
-
-static int
-SHA2_traverse(PyObject *ptr, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(ptr));
- return 0;
-}
-
static void
SHA256_dealloc(PyObject *op)
{
@@ -519,7 +511,7 @@ static PyType_Slot sha256_types_slots[] = {
{Py_tp_dealloc, SHA256_dealloc},
{Py_tp_methods, SHA256_methods},
{Py_tp_getset, SHA256_getseters},
- {Py_tp_traverse, SHA2_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{0,0}
};
@@ -527,7 +519,7 @@ static PyType_Slot sha512_type_slots[] = {
{Py_tp_dealloc, SHA512_dealloc},
{Py_tp_methods, SHA512_methods},
{Py_tp_getset, SHA512_getseters},
- {Py_tp_traverse, SHA2_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{0,0}
};
diff --git a/Modules/sha3module.c b/Modules/sha3module.c
index 5764556bb680f3..de4bf09b8e7e0b 100644
--- a/Modules/sha3module.c
+++ b/Modules/sha3module.c
@@ -226,13 +226,6 @@ SHA3_dealloc(PyObject *self)
Py_DECREF(tp);
}
-static int
-SHA3_traverse(PyObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(self));
- return 0;
-}
-
/* External methods for a hash object */
@@ -424,7 +417,7 @@ static PyGetSetDef SHA3_getseters[] = {
static PyType_Slot type_slots_obj[] = { \
{Py_tp_clear, SHA3_clear}, \
{Py_tp_dealloc, SHA3_dealloc}, \
- {Py_tp_traverse, SHA3_traverse}, \
+ {Py_tp_traverse, _PyObject_VisitType}, \
{Py_tp_doc, (char*)type_doc}, \
{Py_tp_methods, type_methods}, \
{Py_tp_getset, type_getseters}, \
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index c5b16dc4fe4c5f..dc8e08ac3e522e 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -5538,13 +5538,6 @@ sock_finalize(PyObject *self)
PyErr_SetRaisedException(exc);
}
-static int
-sock_traverse(PyObject *s, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(s));
- return 0;
-}
-
static void
sock_dealloc(PyObject *s)
{
@@ -5843,7 +5836,7 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto,
static PyType_Slot sock_slots[] = {
{Py_tp_dealloc, sock_dealloc},
- {Py_tp_traverse, sock_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{Py_tp_repr, sock_repr},
{Py_tp_doc, (void *)sock_doc},
{Py_tp_methods, sock_methods},
diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c
index 2a30030a2a1153..41725e5aec1641 100644
--- a/Modules/unicodedata.c
+++ b/Modules/unicodedata.c
@@ -1594,13 +1594,6 @@ static PyMethodDef unicodedata_functions[] = {
{NULL, NULL} /* sentinel */
};
-static int
-ucd_traverse(PyObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(self));
- return 0;
-}
-
static void
ucd_dealloc(PyObject *self)
{
@@ -1612,7 +1605,7 @@ ucd_dealloc(PyObject *self)
static PyType_Slot ucd_type_slots[] = {
{Py_tp_dealloc, ucd_dealloc},
- {Py_tp_traverse, ucd_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{Py_tp_getattro, PyObject_GenericGetAttr},
{Py_tp_methods, unicodedata_functions},
{Py_tp_members, DB_members},
diff --git a/Objects/object.c b/Objects/object.c
index fba86e63cd4a11..bd3ba02f8eb255 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -3384,3 +3384,13 @@ PyUnstable_Object_IsUniquelyReferenced(PyObject *op)
assert(op != NULL);
return _PyObject_IsUniquelyReferenced(op);
}
+
+int
+_PyObject_VisitType(PyObject *op, visitproc visit, void *arg)
+{
+ assert(op != NULL);
+ PyTypeObject *tp = Py_TYPE(op);
+ _PyObject_ASSERT((PyObject *)tp, PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE));
+ Py_VISIT(tp);
+ return 0;
+}
diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c
index cead6e69af5451..522e9fd9c955af 100644
--- a/Objects/typevarobject.c
+++ b/Objects/typevarobject.c
@@ -2304,20 +2304,13 @@ generic_dealloc(PyObject *self)
Py_DECREF(tp);
}
-static int
-generic_traverse(PyObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(self));
- return 0;
-}
-
static PyType_Slot generic_slots[] = {
{Py_tp_doc, (void *)generic_doc},
{Py_tp_methods, generic_methods},
{Py_tp_dealloc, generic_dealloc},
{Py_tp_alloc, PyType_GenericAlloc},
{Py_tp_free, PyObject_GC_Del},
- {Py_tp_traverse, generic_traverse},
+ {Py_tp_traverse, _PyObject_VisitType},
{0, NULL},
};
diff --git a/PC/winreg.c b/PC/winreg.c
index 05a33006c32326..9bbacb0f50bd63 100644
--- a/PC/winreg.c
+++ b/PC/winreg.c
@@ -161,13 +161,6 @@ PyHKEY_deallocFunc(PyObject *ob)
Py_DECREF(tp);
}
-static int
-PyHKEY_traverseFunc(PyObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(Py_TYPE(self));
- return 0;
-}
-
static int
PyHKEY_boolFunc(PyObject *ob)
{
@@ -369,7 +362,7 @@ static PyType_Slot pyhkey_type_slots[] = {
{Py_tp_members, PyHKEY_memberlist},
{Py_tp_methods, PyHKEY_methods},
{Py_tp_doc, (char *)PyHKEY_doc},
- {Py_tp_traverse, PyHKEY_traverseFunc},
+ {Py_tp_traverse, _PyObject_VisitType},
{Py_tp_hash, PyHKEY_hashFunc},
{Py_tp_str, PyHKEY_strFunc},