qnty 0.0.2__py3-none-any.whl → 0.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
qnty/variable.py CHANGED
@@ -10,7 +10,8 @@ from __future__ import annotations
10
10
 
11
11
  from typing import Generic, Self, TypeVar
12
12
 
13
- from .dimension import AREA, DIMENSIONLESS, ENERGY, FORCE, LENGTH, PRESSURE, VOLUME, DimensionSignature
13
+ from .dimension import AREA, DIMENSIONLESS, FORCE, LENGTH, PRESSURE, VOLUME, DimensionSignature
14
+ from .dimension import ENERGY_HEAT_WORK as ENERGY
14
15
  from .unit import UnitConstant, UnitDefinition, registry
15
16
  from .units import DimensionlessUnits, LengthUnits, PressureUnits
16
17
 
File without changes
@@ -0,0 +1,58 @@
1
+ """
2
+ Base Variable Module Definition
3
+ ===============================
4
+
5
+ Provides abstract base class for variable modules and registration functionality.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from typing import Any
10
+
11
+
12
+ class VariableModule(ABC):
13
+ """Abstract base class for variable modules."""
14
+
15
+ @abstractmethod
16
+ def get_variable_class(self) -> type[Any]:
17
+ """Return the variable class for this module."""
18
+ pass
19
+
20
+ @abstractmethod
21
+ def get_setter_class(self) -> type[Any]:
22
+ """Return the setter class for this module."""
23
+ pass
24
+
25
+ @abstractmethod
26
+ def get_expected_dimension(self) -> Any:
27
+ """Return the expected dimension for this variable type."""
28
+ pass
29
+
30
+ def register_to_registry(self, variable_registry):
31
+ """Register this variable module to the given registry."""
32
+ variable_registry.register_module(
33
+ self.get_expected_dimension(),
34
+ self.get_variable_class(),
35
+ self.get_setter_class()
36
+ )
37
+
38
+
39
+ class VariableRegistry:
40
+ """Registry for variable modules."""
41
+
42
+ def __init__(self):
43
+ self._modules = {}
44
+
45
+ def register_module(self, dimension, variable_class, setter_class):
46
+ """Register a variable module."""
47
+ self._modules[dimension] = {
48
+ 'variable_class': variable_class,
49
+ 'setter_class': setter_class
50
+ }
51
+
52
+ def get_variable_class(self, dimension):
53
+ """Get variable class for a dimension."""
54
+ return self._modules.get(dimension, {}).get('variable_class')
55
+
56
+ def get_setter_class(self, dimension):
57
+ """Get setter class for a dimension."""
58
+ return self._modules.get(dimension, {}).get('setter_class')
@@ -0,0 +1,68 @@
1
+ """
2
+ Expression Variable Base Class
3
+ ==============================
4
+
5
+ Base class that extends TypeSafeVariable with mathematical expression
6
+ and equation capabilities.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from ..equation import Equation
12
+ from ..expression import Expression, wrap_operand
13
+ from ..variable import FastQuantity, TypeSafeVariable
14
+
15
+
16
+ class ExpressionVariable(TypeSafeVariable):
17
+ """
18
+ TypeSafeVariable extended with expression and equation capabilities.
19
+
20
+ This adds mathematical operations that create expressions and equations,
21
+ keeping the base TypeSafeVariable free of these dependencies.
22
+ """
23
+
24
+ def equals(self, expression: Expression | TypeSafeVariable | FastQuantity | int | float):
25
+ """Create an equation: self = expression."""
26
+ # Wrap the expression in proper Expression type
27
+ rhs_expr = wrap_operand(expression)
28
+ return Equation(f"{self.name}_eq", self, rhs_expr)
29
+
30
+ def __add__(self, other: TypeSafeVariable | FastQuantity | int | float) -> Expression:
31
+ """Add this variable to another operand, returning an Expression."""
32
+ return wrap_operand(self) + wrap_operand(other)
33
+
34
+ def __radd__(self, other: FastQuantity | int | float) -> Expression:
35
+ """Reverse add for this variable."""
36
+ return wrap_operand(other) + wrap_operand(self)
37
+
38
+ def __sub__(self, other: TypeSafeVariable | FastQuantity | int | float) -> Expression:
39
+ """Subtract another operand from this variable, returning an Expression."""
40
+ return wrap_operand(self) - wrap_operand(other)
41
+
42
+ def __rsub__(self, other: FastQuantity | int | float) -> Expression:
43
+ """Reverse subtract for this variable."""
44
+ return wrap_operand(other) - wrap_operand(self)
45
+
46
+ def __mul__(self, other: TypeSafeVariable | FastQuantity | int | float) -> Expression:
47
+ """Multiply this variable by another operand, returning an Expression."""
48
+ return wrap_operand(self) * wrap_operand(other)
49
+
50
+ def __rmul__(self, other: FastQuantity | int | float) -> Expression:
51
+ """Reverse multiply for this variable."""
52
+ return wrap_operand(other) * wrap_operand(self)
53
+
54
+ def __truediv__(self, other: TypeSafeVariable | FastQuantity | int | float) -> Expression:
55
+ """Divide this variable by another operand, returning an Expression."""
56
+ return wrap_operand(self) / wrap_operand(other)
57
+
58
+ def __rtruediv__(self, other: FastQuantity | int | float) -> Expression:
59
+ """Reverse divide for this variable."""
60
+ return wrap_operand(other) / wrap_operand(self)
61
+
62
+ def __pow__(self, other: TypeSafeVariable | FastQuantity | int | float) -> Expression:
63
+ """Raise this variable to a power, returning an Expression."""
64
+ return wrap_operand(self) ** wrap_operand(other)
65
+
66
+ def __rpow__(self, other: FastQuantity | int | float) -> Expression:
67
+ """Reverse power for this variable."""
68
+ return wrap_operand(other) ** wrap_operand(self)
@@ -0,0 +1,92 @@
1
+ """
2
+ Typed Variable Base Class
3
+ =========================
4
+
5
+ Base class that provides common constructor logic for all typed variables,
6
+ handling both the original syntax and the new value/unit/name syntax.
7
+ """
8
+
9
+ from ..dimension import DimensionSignature
10
+ from ..variable import TypeSafeSetter
11
+ from .expression_variable import ExpressionVariable
12
+
13
+
14
+ class TypedVariable(ExpressionVariable):
15
+ """
16
+ Base class for typed variables with common constructor logic.
17
+
18
+ Subclasses need to define:
19
+ - _setter_class: The setter class to use
20
+ - _expected_dimension: The expected dimension
21
+ - _default_unit_property: The default unit property name for fallback
22
+ """
23
+
24
+ _setter_class: type[TypeSafeSetter] | None = None
25
+ _expected_dimension: DimensionSignature | None = None
26
+ _default_unit_property: str | None = None
27
+
28
+ def __init__(self, *args, is_known: bool = True):
29
+ """
30
+ Flexible constructor supporting multiple syntaxes.
31
+
32
+ Single argument: TypedVariable("name")
33
+ Three arguments: TypedVariable(value, "unit", "name")
34
+ Two arguments (Dimensionless only): TypedVariable(value, "name")
35
+ """
36
+ if self._setter_class is None or self._expected_dimension is None:
37
+ raise NotImplementedError("Subclass must define _setter_class and _expected_dimension")
38
+
39
+ # Handle different argument patterns
40
+ if len(args) == 1:
41
+ # Original syntax: Variable("name")
42
+ super().__init__(args[0], self._expected_dimension, is_known=is_known)
43
+
44
+ elif len(args) == 2 and self.__class__.__name__ == 'Dimensionless':
45
+ # Special case for Dimensionless: (value, "name")
46
+ value, name = args
47
+ super().__init__(name, self._expected_dimension, is_known=is_known)
48
+ setter = self._setter_class(self, value)
49
+ # For DimensionlessSetter, use the dimensionless property
50
+ # Type ignore since we know DimensionlessSetter has this property
51
+ getattr(setter, 'dimensionless', None) # type: ignore
52
+
53
+ elif len(args) == 3:
54
+ # New syntax: Variable(value, "unit", "name")
55
+ # But Dimensionless doesn't support this pattern
56
+ if self.__class__.__name__ == 'Dimensionless':
57
+ raise ValueError(f"{self.__class__.__name__} expects either 1 argument (name) or 2 arguments (value, name), got {len(args)}")
58
+
59
+ value, unit, name = args
60
+ super().__init__(name, self._expected_dimension, is_known=is_known)
61
+
62
+ # Auto-set the value with the specified unit
63
+ setter = self._setter_class(self, value)
64
+
65
+ # Handle special unit aliases
66
+ if unit == "in": # Handle Python reserved word
67
+ unit = "inchs" # Match the actual property name
68
+ elif unit == "inches": # Handle common plural form
69
+ unit = "inchs" # Match the actual property name
70
+
71
+ # Try to find the unit property on the setter
72
+ if hasattr(setter, unit):
73
+ getattr(setter, unit)
74
+ elif hasattr(setter, unit + 's'): # Handle singular/plural
75
+ getattr(setter, unit + 's')
76
+ elif self._default_unit_property and hasattr(setter, self._default_unit_property):
77
+ # Fall back to default unit
78
+ getattr(setter, self._default_unit_property)
79
+ else:
80
+ # Last resort - try to find any valid unit property
81
+ # This helps with forward compatibility
82
+ unit_properties = [attr for attr in dir(setter)
83
+ if not attr.startswith('_') and attr != 'value' and attr != 'variable']
84
+ if unit_properties:
85
+ getattr(setter, unit_properties[0])
86
+
87
+ else:
88
+ # More specific error messages matching test expectations
89
+ if self.__class__.__name__ == 'Dimensionless':
90
+ raise ValueError(f"{self.__class__.__name__} expects either 1 argument (name) or 2 arguments (value, name), got {len(args)}")
91
+ else:
92
+ raise ValueError(f"{self.__class__.__name__} expects either 1 argument (name) or 3 arguments (value, unit, name), got {len(args)}")