Skip to content
Open

typing #2123

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions .mypy

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def _unpickle_measurement(cls, *args):
return _unpickle(application_registry.Measurement, *args)


def set_application_registry(registry):
def set_application_registry(registry) -> None:
"""Set the application registry, which is used for unpickling operations
and when invoking pint.Quantity or pint.Unit directly.

Expand Down
8 changes: 7 additions & 1 deletion pint/_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,10 @@


class Handler(Protocol):
def __getitem__(self, item: type[T]) -> Callable[[T], None]: ...
@overload
def __getitem__(self, Never, /) -> Never:
...

@overload
def __getitem__(self, item: type[T]) -> Callable[[T], None]:
...
185 changes: 185 additions & 0 deletions pint/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,191 @@ def _inner(*args: Any, **kwargs: Any) -> NoReturn:
return _inner


# TODO: remove this warning after v0.10
class BehaviorChangeWarning(UserWarning):
pass


try:
from uncertainties import UFloat, ufloat

unp = None

HAS_UNCERTAINTIES = True
except ImportError:
UFloat = ufloat = unp = None

HAS_UNCERTAINTIES = False


try:
import numpy as np
from numpy import datetime64 as np_datetime64
from numpy import ndarray

HAS_NUMPY = True
NUMPY_VER = np.__version__
if HAS_UNCERTAINTIES:
from uncertainties import unumpy as unp

NUMERIC_TYPES = (Number, Decimal, ndarray, np.number, UFloat)
else:
NUMERIC_TYPES = (Number, Decimal, ndarray, np.number)

def _to_magnitude(
value, force_ndarray: bool = False, force_ndarray_like: bool = False
):
if isinstance(value, (dict, bool)) or value is None:
raise TypeError(f"Invalid magnitude for Quantity: {value!r}")
elif isinstance(value, str) and value == "":
raise ValueError("Quantity magnitude cannot be an empty string.")
elif isinstance(value, (list, tuple)):
return np.asarray(value)
elif HAS_UNCERTAINTIES:
from pint.facets.measurement.objects import Measurement

if isinstance(value, Measurement):
return ufloat(value.value, value.error)
if force_ndarray or (
force_ndarray_like and not is_duck_array_type(type(value))
):
return np.asarray(value)
return value

def _test_array_function_protocol() -> bool:
# Test if the __array_function__ protocol is enabled
try:

class FakeArray:
def __array_function__(self, *args, **kwargs) -> None:
return

np.concatenate([FakeArray()])
return True
except ValueError:
return False

HAS_NUMPY_ARRAY_FUNCTION = _test_array_function_protocol()

NP_NO_VALUE = np._NoValue

except ImportError:
np = None

class ndarray:
pass

class np_datetime64:
pass

HAS_NUMPY = False
NUMPY_VER = "0"
NUMERIC_TYPES = (Number, Decimal)
HAS_NUMPY_ARRAY_FUNCTION = False
NP_NO_VALUE = None

def _to_magnitude(
value, force_ndarray: bool = False, force_ndarray_like: bool = False
):
if force_ndarray or force_ndarray_like:
raise ValueError(
"Cannot force to ndarray or ndarray-like when NumPy is not present."
)
elif isinstance(value, (dict, bool)) or value is None:
raise TypeError(f"Invalid magnitude for Quantity: {value!r}")
elif isinstance(value, str) and value == "":
raise ValueError("Quantity magnitude cannot be an empty string.")
elif isinstance(value, (list, tuple)):
raise TypeError(
"lists and tuples are valid magnitudes for "
"Quantity only when NumPy is present."
)
elif HAS_UNCERTAINTIES:
from pint.facets.measurement.objects import Measurement

if isinstance(value, Measurement):
return ufloat(value.value, value.error)
return value


try:
from babel import Locale
from babel import units as babel_units

babel_parse = Locale.parse

HAS_BABEL = hasattr(babel_units, "format_unit")
except ImportError:
HAS_BABEL = False

babel_parse = missing_dependency("Babel") # noqa: F811 # type:ignore
babel_units = babel_parse

try:
import mip

mip_model = mip.model
mip_Model = mip.Model
mip_INF = mip.INF
mip_INTEGER = mip.INTEGER
mip_xsum = mip.xsum
mip_OptimizationStatus = mip.OptimizationStatus

HAS_MIP = True
except ImportError:
HAS_MIP = False

mip_missing = missing_dependency("mip")
mip_model = mip_missing
mip_Model = mip_missing
mip_INF = mip_missing
mip_INTEGER = mip_missing
mip_xsum = mip_missing
mip_OptimizationStatus = mip_missing

# Defines Logarithm and Exponential for Logarithmic Converter
if HAS_NUMPY:
from numpy import (
exp, # noqa: F401
log, # noqa: F401
)
else:
from math import (
exp, # noqa: F401
log, # noqa: F401
)


# Define location of pint.Quantity in NEP-13 type cast hierarchy by defining upcast
# types using guarded imports

try:
from dask import array as dask_array
from dask.base import compute, persist, visualize
except ImportError:
compute, persist, visualize = None, None, None
dask_array = None


# TODO: merge with upcast_type_map

#: List upcast type names
upcast_type_names = (
"pint_pandas.pint_array.PintArray",
"xarray.core.dataarray.DataArray",
"xarray.core.dataset.Dataset",
"xarray.core.variable.Variable",
"pandas.core.series.Series",
"pandas.core.frame.DataFrame",
"pandas.Series",
"pandas.DataFrame",
"xarray.core.dataarray.DataArray",
)

#: Map type name to the actual type (for upcast types).
upcast_type_map: Mapping[str, type | None] = {k: None for k in upcast_type_names}


def fully_qualified_name(t: type) -> str:
"""Return the fully qualified name of a type."""
module = t.__module__
Expand Down
2 changes: 1 addition & 1 deletion pint/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def to_reference(self, value: Magnitude, inplace: bool = False) -> Magnitude:
def from_reference(self, value: Magnitude, inplace: bool = False) -> Magnitude:
return value

def __init_subclass__(cls, **kwargs: Any):
def __init_subclass__(cls, **kwargs: Any) -> None:
# Get constructor parameters
super().__init_subclass__(**kwargs)
cls._subclasses.append(cls)
Expand Down
8 changes: 4 additions & 4 deletions pint/delegates/formatter/full.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class FullFormatter(BaseFormatter):

locale: Locale | None = None

def __init__(self, registry: UnitRegistry | None = None):
def __init__(self, registry: UnitRegistry | None = None) -> None:
super().__init__(registry)

self._formatters = {}
Expand Down Expand Up @@ -131,7 +131,7 @@ def format_unit(
unit: PlainUnit | Iterable[tuple[str, Any]],
uspec: str = "",
sort_func: SortFunc | None = None,
**babel_kwds: Unpack[BabelKwds],
**babel_kwds: Unpack[BabelKwds] | Unpack[dict[str, None]],
) -> str:
uspec = uspec or self.default_format
sort_func = sort_func or self.default_sort_func
Expand All @@ -143,7 +143,7 @@ def format_quantity(
self,
quantity: PlainQuantity[MagnitudeT],
spec: str = "",
**babel_kwds: Unpack[BabelKwds],
**babel_kwds: Unpack[BabelKwds] | Unpack[dict[str, None]],
) -> str:
spec = spec or self.default_format
# If Compact is selected, do it at the beginning
Expand Down Expand Up @@ -180,7 +180,7 @@ def format_measurement(
self,
measurement: Measurement,
meas_spec: str = "",
**babel_kwds: Unpack[BabelKwds],
**babel_kwds: Unpack[BabelKwds] | Unpack[dict[str, None]],
) -> str:
meas_spec = meas_spec or self.default_format
# If Compact is selected, do it at the beginning
Expand Down
2 changes: 1 addition & 1 deletion pint/delegates/formatter/plain.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@


class BaseFormatter:
def __init__(self, registry: UnitRegistry | None = None):
def __init__(self, registry: UnitRegistry | None = None) -> None:
self._registry = registry


Expand Down
2 changes: 1 addition & 1 deletion pint/delegates/txt_defparser/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class DefinitionSyntaxError(errors.DefinitionSyntaxError, fp.ParsingError):

msg: str

def __init__(self, msg: str, location: str = ""):
def __init__(self, msg: str, location: str = "") -> None:
self.msg = msg
self.location = location

Expand Down
6 changes: 4 additions & 2 deletions pint/delegates/txt_defparser/defparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class _PintParser(fp.Parser[PintRootBlock, ParserConfig]):

_diskcache: fc.DiskCache | None

def __init__(self, config: ParserConfig, *args: ty.Any, **kwargs: ty.Any):
def __init__(self, config: ParserConfig, *args: ty.Any, **kwargs: ty.Any) -> None:
self._diskcache = kwargs.pop("diskcache", None)
super().__init__(config, *args, **kwargs)

Expand All @@ -68,7 +68,9 @@ class DefParser:
plain.CommentDefinition,
)

def __init__(self, default_config: ParserConfig, diskcache: fc.DiskCache):
def __init__(
self, default_config: ParserConfig, diskcache: fc.DiskCache | None
) -> None:
self._default_config = default_config
self._diskcache = diskcache

Expand Down
26 changes: 13 additions & 13 deletions pint/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ class DefinitionError(ValueError, PintError):
definition_type: type
msg: str

def __init__(self, name: str, definition_type: type, msg: str):
def __init__(self, name: str, definition_type: type, msg: str) -> None:
self.name = name
self.definition_type = definition_type
self.msg = msg

def __str__(self):
def __str__(self) -> str:
msg = f"Cannot define '{self.name}' ({self.definition_type}): {self.msg}"
return msg

Expand All @@ -109,10 +109,10 @@ class DefinitionSyntaxError(ValueError, PintError):

msg: str

def __init__(self, msg: str):
def __init__(self, msg: str) -> None:
self.msg = msg

def __str__(self):
def __str__(self) -> str:
return self.msg

def __reduce__(self):
Expand All @@ -125,11 +125,11 @@ class RedefinitionError(ValueError, PintError):
name: str
definition_type: type

def __init__(self, name: str, definition_type: type):
def __init__(self, name: str, definition_type: type) -> None:
self.name = name
self.definition_type = definition_type

def __str__(self):
def __str__(self) -> str:
msg = f"Cannot redefine '{self.name}' ({self.definition_type})"
return msg

Expand All @@ -142,13 +142,13 @@ class UndefinedUnitError(AttributeError, PintError):

unit_names: tuple[str, ...]

def __init__(self, unit_names: str | ty.Iterable[str]):
def __init__(self, unit_names: str | ty.Iterable[str]) -> None:
if isinstance(unit_names, str):
self.unit_names = (unit_names,)
else:
self.unit_names = tuple(unit_names)

def __str__(self):
def __str__(self) -> str:
if len(self.unit_names) == 1:
return f"'{tuple(self.unit_names)[0]}' is not defined in the unit registry"
return f"{tuple(self.unit_names)} are not defined in the unit registry"
Expand Down Expand Up @@ -184,7 +184,7 @@ def __init__(
self.dim2 = dim2
self.extra_msg = extra_msg

def __str__(self):
def __str__(self) -> str:
if self.dim1 or self.dim2:
dim1 = f" ({self.dim1})"
dim2 = f" ({self.dim2})"
Expand Down Expand Up @@ -222,7 +222,7 @@ def yield_units(self):
if self.units2:
yield self.units2

def __str__(self):
def __str__(self) -> str:
return (
"Ambiguous operation with offset unit (%s)."
% ", ".join(str(u) for u in self.yield_units())
Expand Down Expand Up @@ -250,7 +250,7 @@ def yield_units(self):
if self.units2:
yield self.units2

def __str__(self):
def __str__(self) -> str:
return (
"Ambiguous operation with logarithmic unit (%s)."
% ", ".join(str(u) for u in self.yield_units())
Expand All @@ -266,7 +266,7 @@ def __reduce__(self):
class UnitStrippedWarning(UserWarning, PintError):
msg: str

def __init__(self, msg: str):
def __init__(self, msg: str) -> None:
self.msg = msg

def __reduce__(self):
Expand All @@ -280,7 +280,7 @@ class UnexpectedScaleInContainer(Exception):
class UndefinedBehavior(UserWarning, PintError):
msg: str

def __init__(self, msg: str):
def __init__(self, msg: str) -> None:
self.msg = msg

def __reduce__(self):
Expand Down
Loading
Loading