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
@@ -1,167 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
Script to generate comprehensive variables.pyi type stub file.
|
4
|
-
|
5
|
-
This script generates complete type hints for all 105 variable types in the
|
6
|
-
consolidated variables system, providing full IDE autocomplete and type checking
|
7
|
-
support for the fluent API with dimension-specific unit properties.
|
8
|
-
|
9
|
-
Uses the same source of truth as the consolidated variables system.
|
10
|
-
"""
|
11
|
-
|
12
|
-
from pathlib import Path
|
13
|
-
|
14
|
-
try:
|
15
|
-
from .data_processor import (
|
16
|
-
augment_with_prefixed_units,
|
17
|
-
calculate_statistics,
|
18
|
-
convert_to_class_name,
|
19
|
-
get_dimension_constant_name,
|
20
|
-
load_json_data,
|
21
|
-
save_text_file,
|
22
|
-
setup_import_path,
|
23
|
-
)
|
24
|
-
from .doc_generator import generate_class_docstring, generate_init_method, generate_set_method
|
25
|
-
except ImportError:
|
26
|
-
from .data_processor import augment_with_prefixed_units, calculate_statistics, convert_to_class_name, get_dimension_constant_name, load_json_data, save_text_file, setup_import_path
|
27
|
-
from .doc_generator import generate_class_docstring, generate_init_method, generate_set_method
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
def generate_quantities_pyi(parsed_data: dict, dimension_mapping: dict) -> str:
|
33
|
-
"""Generate the variables.pyi type stub content."""
|
34
|
-
|
35
|
-
lines = [
|
36
|
-
'"""',
|
37
|
-
'Type stubs for quantities module - Complete Edition.',
|
38
|
-
'',
|
39
|
-
'Provides complete type hints for IDE autocomplete and type checking',
|
40
|
-
'for quantity classes and their setter relationships.',
|
41
|
-
f'Contains {len([f for f in parsed_data.values() if f.get("units")])} quantity types with {sum(len(f.get("units", [])) for f in parsed_data.values())} total units.',
|
42
|
-
'',
|
43
|
-
'Auto-generated from unit_data.json.',
|
44
|
-
'"""',
|
45
|
-
'',
|
46
|
-
'from typing import Any',
|
47
|
-
'',
|
48
|
-
'from ..quantities.typed_quantity import TypedQuantity',
|
49
|
-
'from . import dimensions as dim',
|
50
|
-
'from . import setters as ts',
|
51
|
-
''
|
52
|
-
]
|
53
|
-
|
54
|
-
# Generate type stubs for quantity classes only (setters are in separate file)
|
55
|
-
sorted_fields = sorted(parsed_data.items())
|
56
|
-
fields_with_units = [(k, v) for k, v in sorted_fields if v.get('units')]
|
57
|
-
|
58
|
-
lines.extend([
|
59
|
-
'# ===== QUANTITY CLASSES =====',
|
60
|
-
'# Type stubs for quantity classes with setter relationships',
|
61
|
-
''
|
62
|
-
])
|
63
|
-
|
64
|
-
for field_name, field_data in fields_with_units:
|
65
|
-
class_name = convert_to_class_name(field_name)
|
66
|
-
setter_name = f"{class_name}Setter"
|
67
|
-
display_name = field_data.get('field', class_name).lower()
|
68
|
-
dimension_constant = get_dimension_constant_name(field_name)
|
69
|
-
units = field_data.get('units', [])
|
70
|
-
is_dimensionless = class_name == 'Dimensionless'
|
71
|
-
|
72
|
-
# Generate quantity class stub
|
73
|
-
lines.append(f'class {class_name}(TypedQuantity):')
|
74
|
-
lines.extend(generate_class_docstring(class_name, display_name, units, is_dimensionless))
|
75
|
-
# Class attributes
|
76
|
-
lines.append(' __slots__ = ()')
|
77
|
-
lines.append(f' _setter_class = ts.{setter_name}')
|
78
|
-
lines.append(f' _expected_dimension = dim.{dimension_constant}')
|
79
|
-
lines.append(' ')
|
80
|
-
|
81
|
-
# Generate __init__ method
|
82
|
-
lines.extend(generate_init_method(class_name, display_name, is_dimensionless, stub_only=True))
|
83
|
-
|
84
|
-
# Generate set method
|
85
|
-
lines.append(' ')
|
86
|
-
lines.extend(generate_set_method(setter_name, display_name, stub_only=True))
|
87
|
-
lines.append(' ')
|
88
|
-
lines.append('')
|
89
|
-
|
90
|
-
lines.extend([
|
91
|
-
'# All quantity classes are defined above',
|
92
|
-
'# Setter classes are defined in setters.pyi',
|
93
|
-
])
|
94
|
-
|
95
|
-
return '\n'.join(lines) + '\n'
|
96
|
-
|
97
|
-
|
98
|
-
def main():
|
99
|
-
"""Main execution function."""
|
100
|
-
# Setup import path and import prefixes
|
101
|
-
setup_import_path()
|
102
|
-
|
103
|
-
# Setup paths using pathlib
|
104
|
-
base_path = Path(__file__).parents[4] # Go up to qnty root
|
105
|
-
data_path = Path(__file__).parent / "data"
|
106
|
-
output_path = Path(__file__).parent / "out"
|
107
|
-
generated_path = base_path / "src" / "qnty" / "generated"
|
108
|
-
|
109
|
-
# Ensure output directories exist
|
110
|
-
output_path.mkdir(exist_ok=True)
|
111
|
-
generated_path.mkdir(exist_ok=True)
|
112
|
-
|
113
|
-
parsed_file = data_path / "unit_data.json"
|
114
|
-
dimension_file = output_path / "dimension_mapping.json"
|
115
|
-
output_file = generated_path / "quantities.pyi"
|
116
|
-
|
117
|
-
print("Loading parsed units data for type stub generation...")
|
118
|
-
|
119
|
-
# Load data using shared processor
|
120
|
-
parsed_data = load_json_data(parsed_file)
|
121
|
-
dimension_mapping = load_json_data(dimension_file) if dimension_file.exists() else {}
|
122
|
-
|
123
|
-
print(f"Loaded {len(parsed_data)} fields with units")
|
124
|
-
|
125
|
-
# Augment data with missing prefixed units using shared processor
|
126
|
-
print("\nAugmenting data with missing prefixed units...")
|
127
|
-
augmented_data, generated_count = augment_with_prefixed_units(parsed_data)
|
128
|
-
print(f"Generated {generated_count} missing prefixed units for type stubs")
|
129
|
-
|
130
|
-
# Count fields with units
|
131
|
-
fields_with_units = sum(1 for field_data in augmented_data.values() if field_data.get('units'))
|
132
|
-
print(f"Found {len(augmented_data)} total fields, {fields_with_units} fields with units")
|
133
|
-
|
134
|
-
# Generate type stub file
|
135
|
-
print("Generating quantities.pyi...")
|
136
|
-
content = generate_quantities_pyi(augmented_data, dimension_mapping)
|
137
|
-
|
138
|
-
# Write output file
|
139
|
-
save_text_file(content, output_file)
|
140
|
-
print(f"Generated type stub file: {output_file}")
|
141
|
-
|
142
|
-
# Print statistics using shared calculator
|
143
|
-
stats = calculate_statistics(augmented_data)
|
144
|
-
lines_count = len(content.splitlines())
|
145
|
-
print("\nStatistics:")
|
146
|
-
print(f" Total fields: {stats['total_fields']}")
|
147
|
-
print(f" Quantity types: {fields_with_units}")
|
148
|
-
print(f" Original units: {stats['original_units']}")
|
149
|
-
print(f" Total units (with prefixes): {stats['total_units']}")
|
150
|
-
print(f" Generated prefixed units: {stats['generated_prefixed_units']}")
|
151
|
-
print(f" Generated lines: {lines_count:,}")
|
152
|
-
|
153
|
-
# Show top quantity types by unit count
|
154
|
-
fields_by_units = [(field_name, len(field_data.get('units', [])), field_data.get('field', ''))
|
155
|
-
for field_name, field_data in augmented_data.items() if field_data.get('units')]
|
156
|
-
fields_by_units.sort(key=lambda x: x[1], reverse=True)
|
157
|
-
|
158
|
-
print("\nTop quantity types by unit count:")
|
159
|
-
for field_name, unit_count, display_name in fields_by_units[:10]:
|
160
|
-
class_name = convert_to_class_name(field_name)
|
161
|
-
print(f" {class_name:<25} : {unit_count:>3} units ({display_name})")
|
162
|
-
|
163
|
-
print("\nQuantity type stubs generated with full IDE support!")
|
164
|
-
|
165
|
-
|
166
|
-
if __name__ == "__main__":
|
167
|
-
main()
|
@@ -1,295 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
Units generator for qnty library.
|
4
|
-
|
5
|
-
This script generates the units.py file from unit_data.json, creating
|
6
|
-
comprehensive unit definitions organized by dimensional groups.
|
7
|
-
"""
|
8
|
-
|
9
|
-
import json
|
10
|
-
from pathlib import Path
|
11
|
-
from typing import Any
|
12
|
-
|
13
|
-
try:
|
14
|
-
from .data_processor import (
|
15
|
-
augment_with_prefixed_units,
|
16
|
-
convert_to_class_name,
|
17
|
-
escape_string,
|
18
|
-
get_dimension_constant_name,
|
19
|
-
get_unit_names_and_aliases,
|
20
|
-
is_valid_python_identifier,
|
21
|
-
load_unit_data,
|
22
|
-
setup_import_path,
|
23
|
-
)
|
24
|
-
except ImportError:
|
25
|
-
from .data_processor import (
|
26
|
-
augment_with_prefixed_units,
|
27
|
-
convert_to_class_name,
|
28
|
-
escape_string,
|
29
|
-
get_dimension_constant_name,
|
30
|
-
get_unit_names_and_aliases,
|
31
|
-
is_valid_python_identifier,
|
32
|
-
load_unit_data,
|
33
|
-
setup_import_path,
|
34
|
-
)
|
35
|
-
|
36
|
-
|
37
|
-
class UnitsGenerator:
|
38
|
-
"""Generator for units.py file."""
|
39
|
-
|
40
|
-
def __init__(self, data_path: Path, output_path: Path, out_dir: Path):
|
41
|
-
"""Initialize with paths."""
|
42
|
-
self.data_path = data_path
|
43
|
-
self.output_path = output_path
|
44
|
-
self.out_dir = out_dir
|
45
|
-
self.out_dir.mkdir(parents=True, exist_ok=True)
|
46
|
-
|
47
|
-
# Setup import path and load data
|
48
|
-
setup_import_path()
|
49
|
-
raw_unit_data = load_unit_data(self.data_path)
|
50
|
-
|
51
|
-
# Augment with prefixed units using shared processor
|
52
|
-
self.unit_data, generated_count = augment_with_prefixed_units(raw_unit_data)
|
53
|
-
print(f"Generated {generated_count} prefixed units")
|
54
|
-
|
55
|
-
# Track generated info
|
56
|
-
self.dimension_constants: set[str] = set()
|
57
|
-
self.field_to_class_mapping: dict[str, str] = {}
|
58
|
-
|
59
|
-
|
60
|
-
def get_class_name(self, field_name: str) -> str:
|
61
|
-
"""Convert field name to class name."""
|
62
|
-
# Use shared utility and add 'Units' suffix
|
63
|
-
class_name = convert_to_class_name(field_name) + 'Units'
|
64
|
-
self.field_to_class_mapping[field_name] = class_name
|
65
|
-
return class_name
|
66
|
-
|
67
|
-
def get_dimension_constant_name(self, field_name: str) -> str:
|
68
|
-
"""Get the dimension constant name for a field."""
|
69
|
-
constant_name = get_dimension_constant_name(field_name)
|
70
|
-
self.dimension_constants.add(constant_name)
|
71
|
-
return constant_name
|
72
|
-
|
73
|
-
|
74
|
-
def generate_header(self) -> list[str]:
|
75
|
-
"""Generate file header with imports."""
|
76
|
-
lines = [
|
77
|
-
'"""',
|
78
|
-
'Comprehensive Units Module',
|
79
|
-
'==========================',
|
80
|
-
'',
|
81
|
-
'Auto-generated unit definitions for all engineering units.',
|
82
|
-
f'Contains {sum(len(field_data["units"]) for field_data in self.unit_data.values())} units',
|
83
|
-
f'across {len(self.unit_data)} fields.',
|
84
|
-
'',
|
85
|
-
'This file is auto-generated by codegen/generators/units_gen.py',
|
86
|
-
'DO NOT EDIT MANUALLY - changes will be overwritten.',
|
87
|
-
'"""',
|
88
|
-
'',
|
89
|
-
]
|
90
|
-
|
91
|
-
lines.extend([
|
92
|
-
'from ..units.registry import UnitConstant, UnitDefinition',
|
93
|
-
'from . import dimensions as dim',
|
94
|
-
'',
|
95
|
-
'',
|
96
|
-
])
|
97
|
-
|
98
|
-
return lines
|
99
|
-
|
100
|
-
def generate_unit_class(self, field_name: str, field_data: dict[str, Any]) -> list[str]:
|
101
|
-
"""Generate a unit class for a field."""
|
102
|
-
class_name = self.get_class_name(field_name)
|
103
|
-
dimension_constant = self.get_dimension_constant_name(field_name)
|
104
|
-
|
105
|
-
lines = [
|
106
|
-
f'class {class_name}:',
|
107
|
-
f' """Unit constants for {field_data["field"]}."""',
|
108
|
-
' __slots__ = ()',
|
109
|
-
'',
|
110
|
-
]
|
111
|
-
|
112
|
-
# Process all units for this field
|
113
|
-
units = field_data.get('units', [])
|
114
|
-
if not units:
|
115
|
-
lines.extend([' pass', '', ''])
|
116
|
-
return lines
|
117
|
-
|
118
|
-
# Generate unit constants using shared name processing
|
119
|
-
for unit_data in units:
|
120
|
-
primary_name, aliases = get_unit_names_and_aliases(unit_data)
|
121
|
-
if not is_valid_python_identifier(primary_name):
|
122
|
-
continue
|
123
|
-
|
124
|
-
full_name = unit_data.get('name', '')
|
125
|
-
symbol = unit_data.get('notation', '')
|
126
|
-
si_factor = unit_data.get('si_conversion', 1.0)
|
127
|
-
|
128
|
-
lines.extend([
|
129
|
-
f' # {full_name}',
|
130
|
-
f' {primary_name} = UnitConstant(UnitDefinition(',
|
131
|
-
f' name="{escape_string(primary_name)}",',
|
132
|
-
f' symbol="{escape_string(symbol)}",',
|
133
|
-
f' dimension=dim.{dimension_constant},',
|
134
|
-
f' si_factor={si_factor},',
|
135
|
-
' si_offset=0.0',
|
136
|
-
' ))',
|
137
|
-
'',
|
138
|
-
])
|
139
|
-
|
140
|
-
# Add aliases as class attributes using shared processing
|
141
|
-
for alias in aliases:
|
142
|
-
if is_valid_python_identifier(alias):
|
143
|
-
lines.append(f' {alias} = {primary_name}')
|
144
|
-
|
145
|
-
lines.append('')
|
146
|
-
return lines
|
147
|
-
|
148
|
-
def generate_registry_function(self) -> list[str]:
|
149
|
-
"""Generate function to register all units with registry."""
|
150
|
-
lines = [
|
151
|
-
'def register_all_units(registry) -> None:',
|
152
|
-
' """Register all unit definitions with the registry."""',
|
153
|
-
' unit_classes = [',
|
154
|
-
]
|
155
|
-
|
156
|
-
# Add all generated unit classes
|
157
|
-
for class_name in sorted(self.field_to_class_mapping.values()):
|
158
|
-
lines.append(f' {class_name},')
|
159
|
-
|
160
|
-
lines.extend([
|
161
|
-
' ]',
|
162
|
-
'',
|
163
|
-
' for unit_class in unit_classes:',
|
164
|
-
' for attr_name in dir(unit_class):',
|
165
|
-
' if not attr_name.startswith("_"):',
|
166
|
-
' unit_constant = getattr(unit_class, attr_name, None)',
|
167
|
-
' if unit_constant is not None and hasattr(unit_constant, "definition"):',
|
168
|
-
' unit_def = unit_constant.definition',
|
169
|
-
' if unit_def.name not in registry.units:',
|
170
|
-
' registry.register_unit(unit_def)',
|
171
|
-
'',
|
172
|
-
' # Finalize registry',
|
173
|
-
' registry.finalize_registration()',
|
174
|
-
'',
|
175
|
-
'',
|
176
|
-
])
|
177
|
-
|
178
|
-
return lines
|
179
|
-
|
180
|
-
def generate_dimensionless_class(self) -> list[str]:
|
181
|
-
"""Generate DimensionlessUnits class for backward compatibility."""
|
182
|
-
return [
|
183
|
-
'# Backward compatibility class',
|
184
|
-
'class DimensionlessUnits:',
|
185
|
-
' """Dimensionless units for backward compatibility."""',
|
186
|
-
' __slots__ = ()',
|
187
|
-
'',
|
188
|
-
' dimensionless = UnitConstant(UnitDefinition(',
|
189
|
-
' name="dimensionless",',
|
190
|
-
' symbol="",',
|
191
|
-
' dimension=dim.DIMENSIONLESS,',
|
192
|
-
' si_factor=1.0,',
|
193
|
-
' si_offset=0.0',
|
194
|
-
' ))',
|
195
|
-
'',
|
196
|
-
'',
|
197
|
-
]
|
198
|
-
|
199
|
-
def generate_exports(self) -> list[str]:
|
200
|
-
"""Generate __all__ export list."""
|
201
|
-
lines = [
|
202
|
-
'# Export list',
|
203
|
-
'__all__ = [',
|
204
|
-
' "register_all_units",',
|
205
|
-
' "DimensionlessUnits",',
|
206
|
-
]
|
207
|
-
|
208
|
-
for class_name in sorted(self.field_to_class_mapping.values()):
|
209
|
-
lines.append(f' "{class_name}",')
|
210
|
-
|
211
|
-
lines.extend([
|
212
|
-
']',
|
213
|
-
'',
|
214
|
-
])
|
215
|
-
|
216
|
-
return lines
|
217
|
-
|
218
|
-
def generate_statistics(self) -> list[str]:
|
219
|
-
"""Generate statistics section."""
|
220
|
-
total_units = sum(len(field_data.get('units', [])) for field_data in self.unit_data.values())
|
221
|
-
|
222
|
-
return [
|
223
|
-
'# Statistics',
|
224
|
-
f'TOTAL_UNITS = {total_units}',
|
225
|
-
f'TOTAL_FIELDS = {len(self.unit_data)}',
|
226
|
-
f'TOTAL_DIMENSIONS = {len(self.dimension_constants)}',
|
227
|
-
]
|
228
|
-
|
229
|
-
def generate(self) -> None:
|
230
|
-
"""Generate the complete units.py file."""
|
231
|
-
# First pass: collect all dimension constants
|
232
|
-
for field_name in self.unit_data.keys():
|
233
|
-
self.get_dimension_constant_name(field_name)
|
234
|
-
self.get_class_name(field_name)
|
235
|
-
|
236
|
-
# Build file content
|
237
|
-
lines = []
|
238
|
-
lines.extend(self.generate_header())
|
239
|
-
|
240
|
-
# Generate unit classes for each field
|
241
|
-
for field_name, field_data in sorted(self.unit_data.items()):
|
242
|
-
lines.extend(self.generate_unit_class(field_name, field_data))
|
243
|
-
|
244
|
-
lines.extend(self.generate_registry_function())
|
245
|
-
lines.extend(self.generate_exports())
|
246
|
-
lines.extend(self.generate_statistics())
|
247
|
-
|
248
|
-
# Write the file
|
249
|
-
content = '\n'.join(lines) + '\n'
|
250
|
-
self.output_path.write_text(content, encoding='utf-8')
|
251
|
-
print(f"Generated {self.output_path}")
|
252
|
-
|
253
|
-
# Save metadata
|
254
|
-
metadata = {
|
255
|
-
'total_units': sum(len(field_data.get('units', [])) for field_data in self.unit_data.values()),
|
256
|
-
'total_fields': len(self.unit_data),
|
257
|
-
'total_dimensions': len(self.dimension_constants),
|
258
|
-
'dimension_constants': sorted(self.dimension_constants),
|
259
|
-
'unit_classes': sorted(self.field_to_class_mapping.values()),
|
260
|
-
}
|
261
|
-
|
262
|
-
metadata_path = self.out_dir / 'units_metadata.json'
|
263
|
-
with open(metadata_path, 'w', encoding='utf-8') as f:
|
264
|
-
json.dump(metadata, f, indent=2)
|
265
|
-
print(f"Saved metadata to {metadata_path}")
|
266
|
-
|
267
|
-
|
268
|
-
def main() -> None:
|
269
|
-
"""Main entry point."""
|
270
|
-
# Set up paths
|
271
|
-
generator_dir = Path(__file__).parent
|
272
|
-
data_path = generator_dir / 'data' / 'unit_data.json'
|
273
|
-
output_path = generator_dir.parent.parent / 'generated' / 'units.py'
|
274
|
-
out_dir = generator_dir / 'out'
|
275
|
-
|
276
|
-
# Create output directory if needed
|
277
|
-
output_path.parent.mkdir(parents=True, exist_ok=True)
|
278
|
-
|
279
|
-
# Check if data file exists
|
280
|
-
if not data_path.exists():
|
281
|
-
print(f"Error: Data file not found at {data_path}")
|
282
|
-
return
|
283
|
-
|
284
|
-
# Run generator
|
285
|
-
generator = UnitsGenerator(data_path, output_path, out_dir)
|
286
|
-
generator.generate()
|
287
|
-
|
288
|
-
print("\nUnits generation complete!")
|
289
|
-
print(f" - Total units: {sum(len(field_data.get('units', [])) for field_data in generator.unit_data.values())}")
|
290
|
-
print(f" - Total fields: {len(generator.unit_data)}")
|
291
|
-
print(f" - Unit classes: {len(generator.field_to_class_mapping)}")
|
292
|
-
|
293
|
-
|
294
|
-
if __name__ == "__main__":
|
295
|
-
main()
|
qnty/expressions/cache.py
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Expression Caching System
|
3
|
-
========================
|
4
|
-
|
5
|
-
Caching infrastructure for optimized expression evaluation and type checking.
|
6
|
-
"""
|
7
|
-
|
8
|
-
from typing import TYPE_CHECKING, Union
|
9
|
-
|
10
|
-
if TYPE_CHECKING:
|
11
|
-
from ..quantities.quantity import Quantity, TypeSafeVariable
|
12
|
-
from .nodes import Expression
|
13
|
-
|
14
|
-
# Import here to avoid circular imports - delayed imports
|
15
|
-
from ..generated.units import DimensionlessUnits
|
16
|
-
from ..quantities.quantity import Quantity, TypeSafeVariable
|
17
|
-
|
18
|
-
# Cache for common types to avoid repeated type checks
|
19
|
-
_NUMERIC_TYPES = (int, float)
|
20
|
-
_DIMENSIONLESS_CONSTANT = None
|
21
|
-
_CACHED_DIMENSIONLESS_QUANTITIES = {} # Cache for common numeric values
|
22
|
-
_MAX_CACHE_SIZE = 50 # Limit cache size to prevent memory bloat
|
23
|
-
_TYPE_CHECK_CACHE = {} # Cache for expensive isinstance checks
|
24
|
-
|
25
|
-
# Expression evaluation cache for repeated operations
|
26
|
-
_EXPRESSION_RESULT_CACHE = {}
|
27
|
-
_MAX_EXPRESSION_CACHE_SIZE = 200
|
28
|
-
|
29
|
-
|
30
|
-
def _get_cached_dimensionless():
|
31
|
-
"""Get cached dimensionless constant for numeric values."""
|
32
|
-
global _DIMENSIONLESS_CONSTANT
|
33
|
-
if _DIMENSIONLESS_CONSTANT is None:
|
34
|
-
_DIMENSIONLESS_CONSTANT = DimensionlessUnits.dimensionless
|
35
|
-
return _DIMENSIONLESS_CONSTANT
|
36
|
-
|
37
|
-
|
38
|
-
def _get_dimensionless_quantity(value: float) -> 'Quantity':
|
39
|
-
"""Get cached dimensionless quantity for common numeric values."""
|
40
|
-
if value in _CACHED_DIMENSIONLESS_QUANTITIES:
|
41
|
-
return _CACHED_DIMENSIONLESS_QUANTITIES[value]
|
42
|
-
|
43
|
-
# Cache common values with size limit
|
44
|
-
if len(_CACHED_DIMENSIONLESS_QUANTITIES) < _MAX_CACHE_SIZE and -10 <= value <= 10:
|
45
|
-
qty = Quantity(value, _get_cached_dimensionless())
|
46
|
-
_CACHED_DIMENSIONLESS_QUANTITIES[value] = qty
|
47
|
-
return qty
|
48
|
-
|
49
|
-
# Don't cache uncommon values
|
50
|
-
return Quantity(value, _get_cached_dimensionless())
|
51
|
-
|
52
|
-
|
53
|
-
def _is_numeric_type(obj) -> bool:
|
54
|
-
"""Cached type check for numeric types."""
|
55
|
-
obj_type = type(obj)
|
56
|
-
if obj_type not in _TYPE_CHECK_CACHE:
|
57
|
-
_TYPE_CHECK_CACHE[obj_type] = obj_type in _NUMERIC_TYPES
|
58
|
-
return _TYPE_CHECK_CACHE[obj_type]
|
59
|
-
|
60
|
-
|
61
|
-
def wrap_operand(operand: Union['Expression', 'TypeSafeVariable', 'Quantity', int, float]) -> 'Expression':
|
62
|
-
"""
|
63
|
-
Optimized operand wrapping with cached type checks.
|
64
|
-
|
65
|
-
This function uses cached type checks for maximum performance.
|
66
|
-
"""
|
67
|
-
# Import Expression classes to avoid circular imports
|
68
|
-
from .nodes import Constant, Expression, VariableReference
|
69
|
-
|
70
|
-
# Fast path: check most common cases first using cached type check
|
71
|
-
if _is_numeric_type(operand):
|
72
|
-
# operand is guaranteed to be int or float at this point
|
73
|
-
return Constant(_get_dimensionless_quantity(float(operand))) # type: ignore[arg-type]
|
74
|
-
|
75
|
-
# Check if already an Expression (using isinstance for speed)
|
76
|
-
if isinstance(operand, Expression):
|
77
|
-
return operand
|
78
|
-
|
79
|
-
# Check for FastQuantity
|
80
|
-
if isinstance(operand, Quantity):
|
81
|
-
return Constant(operand)
|
82
|
-
|
83
|
-
# Check for TypeSafeVariable
|
84
|
-
if isinstance(operand, TypeSafeVariable):
|
85
|
-
return VariableReference(operand)
|
86
|
-
|
87
|
-
# Check for ConfigurableVariable (from composition system)
|
88
|
-
if hasattr(operand, '_variable'):
|
89
|
-
var = getattr(operand, '_variable', None)
|
90
|
-
if isinstance(var, TypeSafeVariable):
|
91
|
-
return VariableReference(var)
|
92
|
-
|
93
|
-
# No duck typing - fail fast for unknown types
|
94
|
-
raise TypeError(f"Cannot convert {type(operand)} to Expression")
|