Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/setuptools/monkey.py: 79%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Monkey patching of distutils.
3"""
5from __future__ import annotations
7import inspect
8import platform
9import sys
10import types
11from typing import Type, TypeVar, cast, overload
13import distutils.filelist
15_T = TypeVar("_T")
16_UnpatchT = TypeVar("_UnpatchT", type, types.FunctionType)
19__all__: list[str] = []
20"""
21Everything is private. Contact the project team
22if you think you need this functionality.
23"""
26def _get_mro(cls):
27 """
28 Returns the bases classes for cls sorted by the MRO.
30 Works around an issue on Jython where inspect.getmro will not return all
31 base classes if multiple classes share the same name. Instead, this
32 function will return a tuple containing the class itself, and the contents
33 of cls.__bases__. See https://github.com/pypa/setuptools/issues/1024.
34 """
35 if platform.python_implementation() == "Jython":
36 return (cls,) + cls.__bases__
37 return inspect.getmro(cls)
40@overload
41def get_unpatched(item: _UnpatchT) -> _UnpatchT: ...
42@overload
43def get_unpatched(item: object) -> None: ...
44def get_unpatched(
45 item: type | types.FunctionType | object,
46) -> type | types.FunctionType | None:
47 if isinstance(item, type):
48 return get_unpatched_class(item)
49 if isinstance(item, types.FunctionType):
50 return get_unpatched_function(item)
51 return None
54def get_unpatched_class(cls: type[_T]) -> type[_T]:
55 """Protect against re-patching the distutils if reloaded
57 Also ensures that no other distutils extension monkeypatched the distutils
58 first.
59 """
60 external_bases = (
61 cast(Type[_T], cls)
62 for cls in _get_mro(cls)
63 if not cls.__module__.startswith('setuptools')
64 )
65 base = next(external_bases)
66 if not base.__module__.startswith('distutils'):
67 msg = "distutils has already been patched by %r" % cls
68 raise AssertionError(msg)
69 return base
72def patch_all():
73 import setuptools
75 # we can't patch distutils.cmd, alas
76 distutils.core.Command = setuptools.Command
78 _patch_distribution_metadata()
80 # Install Distribution throughout the distutils
81 for module in distutils.dist, distutils.core, distutils.cmd:
82 module.Distribution = setuptools.dist.Distribution
84 # Install the patched Extension
85 distutils.core.Extension = setuptools.extension.Extension
86 distutils.extension.Extension = setuptools.extension.Extension
87 if 'distutils.command.build_ext' in sys.modules:
88 sys.modules[
89 'distutils.command.build_ext'
90 ].Extension = setuptools.extension.Extension
93def _patch_distribution_metadata():
94 from . import _core_metadata
96 """Patch write_pkg_file and read_pkg_file for higher metadata standards"""
97 for attr in (
98 'write_pkg_info',
99 'write_pkg_file',
100 'read_pkg_file',
101 'get_metadata_version',
102 'get_fullname',
103 ):
104 new_val = getattr(_core_metadata, attr)
105 setattr(distutils.dist.DistributionMetadata, attr, new_val)
108def patch_func(replacement, target_mod, func_name):
109 """
110 Patch func_name in target_mod with replacement
112 Important - original must be resolved by name to avoid
113 patching an already patched function.
114 """
115 original = getattr(target_mod, func_name)
117 # set the 'unpatched' attribute on the replacement to
118 # point to the original.
119 vars(replacement).setdefault('unpatched', original)
121 # replace the function in the original module
122 setattr(target_mod, func_name, replacement)
125def get_unpatched_function(candidate):
126 return candidate.unpatched