1"""
2Functions for changing global ufunc configuration
3
4This provides helpers which wrap `_get_extobj_dict` and `_make_extobj`, and
5`_extobj_contextvar` from umath.
6"""
7import functools
8
9from numpy._utils import set_module
10
11from .umath import _extobj_contextvar, _get_extobj_dict, _make_extobj
12
13__all__ = [
14 "seterr", "geterr", "setbufsize", "getbufsize", "seterrcall", "geterrcall",
15 "errstate"
16]
17
18
19@set_module('numpy')
20def seterr(all=None, divide=None, over=None, under=None, invalid=None):
21 """
22 Set how floating-point errors are handled.
23
24 Note that operations on integer scalar types (such as `int16`) are
25 handled like floating point, and are affected by these settings.
26
27 Parameters
28 ----------
29 all : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
30 Set treatment for all types of floating-point errors at once:
31
32 - ignore: Take no action when the exception occurs.
33 - warn: Print a :exc:`RuntimeWarning` (via the Python `warnings`
34 module).
35 - raise: Raise a :exc:`FloatingPointError`.
36 - call: Call a function specified using the `seterrcall` function.
37 - print: Print a warning directly to ``stdout``.
38 - log: Record error in a Log object specified by `seterrcall`.
39
40 The default is not to change the current behavior.
41 divide : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
42 Treatment for division by zero.
43 over : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
44 Treatment for floating-point overflow.
45 under : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
46 Treatment for floating-point underflow.
47 invalid : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
48 Treatment for invalid floating-point operation.
49
50 Returns
51 -------
52 old_settings : dict
53 Dictionary containing the old settings.
54
55 See also
56 --------
57 seterrcall : Set a callback function for the 'call' mode.
58 geterr, geterrcall, errstate
59
60
61 Notes
62 -----
63 The floating-point exceptions are defined in the IEEE 754 standard [1]_:
64
65 - Division by zero: infinite result obtained from finite numbers.
66 - Overflow: result too large to be expressed.
67 - Underflow: result so close to zero that some precision
68 was lost.
69 - Invalid operation: result is not an expressible number, typically
70 indicates that a NaN was produced.
71
72 **Concurrency note:** see :ref:`fp_error_handling`
73
74 .. [1] https://en.wikipedia.org/wiki/IEEE_754
75
76 Examples
77 --------
78 >>> import numpy as np
79 >>> orig_settings = np.seterr(all='ignore') # seterr to known value
80 >>> np.int16(32000) * np.int16(3)
81 np.int16(30464)
82 >>> np.seterr(over='raise')
83 {'divide': 'ignore', 'over': 'ignore', 'under': 'ignore', 'invalid': 'ignore'}
84 >>> old_settings = np.seterr(all='warn', over='raise')
85 >>> np.int16(32000) * np.int16(3)
86 Traceback (most recent call last):
87 File "<stdin>", line 1, in <module>
88 FloatingPointError: overflow encountered in scalar multiply
89
90 >>> old_settings = np.seterr(all='print')
91 >>> np.geterr()
92 {'divide': 'print', 'over': 'print', 'under': 'print', 'invalid': 'print'}
93 >>> np.int16(32000) * np.int16(3)
94 np.int16(30464)
95 >>> np.seterr(**orig_settings) # restore original
96 {'divide': 'print', 'over': 'print', 'under': 'print', 'invalid': 'print'}
97
98 """
99
100 old = _get_extobj_dict()
101 # The errstate doesn't include call and bufsize, so pop them:
102 old.pop("call", None)
103 old.pop("bufsize", None)
104
105 extobj = _make_extobj(
106 all=all, divide=divide, over=over, under=under, invalid=invalid)
107 _extobj_contextvar.set(extobj)
108 return old
109
110
111@set_module('numpy')
112def geterr():
113 """
114 Get the current way of handling floating-point errors.
115
116 Returns
117 -------
118 res : dict
119 A dictionary with keys "divide", "over", "under", and "invalid",
120 whose values are from the strings "ignore", "print", "log", "warn",
121 "raise", and "call". The keys represent possible floating-point
122 exceptions, and the values define how these exceptions are handled.
123
124 See Also
125 --------
126 geterrcall, seterr, seterrcall
127
128 Notes
129 -----
130 For complete documentation of the types of floating-point exceptions and
131 treatment options, see `seterr`.
132
133 **Concurrency note:** see :doc:`/reference/routines.err`
134
135 Examples
136 --------
137 >>> import numpy as np
138 >>> np.geterr()
139 {'divide': 'warn', 'over': 'warn', 'under': 'ignore', 'invalid': 'warn'}
140 >>> np.arange(3.) / np.arange(3.) # doctest: +SKIP
141 array([nan, 1., 1.])
142 RuntimeWarning: invalid value encountered in divide
143
144 >>> oldsettings = np.seterr(all='warn', invalid='raise')
145 >>> np.geterr()
146 {'divide': 'warn', 'over': 'warn', 'under': 'warn', 'invalid': 'raise'}
147 >>> np.arange(3.) / np.arange(3.)
148 Traceback (most recent call last):
149 ...
150 FloatingPointError: invalid value encountered in divide
151 >>> oldsettings = np.seterr(**oldsettings) # restore original
152
153 """
154 res = _get_extobj_dict()
155 # The "geterr" doesn't include call and bufsize,:
156 res.pop("call", None)
157 res.pop("bufsize", None)
158 return res
159
160
161@set_module('numpy')
162def setbufsize(size):
163 """
164 Set the size of the buffer used in ufuncs.
165
166 .. versionchanged:: 2.0
167 The scope of setting the buffer is tied to the `numpy.errstate`
168 context. Exiting a ``with errstate():`` will also restore the bufsize.
169
170 Parameters
171 ----------
172 size : int
173 Size of buffer.
174
175 Returns
176 -------
177 bufsize : int
178 Previous size of ufunc buffer in bytes.
179
180 Notes
181 -----
182 **Concurrency note:** see :doc:`/reference/routines.err`
183
184 Examples
185 --------
186 When exiting a `numpy.errstate` context manager the bufsize is restored:
187
188 >>> import numpy as np
189 >>> with np.errstate():
190 ... np.setbufsize(4096)
191 ... print(np.getbufsize())
192 ...
193 8192
194 4096
195 >>> np.getbufsize()
196 8192
197
198 """
199 if size < 0:
200 raise ValueError("buffer size must be non-negative")
201 old = _get_extobj_dict()["bufsize"]
202 extobj = _make_extobj(bufsize=size)
203 _extobj_contextvar.set(extobj)
204 return old
205
206
207@set_module('numpy')
208def getbufsize():
209 """
210 Return the size of the buffer used in ufuncs.
211
212 Returns
213 -------
214 getbufsize : int
215 Size of ufunc buffer in bytes.
216
217 Notes
218 -----
219
220 **Concurrency note:** see :doc:`/reference/routines.err`
221
222
223 Examples
224 --------
225 >>> import numpy as np
226 >>> np.getbufsize()
227 8192
228
229 """
230 return _get_extobj_dict()["bufsize"]
231
232
233@set_module('numpy')
234def seterrcall(func):
235 """
236 Set the floating-point error callback function or log object.
237
238 There are two ways to capture floating-point error messages. The first
239 is to set the error-handler to 'call', using `seterr`. Then, set
240 the function to call using this function.
241
242 The second is to set the error-handler to 'log', using `seterr`.
243 Floating-point errors then trigger a call to the 'write' method of
244 the provided object.
245
246 Parameters
247 ----------
248 func : callable f(err, flag) or object with write method
249 Function to call upon floating-point errors ('call'-mode) or
250 object whose 'write' method is used to log such message ('log'-mode).
251
252 The call function takes two arguments. The first is a string describing
253 the type of error (such as "divide by zero", "overflow", "underflow",
254 or "invalid value"), and the second is the status flag. The flag is a
255 byte, whose four least-significant bits indicate the type of error, one
256 of "divide", "over", "under", "invalid"::
257
258 [0 0 0 0 divide over under invalid]
259
260 In other words, ``flags = divide + 2*over + 4*under + 8*invalid``.
261
262 If an object is provided, its write method should take one argument,
263 a string.
264
265 Returns
266 -------
267 h : callable, log instance or None
268 The old error handler.
269
270 See Also
271 --------
272 seterr, geterr, geterrcall
273
274 Notes
275 -----
276
277 **Concurrency note:** see :doc:`/reference/routines.err`
278
279 Examples
280 --------
281 Callback upon error:
282
283 >>> def err_handler(type, flag):
284 ... print("Floating point error (%s), with flag %s" % (type, flag))
285 ...
286
287 >>> import numpy as np
288
289 >>> orig_handler = np.seterrcall(err_handler)
290 >>> orig_err = np.seterr(all='call')
291
292 >>> np.array([1, 2, 3]) / 0.0
293 Floating point error (divide by zero), with flag 1
294 array([inf, inf, inf])
295
296 >>> np.seterrcall(orig_handler)
297 <function err_handler at 0x...>
298 >>> np.seterr(**orig_err)
299 {'divide': 'call', 'over': 'call', 'under': 'call', 'invalid': 'call'}
300
301 Log error message:
302
303 >>> class Log:
304 ... def write(self, msg):
305 ... print("LOG: %s" % msg)
306 ...
307
308 >>> log = Log()
309 >>> saved_handler = np.seterrcall(log)
310 >>> save_err = np.seterr(all='log')
311
312 >>> np.array([1, 2, 3]) / 0.0
313 LOG: Warning: divide by zero encountered in divide
314 array([inf, inf, inf])
315
316 >>> np.seterrcall(orig_handler)
317 <numpy.Log object at 0x...>
318 >>> np.seterr(**orig_err)
319 {'divide': 'log', 'over': 'log', 'under': 'log', 'invalid': 'log'}
320
321 """
322 old = _get_extobj_dict()["call"]
323 extobj = _make_extobj(call=func)
324 _extobj_contextvar.set(extobj)
325 return old
326
327
328@set_module('numpy')
329def geterrcall():
330 """
331 Return the current callback function used on floating-point errors.
332
333 When the error handling for a floating-point error (one of "divide",
334 "over", "under", or "invalid") is set to 'call' or 'log', the function
335 that is called or the log instance that is written to is returned by
336 `geterrcall`. This function or log instance has been set with
337 `seterrcall`.
338
339 Returns
340 -------
341 errobj : callable, log instance or None
342 The current error handler. If no handler was set through `seterrcall`,
343 ``None`` is returned.
344
345 See Also
346 --------
347 seterrcall, seterr, geterr
348
349 Notes
350 -----
351 For complete documentation of the types of floating-point exceptions and
352 treatment options, see `seterr`.
353
354 **Concurrency note:** see :ref:`fp_error_handling`
355
356 Examples
357 --------
358 >>> import numpy as np
359 >>> np.geterrcall() # we did not yet set a handler, returns None
360
361 >>> orig_settings = np.seterr(all='call')
362 >>> def err_handler(type, flag):
363 ... print("Floating point error (%s), with flag %s" % (type, flag))
364 >>> old_handler = np.seterrcall(err_handler)
365 >>> np.array([1, 2, 3]) / 0.0
366 Floating point error (divide by zero), with flag 1
367 array([inf, inf, inf])
368
369 >>> cur_handler = np.geterrcall()
370 >>> cur_handler is err_handler
371 True
372 >>> old_settings = np.seterr(**orig_settings) # restore original
373 >>> old_handler = np.seterrcall(None) # restore original
374
375 """
376 return _get_extobj_dict()["call"]
377
378
379class _unspecified:
380 pass
381
382
383_Unspecified = _unspecified()
384
385
386@set_module('numpy')
387class errstate:
388 """
389 errstate(**kwargs)
390
391 Context manager for floating-point error handling.
392
393 Using an instance of `errstate` as a context manager allows statements in
394 that context to execute with a known error handling behavior. Upon entering
395 the context the error handling is set with `seterr` and `seterrcall`, and
396 upon exiting it is reset to what it was before.
397
398 .. versionchanged:: 1.17.0
399 `errstate` is also usable as a function decorator, saving
400 a level of indentation if an entire function is wrapped.
401
402 .. versionchanged:: 2.0
403 `errstate` is now fully thread and asyncio safe, but may not be
404 entered more than once.
405 It is not safe to decorate async functions using ``errstate``.
406
407 Parameters
408 ----------
409 kwargs : {divide, over, under, invalid}
410 Keyword arguments. The valid keywords are the possible floating-point
411 exceptions. Each keyword should have a string value that defines the
412 treatment for the particular error. Possible values are
413 {'ignore', 'warn', 'raise', 'call', 'print', 'log'}.
414
415 See Also
416 --------
417 seterr, geterr, seterrcall, geterrcall
418
419 Notes
420 -----
421 For complete documentation of the types of floating-point exceptions and
422 treatment options, see `seterr`.
423
424 **Concurrency note:** see :ref:`fp_error_handling`
425
426 Examples
427 --------
428 >>> import numpy as np
429 >>> olderr = np.seterr(all='ignore') # Set error handling to known state.
430
431 >>> np.arange(3) / 0.
432 array([nan, inf, inf])
433 >>> with np.errstate(divide='ignore'):
434 ... np.arange(3) / 0.
435 array([nan, inf, inf])
436
437 >>> np.sqrt(-1)
438 np.float64(nan)
439 >>> with np.errstate(invalid='raise'):
440 ... np.sqrt(-1)
441 Traceback (most recent call last):
442 File "<stdin>", line 2, in <module>
443 FloatingPointError: invalid value encountered in sqrt
444
445 Outside the context the error handling behavior has not changed:
446
447 >>> np.geterr()
448 {'divide': 'ignore', 'over': 'ignore', 'under': 'ignore', 'invalid': 'ignore'}
449 >>> olderr = np.seterr(**olderr) # restore original state
450
451 """
452 __slots__ = (
453 "_all",
454 "_call",
455 "_divide",
456 "_invalid",
457 "_over",
458 "_token",
459 "_under",
460 )
461
462 def __init__(self, *, call=_Unspecified,
463 all=None, divide=None, over=None, under=None, invalid=None):
464 self._token = None
465 self._call = call
466 self._all = all
467 self._divide = divide
468 self._over = over
469 self._under = under
470 self._invalid = invalid
471
472 def __enter__(self):
473 # Note that __call__ duplicates much of this logic
474 if self._token is not None:
475 raise TypeError("Cannot enter `np.errstate` twice.")
476 if self._call is _Unspecified:
477 extobj = _make_extobj(
478 all=self._all, divide=self._divide, over=self._over,
479 under=self._under, invalid=self._invalid)
480 else:
481 extobj = _make_extobj(
482 call=self._call,
483 all=self._all, divide=self._divide, over=self._over,
484 under=self._under, invalid=self._invalid)
485
486 self._token = _extobj_contextvar.set(extobj)
487
488 def __exit__(self, *exc_info):
489 _extobj_contextvar.reset(self._token)
490
491 def __call__(self, func):
492 # We need to customize `__call__` compared to `ContextDecorator`
493 # because we must store the token per-thread so cannot store it on
494 # the instance (we could create a new instance for this).
495 # This duplicates the code from `__enter__`.
496 @functools.wraps(func)
497 def inner(*args, **kwargs):
498 if self._call is _Unspecified:
499 extobj = _make_extobj(
500 all=self._all, divide=self._divide, over=self._over,
501 under=self._under, invalid=self._invalid)
502 else:
503 extobj = _make_extobj(
504 call=self._call,
505 all=self._all, divide=self._divide, over=self._over,
506 under=self._under, invalid=self._invalid)
507
508 _token = _extobj_contextvar.set(extobj)
509 try:
510 # Call the original, decorated, function:
511 return func(*args, **kwargs)
512 finally:
513 _extobj_contextvar.reset(_token)
514
515 return inner