1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
from __future__ import annotations
import glob
import inspect
import platform
from collections.abc import Callable
from typing import TYPE_CHECKING, Any, ClassVar, cast
import setuptools
from ..dist import Distribution
from ..warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning
from .bdist_egg import bdist_egg as bdist_egg_cls
import distutils.command.install as orig
from distutils.errors import DistutilsArgError
if TYPE_CHECKING:
# This is only used for a type-cast, don't import at runtime or it'll cause deprecation warnings
from .easy_install import easy_install as easy_install_cls
else:
easy_install_cls = None
def __getattr__(name: str): # pragma: no cover
if name == "_install":
SetuptoolsDeprecationWarning.emit(
"`setuptools.command._install` was an internal implementation detail "
+ "that was left in for numpy<1.9 support.",
due_date=(2025, 5, 2), # Originally added on 2024-11-01
)
return orig.install
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
class install(orig.install):
"""Use easy_install to install the package, w/dependencies"""
distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
user_options = orig.install.user_options + [
('old-and-unmanageable', None, "Try not to use this!"),
(
'single-version-externally-managed',
None,
"used by system package builders to create 'flat' eggs",
),
]
boolean_options = orig.install.boolean_options + [
'old-and-unmanageable',
'single-version-externally-managed',
]
# Type the same as distutils.command.install.install.sub_commands
# Must keep the second tuple item potentially None due to invariance
new_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]] = [
('install_egg_info', lambda self: True),
('install_scripts', lambda self: True),
]
_nc = dict(new_commands)
def initialize_options(self):
SetuptoolsDeprecationWarning.emit(
"setup.py install is deprecated.",
"""
Please avoid running ``setup.py`` directly.
Instead, use pypa/build, pypa/installer or other
standards-based tools.
""",
see_url="https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html",
# TODO: Document how to bootstrap setuptools without install
# (e.g. by unzipping the wheel file)
# and then add a due_date to this warning.
)
super().initialize_options()
self.old_and_unmanageable = None
self.single_version_externally_managed = None
def finalize_options(self) -> None:
super().finalize_options()
if self.root:
self.single_version_externally_managed = True
elif self.single_version_externally_managed:
if not self.root and not self.record:
raise DistutilsArgError(
"You must specify --record or --root when building system packages"
)
def handle_extra_path(self):
if self.root or self.single_version_externally_managed:
# explicit backward-compatibility mode, allow extra_path to work
return orig.install.handle_extra_path(self)
# Ignore extra_path when installing an egg (or being run by another
# command without --root or --single-version-externally-managed
self.path_file = None
self.extra_dirs = ''
return None
def run(self):
# Explicit request for old-style install? Just do it
if self.old_and_unmanageable or self.single_version_externally_managed:
return super().run()
if not self._called_from_setup(inspect.currentframe()):
# Run in backward-compatibility mode to support bdist_* commands.
super().run()
else:
self.do_egg_install()
return None
@staticmethod
def _called_from_setup(run_frame):
"""
Attempt to detect whether run() was called from setup() or by another
command. If called by setup(), the parent caller will be the
'run_command' method in 'distutils.dist', and *its* caller will be
the 'run_commands' method. If called any other way, the
immediate caller *might* be 'run_command', but it won't have been
called by 'run_commands'. Return True in that case or if a call stack
is unavailable. Return False otherwise.
"""
if run_frame is None:
msg = "Call stack not available. bdist_* commands may fail."
SetuptoolsWarning.emit(msg)
if platform.python_implementation() == 'IronPython':
msg = "For best results, pass -X:Frames to enable call stack."
SetuptoolsWarning.emit(msg)
return True
frames = inspect.getouterframes(run_frame)
for frame in frames[2:4]:
(caller,) = frame[:1]
info = inspect.getframeinfo(caller)
caller_module = caller.f_globals.get('__name__', '')
if caller_module == "setuptools.dist" and info.function == "run_command":
# Starting from v61.0.0 setuptools overwrites dist.run_command
continue
return caller_module == 'distutils.dist' and info.function == 'run_commands'
return False
def do_egg_install(self) -> None:
easy_install = self.distribution.get_command_class('easy_install')
cmd = cast(
# We'd want to cast easy_install as type[easy_install_cls] but a bug in
# mypy makes it think easy_install() returns a Command on Python 3.12+
# https://github.com/python/mypy/issues/18088
easy_install_cls,
easy_install( # type: ignore[call-arg]
self.distribution,
args="x",
root=self.root,
record=self.record,
),
)
cmd.ensure_finalized() # finalize before bdist_egg munges install cmd
cmd.always_copy_from = '.' # make sure local-dir eggs get installed
# pick up setup-dir .egg files only: no .egg-info
cmd.package_index.scan(glob.glob('*.egg'))
self.run_command('bdist_egg')
bdist_egg = cast(bdist_egg_cls, self.distribution.get_command_obj('bdist_egg'))
args = [bdist_egg.egg_output]
if setuptools.bootstrap_install_from:
# Bootstrap self-installation of setuptools
args.insert(0, setuptools.bootstrap_install_from)
cmd.args = args
cmd.run(show_deprecation=False)
setuptools.bootstrap_install_from = None
# XXX Python 3.1 doesn't see _nc if this is inside the class
install.sub_commands = [
cmd for cmd in orig.install.sub_commands if cmd[0] not in install._nc
] + install.new_commands
|