from typing import Any, Callable, Union
import pint
[docs]def always_iterable(obj, base_type=(str, bytes)):
"""
Ensure that the object it is passed is iterable.
- If ``obj`` is iterable, return an iterator over its items.
- If ``obj`` is not iterable, return a one-item iterable containing ``obj``.
- If ``obj`` is ``None``, return an empty iterable.
By default, binary and text strings are not considered iterable.
If ``base_type`` is set, objects for which ``isinstance(obj, base_type)``
returns ``True`` won't be considered iterable.
Set ``base_type`` to ``None`` to avoid any special handling and treat
objects Python considers iterable as iterable.
.. note::
Copied from :func:`more_itertools.always_iterable`.
"""
if obj is None:
return iter(())
if (base_type is not None) and isinstance(obj, base_type):
return iter((obj,))
try:
return iter(obj)
except TypeError:
return iter((obj,))
[docs]def ensure_units(
value: Any,
default_units: Union[pint.Unit, Callable],
convert: bool = False,
) -> pint.Quantity:
"""Ensure that a value is wrapped in a Pint quantity container.
:param value:
Value to ensure the wrapping of.
:param default_units:
Units to use to initialise the :class:`pint.Quantity` if ``value`` is
not a :class:`pint.Quantity`. A callable can be passed;
in this case, the applied units will be ``default_units()``.
:param convert:
If ``True``, ``value`` will also be converted to ``default_units`` if it
is a :class:`pint.Quantity`.
:returns:
Converted ``value``.
.. versionchanged:: 21.1.0
Relocated from ``pinttr.converters`` to ``pinttr.util``.
"""
if callable(default_units):
units = default_units()
else:
units = default_units
if not isinstance(units, pint.Unit):
raise TypeError("Argument 'units' must be a pint.Units or a UnitGenerator")
if isinstance(value, pint.Quantity):
if convert:
return value.to(units)
else:
return value
else:
return value * units
[docs]def units_compatible(unit1: pint.Unit, unit2: pint.Unit) -> bool:
"""
Check if two units are compatible. Accounts for angle units.
:param unit1:
First unit to check for compatibility.
:param unit2:
Second unit to check for compatibility.
:returns:
``True`` if ``unit1`` and ``unit2`` have the same dimensionality,
``False`` otherwise.
"""
return (1.0 * unit1 / unit2).unitless