"""
pint.errors
~~~~~~~~~~~
Functions and classes related to unit definitions and conversions.
:copyright: 2016 by Pint Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
OFFSET_ERROR_DOCS_HTML = "https://pint.readthedocs.io/en/latest/nonmult.html"
LOG_ERROR_DOCS_HTML = "https://pint.readthedocs.io/en/latest/nonmult.html"
def _file_prefix(filename=None, lineno=None):
if filename and lineno is not None:
return f"While opening {filename}, in line {lineno}: "
elif filename:
return f"While opening {filename}: "
elif lineno is not None:
return f"In line {lineno}: "
else:
return ""
class PintError(Exception):
"""Base exception for all Pint errors."""
class DefinitionSyntaxError(SyntaxError, PintError):
"""Raised when a textual definition has a syntax error."""
def __init__(self, msg, *, filename=None, lineno=None):
super().__init__(msg)
self.filename = filename
self.lineno = lineno
def __str__(self):
return _file_prefix(self.filename, self.lineno) + str(self.args[0])
@property
def __dict__(self):
# SyntaxError.filename and lineno are special fields that don't appear in
# the __dict__. This messes up pickling and deepcopy, as well
# as any other Python library that expects sane behaviour.
return {"filename": self.filename, "lineno": self.lineno}
def __reduce__(self):
return DefinitionSyntaxError, self.args, self.__dict__
class RedefinitionError(ValueError, PintError):
"""Raised when a unit or prefix is redefined."""
def __init__(self, name, definition_type, *, filename=None, lineno=None):
super().__init__(name, definition_type)
self.filename = filename
self.lineno = lineno
def __str__(self):
msg = f"Cannot redefine '{self.args[0]}' ({self.args[1]})"
return _file_prefix(self.filename, self.lineno) + msg
def __reduce__(self):
return RedefinitionError, self.args, self.__dict__
[docs]class UndefinedUnitError(AttributeError, PintError):
"""Raised when the units are not defined in the unit registry."""
def __init__(self, *unit_names):
if len(unit_names) == 1 and not isinstance(unit_names[0], str):
unit_names = unit_names[0]
super().__init__(*unit_names)
def __str__(self):
if len(self.args) == 1:
return f"'{self.args[0]}' is not defined in the unit registry"
return f"{self.args} are not defined in the unit registry"
class PintTypeError(TypeError, PintError):
pass
[docs]class DimensionalityError(PintTypeError):
"""Raised when trying to convert between incompatible units."""
def __init__(self, units1, units2, dim1="", dim2="", *, extra_msg=""):
super().__init__()
self.units1 = units1
self.units2 = units2
self.dim1 = dim1
self.dim2 = dim2
self.extra_msg = extra_msg
def __str__(self):
if self.dim1 or self.dim2:
dim1 = f" ({self.dim1})"
dim2 = f" ({self.dim2})"
else:
dim1 = ""
dim2 = ""
return (
f"Cannot convert from '{self.units1}'{dim1} to "
f"'{self.units2}'{dim2}{self.extra_msg}"
)
def __reduce__(self):
return TypeError.__new__, (DimensionalityError,), self.__dict__
class OffsetUnitCalculusError(PintTypeError):
"""Raised on ambiguous operations with offset units."""
def __str__(self):
return (
"Ambiguous operation with offset unit (%s)."
% ", ".join(str(u) for u in self.args)
+ " See "
+ OFFSET_ERROR_DOCS_HTML
+ " for guidance."
)
class LogarithmicUnitCalculusError(PintTypeError):
"""Raised on inappropriate operations with logarithmic units."""
def __str__(self):
return (
"Ambiguous operation with logarithmic unit (%s)."
% ", ".join(str(u) for u in self.args)
+ " See "
+ LOG_ERROR_DOCS_HTML
+ " for guidance."
)
class UnitStrippedWarning(UserWarning, PintError):
pass