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.4.4, created at 2024-03-22 06:44 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-22 06:44 +0000
1from inspect import Parameter, signature
2import functools
3import warnings
4from importlib import import_module
7__all__ = ["_deprecated"]
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()
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.
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}"
41 if attribute not in all:
42 raise AttributeError(
43 f"`scipy.{sub_package}.{module}` has no attribute `{attribute}`; "
44 f"furthermore, `scipy.{sub_package}.{module}` is deprecated "
45 f"and will be removed in SciPy 2.0.0."
46 )
48 attr = getattr(import_module(correct_import), attribute, None)
50 if attr is not None:
51 message = (
52 f"Please import `{attribute}` from the `{correct_import}` namespace; "
53 f"the `scipy.{sub_package}.{module}` namespace is deprecated "
54 f"and will be removed in SciPy 2.0.0."
55 )
56 else:
57 message = (
58 f"`scipy.{sub_package}.{module}.{attribute}` is deprecated along with "
59 f"the `scipy.{sub_package}.{module}` namespace. "
60 f"`scipy.{sub_package}.{module}.{attribute}` will be removed "
61 f"in SciPy 1.14.0, and the `scipy.{sub_package}.{module}` namespace "
62 f"will be removed in SciPy 2.0.0."
63 )
65 warnings.warn(message, category=DeprecationWarning, stacklevel=3)
67 for module in private_modules:
68 try:
69 return getattr(import_module(f"scipy.{sub_package}.{module}"), attribute)
70 except AttributeError as e:
71 # still raise an error if the attribute isn't in any of the expected
72 # private modules
73 if module == private_modules[-1]:
74 raise e
75 continue
78def _deprecated(msg, stacklevel=2):
79 """Deprecate a function by emitting a warning on use."""
80 def wrap(fun):
81 if isinstance(fun, type):
82 warnings.warn(
83 f"Trying to deprecate class {fun!r}",
84 category=RuntimeWarning, stacklevel=2)
85 return fun
87 @functools.wraps(fun)
88 def call(*args, **kwargs):
89 warnings.warn(msg, category=DeprecationWarning,
90 stacklevel=stacklevel)
91 return fun(*args, **kwargs)
92 call.__doc__ = fun.__doc__
93 return call
95 return wrap
98class _DeprecationHelperStr:
99 """
100 Helper class used by deprecate_cython_api
101 """
102 def __init__(self, content, message):
103 self._content = content
104 self._message = message
106 def __hash__(self):
107 return hash(self._content)
109 def __eq__(self, other):
110 res = (self._content == other)
111 if res:
112 warnings.warn(self._message, category=DeprecationWarning,
113 stacklevel=2)
114 return res
117def deprecate_cython_api(module, routine_name, new_name=None, message=None):
118 """
119 Deprecate an exported cdef function in a public Cython API module.
121 Only functions can be deprecated; typedefs etc. cannot.
123 Parameters
124 ----------
125 module : module
126 Public Cython API module (e.g. scipy.linalg.cython_blas).
127 routine_name : str
128 Name of the routine to deprecate. May also be a fused-type
129 routine (in which case its all specializations are deprecated).
130 new_name : str
131 New name to include in the deprecation warning message
132 message : str
133 Additional text in the deprecation warning message
135 Examples
136 --------
137 Usually, this function would be used in the top-level of the
138 module ``.pyx`` file:
140 >>> from scipy._lib.deprecation import deprecate_cython_api
141 >>> import scipy.linalg.cython_blas as mod
142 >>> deprecate_cython_api(mod, "dgemm", "dgemm_new",
143 ... message="Deprecated in Scipy 1.5.0")
144 >>> del deprecate_cython_api, mod
146 After this, Cython modules that use the deprecated function emit a
147 deprecation warning when they are imported.
149 """
150 old_name = f"{module.__name__}.{routine_name}"
152 if new_name is None:
153 depdoc = "`%s` is deprecated!" % old_name
154 else:
155 depdoc = f"`{old_name}` is deprecated, use `{new_name}` instead!"
157 if message is not None:
158 depdoc += "\n" + message
160 d = module.__pyx_capi__
162 # Check if the function is a fused-type function with a mangled name
163 j = 0
164 has_fused = False
165 while True:
166 fused_name = f"__pyx_fuse_{j}{routine_name}"
167 if fused_name in d:
168 has_fused = True
169 d[_DeprecationHelperStr(fused_name, depdoc)] = d.pop(fused_name)
170 j += 1
171 else:
172 break
174 # If not, apply deprecation to the named routine
175 if not has_fused:
176 d[_DeprecationHelperStr(routine_name, depdoc)] = d.pop(routine_name)
179# taken from scikit-learn, see
180# https://github.com/scikit-learn/scikit-learn/blob/1.3.0/sklearn/utils/validation.py#L38
181def _deprecate_positional_args(func=None, *, version=None):
182 """Decorator for methods that issues warnings for positional arguments.
184 Using the keyword-only argument syntax in pep 3102, arguments after the
185 * will issue a warning when passed as a positional argument.
187 Parameters
188 ----------
189 func : callable, default=None
190 Function to check arguments on.
191 version : callable, default=None
192 The version when positional arguments will result in error.
193 """
194 if version is None:
195 msg = "Need to specify a version where signature will be changed"
196 raise ValueError(msg)
198 def _inner_deprecate_positional_args(f):
199 sig = signature(f)
200 kwonly_args = []
201 all_args = []
203 for name, param in sig.parameters.items():
204 if param.kind == Parameter.POSITIONAL_OR_KEYWORD:
205 all_args.append(name)
206 elif param.kind == Parameter.KEYWORD_ONLY:
207 kwonly_args.append(name)
209 @functools.wraps(f)
210 def inner_f(*args, **kwargs):
211 extra_args = len(args) - len(all_args)
212 if extra_args <= 0:
213 return f(*args, **kwargs)
215 # extra_args > 0
216 args_msg = [
217 f"{name}={arg}"
218 for name, arg in zip(kwonly_args[:extra_args], args[-extra_args:])
219 ]
220 args_msg = ", ".join(args_msg)
221 warnings.warn(
222 (
223 f"You are passing {args_msg} as a positional argument. "
224 "Please change your invocation to use keyword arguments. "
225 f"From SciPy {version}, passing these as positional "
226 "arguments will result in an error."
227 ),
228 DeprecationWarning,
229 stacklevel=2,
230 )
231 kwargs.update(zip(sig.parameters, args))
232 return f(**kwargs)
234 return inner_f
236 if func is not None:
237 return _inner_deprecate_positional_args(func)
239 return _inner_deprecate_positional_args