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

52 statements  

1""" 

2Monkey patching of distutils. 

3""" 

4 

5from __future__ import annotations 

6 

7import inspect 

8import platform 

9import sys 

10import types 

11from typing import Type, TypeVar, cast, overload 

12 

13import distutils.filelist 

14 

15_T = TypeVar("_T") 

16_UnpatchT = TypeVar("_UnpatchT", type, types.FunctionType) 

17 

18 

19__all__: list[str] = [] 

20""" 

21Everything is private. Contact the project team 

22if you think you need this functionality. 

23""" 

24 

25 

26def _get_mro(cls): 

27 """ 

28 Returns the bases classes for cls sorted by the MRO. 

29 

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) 

38 

39 

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 

52 

53 

54def get_unpatched_class(cls: type[_T]) -> type[_T]: 

55 """Protect against re-patching the distutils if reloaded 

56 

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 

70 

71 

72def patch_all(): 

73 import setuptools 

74 

75 # we can't patch distutils.cmd, alas 

76 distutils.core.Command = setuptools.Command 

77 

78 _patch_distribution_metadata() 

79 

80 # Install Distribution throughout the distutils 

81 for module in distutils.dist, distutils.core, distutils.cmd: 

82 module.Distribution = setuptools.dist.Distribution 

83 

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 

91 

92 

93def _patch_distribution_metadata(): 

94 from . import _core_metadata 

95 

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) 

106 

107 

108def patch_func(replacement, target_mod, func_name): 

109 """ 

110 Patch func_name in target_mod with replacement 

111 

112 Important - original must be resolved by name to avoid 

113 patching an already patched function. 

114 """ 

115 original = getattr(target_mod, func_name) 

116 

117 # set the 'unpatched' attribute on the replacement to 

118 # point to the original. 

119 vars(replacement).setdefault('unpatched', original) 

120 

121 # replace the function in the original module 

122 setattr(target_mod, func_name, replacement) 

123 

124 

125def get_unpatched_function(candidate): 

126 return candidate.unpatched