metricengine.units
- class metricengine.units.Percent[source]
Bases:
RatioUnit for percentage values (inherits from Ratio).
- metricengine.units.currency_unit(code)[source]
Create a Money unit class with a specific currency code.
- metricengine.units.USD
alias of
CurrencyMoney
- metricengine.units.EUR
alias of
CurrencyMoney
- metricengine.units.GBP
alias of
CurrencyMoney
- metricengine.units.ZAR
alias of
CurrencyMoney
- class metricengine.units.NewUnit(category, code)[source]
Bases:
objectGeneric unit with category and code dimensions.
A unit represents a measurement dimension with both a category (the type of measurement) and a specific code (the particular unit within that category). Units are immutable and hashable, making them suitable for use as dictionary keys in conversion registries.
- category
The category of measurement (e.g., “Money”, “Quantity”, “Percent”)
- code
The specific unit code within the category (e.g., “USD”, “kg”, “ratio”)
Examples
>>> usd = NewUnit("Money", "USD") >>> str(usd) 'Money[USD]' >>> kg = NewUnit("Quantity", "kg") >>> str(kg) 'Quantity[kg]' >>> ratio = NewUnit("Percent", "ratio") >>> str(ratio) 'Percent[ratio]'
- metricengine.units.MoneyUnit(code)[source]
Create a Money unit with the specified currency code.
- Parameters:
code (
str) – Currency code (e.g., “USD”, “GBP”, “EUR”)- Return type:
- Returns:
NewUnit with “Money” category and the specified code
Example
>>> usd = MoneyUnit("USD") >>> str(usd) 'Money[USD]'
- metricengine.units.Qty(code)[source]
Create a Quantity unit with the specified unit code.
- Parameters:
code (
str) – Quantity unit code (e.g., “kg”, “L”, “m”)- Return type:
- Returns:
NewUnit with “Quantity” category and the specified code
Example
>>> kg = Qty("kg") >>> str(kg) 'Quantity[kg]'
- metricengine.units.Pct(code='ratio')[source]
Create a Percent unit with the specified code.
- Parameters:
code (
str) – Percent unit code, defaults to “ratio”- Return type:
- Returns:
NewUnit with “Percent” category and the specified code
Example
>>> ratio = Pct() >>> str(ratio) 'Percent[ratio]' >>> bp = Pct("bp") >>> str(bp) 'Percent[bp]'
- class metricengine.units.ConversionContext(at=None, meta=<factory>)[source]
Bases:
objectContext information for unit conversions.
Provides metadata and timing information that conversion functions can use to perform dynamic rate lookups or apply business rules. This allows conversion functions to access external data sources like exchange rate APIs or historical rate databases.
- at
Optional timestamp or date string for rate lookups
- meta
Dictionary of additional metadata (rates, tenant info, etc.)
Examples
>>> # Simple context with timestamp >>> ctx = ConversionContext(at="2025-09-06T10:30:00Z") >>> >>> # Context with metadata >>> ctx = ConversionContext( ... at="2025-09-06", ... meta={"rate": "0.79", "source": "ECB"} ... )
- class metricengine.units.Conversion(src, dst, fn)[source]
Bases:
objectRepresents a registered conversion between two units.
Contains the source unit, destination unit, and the function that performs the actual conversion calculation. Conversion functions receive the value to convert and a context object that can provide additional information like exchange rates.
- src
Source unit for the conversion
- dst
Destination unit for the conversion
- fn
Function that performs the conversion, taking (Decimal, ConversionContext) -> Decimal
Examples
>>> usd = MoneyUnit("USD") >>> gbp = MoneyUnit("GBP") >>> >>> def usd_to_gbp(value: Decimal, ctx: ConversionContext) -> Decimal: ... return value * Decimal("0.79") >>> >>> conversion = Conversion(usd, gbp, usd_to_gbp)
-
fn:
Callable[[Decimal,ConversionContext],Decimal]
- metricengine.units.register_conversion(src, dst)[source]
Decorator for registering conversion functions between units.
The decorated function must accept a Decimal value and ConversionContext, and return a Decimal result.
- Parameters:
- Returns:
Decorator function that registers the conversion
- Raises:
ValueError – If the function signature is invalid
Example
>>> usd = MoneyUnit("USD") >>> gbp = MoneyUnit("GBP") >>> >>> @register_conversion(usd, gbp) ... def usd_to_gbp(value: Decimal, ctx: ConversionContext) -> Decimal: ... # Simple fixed rate for example ... return value * Decimal("0.79")
- metricengine.units.get_conversion(src, dst)[source]
Get a registered conversion between two units.
- Parameters:
- Return type:
- Returns:
Conversion object containing the conversion function
- Raises:
KeyError – If no conversion is registered for the unit pair
- metricengine.units.list_conversions()[source]
Get a copy of all registered conversions.
- Return type:
- Returns:
Dictionary mapping unit pairs to their conversions
- class metricengine.units.ConversionPolicy(strict=True, allow_paths=True)[source]
Bases:
objectPolicy configuration for unit conversions.
Controls the behavior of the conversion system, including whether to raise errors on missing conversions and whether to allow multi-hop conversion paths through intermediate units.
- strict
If True, raise KeyError on missing conversions; if False, return original value
- allow_paths
If True, enable multi-hop conversions; if False, only direct conversions
Examples
>>> # Strict policy (default) - raises on missing conversions >>> strict_policy = ConversionPolicy(strict=True, allow_paths=True) >>> >>> # Permissive policy - returns original value on missing conversions >>> permissive_policy = ConversionPolicy(strict=False, allow_paths=True) >>> >>> # Direct-only policy - no multi-hop conversions >>> direct_only = ConversionPolicy(strict=True, allow_paths=False)
- metricengine.units.use_conversions(policy)[source]
Context manager for scoped conversion policy.
Temporarily sets the conversion policy for the duration of the context. The previous policy is restored when the context exits.
- Parameters:
policy (
ConversionPolicy) – ConversionPolicy to use within the context
Example
>>> permissive_policy = ConversionPolicy(strict=False, allow_paths=True) >>> with use_conversions(permissive_policy): ... # Conversions within this block use permissive policy ... result = convert_decimal(value, usd, gbp)
- metricengine.units.get_current_conversion_policy()[source]
Get the current conversion policy from context.
- Return type:
- Returns:
Current ConversionPolicy in effect
- metricengine.units.convert_decimal(value, src, dst, *, at=None, meta=None)[source]
Convert a decimal value from one unit to another.
This function performs unit-to-unit conversion using the registered conversion functions. It handles same-unit conversions by returning the original value unchanged, and supports multi-hop conversions through intermediate units when no direct conversion exists.
The behavior is controlled by the current ConversionPolicy: - strict=True: Raises KeyError on missing conversions - strict=False: Returns original value on missing conversions - allow_paths=True: Enables multi-hop conversions - allow_paths=False: Only allows direct conversions
- Parameters:
- Return type:
- Returns:
Converted decimal value, or original value if conversion fails and strict=False
- Raises:
KeyError – If no conversion path exists and strict=True
ValueError – If conversion function raises an exception and strict=True
Example
>>> usd = MoneyUnit("USD") >>> gbp = MoneyUnit("GBP") >>> >>> # Strict mode (default) - raises on missing conversion >>> result = convert_decimal(Decimal("100"), usd, gbp) >>> >>> # Permissive mode - returns original on missing conversion >>> policy = ConversionPolicy(strict=False) >>> with use_conversions(policy): ... result = convert_decimal(Decimal("100"), usd, gbp) ... # Returns Decimal("100") if no conversion exists