qnty 0.0.9__py3-none-any.whl → 0.1.0__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/__init__.py +2 -3
- qnty/constants/__init__.py +10 -0
- qnty/constants/numerical.py +18 -0
- qnty/constants/solvers.py +6 -0
- qnty/constants/tests.py +6 -0
- qnty/dimensions/__init__.py +23 -0
- qnty/dimensions/base.py +97 -0
- qnty/dimensions/field_dims.py +126 -0
- qnty/dimensions/field_dims.pyi +128 -0
- qnty/dimensions/signature.py +111 -0
- qnty/equations/__init__.py +1 -1
- qnty/equations/equation.py +118 -155
- qnty/equations/system.py +68 -65
- qnty/expressions/__init__.py +25 -46
- qnty/expressions/formatter.py +188 -0
- qnty/expressions/functions.py +46 -68
- qnty/expressions/nodes.py +539 -384
- qnty/expressions/types.py +70 -0
- qnty/problems/__init__.py +145 -0
- qnty/problems/composition.py +1031 -0
- qnty/problems/problem.py +695 -0
- qnty/problems/rules.py +145 -0
- qnty/problems/solving.py +1216 -0
- qnty/problems/validation.py +127 -0
- qnty/quantities/__init__.py +28 -5
- qnty/quantities/base_qnty.py +677 -0
- qnty/quantities/field_converters.py +24004 -0
- qnty/quantities/field_qnty.py +1012 -0
- qnty/{generated/setters.py → quantities/field_setter.py} +3071 -2961
- qnty/{generated/quantities.py → quantities/field_vars.py} +754 -432
- qnty/{generated/quantities.pyi → quantities/field_vars.pyi} +1289 -1290
- qnty/solving/manager.py +50 -44
- qnty/solving/order.py +181 -133
- qnty/solving/solvers/__init__.py +2 -9
- qnty/solving/solvers/base.py +27 -37
- qnty/solving/solvers/iterative.py +115 -135
- qnty/solving/solvers/simultaneous.py +93 -165
- qnty/units/__init__.py +1 -0
- qnty/{generated/units.py → units/field_units.py} +1700 -991
- qnty/units/field_units.pyi +2461 -0
- qnty/units/prefixes.py +58 -105
- qnty/units/registry.py +76 -89
- qnty/utils/__init__.py +16 -0
- qnty/utils/caching/__init__.py +23 -0
- qnty/utils/caching/manager.py +401 -0
- qnty/utils/error_handling/__init__.py +66 -0
- qnty/utils/error_handling/context.py +39 -0
- qnty/utils/error_handling/exceptions.py +96 -0
- qnty/utils/error_handling/handlers.py +171 -0
- qnty/utils/logging.py +4 -4
- qnty/utils/protocols.py +164 -0
- qnty/utils/scope_discovery.py +420 -0
- {qnty-0.0.9.dist-info → qnty-0.1.0.dist-info}/METADATA +1 -1
- qnty-0.1.0.dist-info/RECORD +60 -0
- qnty/_backup/problem_original.py +0 -1251
- qnty/_backup/quantity.py +0 -63
- qnty/codegen/cli.py +0 -125
- qnty/codegen/generators/data/unit_data.json +0 -8807
- qnty/codegen/generators/data_processor.py +0 -345
- qnty/codegen/generators/dimensions_gen.py +0 -434
- qnty/codegen/generators/doc_generator.py +0 -141
- qnty/codegen/generators/out/dimension_mapping.json +0 -974
- qnty/codegen/generators/out/dimension_metadata.json +0 -123
- qnty/codegen/generators/out/units_metadata.json +0 -223
- qnty/codegen/generators/quantities_gen.py +0 -159
- qnty/codegen/generators/setters_gen.py +0 -178
- qnty/codegen/generators/stubs_gen.py +0 -167
- qnty/codegen/generators/units_gen.py +0 -295
- qnty/expressions/cache.py +0 -94
- qnty/generated/dimensions.py +0 -514
- qnty/problem/__init__.py +0 -91
- qnty/problem/base.py +0 -142
- qnty/problem/composition.py +0 -385
- qnty/problem/composition_mixin.py +0 -382
- qnty/problem/equations.py +0 -413
- qnty/problem/metaclass.py +0 -302
- qnty/problem/reconstruction.py +0 -1016
- qnty/problem/solving.py +0 -180
- qnty/problem/validation.py +0 -64
- qnty/problem/variables.py +0 -239
- qnty/quantities/expression_quantity.py +0 -314
- qnty/quantities/quantity.py +0 -428
- qnty/quantities/typed_quantity.py +0 -215
- qnty/validation/__init__.py +0 -0
- qnty/validation/registry.py +0 -0
- qnty/validation/rules.py +0 -167
- qnty-0.0.9.dist-info/RECORD +0 -63
- /qnty/{codegen → extensions}/__init__.py +0 -0
- /qnty/{codegen/generators → extensions/integration}/__init__.py +0 -0
- /qnty/{codegen/generators/utils → extensions/plotting}/__init__.py +0 -0
- /qnty/{generated → extensions/reporting}/__init__.py +0 -0
- {qnty-0.0.9.dist-info → qnty-0.1.0.dist-info}/WHEEL +0 -0
qnty/problem/metaclass.py
DELETED
@@ -1,302 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Metaclass system for EngineeringProblem composition.
|
3
|
-
|
4
|
-
This module provides the metaclass infrastructure that enables clean
|
5
|
-
sub-problem composition syntax at the class definition level. The system
|
6
|
-
automatically detects and proxies sub-problems during class creation,
|
7
|
-
allowing for natural dotted access in equations.
|
8
|
-
|
9
|
-
Key Features:
|
10
|
-
- Automatic sub-problem detection and proxying
|
11
|
-
- Clean composition syntax with dotted access
|
12
|
-
- Proper namespace isolation and variable management
|
13
|
-
- Comprehensive error handling and validation
|
14
|
-
|
15
|
-
Example Usage:
|
16
|
-
class BranchReinforcementProblem(EngineeringProblem):
|
17
|
-
# Sub-problems are automatically detected and proxied
|
18
|
-
header = create_straight_pipe_internal()
|
19
|
-
branch = create_straight_pipe_internal()
|
20
|
-
|
21
|
-
# Configure sub-problem variables with fluent API
|
22
|
-
header.D.set(2.375).inch
|
23
|
-
branch.D.set(1.315).inch
|
24
|
-
|
25
|
-
# Equations can naturally reference sub-problem variables
|
26
|
-
d_1_eqn = d_1.equals((branch.D - 2 * (branch.T_n - branch.c)) / sin(beta))
|
27
|
-
|
28
|
-
# The metaclass handles all the complex namespace management automatically
|
29
|
-
"""
|
30
|
-
|
31
|
-
from __future__ import annotations
|
32
|
-
|
33
|
-
from typing import Any
|
34
|
-
|
35
|
-
from ..validation.rules import Rules
|
36
|
-
from .composition import SubProblemProxy
|
37
|
-
|
38
|
-
# Constants for better maintainability
|
39
|
-
RESERVED_ATTRIBUTES: set[str] = {'name', 'description'}
|
40
|
-
PRIVATE_ATTRIBUTE_PREFIX = '_'
|
41
|
-
SUB_PROBLEM_REQUIRED_ATTRIBUTES: tuple[str, ...] = ('variables', 'equations')
|
42
|
-
|
43
|
-
|
44
|
-
# Custom exceptions for better error handling
|
45
|
-
class MetaclassError(Exception):
|
46
|
-
"""Base exception for metaclass-related errors."""
|
47
|
-
pass
|
48
|
-
|
49
|
-
|
50
|
-
class SubProblemProxyError(MetaclassError):
|
51
|
-
"""Raised when sub-problem proxy creation fails."""
|
52
|
-
pass
|
53
|
-
|
54
|
-
|
55
|
-
class NamespaceError(MetaclassError):
|
56
|
-
"""Raised when namespace operations fail."""
|
57
|
-
pass
|
58
|
-
|
59
|
-
|
60
|
-
class ProblemMeta(type):
|
61
|
-
"""
|
62
|
-
Metaclass that processes class-level sub-problems to create proper namespace proxies
|
63
|
-
BEFORE any equations are evaluated.
|
64
|
-
|
65
|
-
This metaclass enables clean composition syntax like:
|
66
|
-
class MyProblem(EngineeringProblem):
|
67
|
-
header = create_pipe_problem()
|
68
|
-
branch = create_pipe_problem()
|
69
|
-
# Equations can reference header.P, branch.T, etc.
|
70
|
-
"""
|
71
|
-
|
72
|
-
# Declare the attributes that will be dynamically added to created classes
|
73
|
-
_original_sub_problems: dict[str, Any]
|
74
|
-
_proxy_configurations: dict[str, dict[str, Any]]
|
75
|
-
_class_checks: dict[str, Any]
|
76
|
-
|
77
|
-
@classmethod
|
78
|
-
def __prepare__(mcs, *args, **kwargs) -> ProxiedNamespace:
|
79
|
-
"""
|
80
|
-
Called before the class body is evaluated.
|
81
|
-
Returns a custom namespace that proxies sub-problems.
|
82
|
-
|
83
|
-
Args:
|
84
|
-
*args: Positional arguments (name, bases) - unused but required by protocol
|
85
|
-
**kwargs: Additional keyword arguments - unused but required by protocol
|
86
|
-
|
87
|
-
Returns:
|
88
|
-
ProxiedNamespace that will handle sub-problem proxying
|
89
|
-
"""
|
90
|
-
# Parameters are required by metaclass protocol but not used in this implementation
|
91
|
-
del args, kwargs # Explicitly acknowledge unused parameters
|
92
|
-
return ProxiedNamespace()
|
93
|
-
|
94
|
-
def __new__(mcs, name: str, bases: tuple[type, ...], namespace: ProxiedNamespace, **kwargs) -> type:
|
95
|
-
"""
|
96
|
-
Create the new class with properly integrated sub-problems.
|
97
|
-
|
98
|
-
Args:
|
99
|
-
name: Name of the class being created
|
100
|
-
bases: Base classes
|
101
|
-
namespace: The ProxiedNamespace containing proxied sub-problems
|
102
|
-
**kwargs: Additional keyword arguments - unused but required by protocol
|
103
|
-
|
104
|
-
Returns:
|
105
|
-
The newly created class with metaclass attributes
|
106
|
-
|
107
|
-
Raises:
|
108
|
-
MetaclassError: If class creation fails due to metaclass issues
|
109
|
-
"""
|
110
|
-
# kwargs is required by metaclass protocol but not used in this implementation
|
111
|
-
del kwargs # Explicitly acknowledge unused parameter
|
112
|
-
try:
|
113
|
-
# Validate the namespace
|
114
|
-
if not isinstance(namespace, ProxiedNamespace):
|
115
|
-
raise MetaclassError(f"Expected ProxiedNamespace, got {type(namespace)}")
|
116
|
-
|
117
|
-
# Extract the original sub-problems and proxy objects from the namespace
|
118
|
-
sub_problem_proxies = getattr(namespace, '_sub_problem_proxies', {})
|
119
|
-
proxy_objects = getattr(namespace, '_proxy_objects', {})
|
120
|
-
|
121
|
-
# Validate that proxy objects are consistent
|
122
|
-
if set(sub_problem_proxies.keys()) != set(proxy_objects.keys()):
|
123
|
-
raise MetaclassError("Inconsistent proxy state: sub-problem and proxy object keys don't match")
|
124
|
-
|
125
|
-
# Create the class normally
|
126
|
-
cls = super().__new__(mcs, name, bases, dict(namespace))
|
127
|
-
|
128
|
-
# Store the original sub-problems and proxy configurations for later integration
|
129
|
-
cls._original_sub_problems = sub_problem_proxies
|
130
|
-
|
131
|
-
# Extract configurations safely with error handling
|
132
|
-
proxy_configurations = {}
|
133
|
-
for proxy_name, proxy in proxy_objects.items():
|
134
|
-
try:
|
135
|
-
# Cache configurations to avoid recomputation
|
136
|
-
if not hasattr(proxy, '_cached_configurations'):
|
137
|
-
proxy._cached_configurations = proxy.get_configurations()
|
138
|
-
proxy_configurations[proxy_name] = proxy._cached_configurations
|
139
|
-
except Exception as e:
|
140
|
-
raise SubProblemProxyError(f"Failed to get configurations from proxy '{proxy_name}': {e}") from e
|
141
|
-
|
142
|
-
cls._proxy_configurations = proxy_configurations
|
143
|
-
|
144
|
-
# Collect Check objects from class attributes
|
145
|
-
checks = {}
|
146
|
-
for attr_name, attr_value in namespace.items():
|
147
|
-
if isinstance(attr_value, Rules):
|
148
|
-
checks[attr_name] = attr_value
|
149
|
-
|
150
|
-
cls._class_checks = checks
|
151
|
-
|
152
|
-
return cls
|
153
|
-
|
154
|
-
except Exception as e:
|
155
|
-
# Re-raise MetaclassError and SubProblemProxyError as-is
|
156
|
-
if isinstance(e, MetaclassError | SubProblemProxyError):
|
157
|
-
raise
|
158
|
-
# Wrap other exceptions
|
159
|
-
raise MetaclassError(f"Failed to create class '{name}': {e}") from e
|
160
|
-
|
161
|
-
|
162
|
-
class ProxiedNamespace(dict):
|
163
|
-
"""
|
164
|
-
Custom namespace that automatically proxies sub-problems as they're added.
|
165
|
-
|
166
|
-
This namespace intercepts class attribute assignments during class creation
|
167
|
-
and automatically wraps EngineeringProblem objects in SubProblemProxy objects.
|
168
|
-
This enables clean composition syntax where sub-problems can be referenced
|
169
|
-
with dot notation in equations.
|
170
|
-
|
171
|
-
Example:
|
172
|
-
class ComposedProblem(EngineeringProblem):
|
173
|
-
header = create_pipe_problem() # Gets proxied automatically
|
174
|
-
branch = create_pipe_problem() # Gets proxied automatically
|
175
|
-
# Now equations can use header.P, branch.T, etc.
|
176
|
-
"""
|
177
|
-
|
178
|
-
def __init__(self) -> None:
|
179
|
-
"""Initialize the proxied namespace with empty storage."""
|
180
|
-
super().__init__()
|
181
|
-
self._sub_problem_proxies: dict[str, Any] = {}
|
182
|
-
self._proxy_objects: dict[str, SubProblemProxy] = {}
|
183
|
-
|
184
|
-
def __setitem__(self, key: str, value: Any) -> None:
|
185
|
-
"""
|
186
|
-
Intercept attribute assignment and proxy sub-problems automatically.
|
187
|
-
|
188
|
-
Args:
|
189
|
-
key: The attribute name being set
|
190
|
-
value: The value being assigned
|
191
|
-
|
192
|
-
Raises:
|
193
|
-
NamespaceError: If namespace operation fails
|
194
|
-
SubProblemProxyError: If proxy creation fails
|
195
|
-
"""
|
196
|
-
try:
|
197
|
-
if self._is_sub_problem(key, value):
|
198
|
-
self._create_and_store_proxy(key, value)
|
199
|
-
elif self._is_variable_with_auto_symbol(value):
|
200
|
-
self._set_variable_symbol_and_store(key, value)
|
201
|
-
else:
|
202
|
-
super().__setitem__(key, value)
|
203
|
-
except Exception as e:
|
204
|
-
if isinstance(e, NamespaceError | SubProblemProxyError):
|
205
|
-
raise
|
206
|
-
raise NamespaceError(f"Failed to set attribute '{key}': {e}") from e
|
207
|
-
|
208
|
-
def _is_sub_problem(self, key: str, value: Any) -> bool:
|
209
|
-
"""
|
210
|
-
Determine if a value should be treated as a sub-problem.
|
211
|
-
|
212
|
-
Args:
|
213
|
-
key: The attribute name
|
214
|
-
value: The value being assigned
|
215
|
-
|
216
|
-
Returns:
|
217
|
-
True if this should be proxied as a sub-problem
|
218
|
-
"""
|
219
|
-
# Quick checks first (fail fast)
|
220
|
-
if key.startswith(PRIVATE_ATTRIBUTE_PREFIX) or key in RESERVED_ATTRIBUTES:
|
221
|
-
return False
|
222
|
-
|
223
|
-
# Check for None or basic types that definitely aren't sub-problems
|
224
|
-
if value is None or isinstance(value, str | int | float | bool | list | dict):
|
225
|
-
return False
|
226
|
-
|
227
|
-
# Cache hasattr results to avoid repeated attribute lookups
|
228
|
-
if not hasattr(self, '_attr_cache'):
|
229
|
-
self._attr_cache = {}
|
230
|
-
|
231
|
-
# Use object id as cache key since objects are unique
|
232
|
-
cache_key = (id(value), tuple(SUB_PROBLEM_REQUIRED_ATTRIBUTES))
|
233
|
-
if cache_key not in self._attr_cache:
|
234
|
-
self._attr_cache[cache_key] = all(hasattr(value, attr) for attr in SUB_PROBLEM_REQUIRED_ATTRIBUTES)
|
235
|
-
|
236
|
-
return self._attr_cache[cache_key]
|
237
|
-
|
238
|
-
def _is_variable_with_auto_symbol(self, value: Any) -> bool:
|
239
|
-
"""
|
240
|
-
Determine if a value is a Variable that needs automatic symbol assignment.
|
241
|
-
|
242
|
-
Args:
|
243
|
-
value: The value being assigned
|
244
|
-
|
245
|
-
Returns:
|
246
|
-
True if this is a Variable with symbol == "<auto>"
|
247
|
-
"""
|
248
|
-
# Import Variable here to avoid circular imports
|
249
|
-
try:
|
250
|
-
from qnty.quantities.quantity import TypeSafeVariable as Variable
|
251
|
-
return isinstance(value, Variable) and value.symbol == "<auto>"
|
252
|
-
except ImportError:
|
253
|
-
return False
|
254
|
-
|
255
|
-
def _set_variable_symbol_and_store(self, key: str, value: Any) -> None:
|
256
|
-
"""
|
257
|
-
Set the variable's symbol to the attribute name and store it.
|
258
|
-
|
259
|
-
Args:
|
260
|
-
key: The attribute name to use as symbol
|
261
|
-
value: The Variable object
|
262
|
-
"""
|
263
|
-
try:
|
264
|
-
# Set the symbol to the attribute name
|
265
|
-
value.symbol = key
|
266
|
-
# Store the modified variable
|
267
|
-
super().__setitem__(key, value)
|
268
|
-
except Exception as e:
|
269
|
-
raise NamespaceError(f"Failed to set symbol for variable '{key}': {e}") from e
|
270
|
-
|
271
|
-
def _create_and_store_proxy(self, key: str, value: Any) -> None:
|
272
|
-
"""
|
273
|
-
Create a proxy for the sub-problem and store references.
|
274
|
-
|
275
|
-
Args:
|
276
|
-
key: The attribute name for the sub-problem
|
277
|
-
value: The sub-problem object to proxy
|
278
|
-
|
279
|
-
Raises:
|
280
|
-
SubProblemProxyError: If proxy creation fails
|
281
|
-
NamespaceError: If key already exists as a sub-problem
|
282
|
-
"""
|
283
|
-
# Check for conflicts
|
284
|
-
if key in self._sub_problem_proxies:
|
285
|
-
raise NamespaceError(f"Sub-problem '{key}' already exists in namespace")
|
286
|
-
|
287
|
-
try:
|
288
|
-
# Store the original sub-problem
|
289
|
-
self._sub_problem_proxies[key] = value
|
290
|
-
|
291
|
-
# Create and store the proxy
|
292
|
-
proxy = SubProblemProxy(value, key)
|
293
|
-
self._proxy_objects[key] = proxy
|
294
|
-
|
295
|
-
# Set the proxy in the namespace
|
296
|
-
super().__setitem__(key, proxy)
|
297
|
-
|
298
|
-
except Exception as e:
|
299
|
-
# Clean up partial state on failure
|
300
|
-
self._sub_problem_proxies.pop(key, None)
|
301
|
-
self._proxy_objects.pop(key, None)
|
302
|
-
raise SubProblemProxyError(f"Failed to create proxy for sub-problem '{key}': {e}") from e
|