install.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. from distutils.errors import DistutilsArgError
  2. import inspect
  3. import glob
  4. import platform
  5. import distutils.command.install as orig
  6. import setuptools
  7. from ..warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning
  8. # Prior to numpy 1.9, NumPy relies on the '_install' name, so provide it for
  9. # now. See https://github.com/pypa/setuptools/issues/199/
  10. _install = orig.install
  11. class install(orig.install):
  12. """Use easy_install to install the package, w/dependencies"""
  13. user_options = orig.install.user_options + [
  14. ('old-and-unmanageable', None, "Try not to use this!"),
  15. (
  16. 'single-version-externally-managed',
  17. None,
  18. "used by system package builders to create 'flat' eggs",
  19. ),
  20. ]
  21. boolean_options = orig.install.boolean_options + [
  22. 'old-and-unmanageable',
  23. 'single-version-externally-managed',
  24. ]
  25. new_commands = [
  26. ('install_egg_info', lambda self: True),
  27. ('install_scripts', lambda self: True),
  28. ]
  29. _nc = dict(new_commands)
  30. def initialize_options(self):
  31. SetuptoolsDeprecationWarning.emit(
  32. "setup.py install is deprecated.",
  33. """
  34. Please avoid running ``setup.py`` directly.
  35. Instead, use pypa/build, pypa/installer or other
  36. standards-based tools.
  37. """,
  38. see_url="https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html",
  39. # TODO: Document how to bootstrap setuptools without install
  40. # (e.g. by unziping the wheel file)
  41. # and then add a due_date to this warning.
  42. )
  43. orig.install.initialize_options(self)
  44. self.old_and_unmanageable = None
  45. self.single_version_externally_managed = None
  46. def finalize_options(self):
  47. orig.install.finalize_options(self)
  48. if self.root:
  49. self.single_version_externally_managed = True
  50. elif self.single_version_externally_managed:
  51. if not self.root and not self.record:
  52. raise DistutilsArgError(
  53. "You must specify --record or --root when building system"
  54. " packages"
  55. )
  56. def handle_extra_path(self):
  57. if self.root or self.single_version_externally_managed:
  58. # explicit backward-compatibility mode, allow extra_path to work
  59. return orig.install.handle_extra_path(self)
  60. # Ignore extra_path when installing an egg (or being run by another
  61. # command without --root or --single-version-externally-managed
  62. self.path_file = None
  63. self.extra_dirs = ''
  64. def run(self):
  65. # Explicit request for old-style install? Just do it
  66. if self.old_and_unmanageable or self.single_version_externally_managed:
  67. return orig.install.run(self)
  68. if not self._called_from_setup(inspect.currentframe()):
  69. # Run in backward-compatibility mode to support bdist_* commands.
  70. orig.install.run(self)
  71. else:
  72. self.do_egg_install()
  73. @staticmethod
  74. def _called_from_setup(run_frame):
  75. """
  76. Attempt to detect whether run() was called from setup() or by another
  77. command. If called by setup(), the parent caller will be the
  78. 'run_command' method in 'distutils.dist', and *its* caller will be
  79. the 'run_commands' method. If called any other way, the
  80. immediate caller *might* be 'run_command', but it won't have been
  81. called by 'run_commands'. Return True in that case or if a call stack
  82. is unavailable. Return False otherwise.
  83. """
  84. if run_frame is None:
  85. msg = "Call stack not available. bdist_* commands may fail."
  86. SetuptoolsWarning.emit(msg)
  87. if platform.python_implementation() == 'IronPython':
  88. msg = "For best results, pass -X:Frames to enable call stack."
  89. SetuptoolsWarning.emit(msg)
  90. return True
  91. frames = inspect.getouterframes(run_frame)
  92. for frame in frames[2:4]:
  93. (caller,) = frame[:1]
  94. info = inspect.getframeinfo(caller)
  95. caller_module = caller.f_globals.get('__name__', '')
  96. if caller_module == "setuptools.dist" and info.function == "run_command":
  97. # Starting from v61.0.0 setuptools overwrites dist.run_command
  98. continue
  99. return caller_module == 'distutils.dist' and info.function == 'run_commands'
  100. def do_egg_install(self):
  101. easy_install = self.distribution.get_command_class('easy_install')
  102. cmd = easy_install(
  103. self.distribution,
  104. args="x",
  105. root=self.root,
  106. record=self.record,
  107. )
  108. cmd.ensure_finalized() # finalize before bdist_egg munges install cmd
  109. cmd.always_copy_from = '.' # make sure local-dir eggs get installed
  110. # pick up setup-dir .egg files only: no .egg-info
  111. cmd.package_index.scan(glob.glob('*.egg'))
  112. self.run_command('bdist_egg')
  113. args = [self.distribution.get_command_obj('bdist_egg').egg_output]
  114. if setuptools.bootstrap_install_from:
  115. # Bootstrap self-installation of setuptools
  116. args.insert(0, setuptools.bootstrap_install_from)
  117. cmd.args = args
  118. cmd.run(show_deprecation=False)
  119. setuptools.bootstrap_install_from = None
  120. # XXX Python 3.1 doesn't see _nc if this is inside the class
  121. install.sub_commands = [
  122. cmd for cmd in orig.install.sub_commands if cmd[0] not in install._nc
  123. ] + install.new_commands