From c0985fee154e4a97ca2fd6355b40786841339dde Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Fri, 18 Aug 2023 10:47:21 +0100 Subject: [PATCH 1/5] Avoid circular imports between setuptools/__init__ and monkey --- setuptools/__init__.py | 18 +++++++----------- setuptools/monkey.py | 16 ++++++++++------ 2 files changed, 17 insertions(+), 17 deletions(-) Index: setuptools-68.1.2/setuptools/__init__.py =================================================================== --- setuptools-68.1.2.orig/setuptools/__init__.py +++ setuptools-68.1.2/setuptools/__init__.py @@ -5,22 +5,18 @@ import os import re import _distutils_hack.override # noqa: F401 - import distutils.core from distutils.errors import DistutilsOptionError from distutils.util import convert_path as _convert_path +from . import logging, monkey +from . import version as _version_module +from .depends import Require +from .discovery import PackageFinder, PEP420PackageFinder +from .dist import Distribution +from .extension import Extension from .warnings import SetuptoolsDeprecationWarning -import setuptools.version -from setuptools.extension import Extension -from setuptools.dist import Distribution -from setuptools.depends import Require -from setuptools.discovery import PackageFinder, PEP420PackageFinder -from . import monkey -from . import logging - - __all__ = [ 'setup', 'Distribution', @@ -32,7 +28,7 @@ __all__ = [ 'find_namespace_packages', ] -__version__ = setuptools.version.__version__ +__version__ = _version_module.__version__ bootstrap_install_from = None Index: setuptools-68.1.2/setuptools/monkey.py =================================================================== --- setuptools-68.1.2.orig/setuptools/monkey.py +++ setuptools-68.1.2/setuptools/monkey.py @@ -2,15 +2,15 @@ Monkey patching of distutils. """ -import sys -import distutils.filelist +import functools +import inspect import platform +import sys import types -import functools from importlib import import_module -import inspect -import setuptools +import distutils.filelist + __all__ = [] """ @@ -61,6 +61,8 @@ def get_unpatched_class(cls): def patch_all(): + import setuptools + # we can't patch distutils.cmd, alas distutils.core.Command = setuptools.Command @@ -97,9 +99,11 @@ def patch_all(): def _patch_distribution_metadata(): + from . import dist + """Patch write_pkg_file and read_pkg_file for higher metadata standards""" for attr in ('write_pkg_file', 'read_pkg_file', 'get_metadata_version'): - new_val = getattr(setuptools.dist, attr) + new_val = getattr(dist, attr) setattr(distutils.dist.DistributionMetadata, attr, new_val) Index: setuptools-68.1.2/setup.cfg =================================================================== --- setuptools-68.1.2.orig/setup.cfg +++ setuptools-68.1.2/setup.cfg @@ -79,7 +79,7 @@ testing-integration = build[virtualenv] filelock>=3.4.0 docs = - sphinx >= 3.5,<=7.1.2 # workaround, see comments in pypa/setuptools#4020 + sphinx >= 3.5 jaraco.packaging >= 9.3 rst.linker >= 1.9 furo Index: setuptools-68.1.2/setuptools/dist.py =================================================================== --- setuptools-68.1.2.orig/setuptools/dist.py +++ setuptools-68.1.2/setuptools/dist.py @@ -1,60 +1,56 @@ __all__ = ['Distribution'] + import io -import sys -import re -import os -import numbers -import distutils.log -import distutils.core -import distutils.cmd -import distutils.dist -import distutils.command -from distutils.util import strtobool -from distutils.debug import DEBUG -from distutils.fancy_getopt import translate_longopt -from glob import iglob import itertools +import numbers +import os +import re +import sys import textwrap -from contextlib import suppress -from typing import List, Optional, Set, TYPE_CHECKING -from pathlib import Path - from collections import defaultdict +from contextlib import suppress from email import message_from_file +from glob import iglob +from pathlib import Path +from typing import TYPE_CHECKING, List, Optional, Set +import distutils.cmd +import distutils.command +import distutils.core +import distutils.dist +import distutils.log +from distutils.debug import DEBUG from distutils.errors import DistutilsOptionError, DistutilsSetupError +from distutils.fancy_getopt import translate_longopt from distutils.util import rfc822_escape +from distutils.util import strtobool -from setuptools.extern import packaging -from setuptools.extern import ordered_set -from setuptools.extern.more_itertools import unique_everseen, partition - -import setuptools -import setuptools.command -from setuptools import windows_support -from setuptools.monkey import get_unpatched -from setuptools.config import setupcfg, pyprojecttoml -from setuptools.discovery import ConfigDiscovery +from .extern.more_itertools import partition, unique_everseen +from .extern.ordered_set import OrderedSet +from .extern.packaging.markers import InvalidMarker, Marker +from .extern.packaging.specifiers import InvalidSpecifier, SpecifierSet +from .extern.packaging.version import InvalidVersion, Version -from setuptools.extern.packaging import version -from . import _reqs from . import _entry_points from . import _normalization +from . import _reqs +from . import command as _ # noqa -- imported for side-effects from ._importlib import metadata +from .config import setupcfg, pyprojecttoml +from .discovery import ConfigDiscovery +from .monkey import get_unpatched from .warnings import InformationOnly, SetuptoolsDeprecationWarning + if TYPE_CHECKING: from email.message import Message -__import__('setuptools.extern.packaging.specifiers') -__import__('setuptools.extern.packaging.version') - def get_metadata_version(self): mv = getattr(self, 'metadata_version', None) if mv is None: - mv = version.Version('2.1') + mv = Version('2.1') self.metadata_version = mv return mv @@ -102,7 +98,7 @@ def read_pkg_file(self, file): """Reads the metadata values from a file object.""" msg = message_from_file(file) - self.metadata_version = version.Version(msg['metadata-version']) + self.metadata_version = Version(msg['metadata-version']) self.name = _read_field_from_msg(msg, 'name') self.version = _read_field_from_msg(msg, 'version') self.description = _read_field_from_msg(msg, 'summary') @@ -116,9 +112,7 @@ def read_pkg_file(self, file): self.license = _read_field_unescaped_from_msg(msg, 'license') self.long_description = _read_field_unescaped_from_msg(msg, 'description') - if self.long_description is None and self.metadata_version >= version.Version( - '2.1' - ): + if self.long_description is None and self.metadata_version >= Version('2.1'): self.long_description = _read_payload_from_msg(msg) self.description = _read_field_from_msg(msg, 'summary') @@ -129,7 +123,7 @@ def read_pkg_file(self, file): self.classifiers = _read_list_from_msg(msg, 'classifier') # PEP 314 - these fields only exist in 1.1 - if self.metadata_version == version.Version('1.1'): + if self.metadata_version == Version('1.1'): self.requires = _read_list_from_msg(msg, 'requires') self.provides = _read_list_from_msg(msg, 'provides') self.obsoletes = _read_list_from_msg(msg, 'obsoletes') @@ -299,7 +293,7 @@ def _check_extra(extra, reqs): name, sep, marker = extra.partition(':') try: _check_marker(marker) - except packaging.markers.InvalidMarker: + except InvalidMarker: msg = f"Invalid environment marker: {marker} ({extra!r})" raise DistutilsSetupError(msg) from None list(_reqs.parse(reqs)) @@ -308,7 +302,7 @@ def _check_extra(extra, reqs): def _check_marker(marker): if not marker: return - m = packaging.markers.Marker(marker) + m = Marker(marker) m.evaluate() @@ -344,8 +338,8 @@ def check_requirements(dist, attr, value def check_specifier(dist, attr, value): """Verify that value is a valid version specifier""" try: - packaging.specifiers.SpecifierSet(value) - except (packaging.specifiers.InvalidSpecifier, AttributeError) as error: + SpecifierSet(value) + except (InvalidSpecifier, AttributeError) as error: tmpl = ( "{attr!r} must be a string " "containing valid version specifiers; {error}" ) @@ -448,7 +442,7 @@ class Distribution(_Distribution): _DISTUTILS_UNSUPPORTED_METADATA = { 'long_description_content_type': lambda: None, 'project_urls': dict, - 'provides_extras': ordered_set.OrderedSet, + 'provides_extras': OrderedSet, 'license_file': lambda: None, 'license_files': lambda: None, } @@ -499,7 +493,7 @@ class Distribution(_Distribution): # Save the original dependencies before they are processed into the egg format self._orig_extras_require = {} self._orig_install_requires = [] - self._tmp_extras_require = defaultdict(ordered_set.OrderedSet) + self._tmp_extras_require = defaultdict(OrderedSet) self.set_defaults = ConfigDiscovery(self) @@ -535,10 +529,12 @@ class Distribution(_Distribution): @staticmethod def _normalize_version(version): - if isinstance(version, setuptools.sic) or version is None: + from . import sic + + if isinstance(version, sic) or version is None: return version - normalized = str(packaging.version.Version(version)) + normalized = str(Version(version)) if version != normalized: InformationOnly.emit(f"Normalizing '{version}' to '{normalized}'") return normalized @@ -552,8 +548,10 @@ class Distribution(_Distribution): if version is not None: try: - packaging.version.Version(version) - except (packaging.version.InvalidVersion, TypeError): + Version(version) + except (InvalidVersion, TypeError): + from . import sic + SetuptoolsDeprecationWarning.emit( f"Invalid version: {version!r}.", """ @@ -566,7 +564,7 @@ class Distribution(_Distribution): # Warning initially introduced in 26 Sept 2014 # pypa/packaging already removed legacy versions. ) - return setuptools.sic(version) + return sic(version) return version def _finalize_requires(self): @@ -602,7 +600,7 @@ class Distribution(_Distribution): `"extra:{marker}": ["barbazquux"]`. """ spec_ext_reqs = getattr(self, 'extras_require', None) or {} - tmp = defaultdict(ordered_set.OrderedSet) + tmp = defaultdict(OrderedSet) self._tmp_extras_require = getattr(self, '_tmp_extras_require', tmp) for section, v in spec_ext_reqs.items(): # Do not strip empty sections. @@ -903,7 +901,7 @@ class Distribution(_Distribution): def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" - from setuptools.installer import _fetch_build_eggs + from .installer import _fetch_build_eggs return _fetch_build_eggs(self, requires) @@ -946,6 +944,8 @@ class Distribution(_Distribution): ep.load()(self, ep.name, value) def get_egg_cache_dir(self): + from . import windows_support + egg_cache_dir = os.path.join(os.curdir, '.eggs') if not os.path.exists(egg_cache_dir): os.mkdir(egg_cache_dir) @@ -966,7 +966,7 @@ class Distribution(_Distribution): def fetch_build_egg(self, req): """Fetch an egg needed for building""" - from setuptools.installer import fetch_build_egg + from .installer import fetch_build_egg return fetch_build_egg(self, req) Index: setuptools-68.1.2/setuptools/depends.py =================================================================== --- setuptools-68.1.2.orig/setuptools/depends.py +++ setuptools-68.1.2/setuptools/depends.py @@ -3,10 +3,10 @@ import marshal import contextlib import dis -from setuptools.extern.packaging import version -from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE from . import _imp +from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE +from .extern.packaging.version import Version __all__ = ['Require', 'find_module', 'get_module_constant', 'extract_constant'] @@ -19,7 +19,7 @@ class Require: self, name, requested_version, module, homepage='', attribute=None, format=None ): if format is None and requested_version is not None: - format = version.Version + format = Version if format is not None: requested_version = format(requested_version) Index: setuptools-68.1.2/setuptools/tests/test_setuptools.py =================================================================== --- setuptools-68.1.2.orig/setuptools/tests/test_setuptools.py +++ setuptools-68.1.2/setuptools/tests/test_setuptools.py @@ -11,13 +11,13 @@ from zipfile import ZipFile import pytest -from setuptools.extern.packaging import version - import setuptools import setuptools.dist import setuptools.depends as dep from setuptools.depends import Require +from setuptools.extern.packaging.version import Version + @pytest.fixture(autouse=True) def isolated_dir(tmpdir_cwd): @@ -94,7 +94,7 @@ class TestDepends: assert req.name == 'Json' assert req.module == 'json' - assert req.requested_version == version.Version('1.0.3') + assert req.requested_version == Version('1.0.3') assert req.attribute == '__version__' assert req.full_name() == 'Json-1.0.3' Index: setuptools-68.1.2/newsfragments/4023.misc.rst =================================================================== --- /dev/null +++ setuptools-68.1.2/newsfragments/4023.misc.rst @@ -0,0 +1,2 @@ +Avoid circular imports (particularly between ``setuptools/{__init__,dist,monkey}.py``), +or at least delay them, so tools like ``sphinx`` don't have problems analysing the codebase.