Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

184 lignes
6.9 KiB

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