Skip to content

Commit 2733fc3

Browse files
authored
Address circular imports complaints by Sphinx 7.2+ (#4023)
2 parents fdfe711 + 0cf5182 commit 2733fc3

File tree

7 files changed

+78
-76
lines changed

7 files changed

+78
-76
lines changed

newsfragments/4023.misc.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Avoid circular imports (particularly between ``setuptools/{__init__,dist,monkey}.py``),
2+
or at least delay them, so tools like ``sphinx`` don't have problems analysing the codebase.

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ testing-integration =
9292

9393
docs =
9494
# upstream
95-
sphinx >= 3.5,<=7.1.2 # workaround, see comments in pypa/setuptools#4020
95+
sphinx >= 3.5
9696
jaraco.packaging >= 9.3
9797
rst.linker >= 1.9
9898
furo

setuptools/__init__.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,18 @@
55
import re
66

77
import _distutils_hack.override # noqa: F401
8-
98
import distutils.core
109
from distutils.errors import DistutilsOptionError
1110
from distutils.util import convert_path as _convert_path
1211

12+
from . import logging, monkey
13+
from . import version as _version_module
14+
from .depends import Require
15+
from .discovery import PackageFinder, PEP420PackageFinder
16+
from .dist import Distribution
17+
from .extension import Extension
1318
from .warnings import SetuptoolsDeprecationWarning
1419

15-
import setuptools.version
16-
from setuptools.extension import Extension
17-
from setuptools.dist import Distribution
18-
from setuptools.depends import Require
19-
from setuptools.discovery import PackageFinder, PEP420PackageFinder
20-
from . import monkey
21-
from . import logging
22-
23-
2420
__all__ = [
2521
'setup',
2622
'Distribution',
@@ -32,7 +28,7 @@
3228
'find_namespace_packages',
3329
]
3430

35-
__version__ = setuptools.version.__version__
31+
__version__ = _version_module.__version__
3632

3733
bootstrap_install_from = None
3834

setuptools/depends.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import contextlib
44
import dis
55

6-
from setuptools.extern.packaging import version
76

8-
from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE
97
from . import _imp
8+
from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE
9+
from .extern.packaging.version import Version
1010

1111

1212
__all__ = ['Require', 'find_module', 'get_module_constant', 'extract_constant']
@@ -19,7 +19,7 @@ def __init__(
1919
self, name, requested_version, module, homepage='', attribute=None, format=None
2020
):
2121
if format is None and requested_version is not None:
22-
format = version.Version
22+
format = Version
2323

2424
if format is not None:
2525
requested_version = format(requested_version)

setuptools/dist.py

Lines changed: 52 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,56 @@
11
__all__ = ['Distribution']
22

3+
34
import io
4-
import sys
5-
import re
6-
import os
7-
import numbers
8-
import distutils.log
9-
import distutils.core
10-
import distutils.cmd
11-
import distutils.dist
12-
import distutils.command
13-
from distutils.util import strtobool
14-
from distutils.debug import DEBUG
15-
from distutils.fancy_getopt import translate_longopt
16-
from glob import iglob
175
import itertools
6+
import numbers
7+
import os
8+
import re
9+
import sys
1810
import textwrap
19-
from contextlib import suppress
20-
from typing import List, Optional, Set, TYPE_CHECKING
21-
from pathlib import Path
22-
2311
from collections import defaultdict
12+
from contextlib import suppress
2413
from email import message_from_file
14+
from glob import iglob
15+
from pathlib import Path
16+
from typing import TYPE_CHECKING, List, Optional, Set
2517

18+
import distutils.cmd
19+
import distutils.command
20+
import distutils.core
21+
import distutils.dist
22+
import distutils.log
23+
from distutils.debug import DEBUG
2624
from distutils.errors import DistutilsOptionError, DistutilsSetupError
25+
from distutils.fancy_getopt import translate_longopt
2726
from distutils.util import rfc822_escape
27+
from distutils.util import strtobool
2828

29-
from setuptools.extern import packaging
30-
from setuptools.extern import ordered_set
31-
from setuptools.extern.more_itertools import unique_everseen, partition
32-
33-
import setuptools
34-
import setuptools.command
35-
from setuptools import windows_support
36-
from setuptools.monkey import get_unpatched
37-
from setuptools.config import setupcfg, pyprojecttoml
38-
from setuptools.discovery import ConfigDiscovery
29+
from .extern.more_itertools import partition, unique_everseen
30+
from .extern.ordered_set import OrderedSet
31+
from .extern.packaging.markers import InvalidMarker, Marker
32+
from .extern.packaging.specifiers import InvalidSpecifier, SpecifierSet
33+
from .extern.packaging.version import InvalidVersion, Version
3934

40-
from setuptools.extern.packaging import version
41-
from . import _reqs
4235
from . import _entry_points
4336
from . import _normalization
37+
from . import _reqs
38+
from . import command as _ # noqa -- imported for side-effects
4439
from ._importlib import metadata
40+
from .config import setupcfg, pyprojecttoml
41+
from .discovery import ConfigDiscovery
42+
from .monkey import get_unpatched
4543
from .warnings import InformationOnly, SetuptoolsDeprecationWarning
4644

45+
4746
if TYPE_CHECKING:
4847
from email.message import Message
4948

50-
__import__('setuptools.extern.packaging.specifiers')
51-
__import__('setuptools.extern.packaging.version')
52-
5349

5450
def get_metadata_version(self):
5551
mv = getattr(self, 'metadata_version', None)
5652
if mv is None:
57-
mv = version.Version('2.1')
53+
mv = Version('2.1')
5854
self.metadata_version = mv
5955
return mv
6056

@@ -102,7 +98,7 @@ def read_pkg_file(self, file):
10298
"""Reads the metadata values from a file object."""
10399
msg = message_from_file(file)
104100

105-
self.metadata_version = version.Version(msg['metadata-version'])
101+
self.metadata_version = Version(msg['metadata-version'])
106102
self.name = _read_field_from_msg(msg, 'name')
107103
self.version = _read_field_from_msg(msg, 'version')
108104
self.description = _read_field_from_msg(msg, 'summary')
@@ -116,9 +112,7 @@ def read_pkg_file(self, file):
116112
self.license = _read_field_unescaped_from_msg(msg, 'license')
117113

118114
self.long_description = _read_field_unescaped_from_msg(msg, 'description')
119-
if self.long_description is None and self.metadata_version >= version.Version(
120-
'2.1'
121-
):
115+
if self.long_description is None and self.metadata_version >= Version('2.1'):
122116
self.long_description = _read_payload_from_msg(msg)
123117
self.description = _read_field_from_msg(msg, 'summary')
124118

@@ -129,7 +123,7 @@ def read_pkg_file(self, file):
129123
self.classifiers = _read_list_from_msg(msg, 'classifier')
130124

131125
# PEP 314 - these fields only exist in 1.1
132-
if self.metadata_version == version.Version('1.1'):
126+
if self.metadata_version == Version('1.1'):
133127
self.requires = _read_list_from_msg(msg, 'requires')
134128
self.provides = _read_list_from_msg(msg, 'provides')
135129
self.obsoletes = _read_list_from_msg(msg, 'obsoletes')
@@ -299,7 +293,7 @@ def _check_extra(extra, reqs):
299293
name, sep, marker = extra.partition(':')
300294
try:
301295
_check_marker(marker)
302-
except packaging.markers.InvalidMarker:
296+
except InvalidMarker:
303297
msg = f"Invalid environment marker: {marker} ({extra!r})"
304298
raise DistutilsSetupError(msg) from None
305299
list(_reqs.parse(reqs))
@@ -308,7 +302,7 @@ def _check_extra(extra, reqs):
308302
def _check_marker(marker):
309303
if not marker:
310304
return
311-
m = packaging.markers.Marker(marker)
305+
m = Marker(marker)
312306
m.evaluate()
313307

314308

@@ -344,8 +338,8 @@ def check_requirements(dist, attr, value):
344338
def check_specifier(dist, attr, value):
345339
"""Verify that value is a valid version specifier"""
346340
try:
347-
packaging.specifiers.SpecifierSet(value)
348-
except (packaging.specifiers.InvalidSpecifier, AttributeError) as error:
341+
SpecifierSet(value)
342+
except (InvalidSpecifier, AttributeError) as error:
349343
tmpl = (
350344
"{attr!r} must be a string " "containing valid version specifiers; {error}"
351345
)
@@ -448,7 +442,7 @@ class Distribution(_Distribution):
448442
_DISTUTILS_UNSUPPORTED_METADATA = {
449443
'long_description_content_type': lambda: None,
450444
'project_urls': dict,
451-
'provides_extras': ordered_set.OrderedSet,
445+
'provides_extras': OrderedSet,
452446
'license_file': lambda: None,
453447
'license_files': lambda: None,
454448
}
@@ -499,7 +493,7 @@ def __init__(self, attrs=None):
499493
# Save the original dependencies before they are processed into the egg format
500494
self._orig_extras_require = {}
501495
self._orig_install_requires = []
502-
self._tmp_extras_require = defaultdict(ordered_set.OrderedSet)
496+
self._tmp_extras_require = defaultdict(OrderedSet)
503497

504498
self.set_defaults = ConfigDiscovery(self)
505499

@@ -535,10 +529,12 @@ def _set_metadata_defaults(self, attrs):
535529

536530
@staticmethod
537531
def _normalize_version(version):
538-
if isinstance(version, setuptools.sic) or version is None:
532+
from . import sic
533+
534+
if isinstance(version, sic) or version is None:
539535
return version
540536

541-
normalized = str(packaging.version.Version(version))
537+
normalized = str(Version(version))
542538
if version != normalized:
543539
InformationOnly.emit(f"Normalizing '{version}' to '{normalized}'")
544540
return normalized
@@ -552,8 +548,10 @@ def _validate_version(version):
552548

553549
if version is not None:
554550
try:
555-
packaging.version.Version(version)
556-
except (packaging.version.InvalidVersion, TypeError):
551+
Version(version)
552+
except (InvalidVersion, TypeError):
553+
from . import sic
554+
557555
SetuptoolsDeprecationWarning.emit(
558556
f"Invalid version: {version!r}.",
559557
"""
@@ -566,7 +564,7 @@ def _validate_version(version):
566564
# Warning initially introduced in 26 Sept 2014
567565
# pypa/packaging already removed legacy versions.
568566
)
569-
return setuptools.sic(version)
567+
return sic(version)
570568
return version
571569

572570
def _finalize_requires(self):
@@ -602,7 +600,7 @@ def _convert_extras_requirements(self):
602600
`"extra:{marker}": ["barbazquux"]`.
603601
"""
604602
spec_ext_reqs = getattr(self, 'extras_require', None) or {}
605-
tmp = defaultdict(ordered_set.OrderedSet)
603+
tmp = defaultdict(OrderedSet)
606604
self._tmp_extras_require = getattr(self, '_tmp_extras_require', tmp)
607605
for section, v in spec_ext_reqs.items():
608606
# Do not strip empty sections.
@@ -903,7 +901,7 @@ def parse_config_files(self, filenames=None, ignore_option_errors=False):
903901

904902
def fetch_build_eggs(self, requires):
905903
"""Resolve pre-setup requirements"""
906-
from setuptools.installer import _fetch_build_eggs
904+
from .installer import _fetch_build_eggs
907905

908906
return _fetch_build_eggs(self, requires)
909907

@@ -946,6 +944,8 @@ def _finalize_setup_keywords(self):
946944
ep.load()(self, ep.name, value)
947945

948946
def get_egg_cache_dir(self):
947+
from . import windows_support
948+
949949
egg_cache_dir = os.path.join(os.curdir, '.eggs')
950950
if not os.path.exists(egg_cache_dir):
951951
os.mkdir(egg_cache_dir)
@@ -966,7 +966,7 @@ def get_egg_cache_dir(self):
966966

967967
def fetch_build_egg(self, req):
968968
"""Fetch an egg needed for building"""
969-
from setuptools.installer import fetch_build_egg
969+
from .installer import fetch_build_egg
970970

971971
return fetch_build_egg(self, req)
972972

setuptools/monkey.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
Monkey patching of distutils.
33
"""
44

5-
import sys
6-
import distutils.filelist
5+
import functools
6+
import inspect
77
import platform
8+
import sys
89
import types
9-
import functools
1010
from importlib import import_module
11-
import inspect
1211

13-
import setuptools
12+
import distutils.filelist
13+
1414

1515
__all__ = []
1616
"""
@@ -61,6 +61,8 @@ def get_unpatched_class(cls):
6161

6262

6363
def patch_all():
64+
import setuptools
65+
6466
# we can't patch distutils.cmd, alas
6567
distutils.core.Command = setuptools.Command
6668

@@ -97,9 +99,11 @@ def patch_all():
9799

98100

99101
def _patch_distribution_metadata():
102+
from . import dist
103+
100104
"""Patch write_pkg_file and read_pkg_file for higher metadata standards"""
101105
for attr in ('write_pkg_file', 'read_pkg_file', 'get_metadata_version'):
102-
new_val = getattr(setuptools.dist, attr)
106+
new_val = getattr(dist, attr)
103107
setattr(distutils.dist.DistributionMetadata, attr, new_val)
104108

105109

setuptools/tests/test_setuptools.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111

1212
import pytest
1313

14-
from setuptools.extern.packaging import version
15-
1614
import setuptools
1715
import setuptools.dist
1816
import setuptools.depends as dep
1917
from setuptools.depends import Require
2018

19+
from setuptools.extern.packaging.version import Version
20+
2121

2222
@pytest.fixture(autouse=True)
2323
def isolated_dir(tmpdir_cwd):
@@ -94,7 +94,7 @@ def testRequire(self):
9494

9595
assert req.name == 'Json'
9696
assert req.module == 'json'
97-
assert req.requested_version == version.Version('1.0.3')
97+
assert req.requested_version == Version('1.0.3')
9898
assert req.attribute == '__version__'
9999
assert req.full_name() == 'Json-1.0.3'
100100

0 commit comments

Comments
 (0)