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: 2 additions & 0 deletions docs/dependency_groups.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ the ``[dependency-groups]`` table.
This module provides tools for resolving group names to lists of requirements,
most notably expanding ``include-group`` directives.

.. versionadded:: 26.1

Usage
-----

Expand Down
2 changes: 2 additions & 0 deletions docs/direct_url.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Direct URLs

Parse and validate `direct_url.json files <https://packaging.python.org/en/latest/specifications/direct-url/>`_.

.. versionadded:: 26.1

Usage
-----

Expand Down
2 changes: 2 additions & 0 deletions docs/errors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Currently this contains :class:`~packaging.errors.ExceptionGroup`, a simple
backport of the stdlib :external:exc:`ExceptionGroup`. It is recommended to use
the stdlib module on Python 3.11+, but this does reexport that as well.

.. versionadded:: 26.1

Recommended Usage
-----------------

Expand Down
2 changes: 2 additions & 0 deletions docs/licenses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Helper for canonicalizing SPDX
`License-Expression metadata <https://peps.python.org/pep-0639/#term-license-expression>`__
as `defined in PEP 639 <https://peps.python.org/pep-0639/#spdx>`__.

.. versionadded:: 24.2


Reference
---------
Expand Down
2 changes: 2 additions & 0 deletions docs/pylock.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Lock Files

Parse and validate `pylock.toml files <https://packaging.python.org/en/latest/specifications/pylock-toml/>`_.

.. versionadded:: 26.0

Usage
-----

Expand Down
2 changes: 2 additions & 0 deletions docs/ranges.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ intervals on the PEP 440 ordering. It supports intersection, union,
complement, and difference, so tooling that combines many requirements, such
as a resolver, can work on the intervals directly.

.. versionadded:: 26.3

Usage
-----

Expand Down
12 changes: 9 additions & 3 deletions docs/requirements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ Reference
Added equality (``__eq__``) and hashing (``__hash__``) so requirements
can be compared and stored in sets / dicts.

.. versionchanged:: 23.2
Equality and hashing began canonicalizing requirement names, so
requirements whose names differ only by normalization (e.g.
``Requirement("Foo")`` vs ``Requirement("foo")``) now compare and hash
equal.

Instances are safe to serialize with :mod:`pickle`. They use a stable
format so the same pickle can be loaded in future packaging releases.

Expand All @@ -99,9 +105,9 @@ Reference
specifier's explicit :attr:`~packaging.specifiers.SpecifierSet.prereleases`
override; it is now included again.

Equality and hashing normalize requirement names, extras, and
equivalent specifiers. The string representation still preserves the
parsed name and extras spelling.
Equality and hashing normalize extras and equivalent specifiers. The
string representation still preserves the parsed name and extras
spelling.

:param str requirement_string: The string representation of a requirement.
:raises InvalidRequirement: If the given ``requirement_string`` is not parseable,
Expand Down
10 changes: 10 additions & 0 deletions src/packaging/markers.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ def __dir__() -> list[str]:
* ``"metadata"`` (for core metadata; default)
* ``"lock_file"`` (for lock files)
* ``"requirement"`` (i.e. all other situations)

.. versionadded:: 25.0
"""

MARKERS_ALLOWING_SET = {"extras", "dependency_groups"}
Expand Down Expand Up @@ -78,6 +80,11 @@ class UndefinedEnvironmentName(KeyError):

Subclasses :class:`KeyError` so that code catching the bare ``KeyError`` that
a missing environment lookup historically produced keeps working.

.. versionchanged:: 26.3
Now subclasses :class:`KeyError` (was :class:`ValueError`) and is raised by
:meth:`Marker.evaluate` for missing environment keys, where a bare
``KeyError`` was raised before.
"""


Expand Down Expand Up @@ -518,6 +525,9 @@ def evaluate(
is missing from the evaluation environment.
:returns: ``True`` if the marker matches, otherwise ``False``.

.. versionchanged:: 25.0
Added the ``context`` parameter, which influences which marker names
are considered valid.
"""
current_environment = cast(
"dict[str, str | AbstractSet[str]]", default_environment()
Expand Down
41 changes: 36 additions & 5 deletions src/packaging/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ def __dir__() -> list[str]:


class InvalidMetadata(ValueError):
"""A metadata field contains invalid data."""
"""A metadata field contains invalid data.

.. versionadded:: 23.2
"""

field: str
"""The name of the field that contains invalid data."""
Expand Down Expand Up @@ -130,11 +133,15 @@ class RawMetadata(TypedDict, total=False):

# Metadata 2.4 - PEP 639
license_expression: str
""".. versionadded:: 24.2"""
license_files: list[str]
""".. versionadded:: 24.2"""

# Metadata 2.5 - PEP 794
import_names: list[str]
""".. versionadded:: 26.0"""
import_namespaces: list[str]
""".. versionadded:: 26.0"""

# Metadata 2.6 - PEP 808 (no new fields, behavior change for Dynamic)

Expand Down Expand Up @@ -305,6 +312,8 @@ class RFC822Policy(email.policy.EmailPolicy):
"""
This is :class:`email.policy.EmailPolicy`, but with a simple ``header_store_parse``
implementation that handles multi-line values, and some nice defaults.

.. versionadded:: 26.0
"""

utf8 = True
Expand All @@ -323,6 +332,8 @@ class RFC822Message(email.message.EmailMessage):
This is :class:`email.message.EmailMessage` with two small changes: it defaults to
our `RFC822Policy`, and it correctly writes unicode when being called
with `bytes()`.

.. versionadded:: 26.0
"""

def __init__(self) -> None:
Expand Down Expand Up @@ -800,6 +811,12 @@ class Metadata:
metadata fields instead of only using built-in types. Any invalid metadata
will cause :exc:`InvalidMetadata` to be raised (with a
:py:attr:`~BaseException.__cause__` attribute as appropriate).

.. versionadded:: 23.2

.. versionchanged:: 24.0
Optional attributes now return None when the field is absent instead of
raising.
"""

_raw: RawMetadata
Expand Down Expand Up @@ -940,9 +957,15 @@ def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata:
license_expression: _Validator[NormalizedLicenseExpression | None] = _Validator(
added="2.4"
)
""":external:ref:`core-metadata-license-expression`"""
""":external:ref:`core-metadata-license-expression`

.. versionadded:: 24.2
"""
license_files: _Validator[list[str] | None] = _Validator(added="2.4")
""":external:ref:`core-metadata-license-file`"""
""":external:ref:`core-metadata-license-file`

.. versionadded:: 24.2
"""
classifiers: _Validator[list[str] | None] = _Validator(added="1.1")
""":external:ref:`core-metadata-classifier`"""
requires_dist: _Validator[list[requirements.Requirement] | None] = _Validator(
Expand Down Expand Up @@ -970,9 +993,15 @@ def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata:
obsoletes_dist: _Validator[list[str] | None] = _Validator(added="1.2")
""":external:ref:`core-metadata-obsoletes-dist`"""
import_names: _Validator[list[str] | None] = _Validator(added="2.5")
""":external:ref:`core-metadata-import-name`"""
""":external:ref:`core-metadata-import-name`

.. versionadded:: 26.0
"""
import_namespaces: _Validator[list[str] | None] = _Validator(added="2.5")
""":external:ref:`core-metadata-import-namespace`"""
""":external:ref:`core-metadata-import-namespace`

.. versionadded:: 26.0
"""
requires: _Validator[list[str] | None] = _Validator(added="1.1")
"""``Requires`` (deprecated)"""
provides: _Validator[list[str] | None] = _Validator(added="1.1")
Expand All @@ -983,6 +1012,8 @@ def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata:
def as_rfc822(self) -> RFC822Message:
"""
Return an RFC822 message with the metadata.

.. versionadded:: 26.0
"""
message = RFC822Message()
self._write_metadata(message)
Expand Down
5 changes: 4 additions & 1 deletion src/packaging/pylock.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,10 @@ def _from_dict(cls, d: Mapping[str, Any]) -> Self:

@property
def filename(self) -> str:
"""Get the filename of the sdist."""
"""Get the filename of the sdist.

.. versionadded:: 26.1
"""
filename = self.name or _path_name(self.path) or _url_name(self.url)
if not filename:
raise PylockValidationError("Cannot determine sdist filename")
Expand Down
6 changes: 6 additions & 0 deletions src/packaging/requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ class Requirement:
Added equality (``__eq__``) and hashing (``__hash__``) so requirements
can be compared and stored in sets / dicts.
.. versionchanged:: 23.2
Equality and hashing began canonicalizing requirement names, so
requirements whose names differ only by normalization (e.g.
``Requirement("Foo")`` vs ``Requirement("foo")``) now compare and hash
equal.
Instances are safe to serialize with :mod:`pickle`. They use a stable
format so the same pickle can be loaded in future packaging releases.
Expand Down
21 changes: 21 additions & 0 deletions src/packaging/specifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,14 @@ def contains(self, item: UnparsedVersion, prereleases: bool | None = None) -> bo
False
>>> Specifier(">=1.2.3").contains("1.3.0a1")
True

.. versionchanged:: 26.0

With ``prereleases=None``, a prerelease now matches. A single
version has no alternatives, so the :pep:`440` rule to accept
prereleases when nothing else satisfies the specifier applies.
Earlier versions rejected it. An unparsable version now returns
``False`` instead of raising :exc:`~packaging.version.InvalidVersion`.
"""
# ``===`` compares the raw string, so a Version parse here would
# be wasted.
Expand Down Expand Up @@ -1184,6 +1192,14 @@ def contains(
False
>>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True)
True

.. versionchanged:: 26.0

With ``prereleases=None``, a prerelease now matches. A single
version has no alternatives, so the :pep:`440` rule to accept
prereleases when nothing else satisfies the specifiers applies.
Earlier versions rejected it. An unparsable version now returns
``False`` instead of raising :exc:`~packaging.version.InvalidVersion`.
"""
version = coerce_version(item)

Expand Down Expand Up @@ -1297,6 +1313,11 @@ def filter(
>>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True))
['1.3', '1.5a1']

.. versionchanged:: 26.0

Prerelease filtering now follows the PEP 440 recommendation of
yielding prereleases only when no final release is present.

.. versionchanged:: 26.1

Added the ``key`` parameter.
Expand Down
22 changes: 22 additions & 0 deletions src/packaging/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,15 @@ def __dir__() -> list[str]:
PythonVersion = Sequence[int]
"""
A sequence of integers describing a Python version, e.g. ``(3, 13)``.

.. versionadded:: 20.0
"""

AppleVersion = tuple[int, int]
"""
A ``(major, minor)`` integer pair describing an Apple OS version.

.. versionadded:: 24.2
"""
_T = TypeVar("_T")

Expand Down Expand Up @@ -419,6 +423,8 @@ def cpython_tags(
:param Iterable platforms: Iterable of compatible platforms. Defaults to the
platforms compatible with the current system.
:param bool warn: Whether warnings should be logged. Defaults to ``False``.

.. versionadded:: 20.0
"""
if not python_version:
python_version = sys.version_info[:2]
Expand Down Expand Up @@ -536,6 +542,8 @@ def generic_tags(
:param Iterable platforms: Iterable of compatible platforms. Defaults to the
platforms compatible with the current system.
:param bool warn: Whether warnings should be logged. Defaults to ``False``.

.. versionadded:: 20.0
"""
if not interpreter:
interp_name = interpreter_name()
Expand Down Expand Up @@ -587,6 +595,8 @@ def compatible_tags(
``"cp38"``. Defaults to the current interpreter.
:param Iterable platforms: Iterable of compatible platforms. Defaults to the
platforms compatible with the current system.

.. versionadded:: 20.0
"""
if not python_version:
python_version = sys.version_info[:2]
Expand Down Expand Up @@ -665,6 +675,8 @@ def mac_platforms(
- On Windows, platform compatibility is statically specified
- On Linux, code must be run on the system itself to determine
compatibility

.. versionadded:: 20.0
"""
if version is None or arch is None:
version_str, _, cpu_arch = platform.mac_ver()
Expand Down Expand Up @@ -749,6 +761,8 @@ def ios_platforms(
.. note::
Behavior of this method is undefined if invoked on non-iOS platforms
without providing explicit version and multiarch arguments.

.. versionadded:: 24.2
"""
if version is None:
# if iOS is the current platform, ios_ver *must* be defined. However,
Expand Down Expand Up @@ -808,6 +822,8 @@ def android_platforms(
e.g. ``arm64_v8a``. Defaults to the current system's ABI , as returned by
``sysconfig.get_platform``. Hyphens and periods will be replaced with
underscores.

.. versionadded:: 25.0
"""
if platform.system() != "Android" and (api_level is None or abi is None):
raise TypeError(
Expand Down Expand Up @@ -866,6 +882,8 @@ def _generic_platforms() -> Iterator[str]:
def platform_tags() -> Iterator[str]:
"""
Yields the :attr:`~Tag.platform` tags for the running interpreter.

.. versionadded:: 21.1
"""
if platform.system() == "Darwin":
return mac_platforms()
Expand All @@ -889,6 +907,8 @@ def interpreter_name() -> str:
be returned when appropriate.

This typically acts as the prefix to the :attr:`~Tag.interpreter` tag.

.. versionadded:: 20.0
"""
name = sys.implementation.name
return INTERPRETER_SHORT_NAMES.get(name) or name
Expand All @@ -901,6 +921,8 @@ def interpreter_version(*, warn: bool = False) -> str:
This typically acts as the suffix to the :attr:`~Tag.interpreter` tag.

:param bool warn: Whether warnings should be logged. Defaults to ``False``.

.. versionadded:: 20.0
"""
version = _get_config_var("py_version_nodot", warn=warn)
return str(version) if version else _version_nodot(sys.version_info[:2])
Expand Down
Loading
Loading