Coverage for /usr/lib/python3/dist-packages/sympy/utilities/exceptions.py: 41%
37 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1"""
2General SymPy exceptions and warnings.
3"""
5import warnings
6import contextlib
8from textwrap import dedent
11class SymPyDeprecationWarning(DeprecationWarning):
12 r"""
13 A warning for deprecated features of SymPy.
15 See the :ref:`deprecation-policy` document for details on when and how
16 things should be deprecated in SymPy.
18 Note that simply constructing this class will not cause a warning to be
19 issued. To do that, you must call the :func`sympy_deprecation_warning`
20 function. For this reason, it is not recommended to ever construct this
21 class directly.
23 Explanation
24 ===========
26 The ``SymPyDeprecationWarning`` class is a subclass of
27 ``DeprecationWarning`` that is used for all deprecations in SymPy. A
28 special subclass is used so that we can automatically augment the warning
29 message with additional metadata about the version the deprecation was
30 introduced in and a link to the documentation. This also allows users to
31 explicitly filter deprecation warnings from SymPy using ``warnings``
32 filters (see :ref:`silencing-sympy-deprecation-warnings`).
34 Additionally, ``SymPyDeprecationWarning`` is enabled to be shown by
35 default, unlike normal ``DeprecationWarning``\s, which are only shown by
36 default in interactive sessions. This ensures that deprecation warnings in
37 SymPy will actually be seen by users.
39 See the documentation of :func:`sympy_deprecation_warning` for a
40 description of the parameters to this function.
42 To mark a function as deprecated, you can use the :func:`@deprecated
43 <sympy.utilities.decorator.deprecated>` decorator.
45 See Also
46 ========
47 sympy.utilities.exceptions.sympy_deprecation_warning
48 sympy.utilities.exceptions.ignore_warnings
49 sympy.utilities.decorator.deprecated
50 sympy.testing.pytest.warns_deprecated_sympy
52 """
53 def __init__(self, message, *, deprecated_since_version, active_deprecations_target):
55 super().__init__(message, deprecated_since_version,
56 active_deprecations_target)
57 self.message = message
58 if not isinstance(deprecated_since_version, str):
59 raise TypeError(f"'deprecated_since_version' should be a string, got {deprecated_since_version!r}")
60 self.deprecated_since_version = deprecated_since_version
61 self.active_deprecations_target = active_deprecations_target
62 if any(i in active_deprecations_target for i in '()='):
63 raise ValueError("active_deprecations_target be the part inside of the '(...)='")
65 self.full_message = f"""
67{dedent(message).strip()}
69See https://docs.sympy.org/latest/explanation/active-deprecations.html#{active_deprecations_target}
70for details.
72This has been deprecated since SymPy version {deprecated_since_version}. It
73will be removed in a future version of SymPy.
74"""
76 def __str__(self):
77 return self.full_message
79 def __repr__(self):
80 return f"{self.__class__.__name__}({self.message!r}, deprecated_since_version={self.deprecated_since_version!r}, active_deprecations_target={self.active_deprecations_target!r})"
82 def __eq__(self, other):
83 return isinstance(other, SymPyDeprecationWarning) and self.args == other.args
85 # Make pickling work. The by default, it tries to recreate the expression
86 # from its args, but this doesn't work because of our keyword-only
87 # arguments.
88 @classmethod
89 def _new(cls, message, deprecated_since_version,
90 active_deprecations_target):
91 return cls(message, deprecated_since_version=deprecated_since_version, active_deprecations_target=active_deprecations_target)
93 def __reduce__(self):
94 return (self._new, (self.message, self.deprecated_since_version, self.active_deprecations_target))
96# Python by default hides DeprecationWarnings, which we do not want.
97warnings.simplefilter("once", SymPyDeprecationWarning)
99def sympy_deprecation_warning(message, *, deprecated_since_version,
100 active_deprecations_target, stacklevel=3):
101 r'''
102 Warn that a feature is deprecated in SymPy.
104 See the :ref:`deprecation-policy` document for details on when and how
105 things should be deprecated in SymPy.
107 To mark an entire function or class as deprecated, you can use the
108 :func:`@deprecated <sympy.utilities.decorator.deprecated>` decorator.
110 Parameters
111 ==========
113 message: str
115 The deprecation message. This may span multiple lines and contain
116 code examples. Messages should be wrapped to 80 characters. The
117 message is automatically dedented and leading and trailing whitespace
118 stripped. Messages may include dynamic content based on the user
119 input, but avoid using ``str(expression)`` if an expression can be
120 arbitrary, as it might be huge and make the warning message
121 unreadable.
123 deprecated_since_version: str
125 The version of SymPy the feature has been deprecated since. For new
126 deprecations, this should be the version in `sympy/release.py
127 <https://github.com/sympy/sympy/blob/master/sympy/release.py>`_
128 without the ``.dev``. If the next SymPy version ends up being
129 different from this, the release manager will need to update any
130 ``SymPyDeprecationWarning``\s using the incorrect version. This
131 argument is required and must be passed as a keyword argument.
132 (example: ``deprecated_since_version="1.10"``).
134 active_deprecations_target: str
136 The Sphinx target corresponding to the section for the deprecation in
137 the :ref:`active-deprecations` document (see
138 ``doc/src/explanation/active-deprecations.md``). This is used to
139 automatically generate a URL to the page in the warning message. This
140 argument is required and must be passed as a keyword argument.
141 (example: ``active_deprecations_target="deprecated-feature-abc"``)
143 stacklevel: int (default: 3)
145 The ``stacklevel`` parameter that is passed to ``warnings.warn``. If
146 you create a wrapper that calls this function, this should be
147 increased so that the warning message shows the user line of code that
148 produced the warning. Note that in some cases there will be multiple
149 possible different user code paths that could result in the warning.
150 In that case, just choose the smallest common stacklevel.
152 Examples
153 ========
155 >>> from sympy.utilities.exceptions import sympy_deprecation_warning
156 >>> def is_this_zero(x, y=0):
157 ... """
158 ... Determine if x = 0.
159 ...
160 ... Parameters
161 ... ==========
162 ...
163 ... x : Expr
164 ... The expression to check.
165 ...
166 ... y : Expr, optional
167 ... If provided, check if x = y.
168 ...
169 ... .. deprecated:: 1.1
170 ...
171 ... The ``y`` argument to ``is_this_zero`` is deprecated. Use
172 ... ``is_this_zero(x - y)`` instead.
173 ...
174 ... """
175 ... from sympy import simplify
176 ...
177 ... if y != 0:
178 ... sympy_deprecation_warning("""
179 ... The y argument to is_zero() is deprecated. Use is_zero(x - y) instead.""",
180 ... deprecated_since_version="1.1",
181 ... active_deprecations_target='is-this-zero-y-deprecation')
182 ... return simplify(x - y) == 0
183 >>> is_this_zero(0)
184 True
185 >>> is_this_zero(1, 1) # doctest: +SKIP
186 <stdin>:1: SymPyDeprecationWarning:
187 <BLANKLINE>
188 The y argument to is_zero() is deprecated. Use is_zero(x - y) instead.
189 <BLANKLINE>
190 See https://docs.sympy.org/latest/explanation/active-deprecations.html#is-this-zero-y-deprecation
191 for details.
192 <BLANKLINE>
193 This has been deprecated since SymPy version 1.1. It
194 will be removed in a future version of SymPy.
195 <BLANKLINE>
196 is_this_zero(1, 1)
197 True
199 See Also
200 ========
202 sympy.utilities.exceptions.SymPyDeprecationWarning
203 sympy.utilities.exceptions.ignore_warnings
204 sympy.utilities.decorator.deprecated
205 sympy.testing.pytest.warns_deprecated_sympy
207 '''
208 w = SymPyDeprecationWarning(message,
209 deprecated_since_version=deprecated_since_version,
210 active_deprecations_target=active_deprecations_target)
211 warnings.warn(w, stacklevel=stacklevel)
214@contextlib.contextmanager
215def ignore_warnings(warningcls):
216 '''
217 Context manager to suppress warnings during tests.
219 .. note::
221 Do not use this with SymPyDeprecationWarning in the tests.
222 warns_deprecated_sympy() should be used instead.
224 This function is useful for suppressing warnings during tests. The warns
225 function should be used to assert that a warning is raised. The
226 ignore_warnings function is useful in situation when the warning is not
227 guaranteed to be raised (e.g. on importing a module) or if the warning
228 comes from third-party code.
230 This function is also useful to prevent the same or similar warnings from
231 being issue twice due to recursive calls.
233 When the warning is coming (reliably) from SymPy the warns function should
234 be preferred to ignore_warnings.
236 >>> from sympy.utilities.exceptions import ignore_warnings
237 >>> import warnings
239 Here's a warning:
241 >>> with warnings.catch_warnings(): # reset warnings in doctest
242 ... warnings.simplefilter('error')
243 ... warnings.warn('deprecated', UserWarning)
244 Traceback (most recent call last):
245 ...
246 UserWarning: deprecated
248 Let's suppress it with ignore_warnings:
250 >>> with warnings.catch_warnings(): # reset warnings in doctest
251 ... warnings.simplefilter('error')
252 ... with ignore_warnings(UserWarning):
253 ... warnings.warn('deprecated', UserWarning)
255 (No warning emitted)
257 See Also
258 ========
259 sympy.utilities.exceptions.SymPyDeprecationWarning
260 sympy.utilities.exceptions.sympy_deprecation_warning
261 sympy.utilities.decorator.deprecated
262 sympy.testing.pytest.warns_deprecated_sympy
264 '''
265 # Absorbs all warnings in warnrec
266 with warnings.catch_warnings(record=True) as warnrec:
267 # Make sure our warning doesn't get filtered
268 warnings.simplefilter("always", warningcls)
269 # Now run the test
270 yield
272 # Reissue any warnings that we aren't testing for
273 for w in warnrec:
274 if not issubclass(w.category, warningcls):
275 warnings.warn_explicit(w.message, w.category, w.filename, w.lineno)