Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/scipy/_lib/deprecation.py: 36%

92 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-23 06:43 +0000

1from inspect import Parameter, signature 

2import functools 

3import warnings 

4from importlib import import_module 

5 

6 

7__all__ = ["_deprecated"] 

8 

9 

10# Object to use as default value for arguments to be deprecated. This should 

11# be used over 'None' as the user could parse 'None' as a positional argument 

12_NoValue = object() 

13 

14def _sub_module_deprecation(*, sub_package, module, private_modules, all, 

15 attribute, correct_module=None): 

16 """Helper function for deprecating modules that are public but were 

17 intended to be private. 

18 

19 Parameters 

20 ---------- 

21 sub_package : str 

22 Subpackage the module belongs to eg. stats 

23 module : str 

24 Public but intended private module to deprecate 

25 private_modules : list 

26 Private replacement(s) for `module`; should contain the 

27 content of ``all``, possibly spread over several modules. 

28 all : list 

29 ``__all__`` belonging to `module` 

30 attribute : str 

31 The attribute in `module` being accessed 

32 correct_module : str, optional 

33 Module in `sub_package` that `attribute` should be imported from. 

34 Default is that `attribute` should be imported from ``scipy.sub_package``. 

35 """ 

36 if correct_module is not None: 

37 correct_import = f"scipy.{sub_package}.{correct_module}" 

38 else: 

39 correct_import = f"scipy.{sub_package}" 

40 

41 if attribute not in all: 

42 raise AttributeError( 

43 f"`scipy.{sub_package}.{module}` has no attribute `{attribute}`; furthermore, " 

44 f"`scipy.{sub_package}.{module}` is deprecated and will be removed in " 

45 "SciPy 2.0.0.") 

46 

47 attr = getattr(import_module(correct_import), attribute, None) 

48 

49 if attr is not None: 

50 message = (f"Please import `{attribute}` from the `{correct_import}` namespace; " 

51 f"the `scipy.{sub_package}.{module}` namespace is deprecated and " 

52 "will be removed in SciPy 2.0.0.") 

53 else: 

54 message = (f"`scipy.{sub_package}.{module}.{attribute}` is deprecated along with " 

55 f"the `scipy.{sub_package}.{module}` namespace. " 

56 f"`scipy.{sub_package}.{module}.{attribute}` will be removed in SciPy 1.13.0, and " 

57 f"the `scipy.{sub_package}.{module}` namespace will be removed in SciPy 2.0.0.") 

58 

59 warnings.warn(message, category=DeprecationWarning, stacklevel=3) 

60 

61 for module in private_modules: 

62 try: 

63 return getattr(import_module(f"scipy.{sub_package}.{module}"), attribute) 

64 except AttributeError as e: 

65 # still raise an error if the attribute isn't in any of the expected 

66 # private modules 

67 if module == private_modules[-1]: 

68 raise e 

69 continue 

70 

71 

72def _deprecated(msg, stacklevel=2): 

73 """Deprecate a function by emitting a warning on use.""" 

74 def wrap(fun): 

75 if isinstance(fun, type): 

76 warnings.warn( 

77 f"Trying to deprecate class {fun!r}", 

78 category=RuntimeWarning, stacklevel=2) 

79 return fun 

80 

81 @functools.wraps(fun) 

82 def call(*args, **kwargs): 

83 warnings.warn(msg, category=DeprecationWarning, 

84 stacklevel=stacklevel) 

85 return fun(*args, **kwargs) 

86 call.__doc__ = fun.__doc__ 

87 return call 

88 

89 return wrap 

90 

91 

92class _DeprecationHelperStr: 

93 """ 

94 Helper class used by deprecate_cython_api 

95 """ 

96 def __init__(self, content, message): 

97 self._content = content 

98 self._message = message 

99 

100 def __hash__(self): 

101 return hash(self._content) 

102 

103 def __eq__(self, other): 

104 res = (self._content == other) 

105 if res: 

106 warnings.warn(self._message, category=DeprecationWarning, 

107 stacklevel=2) 

108 return res 

109 

110 

111def deprecate_cython_api(module, routine_name, new_name=None, message=None): 

112 """ 

113 Deprecate an exported cdef function in a public Cython API module. 

114 

115 Only functions can be deprecated; typedefs etc. cannot. 

116 

117 Parameters 

118 ---------- 

119 module : module 

120 Public Cython API module (e.g. scipy.linalg.cython_blas). 

121 routine_name : str 

122 Name of the routine to deprecate. May also be a fused-type 

123 routine (in which case its all specializations are deprecated). 

124 new_name : str 

125 New name to include in the deprecation warning message 

126 message : str 

127 Additional text in the deprecation warning message 

128 

129 Examples 

130 -------- 

131 Usually, this function would be used in the top-level of the 

132 module ``.pyx`` file: 

133 

134 >>> from scipy._lib.deprecation import deprecate_cython_api 

135 >>> import scipy.linalg.cython_blas as mod 

136 >>> deprecate_cython_api(mod, "dgemm", "dgemm_new", 

137 ... message="Deprecated in Scipy 1.5.0") 

138 >>> del deprecate_cython_api, mod 

139 

140 After this, Cython modules that use the deprecated function emit a 

141 deprecation warning when they are imported. 

142 

143 """ 

144 old_name = f"{module.__name__}.{routine_name}" 

145 

146 if new_name is None: 

147 depdoc = "`%s` is deprecated!" % old_name 

148 else: 

149 depdoc = "`%s` is deprecated, use `%s` instead!" % \ 

150 (old_name, new_name) 

151 

152 if message is not None: 

153 depdoc += "\n" + message 

154 

155 d = module.__pyx_capi__ 

156 

157 # Check if the function is a fused-type function with a mangled name 

158 j = 0 

159 has_fused = False 

160 while True: 

161 fused_name = f"__pyx_fuse_{j}{routine_name}" 

162 if fused_name in d: 

163 has_fused = True 

164 d[_DeprecationHelperStr(fused_name, depdoc)] = d.pop(fused_name) 

165 j += 1 

166 else: 

167 break 

168 

169 # If not, apply deprecation to the named routine 

170 if not has_fused: 

171 d[_DeprecationHelperStr(routine_name, depdoc)] = d.pop(routine_name) 

172 

173 

174# taken from scikit-learn, see 

175# https://github.com/scikit-learn/scikit-learn/blob/1.3.0/sklearn/utils/validation.py#L38 

176def _deprecate_positional_args(func=None, *, version=None): 

177 """Decorator for methods that issues warnings for positional arguments. 

178 

179 Using the keyword-only argument syntax in pep 3102, arguments after the 

180 * will issue a warning when passed as a positional argument. 

181 

182 Parameters 

183 ---------- 

184 func : callable, default=None 

185 Function to check arguments on. 

186 version : callable, default=None 

187 The version when positional arguments will result in error. 

188 """ 

189 if version is None: 

190 msg = "Need to specify a version where signature will be changed" 

191 raise ValueError(msg) 

192 

193 def _inner_deprecate_positional_args(f): 

194 sig = signature(f) 

195 kwonly_args = [] 

196 all_args = [] 

197 

198 for name, param in sig.parameters.items(): 

199 if param.kind == Parameter.POSITIONAL_OR_KEYWORD: 

200 all_args.append(name) 

201 elif param.kind == Parameter.KEYWORD_ONLY: 

202 kwonly_args.append(name) 

203 

204 @functools.wraps(f) 

205 def inner_f(*args, **kwargs): 

206 extra_args = len(args) - len(all_args) 

207 if extra_args <= 0: 

208 return f(*args, **kwargs) 

209 

210 # extra_args > 0 

211 args_msg = [ 

212 f"{name}={arg}" 

213 for name, arg in zip(kwonly_args[:extra_args], args[-extra_args:]) 

214 ] 

215 args_msg = ", ".join(args_msg) 

216 warnings.warn( 

217 ( 

218 f"You are passing {args_msg} as a positional argument. " 

219 "Please change your invocation to use keyword arguments. " 

220 f"From SciPy {version}, passing these as positional " 

221 "arguments will result in an error." 

222 ), 

223 DeprecationWarning, 

224 stacklevel=2, 

225 ) 

226 kwargs.update(zip(sig.parameters, args)) 

227 return f(**kwargs) 

228 

229 return inner_f 

230 

231 if func is not None: 

232 return _inner_deprecate_positional_args(func) 

233 

234 return _inner_deprecate_positional_args