From 86f8abbd2c46806f5ff0efd6f53c56b2e21ea6e8 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Wed, 6 Apr 2022 11:05:07 +0100 Subject: [PATCH 1/2] seterr --- cf/data/data.py | 200 ---------------------------------- cf/data/mixin/deprecations.py | 149 +++++++++++++++++++++++++ cf/functions.py | 73 ++++++++----- 3 files changed, 197 insertions(+), 225 deletions(-) diff --git a/cf/data/data.py b/cf/data/data.py index f9c2d3bb50..a72636358a 100644 --- a/cf/data/data.py +++ b/cf/data/data.py @@ -183,30 +183,6 @@ def wrapper(*args, **kwargs): _year_length = 365.242198781 _month_length = _year_length / 12 -# -------------------------------------------------------------------- -# _seterr = How floating-point errors in the results of arithmetic -# operations are handled. These defaults are those of -# numpy 1.10.1. -# -------------------------------------------------------------------- -_seterr = { - "divide": "warn", - "invalid": "warn", - "over": "warn", - "under": "ignore", -} - -# -------------------------------------------------------------------- -# _seterr_raise_to_ignore = As _seterr but with any values of 'raise' -# changed to 'ignore'. -# -------------------------------------------------------------------- -_seterr_raise_to_ignore = _seterr.copy() - - -for key, value in _seterr.items(): - if value == "raise": - _seterr_raise_to_ignore[key] = "ignore" -# --- End: for - # -------------------------------------------------------------------- # _mask_fpe[0] = Whether or not to automatically set # FloatingPointError exceptions to masked values in @@ -6566,182 +6542,6 @@ def mask_fpe(*arg): return old - @staticmethod - def seterr(all=None, divide=None, over=None, under=None, invalid=None): - """Set how floating-point errors in the results of arithmetic - operations are handled. - - The options for handling floating-point errors are: - - ============ ======================================================== - Treatment Action - ============ ======================================================== - ``'ignore'`` Take no action. Allows invalid values to occur in the - result data array. - - ``'warn'`` Print a `RuntimeWarning` (via the Python `warnings` - module). Allows invalid values to occur in the result - data array. - - ``'raise'`` Raise a `FloatingPointError` exception. - ============ ======================================================== - - The different types of floating-point errors are: - - ================= ================================= ================= - Error Description Default treatment - ================= ================================= ================= - Division by zero Infinite result obtained from ``'warn'`` - finite numbers. - - Overflow Result too large to be expressed. ``'warn'`` - - Invalid operation Result is not an expressible ``'warn'`` - number, typically indicates that - a NaN was produced. - - Underflow Result so close to zero that some ``'ignore'`` - precision was lost. - ================= ================================= ================= - - Note that operations on integer scalar types (such as int16) are - handled like floating point, and are affected by these settings. - - If called without any arguments then the current behaviour is - returned. - - .. seealso:: `cf.Data.mask_fpe`, `mask_invalid` - - :Parameters: - - all: `str`, optional - Set the treatment for all types of floating-point errors - at once. The default is not to change the current - behaviour. - - divide: `str`, optional - Set the treatment for division by zero. The default is not - to change the current behaviour. - - over: `str`, optional - Set the treatment for floating-point overflow. The default - is not to change the current behaviour. - - under: `str`, optional - Set the treatment for floating-point underflow. The - default is not to change the current behaviour. - - invalid: `str`, optional - Set the treatment for invalid floating-point - operation. The default is not to change the current - behaviour. - - :Returns: - - `dict` - The behaviour prior to the change, or the current - behaviour if no new values are specified. - - **Examples:** - - Set treatment for all types of floating-point errors to - ``'raise'`` and then reset to the previous behaviours: - - >>> cf.Data.seterr() - {'divide': 'warn', 'invalid': 'warn', 'over': 'warn', 'under': 'ignore'} - >>> old = cf.Data.seterr('raise') - >>> cf.Data.seterr(**old) - {'divide': 'raise', 'invalid': 'raise', 'over': 'raise', 'under': 'raise'} - >>> cf.Data.seterr() - {'divide': 'warn', 'invalid': 'warn', 'over': 'warn', 'under': 'ignore'} - - Set the treatment of division by zero to ``'ignore'`` and overflow - to ``'warn'`` without changing the treatment of underflow and - invalid operation: - - >>> cf.Data.seterr(divide='ignore', over='warn') - {'divide': 'warn', 'invalid': 'warn', 'over': 'warn', 'under': 'ignore'} - >>> cf.Data.seterr() - {'divide': 'ignore', 'invalid': 'warn', 'over': 'ignore', 'under': 'ignore'} - - Some examples with data arrays: - - >>> d = cf.Data([0., 1]) - >>> e = cf.Data([1., 2]) - - >>> old = cf.Data.seterr('ignore') - >>> e/d - - >>> e**12345 - - - >>> cf.Data.seterr(divide='warn') - {'divide': 'ignore', 'invalid': 'ignore', 'over': 'ignore', 'under': 'ignore'} - >>> e/d - RuntimeWarning: divide by zero encountered in divide - - >>> e**12345 - - - >>> old = cf.Data.mask_fpe(False) - >>> cf.Data.seterr(over='raise') - {'divide': 'warn', 'invalid': 'ignore', 'over': 'ignore', 'under': 'ignore'} - >>> e/d - RuntimeWarning: divide by zero encountered in divide - - >>> e**12345 - FloatingPointError: overflow encountered in power - - >>> cf.Data.mask_fpe(True) - False - >>> cf.Data.seterr(divide='ignore') - {'divide': 'warn', 'invalid': 'ignore', 'over': 'raise', 'under': 'ignore'} - >>> e/d - - >>> e**12345 - - - """ - old = _seterr.copy() - - if all: - _seterr.update( - {"divide": all, "invalid": all, "under": all, "over": all} - ) - if all == "raise": - _seterr_raise_to_ignore.update( - { - "divide": "ignore", - "invalid": "ignore", - "under": "ignore", - "over": "ignore", - } - ) - - else: - if divide: - _seterr["divide"] = divide - if divide == "raise": - _seterr_raise_to_ignore["divide"] = "ignore" - - if over: - _seterr["over"] = over - if over == "raise": - _seterr_raise_to_ignore["over"] = "ignore" - - if under: - _seterr["under"] = under - if under == "raise": - _seterr_raise_to_ignore["under"] = "ignore" - - if invalid: - _seterr["invalid"] = invalid - if invalid == "raise": - _seterr_raise_to_ignore["invalid"] = "ignore" - # --- End: if - - return old - # `arctan2`, AT2 seealso @daskified(_DASKIFIED_VERBOSE) @_inplace_enabled(default=False) diff --git a/cf/data/mixin/deprecations.py b/cf/data/mixin/deprecations.py index 34c910c62c..8671e4d777 100644 --- a/cf/data/mixin/deprecations.py +++ b/cf/data/mixin/deprecations.py @@ -1,6 +1,7 @@ from ...functions import ( _DEPRECATION_ERROR_ATTRIBUTE, _DEPRECATION_ERROR_METHOD, + DeprecationError, ) @@ -346,3 +347,151 @@ def partition_boundaries(self): _DEPRECATION_ERROR_METHOD( "TODODASK - consider using 'chunks' instead" ) # pragma: no cover + + @staticmethod + def seterr(all=None, divide=None, over=None, under=None, invalid=None): + """Set how floating-point errors in the results of arithmetic + operations are handled. + + Deprecated at version TODODASK. It is currently not possible + to control how floating-point errors are handled, due to the + use of `dask` for handling all array manipulations. This may + change in the future (see + https://github.com/dask/dask/issues/3245 for more details). + + The options for handling floating-point errors are: + + ============ ======================================================== + Treatment Action + ============ ======================================================== + ``'ignore'`` Take no action. Allows invalid values to occur in the + result data array. + + ``'warn'`` Print a `RuntimeWarning` (via the Python `warnings` + module). Allows invalid values to occur in the result + data array. + + ``'raise'`` Raise a `FloatingPointError` exception. + ============ ======================================================== + + The different types of floating-point errors are: + + ================= ================================= ================= + Error Description Default treatment + ================= ================================= ================= + Division by zero Infinite result obtained from ``'warn'`` + finite numbers. + + Overflow Result too large to be expressed. ``'warn'`` + + Invalid operation Result is not an expressible ``'warn'`` + number, typically indicates that + a NaN was produced. + + Underflow Result so close to zero that some ``'ignore'`` + precision was lost. + ================= ================================= ================= + + Note that operations on integer scalar types (such as int16) are + handled like floating point, and are affected by these settings. + + If called without any arguments then the current behaviour is + returned. + + .. seealso:: `cf.Data.mask_fpe`, `mask_invalid` + + :Parameters: + + all: `str`, optional + Set the treatment for all types of floating-point errors + at once. The default is not to change the current + behaviour. + + divide: `str`, optional + Set the treatment for division by zero. The default is not + to change the current behaviour. + + over: `str`, optional + Set the treatment for floating-point overflow. The default + is not to change the current behaviour. + + under: `str`, optional + Set the treatment for floating-point underflow. The + default is not to change the current behaviour. + + invalid: `str`, optional + Set the treatment for invalid floating-point + operation. The default is not to change the current + behaviour. + + :Returns: + + `dict` + The behaviour prior to the change, or the current + behaviour if no new values are specified. + + **Examples:** + + Set treatment for all types of floating-point errors to + ``'raise'`` and then reset to the previous behaviours: + + >>> cf.Data.seterr() + {'divide': 'warn', 'invalid': 'warn', 'over': 'warn', 'under': 'ignore'} + >>> old = cf.Data.seterr('raise') + >>> cf.Data.seterr(**old) + {'divide': 'raise', 'invalid': 'raise', 'over': 'raise', 'under': 'raise'} + >>> cf.Data.seterr() + {'divide': 'warn', 'invalid': 'warn', 'over': 'warn', 'under': 'ignore'} + + Set the treatment of division by zero to ``'ignore'`` and overflow + to ``'warn'`` without changing the treatment of underflow and + invalid operation: + + >>> cf.Data.seterr(divide='ignore', over='warn') + {'divide': 'warn', 'invalid': 'warn', 'over': 'warn', 'under': 'ignore'} + >>> cf.Data.seterr() + {'divide': 'ignore', 'invalid': 'warn', 'over': 'ignore', 'under': 'ignore'} + + Some examples with data arrays: + + >>> d = cf.Data([0., 1]) + >>> e = cf.Data([1., 2]) + + >>> old = cf.Data.seterr('ignore') + >>> e/d + + >>> e**12345 + + + >>> cf.Data.seterr(divide='warn') + {'divide': 'ignore', 'invalid': 'ignore', 'over': 'ignore', 'under': 'ignore'} + >>> e/d + RuntimeWarning: divide by zero encountered in divide + + >>> e**12345 + + + >>> old = cf.Data.mask_fpe(False) + >>> cf.Data.seterr(over='raise') + {'divide': 'warn', 'invalid': 'ignore', 'over': 'ignore', 'under': 'ignore'} + >>> e/d + RuntimeWarning: divide by zero encountered in divide + + >>> e**12345 + FloatingPointError: overflow encountered in power + + >>> cf.Data.mask_fpe(True) + False + >>> cf.Data.seterr(divide='ignore') + {'divide': 'warn', 'invalid': 'ignore', 'over': 'raise', 'under': 'ignore'} + >>> e/d + + >>> e**12345 + + + """ + raise DeprecationError( + "Data method 'seterr' has been deprecated at version TODODASK " + "and is not available.\n\n" + "It is currently not possible to control how floating-point errors are handled, due to the use of `dask` for handling all array manipulations. This may change in the future (see https://github.com/dask/dask/issues/3245 for more details)." + ) diff --git a/cf/functions.py b/cf/functions.py index ebb12c20fa..a7c085da66 100644 --- a/cf/functions.py +++ b/cf/functions.py @@ -13,7 +13,6 @@ from collections.abc import Iterable from itertools import product from marshal import dumps -from math import ceil as math_ceil from numbers import Integral from os import getpid, listdir, mkdir from os.path import abspath as _os_path_abspath @@ -25,9 +24,9 @@ import cfdm import netCDF4 +import numpy as np from dask import config from dask.utils import parse_bytes -import numpy as np from numpy import __file__ as _numpy__file__ from numpy import __version__ as _numpy__version__ from numpy import all as _numpy_all @@ -3108,10 +3107,13 @@ def _DEPRECATION_ERROR_ARG( version="3.0.0", removed_at="4.0.0", ): + if removed_at: + removed_at = f" and will be removed at version {removed_at}" + raise DeprecationError( f"Argument {arg!r} of method '{instance.__class__.__name__}.{method}' " - f"has been deprecated at version {version} and is no longer available " - f"and will be removed at version {removed_at}. {message}" + f"has been deprecated at version {version} and is no longer available" + f"{removed_at}. {message}" ) @@ -3125,6 +3127,9 @@ def _DEPRECATION_ERROR_FUNCTION_KWARGS( version="3.0.0", removed_at="4.0.0", ): + if removed_at: + removed_at = f" and will be removed at version {removed_at}" + # Unsafe to set mutable '{}' as default in the func signature. if kwargs is None: # distinguish from falsy '{}' kwargs = {} @@ -3138,8 +3143,8 @@ def _DEPRECATION_ERROR_FUNCTION_KWARGS( for key in kwargs.keys(): raise DeprecationError( f"Keyword {key!r} of function '{func}' has been deprecated at " - f"version {version} and is no longer available and will be " - f"removed at version {removed_at}. {message}" + f"version {version} and is no longer available{removed_at}. " + f"{message}" ) @@ -3157,6 +3162,9 @@ def _DEPRECATION_ERROR_KWARGS( version="3.0.0", removed_at="4.0.0", ): + if removed_at: + removed_at = f" and will be removed at version {removed_at}" + # Unsafe to set mutable '{}' as default in the func signature. if kwargs is None: # distinguish from falsy '{}' kwargs = {} @@ -3170,8 +3178,8 @@ def _DEPRECATION_ERROR_KWARGS( raise DeprecationError( f"Keyword {key!r} of method " f"'{instance.__class__.__name__}.{method}' has been deprecated " - f"at version {version} and is no longer available and will be " - f"removed at version {removed_at}. {message}" + f"at version {version} and is no longer available{removed_at}. " + f"{message}" ) @@ -3184,31 +3192,38 @@ def _DEPRECATION_ERROR_KWARG_VALUE( version="3.0.0", removed_at="4.0.0", ): + if removed_at: + removed_at = f" and will be removed at version {removed_at}" + raise DeprecationError( f"Value {value!r} of keyword {kwarg!r} of method " f"'{instance.__class__.__name__}.{method}' has been deprecated at " - f"version {version} and is no longer available and will be removed " - f"at version {removed_at}. {message}" + f"version {version} and is no longer available{removed_at}. {message}" ) def _DEPRECATION_ERROR_METHOD( - instance, method, message="", version="3.0.0", removed_at="4.0.0" + instance, method, message="", version="3.0.0", removed_at="" ): + if removed_at: + removed_at = f" and will be removed at version {removed_at}" + raise DeprecationError( f"{instance.__class__.__name__} method {method!r} has been deprecated " - f"at version {version} and is no longer available and will be " - f"removed at version {removed_at}. {message}" + f"at version {version} and is no longer available{removed_at}. " + f"{message}" ) def _DEPRECATION_ERROR_ATTRIBUTE( - instance, attribute, message="", version="3.0.0", removed_at="4.0.0" + instance, attribute, message="", version="3.0.0", removed_at="" ): + if removed_at: + removed_at = f" and will be removed at version {removed_at}" + warnings.warn( f"{instance.__class__.__name__} attribute {attribute!r} has been " - f"deprecated at version {version} and will be removed at version " - f"{removed_at}. {message}", + f"deprecated at version {version}{removed_at}. {message}", DeprecationWarning, ) @@ -3216,35 +3231,44 @@ def _DEPRECATION_ERROR_ATTRIBUTE( def _DEPRECATION_ERROR_FUNCTION( func, message="", version="3.0.0", removed_at="4.0.0" ): + if removed_at: + removed_at = f" and will be removed at version {removed_at}" + raise DeprecationError( f"Function {func!r} has been deprecated at version {version} and is " - f"no longer available and will be removed at version {removed_at}. " - f"{message}" + f"no longer available{removed_at}. {message}" ) def _DEPRECATION_ERROR_CLASS( cls, message="", version="3.0.0", removed_at="4.0.0" ): + if removed_at: + removed_at = f" and will be removed at version {removed_at}" + raise DeprecationError( f"Class {cls!r} has been deprecated at version {version} and is no " - f"longer available and will be removed at version {removed_at}. " - f"{message}" + f"longer available{removed_at}. {message}" ) def _DEPRECATION_WARNING_METHOD( instance, method, message="", new=None, version="3.0.0", removed_at="4.0.0" ): + if removed_at: + removed_at = f" and will be removed at version {removed_at}" + warnings.warn( f"{instance.__class__.__name__} method {method!r} has been deprecated " - f"at version {version} and will be removed at version {removed_at}. " - f"{message}", + f"at version {version}{removed_at}. {message}", DeprecationWarning, ) def _DEPRECATION_ERROR_DICT(message="", version="3.0.0", removed_at="4.0.0"): + if removed_at: + removed_at = f" and will be removed at version {removed_at}" + raise DeprecationError( "Use of a 'dict' to identify constructs has been deprecated at " f"version {version} and is no longer available and will be removed " @@ -3255,9 +3279,8 @@ def _DEPRECATION_ERROR_DICT(message="", version="3.0.0", removed_at="4.0.0"): def _DEPRECATION_ERROR_SEQUENCE(instance, version="3.0.0", removed_at="4.0.0"): raise DeprecationError( f"Use of a {instance.__class__.__name__!r} to identify constructs " - f"has been deprecated at version {version} and is no longer available " - "and will be removed at version {removed_at}. " - "Use the * operator to unpack the arguments instead." + f"has been deprecated at version {version} and is no longer available" + f"{removed_at}. Use the * operator to unpack the arguments instead." ) From 35fab39d37c693ca24ba59af864b3179f7836d6a Mon Sep 17 00:00:00 2001 From: David Hassell Date: Fri, 22 Apr 2022 08:49:36 +0100 Subject: [PATCH 2/2] Linting Co-authored-by: Sadie L. Bartholomew --- cf/data/mixin/deprecations.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cf/data/mixin/deprecations.py b/cf/data/mixin/deprecations.py index 1db351a172..cf1a80cf14 100644 --- a/cf/data/mixin/deprecations.py +++ b/cf/data/mixin/deprecations.py @@ -533,5 +533,8 @@ def seterr(all=None, divide=None, over=None, under=None, invalid=None): raise DeprecationError( "Data method 'seterr' has been deprecated at version TODODASK " "and is not available.\n\n" - "It is currently not possible to control how floating-point errors are handled, due to the use of `dask` for handling all array manipulations. This may change in the future (see https://github.com/dask/dask/issues/3245 for more details)." + "It is currently not possible to control how floating-point errors " + "are handled, due to the use of `dask` for handling all array " + "manipulations. This may change in the future (see " + "https://github.com/dask/dask/issues/3245 for more details)." )