Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pip/_internal/utils/deprecation.py: 40%

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

35 statements  

1""" 

2A module that implements tooling to enable easy warnings about deprecations. 

3""" 

4 

5from __future__ import annotations 

6 

7import logging 

8import warnings 

9from typing import Any, TextIO 

10 

11from pip._vendor.packaging.version import parse 

12 

13from pip import __version__ as current_version # NOTE: tests patch this name. 

14 

15DEPRECATION_MSG_PREFIX = "DEPRECATION: " 

16 

17 

18class PipDeprecationWarning(Warning): 

19 include_source: bool = False 

20 

21 

22_original_showwarning: Any = None 

23 

24 

25# Warnings <-> Logging Integration 

26def _showwarning( 

27 message: Warning | str, 

28 category: type[Warning], 

29 filename: str, 

30 lineno: int, 

31 file: TextIO | None = None, 

32 line: str | None = None, 

33) -> None: 

34 if file is not None: 

35 if _original_showwarning is not None: 

36 _original_showwarning(message, category, filename, lineno, file, line) 

37 elif issubclass(category, PipDeprecationWarning): 

38 # We use a specially named logger which will handle all of the 

39 # deprecation messages for pip. 

40 logger = logging.getLogger("pip._internal.deprecations") 

41 if isinstance(message, PipDeprecationWarning) and message.include_source: 

42 logger.warning("%s (%s:%s)", message, filename, lineno) 

43 else: 

44 logger.warning(message) 

45 else: 

46 _original_showwarning(message, category, filename, lineno, file, line) 

47 

48 

49def install_warning_logger() -> None: 

50 # Enable our Deprecation Warnings 

51 warnings.simplefilter("default", PipDeprecationWarning, append=True) 

52 

53 global _original_showwarning 

54 

55 if _original_showwarning is None: 

56 _original_showwarning = warnings.showwarning 

57 warnings.showwarning = _showwarning 

58 

59 

60def deprecated( 

61 *, 

62 reason: str, 

63 replacement: str | None, 

64 gone_in: str | None, 

65 feature_flag: str | None = None, 

66 issue: int | None = None, 

67 stacklevel: int = 2, 

68 include_source: bool = False, 

69) -> None: 

70 """Helper to deprecate existing functionality. 

71 

72 reason: 

73 Textual reason shown to the user about why this functionality has 

74 been deprecated. Should be a complete sentence. 

75 replacement: 

76 Textual suggestion shown to the user about what alternative 

77 functionality they can use. 

78 gone_in: 

79 The version of pip does this functionality should get removed in. 

80 Raises an error if pip's current version is greater than or equal to 

81 this. 

82 feature_flag: 

83 Command-line flag of the form --use-feature={feature_flag} for testing 

84 upcoming functionality. 

85 issue: 

86 Issue number on the tracker that would serve as a useful place for 

87 users to find related discussion and provide feedback. 

88 stacklevel: 

89 How many frames up the call stack to attribute the warning to. 

90 Defaults to 2 (the caller of deprecated()). 

91 include_source: 

92 If True, include the source filename and line number in the warning 

93 output. Useful when the warning originates from external code. 

94 """ 

95 

96 # Determine whether or not the feature is already gone in this version. 

97 is_gone = gone_in is not None and parse(current_version) >= parse(gone_in) 

98 

99 message_parts = [ 

100 (reason, f"{DEPRECATION_MSG_PREFIX}{{}}"), 

101 ( 

102 gone_in, 

103 ( 

104 "pip {} will enforce this behaviour change." 

105 if not is_gone 

106 else "Since pip {}, this is no longer supported." 

107 ), 

108 ), 

109 ( 

110 replacement, 

111 "A possible replacement is {}.", 

112 ), 

113 ( 

114 feature_flag, 

115 ( 

116 "You can use the flag --use-feature={} to test the upcoming behaviour." 

117 if not is_gone 

118 else None 

119 ), 

120 ), 

121 ( 

122 issue, 

123 "Discussion can be found at https://github.com/pypa/pip/issues/{}", 

124 ), 

125 ] 

126 

127 message = " ".join( 

128 format_str.format(value) 

129 for value, format_str in message_parts 

130 if format_str is not None and value is not None 

131 ) 

132 

133 # Raise as an error if this behaviour is deprecated. 

134 if is_gone: 

135 raise PipDeprecationWarning(message) 

136 

137 warning = PipDeprecationWarning(message) 

138 warning.include_source = include_source 

139 warnings.warn(warning, stacklevel=stacklevel)