🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

setuptools

Package Overview
Dependencies
Maintainers
3
Versions
624
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

setuptools - pypi Package Compare versions

Comparing version
82.0.0
to
82.0.1
docs/deprecated/pkg_resources.rst

Sorry, the diff of this file is too big to display

+202
===========================================================================================
Drawbacks of installing source distributions (``sdist``) and how to improve reproducibility
===========================================================================================
.. admonition:: Scope and audience
This page contains relevant information for **package consumers** (people
installing third-party projects that may have been built with Setuptools).
It explains why installing from :external+PyPUG:term:`Source Distributions
<Source Distribution (or "sdist")>` can be less predictable than installing
from :external+PyPUG:term:`wheels <Wheel>`, and contains tips on how to improve
**installation-time** reproducibility. It does **not** describe how to build
packages with Setuptools, nor is it a statement of policy about what
publishers must do [#publishing]_.
The ``sdist`` format was one of the first packaging formats to be created by the
Python community (predating the advent of ``wheel``). Although very
useful today to distribute and share Python libraries and applications,
``sdist``\s are notoriously difficult to work with in circumstances that
require high build reproducibility and tolerance to disruptions.
This guide reviews the concept of ``sdist``, highlights its potential uses
and drawbacks and explores potential practices to improve build reproducibility
when relying on ``sdist``\s.
What is an ``sdist``?
=====================
You can read more about the ``sdist`` format and its ``wheel`` counterpart in
:external+PyPUG:doc:`discussions/package-formats`, but for the sake of this
document an ``sdist`` can be considered a simple ``.tar.gz`` archive that
contains all the files necessary to build a Python project that later will be
installed in the end-user's environment.
The most defining characteristic of the ``sdist`` format is its
platform-independence, as the distributions do not include binary executable files.
This format is very flexible and, although usually composed by a simple copy
of the source code files with some extra metadata files added, it can also include
platform-independent code automatically generated during the build
phase [#examples]_.
When is an ``sdist`` useful?
============================
Sometimes it can be tricky to distribute Python packages that contain binary
extensions, especially when they are built for platforms that do not define a
cross-version stable ABI_.
Moreover package indexes like PyPI_ may restrict their offer to a handful of
well-known platforms.
Finally, for certain edge cases, the build process may require machine specific
parameters.
In this context, distributing code via ``sdist``\s becomes a valuable fallback.
It allows users in other platforms to access the source code
and attempt to recompile the extensions locally.
What are the drawbacks of an ``sdist``?
=======================================
Despite their usefulness, working with ``sdist``\s can be challenging. One
major difficulty is reconstructing a compatible build environment in which the
``sdist`` can be processed into a ``wheel``, especially when it comes to build
dependencies.
While :pep:`518` introduced a standard for declaring build dependencies
distributed as Python packages (e.g. via PyPI), many projects also rely on
non-Python dependencies, such as compilers and binary system-level libraries,
that are not declared as a standard metadata. These dependencies can vary
significantly across systems and its installation is often not automated and
undocumented, i.e., simply assumed to be present.
Another issue is *tooling drift*: even if a project was originally buildable
from its ``sdist``, changes in the build dependencies (e.g., updates,
deprecations and security fixes) can break compatibility over time [#pinning]_.
This is a natural tendency of software systems and especially true for older
projects.
Therefore, mission-critical systems and environments that cannot afford
unforeseen/unintended interruptions should not rely on ``sdist``\s.
If your project or product requires high reliability and minimal disruption,
you should adapt your workflow to increase resiliency and reproducibility or
disallow ``sdist``\s all together.
How to improve reproducibility in your workflow and avoid ``sdist`` drawbacks?
==============================================================================
The first step to improve your workflow is to determine whether your workflow
is directly or indirectly relying on ``sdist``\s — and to prevent them from being
compiled on demand.
Installers like ``pip`` or ``uv`` have options that help with this.
For example, you can set the environment variable |PIP_ONLY_BINARY|_ with
the value ``:all:``, to prevent ``sdist``\s from being installed
(see the corresponding `uv alternative`_).
When this setting is enabled, any installation that fails will indicate which
packages are not available as ``wheel``\s, helping you pinpoint installations
relying on ``sdist``\s.
Once these packages are identified, the next step is to build them in
a controlled environment.
You can use ``pip``\'s |PIP_CONSTRAINT|_ / |PIP_BUILD_CONSTRAINT|_
environment variables or the
|build-constraint|_ ``uv``\'s CLI option to enforce specific versions of
Python packages [#build-isolation]_.
To further improve the consistency of OS-level tools and libraries,
you can leverage your CI/CD provider's configuration method, for example
`GitHub Workflows`_, `Bitbucket Pipelines`_, `GitLab CI/CD`_, Jenkins_,
CircleCI_ or Semaphore_.
Alternatively, you can use containers (e.g. docker_, nerdctl_ or podman_),
immutable operating system distributions or package managers (e.g. `NixOS/Nix`_)
or configuration management tools (e.g. Ansible_, chef_ or puppet_)
to implement `Infrastructure as Code`_ (IaC) and ensure build environments
are reproducible and version-controlled.
Consider caching the resulting ``wheel``\s
locally via |wheelhouse directories|_ or hosting them in
*private package indexes* (such as devpi_).
This allows you to serve pre-built distributions internally,
which reduces reliance on external sources, improves build stability,
and often results in faster workflows as a welcome side effect.
Finally, it's important to regularly audit your pinned or cached (build)
dependencies for known security vulnerabilities and critical bug fixes and/or
update them accordingly.
This can be done through an *out-of-band* workflow — such as a scheduled job
or a monthly CI/CD pipeline — that does not interfere with your
mission-critical or low-tolerance environments. This approach ensures that your
systems remain secure and up to date without compromising the stability of your
primary workflows.
.. rubric:: Footnotes
.. [#publishing]
The PyPA recommendation, documented in the `packaging tutorial`_, is to
publish both ``sdists`` and ``wheels``.
.. [#examples]
Examples of platform-independent generated code in ``sdist``\s include
``.pyx`` files transpiled into ``.c`` and Python code created from
``.proto``, JSON schema or grammar files, etc.
.. [#pinning]
Although developers can try to minimize the impact of tooling drift by
locking the version of build dependencies, this approach also has
its own drawbacks. In fact, it is very common in the Python community to
avoid specifying version caps. For a deeper discussion on this topic, see:
https://iscinumpy.dev/post/bound-version-constraints/ and
https://hynek.me/articles/semver-will-not-save-you/.
.. [#build-isolation]
When a virtual environment with hand picked versions of build dependencies
is crafted (either manually or via tools supporting one of the
:external+PyPUG:doc:`specifications/pylock-toml` or
:external+pip:doc:`reference/requirements-file-format`), it is also possible
to use features like |no-isolation|_, |no-build-isolation|_ or the
`equivalent uv settings`_ to ensure packages are built against the currently
active virtual environment.
.. _ABI: https://en.wikipedia.org/wiki/Application_binary_interface
.. _PyPI: https://pypi.org
.. |PIP_ONLY_BINARY| replace:: ``PIP_ONLY_BINARY``
.. _PIP_ONLY_BINARY: https://pip.pypa.io/en/stable/cli/pip_install/#cmdoption-only-binary
.. _uv alternative: https://docs.astral.sh/uv/reference/settings/#pip_only-binary
.. |PIP_CONSTRAINT| replace:: ``PIP_CONSTRAINT``
.. _PIP_CONSTRAINT: https://pip.pypa.io/en/stable/cli/pip_install/#cmdoption-c
.. |PIP_BUILD_CONSTRAINT| replace:: ``PIP_BUILD_CONSTRAINT``
.. _PIP_BUILD_CONSTRAINT: https://pip.pypa.io/en/stable/cli/pip_download/#cmdoption-build-constraint
.. |build-constraint| replace:: ``--build-constraint``
.. _build-constraint: https://docs.astral.sh/uv/concepts/projects/build/#build-constraints
.. _GitHub Workflows: https://docs.github.com/en/actions/writing-workflows
.. _Bitbucket Pipelines: https://www.atlassian.com/software/bitbucket/features/pipelines
.. _GitLab CI/CD: https://docs.gitlab.com/ci/
.. _Jenkins: https://www.jenkins.io/doc/
.. _CircleCI: https://circleci.com
.. _Semaphore: https://semaphore.io
.. _docker: https://www.docker.com
.. _nerdctl: https://github.com/containerd/nerdctl
.. _podman: https://podman.io
.. _NixOS/Nix: https://nixos.org
.. _Ansible: https://docs.ansible.com
.. _chef: https://docs.chef.io
.. _puppet: https://www.puppet.com/docs/index.html
.. _Infrastructure as Code: https://en.wikipedia.org/wiki/Infrastructure_as_code
.. |wheelhouse directories| replace:: *"wheelhouse" directories*
.. _wheelhouse directories: https://pip.pypa.io/en/stable/cli/pip_wheel/#examples
.. _devpi: https://doc.devpi.net/
.. |no-isolation| replace:: ``--no-isolation``
.. _no-isolation: https://build.pypa.io/en/stable/#python--m-build---no-isolation
.. |no-build-isolation| replace:: ``--no-build-isolation``
.. _no-build-isolation: https://pip.pypa.io/en/stable/cli/pip_install/#cmdoption-no-build-isolation
.. _equivalent uv settings: https://docs.astral.sh/uv/concepts/projects/config/#build-isolation
.. _packaging tutorial: https://packaging.python.org/en/latest/tutorials/packaging-projects/#generating-distribution-archives
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0"
processorArchitecture="X86"
name="%(name)s"
type="win32"/>
<!-- Identify the application security requirements. -->
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
+0
-1

@@ -35,3 +35,2 @@ import platform

'_distutils_hack',
'pkg_resources/tests/data',
'setuptools/_vendor',

@@ -38,0 +37,0 @@ 'setuptools/config/_validate_pyproject',

@@ -149,2 +149,3 @@ from __future__ import annotations

"userguide/commands": "/deprecated/commands.html",
"pkg_resources": "/deprecated/pkg_resources.html",
}

@@ -242,4 +243,4 @@

intersphinx_mapping.update({
'pip': ('https://pip.pypa.io/en/latest', None),
'build': ('https://build.pypa.io/en/latest', None),
'pip': ('https://pip.pypa.io/en/stable', None),
'build': ('https://build.pypa.io/en/stable', None),
'PyPUG': ('https://packaging.python.org/en/latest', None),

@@ -246,0 +247,0 @@ 'pytest': ('https://docs.pytest.org/en/stable', None),

@@ -26,1 +26,11 @@ ======================================================

commands
pkg_resources
Notes for Consumers of Packages Built with Setuptools
=====================================================
.. toctree::
:maxdepth: 1
sdist-reproducibility

@@ -125,4 +125,3 @@ ================================

cannot declare dependencies other than through
``setuptools/_vendor/vendored.txt`` and
``pkg_resources/_vendor/vendored.txt``.
``setuptools/_vendor/vendored.txt``.

@@ -129,0 +128,0 @@ All the dependencies specified in these files are "vendorized" using a

@@ -24,3 +24,2 @@ .. image:: images/banner-640x320.svg

build_meta
pkg_resources
references/keywords

@@ -27,0 +26,0 @@ setuptools

@@ -6,5 +6,4 @@ ==================================================

``Setuptools`` is a collection of enhancements to the Python ``distutils``
that allow developers to more easily build and
distribute Python packages, especially ones that have dependencies on other
packages.
that allow developers to more easily build Python packages, including those
that have dependencies on other packages and C/C++ extension modules.

@@ -11,0 +10,0 @@ Packages built and distributed using ``setuptools`` look to the user like

@@ -296,3 +296,3 @@ .. _Creating ``distutils`` Extensions:

.. _distutils: https://docs.python.org/3.9/library/distutils.html
.. _distutils: https://setuptools.pypa.io/en/latest/deprecated/distutils/index.html

@@ -299,0 +299,0 @@

@@ -6,4 +6,2 @@ recursive-include setuptools *.py *.exe *.xml *.tmpl

recursive-include setuptools/_vendor *
recursive-include pkg_resources *.py *.txt
recursive-include pkg_resources/tests/data *
recursive-include tools *

@@ -10,0 +8,0 @@ recursive-include newsfragments *

+10
-16

@@ -34,5 +34,4 @@ [mypy]

| ^setuptools/_distutils/
# Duplicate module name
| ^pkg_resources/tests/data/my-test-package-source/setup.py$
)
[mypy-setuptools.*]

@@ -47,19 +46,15 @@ disable_error_code =

# - pkg_resources tests create modules that won't exists statically before the test is run.
# Let's ignore all "import-not-found" since, if an import really wasn't found, then the test would fail.
[mypy-pkg_resources.tests.*]
disable_error_code = import-not-found
# - distutils doesn't exist on Python 3.12, unfortunately, this means typing
# will be missing for subclasses of distutils on Python 3.12 until either:
# - support for `SETUPTOOLS_USE_DISTUTILS=stdlib` is dropped (#3625)
# for setuptools to import `_distutils` directly
# - or non-stdlib distutils typings are exposed
# distutils doesn't exist on Python 3.12, unfortunately, this means typing
# will be missing for subclasses of distutils on Python 3.12 until either:
# - support for `SETUPTOOLS_USE_DISTUTILS=stdlib` is dropped (#3625)
# for setuptools to import `_distutils` directly
# - or non-stdlib distutils typings are exposed
[mypy-distutils.*]
ignore_missing_imports = True
# - wheel: does not intend on exposing a programmatic API https://github.com/pypa/wheel/pull/610#issuecomment-2081687671
# wheel: does not intend on exposing a programmatic API https://github.com/pypa/wheel/pull/610#issuecomment-2081687671
[mypy-wheel.*]
follow_untyped_imports = True
# - The following are not marked as py.typed:
# The following are not marked as py.typed:
# - jaraco: Since mypy 1.12, the root name of the untyped namespace package gets called-out too

@@ -70,4 +65,3 @@ # - jaraco.develop: https://github.com/jaraco/jaraco.develop/issues/22

# - jaraco.path: https://github.com/jaraco/jaraco.path/issues/2
# - jaraco.text: https://github.com/jaraco/jaraco.text/issues/17
[mypy-jaraco,jaraco.develop.*,jaraco.envs,jaraco.packaging.*,jaraco.path,jaraco.text]
[mypy-jaraco,jaraco.develop.*,jaraco.envs,jaraco.packaging.*,jaraco.path]
follow_untyped_imports = True

@@ -74,0 +68,0 @@

Metadata-Version: 2.4
Name: setuptools
Version: 82.0.0
Summary: Easily download, build, install, upgrade, and uninstall Python packages
Version: 82.0.1
Summary: Most extensible Python build backend with support for C/C++ extension modules
Author-email: Python Packaging Authority <distutils-sig@python.org>

@@ -66,3 +66,2 @@ License-Expression: MIT

Requires-Dist: wheel>=0.43.0; extra == "core"
Requires-Dist: platformdirs>=4.2.2; extra == "core"
Requires-Dist: jaraco.functools>=4; extra == "core"

@@ -69,0 +68,0 @@ Requires-Dist: more_itertools; extra == "core"

@@ -13,7 +13,7 @@ [build-system]

name = "setuptools"
version = "82.0.0"
version = "82.0.1"
authors = [
{ name = "Python Packaging Authority", email = "distutils-sig@python.org" },
]
description = "Easily download, build, install, upgrade, and uninstall Python packages"
description = "Most extensible Python build backend with support for C/C++ extension modules"
readme = "README.rst"

@@ -106,5 +106,2 @@ classifiers = [

# pkg_resources
"platformdirs >= 4.2.2", # Made ctypes optional (see #4461)
# for distutils

@@ -111,0 +108,0 @@ "jaraco.functools >= 4",

Metadata-Version: 2.4
Name: setuptools
Version: 82.0.0
Summary: Easily download, build, install, upgrade, and uninstall Python packages
Version: 82.0.1
Summary: Most extensible Python build backend with support for C/C++ extension modules
Author-email: Python Packaging Authority <distutils-sig@python.org>

@@ -66,3 +66,2 @@ License-Expression: MIT

Requires-Dist: wheel>=0.43.0; extra == "core"
Requires-Dist: platformdirs>=4.2.2; extra == "core"
Requires-Dist: jaraco.functools>=4; extra == "core"

@@ -69,0 +68,0 @@ Requires-Dist: more_itertools; extra == "core"

@@ -16,3 +16,2 @@

wheel>=0.43.0
platformdirs>=4.2.2
jaraco.functools>=4

@@ -19,0 +18,0 @@ more_itertools

@@ -20,3 +20,2 @@ LICENSE

docs/index.rst
docs/pkg_resources.rst
docs/python 2 sunset.rst

@@ -32,4 +31,6 @@ docs/roadmap.rst

docs/deprecated/index.rst
docs/deprecated/pkg_resources.rst
docs/deprecated/python_eggs.rst
docs/deprecated/resource_extraction.rst
docs/deprecated/sdist-reproducibility.rst
docs/deprecated/zip_safe.rst

@@ -100,2 +101,3 @@ docs/deprecated/distutils/_setuptools_disclaimer.rst

setuptools/launch.py
setuptools/launcher manifest.xml
setuptools/logging.py

@@ -419,3 +421,2 @@ setuptools/modified.py

setuptools/command/install_scripts.py
setuptools/command/launcher manifest.xml
setuptools/command/rotate.py

@@ -422,0 +423,0 @@ setuptools/command/saveopts.py

@@ -271,6 +271,14 @@ """Translation layer between pyproject config and setuptools distribution and

args = ({k.replace("-", "_"): v for k, v in x.items()} for x in val)
new = [Extension(**kw) for kw in args]
new = (Extension(**_adjust_ext_attrs(kw)) for kw in args)
return [*existing, *new]
def _adjust_ext_attrs(attrs: dict) -> dict:
# https://github.com/pypa/setuptools/issues/4810
# In TOML there is no differentiation between tuples and lists,
# and distutils requires tuples...
attrs["define_macros"] = list(map(tuple, attrs.get("define_macros") or []))
return attrs
def _noop(_dist: Distribution, val: _T) -> _T:

@@ -277,0 +285,0 @@ return val

@@ -0,1 +1,3 @@

from __future__ import annotations
import io

@@ -9,3 +11,3 @@ import json

from textwrap import indent, wrap
from typing import Any, Dict, Generator, Iterator, List, Optional, Sequence, Union
from typing import Any, Generator, Iterator, Sequence

@@ -40,3 +42,3 @@ from .fastjsonschema_exceptions import JsonSchemaValueException

_CAMEL_CASE_SPLITTER = re.compile(r"\W+|([A-Z][^A-Z\W]*)")
_IDENTIFIER = re.compile(r"^[\w_]+$", re.I)
_IDENTIFIER = re.compile(r"^[\w_]+$", re.IGNORECASE)

@@ -78,3 +80,3 @@ _TOML_JARGON = {

@classmethod
def _from_jsonschema(cls, ex: JsonSchemaValueException) -> "Self":
def _from_jsonschema(cls, ex: JsonSchemaValueException) -> Self:
formatter = _ErrorFormatting(ex)

@@ -179,4 +181,4 @@ obj = cls(str(formatter), ex.value, formatter.name, ex.definition, ex.rule)

def __init__(self, jargon: Optional[Dict[str, str]] = None):
self.jargon: Dict[str, str] = jargon or {}
def __init__(self, jargon: dict[str, str] | None = None):
self.jargon: dict[str, str] = jargon or {}
# Clarify confusing terms

@@ -214,3 +216,3 @@ self._terms = {

def _jargon(self, term: Union[str, List[str]]) -> Union[str, List[str]]:
def _jargon(self, term: str | list[str]) -> str | list[str]:
if isinstance(term, list):

@@ -222,3 +224,3 @@ return [self.jargon.get(t, t) for t in term]

self,
schema: Union[dict, List[dict]],
schema: dict | list[dict],
prefix: str = "",

@@ -270,4 +272,4 @@ *,

def _filter_unecessary(
self, schema: Dict[str, Any], path: Sequence[str]
) -> Dict[str, Any]:
self, schema: dict[str, Any], path: Sequence[str]
) -> dict[str, Any]:
return {

@@ -279,3 +281,3 @@ key: value

def _handle_simple_dict(self, value: dict, path: Sequence[str]) -> Optional[str]:
def _handle_simple_dict(self, value: dict, path: Sequence[str]) -> str | None:
inline = any(p in value for p in self._guess_inline_defs)

@@ -339,3 +341,3 @@ simple = not any(isinstance(v, (list, dict)) for v in value.values())

def _separate_terms(word: str) -> List[str]:
def _separate_terms(word: str) -> list[str]:
"""

@@ -342,0 +344,0 @@ >>> _separate_terms("FooBar-foo")

@@ -6,4 +6,6 @@ """The purpose of this module is implement PEP 621 validations that are

import collections
import itertools
from inspect import cleandoc
from typing import Mapping, TypeVar
from typing import Generator, Iterable, Mapping, TypeVar

@@ -23,4 +25,3 @@ from .error_reporting import ValidationError

_URL = (
"https://packaging.python.org/en/latest/specifications/"
"pyproject-toml/#dynamic"
"https://packaging.python.org/en/latest/specifications/pyproject-toml/#dynamic"
)

@@ -36,2 +37,20 @@

class ImportNameCollision(ValidationError):
_DESC = """According to PEP 794:
All import-names and import-namespaces items must be unique.
"""
__doc__ = _DESC
_URL = "https://peps.python.org/pep-0794/"
class ImportNameMissing(ValidationError):
_DESC = """According to PEP 794:
An import name must have all parents listed.
"""
__doc__ = _DESC
_URL = "https://peps.python.org/pep-0794/"
def validate_project_dynamic(pyproject: T) -> T:

@@ -85,2 +104,52 @@ project_table = pyproject.get("project", {})

EXTRA_VALIDATIONS = (validate_project_dynamic, validate_include_depenency)
def _remove_private(items: Iterable[str]) -> Generator[str, None, None]:
for item in items:
yield item.partition(";")[0].rstrip()
def validate_import_name_issues(pyproject: T) -> T:
project = pyproject.get("project", {})
import_names = collections.Counter(_remove_private(project.get("import-names", [])))
import_namespaces = collections.Counter(
_remove_private(project.get("import-namespaces", []))
)
duplicated = [k for k, v in (import_names + import_namespaces).items() if v > 1]
if duplicated:
raise ImportNameCollision(
message="Duplicated names are not allowed in import-names/import-namespaces",
value=duplicated,
name="data.project.importnames(paces)",
definition={
"description": cleandoc(ImportNameCollision._DESC),
"see": ImportNameCollision._URL,
},
rule="PEP 794",
)
names = frozenset(import_names + import_namespaces)
for name in names:
for parent in itertools.accumulate(
name.split(".")[:-1], lambda a, b: f"{a}.{b}"
):
if parent not in names:
raise ImportNameMissing(
message="All parents of an import name must also be listed in import-namespace/import-names",
value=name,
name="data.project.importnames(paces)",
definition={
"description": cleandoc(ImportNameMissing._DESC),
"see": ImportNameMissing._URL,
},
rule="PEP 794",
)
return pyproject
EXTRA_VALIDATIONS = (
validate_project_dynamic,
validate_include_depenency,
validate_import_name_issues,
)

@@ -10,3 +10,5 @@ """

import builtins
from __future__ import annotations
import keyword
import logging

@@ -20,2 +22,4 @@ import os

if typing.TYPE_CHECKING:
import builtins
from typing_extensions import Literal

@@ -59,3 +63,5 @@

VERSION_REGEX = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.X | re.I)
VERSION_REGEX = re.compile(
r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE
)

@@ -74,3 +80,3 @@

PEP508_IDENTIFIER_PATTERN = r"([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])"
PEP508_IDENTIFIER_REGEX = re.compile(f"^{PEP508_IDENTIFIER_PATTERN}$", re.I)
PEP508_IDENTIFIER_REGEX = re.compile(f"^{PEP508_IDENTIFIER_PATTERN}$", re.IGNORECASE)

@@ -100,5 +106,5 @@

_req.Requirement(value)
return True
except _req.InvalidRequirement:
return False
return True

@@ -112,3 +118,3 @@ except ImportError: # pragma: no cover

def pep508(value: str) -> bool:
def pep508(value: str) -> bool: # noqa: ARG001
return True

@@ -172,3 +178,3 @@

downloaded: typing.Union[None, "Literal[False]", typing.Set[str]]
downloaded: None | Literal[False] | set[str]
"""

@@ -210,3 +216,3 @@ None => not cached yet

self.downloaded = set(_download_classifiers().splitlines())
except Exception:
except Exception: # noqa: BLE001
self.downloaded = False

@@ -264,7 +270,7 @@ _logger.debug("Problem with download, skipping validation")

)
if not (value.startswith("/") or value.startswith("\\") or "@" in value):
if not (value.startswith(("/", "\\")) or "@" in value):
parts = urlparse(f"http://{value}")
return bool(parts.scheme and parts.netloc)
except Exception:
except Exception: # noqa: BLE001
return False

@@ -275,7 +281,9 @@

ENTRYPOINT_PATTERN = r"[^\[\s=]([^=]*[^\s=])?"
ENTRYPOINT_REGEX = re.compile(f"^{ENTRYPOINT_PATTERN}$", re.I)
ENTRYPOINT_REGEX = re.compile(f"^{ENTRYPOINT_PATTERN}$", re.IGNORECASE)
RECOMMEDED_ENTRYPOINT_PATTERN = r"[\w.-]+"
RECOMMEDED_ENTRYPOINT_REGEX = re.compile(f"^{RECOMMEDED_ENTRYPOINT_PATTERN}$", re.I)
RECOMMEDED_ENTRYPOINT_REGEX = re.compile(
f"^{RECOMMEDED_ENTRYPOINT_PATTERN}$", re.IGNORECASE
)
ENTRYPOINT_GROUP_PATTERN = r"\w+(\.\w+)*"
ENTRYPOINT_GROUP_REGEX = re.compile(f"^{ENTRYPOINT_GROUP_PATTERN}$", re.I)
ENTRYPOINT_GROUP_REGEX = re.compile(f"^{ENTRYPOINT_GROUP_PATTERN}$", re.IGNORECASE)

@@ -381,3 +389,8 @@

def uint(value: builtins.int) -> bool:
def uint32(value: builtins.int) -> bool:
r"""Unsigned 32-bit integer (:math:`0 \leq x < 2^{32}`)"""
return 0 <= value < 2**32
def uint64(value: builtins.int) -> bool:
r"""Unsigned 64-bit integer (:math:`0 \leq x < 2^{64}`)"""

@@ -387,2 +400,27 @@ return 0 <= value < 2**64

def uint(value: builtins.int) -> bool:
r"""Signed 64-bit integer (:math:`0 \leq x < 2^{64}`)"""
return 0 <= value < 2**64
def int8(value: builtins.int) -> bool:
r"""Signed 8-bit integer (:math:`-2^{7} \leq x < 2^{7}`)"""
return -(2**7) <= value < 2**7
def int16(value: builtins.int) -> bool:
r"""Signed 16-bit integer (:math:`-2^{15} \leq x < 2^{15}`)"""
return -(2**15) <= value < 2**15
def int32(value: builtins.int) -> bool:
r"""Signed 32-bit integer (:math:`-2^{31} \leq x < 2^{31}`)"""
return -(2**31) <= value < 2**31
def int64(value: builtins.int) -> bool:
r"""Signed 64-bit integer (:math:`-2^{63} \leq x < 2^{63}`)"""
return -(2**63) <= value < 2**63
def int(value: builtins.int) -> bool:

@@ -402,5 +440,5 @@ r"""Signed 64-bit integer (:math:`-2^{63} \leq x < 2^{63}`)"""

_licenses.canonicalize_license_expression(value)
return True
except _licenses.InvalidLicenseExpression:
return False
return True

@@ -414,3 +452,27 @@ except ImportError: # pragma: no cover

def SPDX(value: str) -> bool:
def SPDX(value: str) -> bool: # noqa: ARG001
return True
VALID_IMPORT_NAME = re.compile(
r"""
^ # start of string
[A-Za-z_][A-Za-z_0-9]+ # a valid Python identifier
(?:\.[A-Za-z_][A-Za-z_0-9]*)* # optionally followed by .identifier's
(?:\s*;\s*private)? # optionally followed by ; private
$ # end of string
""",
re.VERBOSE,
)
def import_name(value: str) -> bool:
"""This is a valid import name. It has to be series of python identifiers
(not keywords), separated by dots, optionally followed by a semicolon and
the keyword "private".
"""
if VALID_IMPORT_NAME.match(value) is None:
return False
idents, _, _ = value.partition(";")
return all(not keyword.iskeyword(ident) for ident in idents.rstrip().split("."))
The code contained in this directory was automatically generated using the
following command:
python -m validate_pyproject.pre_compile --output-dir=setuptools/config/_validate_pyproject --enable-plugins setuptools distutils --very-verbose -t setuptools=setuptools/config/setuptools.schema.json -t distutils=setuptools/config/distutils.schema.json
python -m validate_pyproject.pre_compile --output-dir=setuptools/config/_validate_pyproject --enable-plugins setuptools distutils --very-verbose -t distutils=setuptools/config/distutils.schema.json -t setuptools=setuptools/config/setuptools.schema.json

@@ -6,0 +6,0 @@ Please avoid changing it manually.

@@ -137,2 +137,11 @@ """

fields = ("import-names", "import-namespaces")
places = (project_table, project_table.get("dynamic", []))
if any(field in place for field in fields for place in places):
raise NotImplementedError(
"Setuptools does not support `import-names` and `import-namespaces`"
" in `pyproject.toml` yet. If your are interested in this feature, "
" please consider submitting a contribution via pull requests."
)
with _ignore_errors(ignore_option_errors):

@@ -139,0 +148,0 @@ # Don't complain about unrelated errors (e.g. tools not using the "tool" table)

@@ -119,3 +119,3 @@ {

"propertyNames": {
"anyOf": [{"type": "string", "format": "python-module-name"}, {"const": "*"}]
"anyOf": [{"$ref": "#/definitions/package-name"}, {"const": "*"}]
},

@@ -144,3 +144,3 @@ "patternProperties": {

"propertyNames": {
"anyOf": [{"type": "string", "format": "python-module-name"}, {"const": "*"}]
"anyOf": [{"$ref": "#/definitions/package-name"}, {"const": "*"}]
},

@@ -147,0 +147,0 @@ "patternProperties": {

@@ -538,5 +538,10 @@ """Make sure that applying the configuration from pyproject.toml is equivalent to

class TestExtModules:
def make_dist(self, toml_config):
pyproject = Path("pyproject.toml")
pyproject.write_text(cleandoc(toml_config), encoding="utf-8")
with pytest.warns(pyprojecttoml._ExperimentalConfiguration):
return pyprojecttoml.apply_configuration(Distribution({}), pyproject)
def test_pyproject_sets_attribute(self, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
pyproject = Path("pyproject.toml")
toml_config = """

@@ -551,5 +556,3 @@ [project]

"""
pyproject.write_text(cleandoc(toml_config), encoding="utf-8")
with pytest.warns(pyprojecttoml._ExperimentalConfiguration):
dist = pyprojecttoml.apply_configuration(Distribution({}), pyproject)
dist = self.make_dist(toml_config)
assert len(dist.ext_modules) == 1

@@ -559,3 +562,20 @@ assert dist.ext_modules[0].name == "my.ext"

def test_pyproject_define_macros_as_tuples(self, tmp_path, monkeypatch):
# https://github.com/pypa/setuptools/issues/4810
monkeypatch.chdir(tmp_path)
toml_config = """
[project]
name = "test"
version = "42.0"
[[tool.setuptools.ext-modules]]
name = "my.ext"
sources = ["hello.c", "world.c"]
define-macros = [["FIRST_SINGLE"], ["SECOND_TWO", "1"]]
"""
dist = self.make_dist(toml_config)
assert isinstance(dist.ext_modules[0].define_macros[0], tuple)
assert dist.ext_modules[0].define_macros[0] == ("FIRST_SINGLE",)
assert dist.ext_modules[0].define_macros[1] == ("SECOND_TWO", "1")
class TestDeprecatedFields:

@@ -562,0 +582,0 @@ def test_namespace_packages(self, tmp_path):

@@ -289,2 +289,25 @@ import re

class TestImportNames:
EXAMPLES = [
'import-names = ["hello", "world"]',
'import-namespaces = ["hello", "world"]',
'dynamic = ["import-names"]',
'dynamic = ["import-namespaces"]',
]
@pytest.mark.parametrize("example", EXAMPLES)
def test_not_implemented(self, monkeypatch, tmp_path, example):
monkeypatch.chdir(tmp_path)
pyproject = Path("pyproject.toml")
toml_config = f"""
[project]
name = 'proj'
version = '42'
{example}
"""
pyproject.write_text(cleandoc(toml_config), encoding="utf-8")
with pytest.raises(NotImplementedError, match='import-names'):
apply_configuration(Distribution({}), pyproject)
@pytest.mark.parametrize(

@@ -291,0 +314,0 @@ "example",

@@ -80,26 +80,30 @@ """Tests for the 'setuptools' package"""

@pytest.fixture
def sample_module(self, monkeypatch, tmp_path):
monkeypatch.syspath_prepend(str(tmp_path))
module = "mod_with_version"
version = "2.0.9"
file = tmp_path / f"{module}.py"
file.write_text(f"__version__ = {version!r}", encoding="utf-8")
return (module, version)
@needs_bytecode
def testModuleExtract(self):
from json import __version__
assert dep.get_module_constant('json', '__version__') == __version__
def testModuleExtract(self, sample_module):
(module, version) = sample_module
assert dep.get_module_constant(module, '__version__') == version
assert dep.get_module_constant('sys', 'version') == sys.version
assert (
dep.get_module_constant('setuptools.tests.test_setuptools', '__doc__')
== __doc__
)
assert dep.get_module_constant(__name__, '__doc__') == __doc__
@needs_bytecode
def testRequire(self):
req = Require('Json', '1.0.3', 'json')
def testRequire(self, sample_module):
(module, version) = sample_module
req = Require('GivenName', '1.0.3', module)
assert req.name == 'Json'
assert req.module == 'json'
assert req.name == 'GivenName'
assert req.module == module
assert req.requested_version == Version('1.0.3')
assert req.attribute == '__version__'
assert req.full_name() == 'Json-1.0.3'
assert req.full_name() == 'GivenName-1.0.3'
from json import __version__
assert str(req.get_version()) == __version__
assert str(req.get_version()) == version
assert req.version_ok('1.0.9')

@@ -106,0 +110,0 @@ assert not req.version_ok('0.9.1')

@@ -25,3 +25,2 @@ [testenv]

SSH_AUTH_SOCK # for exercise.py if repo was checked out with ssh
windir # required for test_pkg_resources
# honor git config in pytest-perf

@@ -87,3 +86,3 @@ HOME

deps =
validate-pyproject[all]==0.23
validate-pyproject[all]==0.25
commands =

@@ -90,0 +89,0 @@ python -m tools.generate_validation_code

Sorry, the diff of this file is too big to display

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0"
processorArchitecture="X86"
name="%(name)s"
type="win32"/>
<!-- Identify the application security requirements. -->
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display