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.
 
 
 
 

127 lignes
3.6 KiB

  1. """
  2. Monkey patching of distutils.
  3. """
  4. from __future__ import annotations
  5. import inspect
  6. import platform
  7. import sys
  8. import types
  9. from typing import TypeVar, cast, overload
  10. import distutils.filelist
  11. _T = TypeVar("_T")
  12. _UnpatchT = TypeVar("_UnpatchT", type, types.FunctionType)
  13. __all__: list[str] = []
  14. """
  15. Everything is private. Contact the project team
  16. if you think you need this functionality.
  17. """
  18. def _get_mro(cls):
  19. """
  20. Returns the bases classes for cls sorted by the MRO.
  21. Works around an issue on Jython where inspect.getmro will not return all
  22. base classes if multiple classes share the same name. Instead, this
  23. function will return a tuple containing the class itself, and the contents
  24. of cls.__bases__. See https://github.com/pypa/setuptools/issues/1024.
  25. """
  26. if platform.python_implementation() == "Jython":
  27. return (cls,) + cls.__bases__
  28. return inspect.getmro(cls)
  29. @overload
  30. def get_unpatched(item: _UnpatchT) -> _UnpatchT: ...
  31. @overload
  32. def get_unpatched(item: object) -> None: ...
  33. def get_unpatched(
  34. item: type | types.FunctionType | object,
  35. ) -> type | types.FunctionType | None:
  36. if isinstance(item, type):
  37. return get_unpatched_class(item)
  38. if isinstance(item, types.FunctionType):
  39. return get_unpatched_function(item)
  40. return None
  41. def get_unpatched_class(cls: type[_T]) -> type[_T]:
  42. """Protect against re-patching the distutils if reloaded
  43. Also ensures that no other distutils extension monkeypatched the distutils
  44. first.
  45. """
  46. external_bases = (
  47. cast(type[_T], cls)
  48. for cls in _get_mro(cls)
  49. if not cls.__module__.startswith('setuptools')
  50. )
  51. base = next(external_bases)
  52. if not base.__module__.startswith('distutils'):
  53. msg = f"distutils has already been patched by {cls!r}"
  54. raise AssertionError(msg)
  55. return base
  56. def patch_all():
  57. import setuptools
  58. # we can't patch distutils.cmd, alas
  59. distutils.core.Command = setuptools.Command # type: ignore[misc,assignment] # monkeypatching
  60. _patch_distribution_metadata()
  61. # Install Distribution throughout the distutils
  62. for module in distutils.dist, distutils.core, distutils.cmd:
  63. module.Distribution = setuptools.dist.Distribution
  64. # Install the patched Extension
  65. distutils.core.Extension = setuptools.extension.Extension # type: ignore[misc,assignment] # monkeypatching
  66. distutils.extension.Extension = setuptools.extension.Extension # type: ignore[misc,assignment] # monkeypatching
  67. if 'distutils.command.build_ext' in sys.modules:
  68. sys.modules[
  69. 'distutils.command.build_ext'
  70. ].Extension = setuptools.extension.Extension
  71. def _patch_distribution_metadata():
  72. from . import _core_metadata
  73. """Patch write_pkg_file and read_pkg_file for higher metadata standards"""
  74. for attr in (
  75. 'write_pkg_info',
  76. 'write_pkg_file',
  77. 'read_pkg_file',
  78. 'get_metadata_version',
  79. 'get_fullname',
  80. ):
  81. new_val = getattr(_core_metadata, attr)
  82. setattr(distutils.dist.DistributionMetadata, attr, new_val)
  83. def patch_func(replacement, target_mod, func_name):
  84. """
  85. Patch func_name in target_mod with replacement
  86. Important - original must be resolved by name to avoid
  87. patching an already patched function.
  88. """
  89. original = getattr(target_mod, func_name)
  90. # set the 'unpatched' attribute on the replacement to
  91. # point to the original.
  92. vars(replacement).setdefault('unpatched', original)
  93. # replace the function in the original module
  94. setattr(target_mod, func_name, replacement)
  95. def get_unpatched_function(candidate):
  96. return candidate.unpatched