|
1 | | -import glob |
2 | | -import os |
| 1 | +import site |
| 2 | +import subprocess |
| 3 | +import sys |
3 | 4 |
|
4 | | -import setuptools |
5 | | -from setuptools import _normalization, _path, namespaces |
6 | | -from setuptools.command.easy_install import easy_install |
| 5 | +from setuptools import Command |
| 6 | +from setuptools.warnings import SetuptoolsDeprecationWarning |
7 | 7 |
|
8 | | -from ..unicode_utils import _read_utf8_with_fallback |
9 | 8 |
|
10 | | -from distutils import log |
11 | | -from distutils.errors import DistutilsOptionError |
12 | | -from distutils.util import convert_path |
13 | | - |
14 | | - |
15 | | -class develop(namespaces.DevelopInstaller, easy_install): |
| 9 | +class develop(Command): |
16 | 10 | """Set up package for development""" |
17 | 11 |
|
18 | | - description = "install package in 'development mode'" |
19 | | - |
20 | | - user_options = easy_install.user_options + [ |
21 | | - ("uninstall", "u", "Uninstall this source package"), |
22 | | - ("egg-path=", None, "Set the path to be used in the .egg-link file"), |
| 12 | + user_options = [ |
| 13 | + ("install-dir=", "d", "install package to DIR"), |
| 14 | + ('no-deps', 'N', "don't install dependencies"), |
| 15 | + ('user', None, f"install in user site-package '{site.USER_SITE}'"), |
| 16 | + ('prefix=', None, "installation prefix"), |
| 17 | + ("index-url=", "i", "base URL of Python Package Index"), |
| 18 | + ] |
| 19 | + boolean_options = [ |
| 20 | + 'no-deps', |
| 21 | + 'user', |
23 | 22 | ] |
24 | 23 |
|
25 | | - boolean_options = easy_install.boolean_options + ['uninstall'] |
26 | | - |
27 | | - command_consumes_arguments = False # override base |
| 24 | + install_dir = None |
| 25 | + no_deps = False |
| 26 | + user = False |
| 27 | + prefix = None |
| 28 | + index_url = None |
28 | 29 |
|
29 | 30 | def run(self): |
30 | | - if self.uninstall: |
31 | | - self.multi_version = True |
32 | | - self.uninstall_link() |
33 | | - self.uninstall_namespaces() |
34 | | - else: |
35 | | - self.install_for_development() |
36 | | - self.warn_deprecated_options() |
| 31 | + cmd = ( |
| 32 | + [sys.executable, '-m', 'pip', 'install', '-e', '.', '--use-pep517'] |
| 33 | + + ['--target', self.install_dir] * bool(self.install_dir) |
| 34 | + + ['--no-deps'] * self.no_deps |
| 35 | + + ['--user'] * self.user |
| 36 | + + ['--prefix', self.prefix] * bool(self.prefix) |
| 37 | + + ['--index-url', self.index_url] * bool(self.prefix) |
| 38 | + ) |
| 39 | + subprocess.check_call(cmd) |
37 | 40 |
|
38 | 41 | def initialize_options(self): |
39 | | - self.uninstall = None |
40 | | - self.egg_path = None |
41 | | - easy_install.initialize_options(self) |
42 | | - self.setup_path = None |
43 | | - self.always_copy_from = '.' # always copy eggs installed in curdir |
| 42 | + DevelopDeprecationWarning.emit() |
44 | 43 |
|
45 | 44 | def finalize_options(self) -> None: |
46 | | - import pkg_resources |
47 | | - |
48 | | - ei = self.get_finalized_command("egg_info") |
49 | | - self.args = [ei.egg_name] |
50 | | - |
51 | | - easy_install.finalize_options(self) |
52 | | - self.expand_basedirs() |
53 | | - self.expand_dirs() |
54 | | - # pick up setup-dir .egg files only: no .egg-info |
55 | | - self.package_index.scan(glob.glob('*.egg')) |
56 | | - |
57 | | - egg_link_fn = ( |
58 | | - _normalization.filename_component_broken(ei.egg_name) + '.egg-link' |
59 | | - ) |
60 | | - self.egg_link = os.path.join(self.install_dir, egg_link_fn) |
61 | | - self.egg_base = ei.egg_base |
62 | | - if self.egg_path is None: |
63 | | - self.egg_path = os.path.abspath(ei.egg_base) |
64 | | - |
65 | | - target = _path.normpath(self.egg_base) |
66 | | - egg_path = _path.normpath(os.path.join(self.install_dir, self.egg_path)) |
67 | | - if egg_path != target: |
68 | | - raise DistutilsOptionError( |
69 | | - "--egg-path must be a relative path from the install" |
70 | | - " directory to " + target |
71 | | - ) |
72 | | - |
73 | | - # Make a distribution for the package's source |
74 | | - self.dist = pkg_resources.Distribution( |
75 | | - target, |
76 | | - pkg_resources.PathMetadata(target, os.path.abspath(ei.egg_info)), |
77 | | - project_name=ei.egg_name, |
78 | | - ) |
79 | | - |
80 | | - self.setup_path = self._resolve_setup_path( |
81 | | - self.egg_base, |
82 | | - self.install_dir, |
83 | | - self.egg_path, |
84 | | - ) |
85 | | - |
86 | | - @staticmethod |
87 | | - def _resolve_setup_path(egg_base, install_dir, egg_path): |
88 | | - """ |
89 | | - Generate a path from egg_base back to '.' where the |
90 | | - setup script resides and ensure that path points to the |
91 | | - setup path from $install_dir/$egg_path. |
92 | | - """ |
93 | | - path_to_setup = egg_base.replace(os.sep, '/').rstrip('/') |
94 | | - if path_to_setup != os.curdir: |
95 | | - path_to_setup = '../' * (path_to_setup.count('/') + 1) |
96 | | - resolved = _path.normpath(os.path.join(install_dir, egg_path, path_to_setup)) |
97 | | - curdir = _path.normpath(os.curdir) |
98 | | - if resolved != curdir: |
99 | | - raise DistutilsOptionError( |
100 | | - "Can't get a consistent path to setup script from" |
101 | | - " installation directory", |
102 | | - resolved, |
103 | | - curdir, |
104 | | - ) |
105 | | - return path_to_setup |
106 | | - |
107 | | - def install_for_development(self) -> None: |
108 | | - self.run_command('egg_info') |
109 | | - |
110 | | - # Build extensions in-place |
111 | | - self.reinitialize_command('build_ext', inplace=True) |
112 | | - self.run_command('build_ext') |
| 45 | + pass |
113 | 46 |
|
114 | | - if setuptools.bootstrap_install_from: |
115 | | - self.easy_install(setuptools.bootstrap_install_from) |
116 | | - setuptools.bootstrap_install_from = None |
117 | 47 |
|
118 | | - self.install_namespaces() |
119 | | - |
120 | | - # create an .egg-link in the installation dir, pointing to our egg |
121 | | - log.info("Creating %s (link to %s)", self.egg_link, self.egg_base) |
122 | | - if not self.dry_run: |
123 | | - with open(self.egg_link, "w", encoding="utf-8") as f: |
124 | | - f.write(self.egg_path + "\n" + self.setup_path) |
125 | | - # postprocess the installed distro, fixing up .pth, installing scripts, |
126 | | - # and handling requirements |
127 | | - self.process_distribution(None, self.dist, not self.no_deps) |
128 | | - |
129 | | - def uninstall_link(self) -> None: |
130 | | - if os.path.exists(self.egg_link): |
131 | | - log.info("Removing %s (link to %s)", self.egg_link, self.egg_base) |
132 | | - |
133 | | - contents = [ |
134 | | - line.rstrip() |
135 | | - for line in _read_utf8_with_fallback(self.egg_link).splitlines() |
136 | | - ] |
137 | | - |
138 | | - if contents not in ([self.egg_path], [self.egg_path, self.setup_path]): |
139 | | - log.warn("Link points to %s: uninstall aborted", contents) |
140 | | - return |
141 | | - if not self.dry_run: |
142 | | - os.unlink(self.egg_link) |
143 | | - if not self.dry_run: |
144 | | - self.update_pth(self.dist) # remove any .pth link to us |
145 | | - if self.distribution.scripts: |
146 | | - # XXX should also check for entry point scripts! |
147 | | - log.warn("Note: you must uninstall or replace scripts manually!") |
148 | | - |
149 | | - def install_egg_scripts(self, dist): |
150 | | - if dist is not self.dist: |
151 | | - # Installing a dependency, so fall back to normal behavior |
152 | | - return easy_install.install_egg_scripts(self, dist) |
153 | | - |
154 | | - # create wrapper scripts in the script dir, pointing to dist.scripts |
155 | | - |
156 | | - # new-style... |
157 | | - self.install_wrapper_scripts(dist) |
158 | | - |
159 | | - # ...and old-style |
160 | | - for script_name in self.distribution.scripts or []: |
161 | | - script_path = os.path.abspath(convert_path(script_name)) |
162 | | - script_name = os.path.basename(script_path) |
163 | | - script_text = _read_utf8_with_fallback(script_path) |
164 | | - self.install_script(dist, script_name, script_text, script_path) |
165 | | - |
166 | | - return None |
167 | | - |
168 | | - def install_wrapper_scripts(self, dist): |
169 | | - dist = VersionlessRequirement(dist) |
170 | | - return easy_install.install_wrapper_scripts(self, dist) |
171 | | - |
172 | | - |
173 | | -class VersionlessRequirement: |
174 | | - """ |
175 | | - Adapt a pkg_resources.Distribution to simply return the project |
176 | | - name as the 'requirement' so that scripts will work across |
177 | | - multiple versions. |
178 | | -
|
179 | | - >>> from pkg_resources import Distribution |
180 | | - >>> dist = Distribution(project_name='foo', version='1.0') |
181 | | - >>> str(dist.as_requirement()) |
182 | | - 'foo==1.0' |
183 | | - >>> adapted_dist = VersionlessRequirement(dist) |
184 | | - >>> str(adapted_dist.as_requirement()) |
185 | | - 'foo' |
| 48 | +class DevelopDeprecationWarning(SetuptoolsDeprecationWarning): |
| 49 | + _SUMMARY = "develop command is deprecated." |
| 50 | + _DETAILS = """ |
| 51 | + Please avoid running ``setup.py`` and ``develop``. |
| 52 | + Instead, use standards-based tools like pip or uv. |
186 | 53 | """ |
187 | | - |
188 | | - def __init__(self, dist) -> None: |
189 | | - self.__dist = dist |
190 | | - |
191 | | - def __getattr__(self, name: str): |
192 | | - return getattr(self.__dist, name) |
193 | | - |
194 | | - def as_requirement(self): |
195 | | - return self.project_name |
| 54 | + _SEE_URL = "https://github.com/pypa/setuptools/issues/917" |
| 55 | + _DUE_DATE = 2025, 10, 31 |
0 commit comments