qnty 0.0.8__py3-none-any.whl → 0.0.9__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.
Files changed (74) hide show
  1. qnty/__init__.py +140 -58
  2. qnty/_backup/problem_original.py +1251 -0
  3. qnty/_backup/quantity.py +63 -0
  4. qnty/codegen/cli.py +125 -0
  5. qnty/codegen/generators/data/unit_data.json +8807 -0
  6. qnty/codegen/generators/data_processor.py +345 -0
  7. qnty/codegen/generators/dimensions_gen.py +434 -0
  8. qnty/codegen/generators/doc_generator.py +141 -0
  9. qnty/codegen/generators/out/dimension_mapping.json +974 -0
  10. qnty/codegen/generators/out/dimension_metadata.json +123 -0
  11. qnty/codegen/generators/out/units_metadata.json +223 -0
  12. qnty/codegen/generators/quantities_gen.py +159 -0
  13. qnty/codegen/generators/setters_gen.py +178 -0
  14. qnty/codegen/generators/stubs_gen.py +167 -0
  15. qnty/codegen/generators/units_gen.py +295 -0
  16. qnty/codegen/generators/utils/__init__.py +0 -0
  17. qnty/equations/__init__.py +4 -0
  18. qnty/{equation.py → equations/equation.py} +78 -118
  19. qnty/equations/system.py +127 -0
  20. qnty/expressions/__init__.py +61 -0
  21. qnty/expressions/cache.py +94 -0
  22. qnty/expressions/functions.py +96 -0
  23. qnty/{expression.py → expressions/nodes.py} +209 -216
  24. qnty/generated/__init__.py +0 -0
  25. qnty/generated/dimensions.py +514 -0
  26. qnty/generated/quantities.py +6003 -0
  27. qnty/generated/quantities.pyi +4192 -0
  28. qnty/generated/setters.py +12210 -0
  29. qnty/generated/units.py +9798 -0
  30. qnty/problem/__init__.py +91 -0
  31. qnty/problem/base.py +142 -0
  32. qnty/problem/composition.py +385 -0
  33. qnty/problem/composition_mixin.py +382 -0
  34. qnty/problem/equations.py +413 -0
  35. qnty/problem/metaclass.py +302 -0
  36. qnty/problem/reconstruction.py +1016 -0
  37. qnty/problem/solving.py +180 -0
  38. qnty/problem/validation.py +64 -0
  39. qnty/problem/variables.py +239 -0
  40. qnty/quantities/__init__.py +6 -0
  41. qnty/quantities/expression_quantity.py +314 -0
  42. qnty/quantities/quantity.py +428 -0
  43. qnty/quantities/typed_quantity.py +215 -0
  44. qnty/solving/__init__.py +0 -0
  45. qnty/solving/manager.py +90 -0
  46. qnty/solving/order.py +355 -0
  47. qnty/solving/solvers/__init__.py +20 -0
  48. qnty/solving/solvers/base.py +92 -0
  49. qnty/solving/solvers/iterative.py +185 -0
  50. qnty/solving/solvers/simultaneous.py +547 -0
  51. qnty/units/__init__.py +0 -0
  52. qnty/{prefixes.py → units/prefixes.py} +54 -33
  53. qnty/{unit.py → units/registry.py} +73 -32
  54. qnty/utils/__init__.py +0 -0
  55. qnty/utils/logging.py +40 -0
  56. qnty/validation/__init__.py +0 -0
  57. qnty/validation/registry.py +0 -0
  58. qnty/validation/rules.py +167 -0
  59. qnty-0.0.9.dist-info/METADATA +199 -0
  60. qnty-0.0.9.dist-info/RECORD +63 -0
  61. qnty/dimension.py +0 -186
  62. qnty/unit_types/base.py +0 -47
  63. qnty/units.py +0 -8113
  64. qnty/variable.py +0 -300
  65. qnty/variable_types/base.py +0 -58
  66. qnty/variable_types/expression_variable.py +0 -106
  67. qnty/variable_types/typed_variable.py +0 -87
  68. qnty/variables.py +0 -2298
  69. qnty/variables.pyi +0 -6148
  70. qnty-0.0.8.dist-info/METADATA +0 -355
  71. qnty-0.0.8.dist-info/RECORD +0 -19
  72. /qnty/{unit_types → codegen}/__init__.py +0 -0
  73. /qnty/{variable_types → codegen/generators}/__init__.py +0 -0
  74. {qnty-0.0.8.dist-info → qnty-0.0.9.dist-info}/WHEEL +0 -0
@@ -0,0 +1,434 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Dimension generator for qnty library.
4
+
5
+ This script generates the dimensions.py file from unit_data.json, creating
6
+ all necessary dimension signatures programmatically without hardcoding.
7
+ """
8
+
9
+ import json
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ # Configuration
14
+ BASE_DIMENSIONS = {
15
+ 'LENGTH': {'prime': 2, 'params': {'length': 1}},
16
+ 'MASS': {'prime': 3, 'params': {'mass': 1}},
17
+ 'TIME': {'prime': 5, 'params': {'time': 1}},
18
+ 'CURRENT': {'prime': 7, 'params': {'current': 1}},
19
+ 'TEMPERATURE': {'prime': 11, 'params': {'temp': 1}},
20
+ 'AMOUNT': {'prime': 13, 'params': {'amount': 1}},
21
+ 'LUMINOSITY': {'prime': 17, 'params': {'luminosity': 1}},
22
+ 'DIMENSIONLESS': {'prime': 1, 'params': {}},
23
+ }
24
+
25
+ DIMENSION_SYMBOLS = {
26
+ 'length': 'L',
27
+ 'mass': 'M',
28
+ 'time': 'T',
29
+ 'current': 'A',
30
+ 'temp': 'Θ',
31
+ 'amount': 'N',
32
+ 'luminosity': 'J',
33
+ }
34
+
35
+ # All dimensions will be explicitly defined - no lazy loading needed
36
+
37
+
38
+ class DimensionGenerator:
39
+ """Generator for dimensions.py file."""
40
+
41
+ def __init__(self, data_path: Path, output_path: Path, out_dir: Path):
42
+ """Initialize with paths."""
43
+ self.data_path = data_path
44
+ self.output_path = output_path
45
+ self.out_dir = out_dir
46
+ self.out_dir.mkdir(parents=True, exist_ok=True)
47
+
48
+ # Load unit data
49
+ with open(self.data_path, encoding='utf-8') as f:
50
+ self.unit_data: dict[str, Any] = json.load(f)
51
+
52
+ # Track all discovered dimensions
53
+ self.all_dimensions: dict[str, dict[str, int]] = {}
54
+ self.common_signatures: dict[tuple[int, ...], float] = {} # For cache optimization
55
+
56
+ def calculate_signature(self, dims: dict[str, int]) -> float:
57
+ """Calculate prime factorization signature for dimensions."""
58
+ if not dims:
59
+ return 1.0
60
+
61
+ signature = 1.0
62
+ prime_map = {
63
+ 'length': 2,
64
+ 'mass': 3,
65
+ 'time': 5,
66
+ 'current': 7,
67
+ 'temp': 11,
68
+ 'amount': 13,
69
+ 'luminosity': 17,
70
+ }
71
+
72
+ for dim_name, power in dims.items():
73
+ if power != 0 and dim_name in prime_map:
74
+ signature *= prime_map[dim_name] ** power
75
+
76
+ return signature
77
+
78
+ def tuple_from_dims(self, dims: dict[str, int]) -> tuple[int, ...]:
79
+ """Create ordered tuple for dimension cache key."""
80
+ return (
81
+ dims.get('length', 0),
82
+ dims.get('mass', 0),
83
+ dims.get('time', 0),
84
+ dims.get('current', 0),
85
+ dims.get('temp', 0),
86
+ dims.get('amount', 0),
87
+ dims.get('luminosity', 0),
88
+ )
89
+
90
+ def format_dimension_comment(self, dims: dict[str, int]) -> str:
91
+ """Create readable comment for a dimension."""
92
+ if not dims:
93
+ return "Dimensionless"
94
+
95
+ parts = []
96
+ for dim, power in sorted(dims.items()):
97
+ if power != 0:
98
+ symbol = DIMENSION_SYMBOLS.get(dim, dim[0].upper())
99
+ if power == 1:
100
+ parts.append(symbol)
101
+ else:
102
+ parts.append(f"{symbol}^{power}")
103
+
104
+ return ' '.join(parts) if parts else "Dimensionless"
105
+
106
+ def extract_all_dimensions(self) -> None:
107
+ """Extract all unique dimensions from unit data."""
108
+ # Add base dimensions
109
+ for name, config in BASE_DIMENSIONS.items():
110
+ self.all_dimensions[name] = config['params']
111
+
112
+ # Extract from unit data
113
+ for field_name, field_data in self.unit_data.items():
114
+ dims = field_data.get('dimensions', {})
115
+ name = field_name.upper()
116
+
117
+ # Skip if it's a base dimension
118
+ if name not in BASE_DIMENSIONS:
119
+ self.all_dimensions[name] = dims
120
+
121
+ # Build common signatures cache (programmatically)
122
+ for _name, dims in self.all_dimensions.items():
123
+ dim_tuple = self.tuple_from_dims(dims)
124
+ signature = self.calculate_signature(dims)
125
+ self.common_signatures[dim_tuple] = signature
126
+
127
+ def generate_common_signatures_dict(self) -> list[str]:
128
+ """Generate the _COMMON_SIGNATURES dictionary programmatically."""
129
+ lines = []
130
+ lines.append(" # Pre-computed signature cache for common dimensions")
131
+ lines.append(" _COMMON_SIGNATURES: ClassVar[dict[tuple[int, ...], int | float]] = {")
132
+
133
+ # Sort by complexity (number of non-zero dimensions) and then alphabetically
134
+ sorted_sigs = sorted(self.common_signatures.items(),
135
+ key=lambda x: (sum(abs(v) for v in x[0]), x[0]))
136
+
137
+ # Limit to most common ones to avoid huge cache
138
+ max_cache_entries = 50
139
+ for dim_tuple, signature in sorted_sigs[:max_cache_entries]:
140
+ # Find dimensions with this signature for comment
141
+ matching_dims = [name for name, dims in self.all_dimensions.items()
142
+ if self.tuple_from_dims(dims) == dim_tuple]
143
+
144
+ if matching_dims:
145
+ # Pick the shortest/most descriptive name
146
+ best_name = min(matching_dims, key=len)
147
+ dims = self.all_dimensions[best_name]
148
+ comment = self.format_dimension_comment(dims)
149
+
150
+ # Format the signature value nicely
151
+ if signature == int(signature):
152
+ sig_str = str(int(signature))
153
+ else:
154
+ sig_str = f"{signature:.10g}" # Use general format to avoid long decimals
155
+
156
+ lines.append(f" {dim_tuple}: {sig_str}, # {comment}")
157
+
158
+ lines.append(" }")
159
+
160
+ return lines
161
+
162
+ def generate_header(self) -> list[str]:
163
+ """Generate the file header."""
164
+ lines = [
165
+ '"""',
166
+ 'Dimension System',
167
+ '================',
168
+ '',
169
+ 'Compile-time dimensional analysis using type system for ultra-fast operations.',
170
+ '',
171
+ 'This file is auto-generated by codegen/generators/dimensions_gen.py',
172
+ 'DO NOT EDIT MANUALLY - changes will be overwritten.',
173
+ '"""',
174
+ '',
175
+ 'from dataclasses import dataclass',
176
+ 'from enum import IntEnum',
177
+ 'from typing import ClassVar, final',
178
+ '',
179
+ '',
180
+ ]
181
+ return lines
182
+
183
+ def generate_base_dimension_class(self) -> list[str]:
184
+ """Generate the BaseDimension IntEnum."""
185
+ lines = [
186
+ 'class BaseDimension(IntEnum):',
187
+ ' """Base dimensions as prime numbers for efficient bit operations."""',
188
+ ]
189
+
190
+ for name, config in BASE_DIMENSIONS.items():
191
+ if name == 'DIMENSIONLESS':
192
+ lines.append(f" {name} = {config['prime']} # Must be 1 to act as multiplicative identity")
193
+ else:
194
+ lines.append(f" {name} = {config['prime']}")
195
+
196
+ lines.extend(['', ''])
197
+ return lines
198
+
199
+ def generate_dimension_signature_class(self) -> list[str]:
200
+ """Generate the DimensionSignature class."""
201
+ lines = [
202
+ '@final',
203
+ '@dataclass(frozen=True, slots=True)',
204
+ 'class DimensionSignature:',
205
+ ' """Immutable dimension signature for zero-cost dimensional analysis."""',
206
+ ' ',
207
+ ' # Store as bit pattern for ultra-fast comparison',
208
+ ' _signature: int | float = 1',
209
+ ' ',
210
+ ]
211
+
212
+ # Add common signatures cache
213
+ lines.extend(self.generate_common_signatures_dict())
214
+
215
+ lines.extend([
216
+ ' ',
217
+ ' # Instance cache for interning common dimensions',
218
+ ' _INSTANCE_CACHE: ClassVar[dict[int | float, "DimensionSignature"]] = {}',
219
+ ' ',
220
+ ' def __new__(cls, signature: int | float = 1):',
221
+ ' """Optimized constructor with instance interning."""',
222
+ ' if signature in cls._INSTANCE_CACHE:',
223
+ ' return cls._INSTANCE_CACHE[signature]',
224
+ ' ',
225
+ ' instance = object.__new__(cls)',
226
+ ' ',
227
+ ' # Cache common signatures',
228
+ ' if len(cls._INSTANCE_CACHE) < 100: # Limit cache size',
229
+ ' cls._INSTANCE_CACHE[signature] = instance',
230
+ ' ',
231
+ ' return instance',
232
+ ' ',
233
+ ' @classmethod',
234
+ ' def create(cls, length=0, mass=0, time=0, current=0, temp=0, amount=0, luminosity=0):',
235
+ ' """Create dimension from exponents with optimized lookup."""',
236
+ ' # Check cache first',
237
+ ' key = (length, mass, time, current, temp, amount, luminosity)',
238
+ ' if key in cls._COMMON_SIGNATURES:',
239
+ ' return cls(cls._COMMON_SIGNATURES[key])',
240
+ ' ',
241
+ ' # Fast path for dimensionless',
242
+ ' if not any([length, mass, time, current, temp, amount, luminosity]):',
243
+ ' return cls(1)',
244
+ ' ',
245
+ ' # Compute signature',
246
+ ' signature = 1',
247
+ ' if length != 0:',
248
+ ' signature *= BaseDimension.LENGTH ** length',
249
+ ' if mass != 0:',
250
+ ' signature *= BaseDimension.MASS ** mass',
251
+ ' if time != 0:',
252
+ ' signature *= BaseDimension.TIME ** time',
253
+ ' if current != 0:',
254
+ ' signature *= BaseDimension.CURRENT ** current',
255
+ ' if temp != 0:',
256
+ ' signature *= BaseDimension.TEMPERATURE ** temp',
257
+ ' if amount != 0:',
258
+ ' signature *= BaseDimension.AMOUNT ** amount',
259
+ ' if luminosity != 0:',
260
+ ' signature *= BaseDimension.LUMINOSITY ** luminosity',
261
+ ' ',
262
+ ' return cls(signature)',
263
+ ' ',
264
+ ' def __mul__(self, other):',
265
+ ' """Multiply dimensions."""',
266
+ ' return DimensionSignature(self._signature * other._signature)',
267
+ ' ',
268
+ ' def __truediv__(self, other):',
269
+ ' """Divide dimensions."""',
270
+ ' return DimensionSignature(self._signature / other._signature)',
271
+ ' ',
272
+ ' def __pow__(self, power):',
273
+ ' """Raise dimension to a power."""',
274
+ ' if power == 1:',
275
+ ' return self',
276
+ ' if power == 0:',
277
+ ' return DimensionSignature(1)',
278
+ ' return DimensionSignature(self._signature ** power)',
279
+ ' ',
280
+ ' def is_compatible(self, other):',
281
+ ' """Check dimensional compatibility."""',
282
+ ' return self._signature == other._signature',
283
+ ' ',
284
+ ' def __eq__(self, other):',
285
+ ' """Check equality."""',
286
+ ' if self is other:',
287
+ ' return True',
288
+ ' return isinstance(other, DimensionSignature) and self._signature == other._signature',
289
+ ' ',
290
+ ' def __hash__(self):',
291
+ ' """Hash based on signature."""',
292
+ ' return hash(self._signature)',
293
+ '',
294
+ '',
295
+ ])
296
+
297
+ return lines
298
+
299
+ def generate_dimension_constants(self) -> list[str]:
300
+ """Generate dimension constant definitions."""
301
+ lines = []
302
+
303
+ # Generate signature lookup dictionary
304
+ lines.append('# Pre-computed dimension signatures for all dimensions')
305
+ lines.append('_DIMENSION_SIGNATURES = {')
306
+
307
+ for name in sorted(self.all_dimensions.keys()):
308
+ dims = self.all_dimensions[name]
309
+ signature = self.calculate_signature(dims)
310
+ comment = self.format_dimension_comment(dims)
311
+
312
+ if signature == int(signature):
313
+ sig_str = str(int(signature))
314
+ else:
315
+ sig_str = f"{signature:.10g}"
316
+
317
+ lines.append(f' "{name}": {sig_str}, # {comment}')
318
+
319
+ lines.append('}')
320
+ lines.append('')
321
+
322
+ # Lazy loading infrastructure
323
+ lines.extend([
324
+ '# Lazy loading cache',
325
+ '_dimension_cache: dict[str, DimensionSignature] = {}',
326
+ '',
327
+ 'def __getattr__(name: str) -> DimensionSignature:',
328
+ ' """Lazy load dimension constants."""',
329
+ ' if name in _DIMENSION_SIGNATURES:',
330
+ ' if name not in _dimension_cache:',
331
+ ' _dimension_cache[name] = DimensionSignature(_DIMENSION_SIGNATURES[name])',
332
+ ' return _dimension_cache[name]',
333
+ ' raise AttributeError(f"module {__name__!r} has no attribute {name!r}")',
334
+ '',
335
+ ])
336
+
337
+ # Generate ALL dimensions found in unit data - no hardcoded mappings
338
+ lines.append('# All dimension constants generated from unit data')
339
+
340
+ # Generate constants for ALL fields in the JSON data
341
+ for field_name in sorted(self.unit_data.keys()):
342
+ field_data = self.unit_data[field_name]
343
+ dims = field_data.get('dimensions', {})
344
+ const_name = field_name.upper()
345
+
346
+ signature = self.calculate_signature(dims)
347
+ comment = self.format_dimension_comment(dims)
348
+
349
+ if signature == int(signature):
350
+ sig_str = str(int(signature))
351
+ else:
352
+ sig_str = f"{signature:.10g}"
353
+
354
+ lines.append(f'{const_name} = DimensionSignature({sig_str}) # {comment}')
355
+
356
+ lines.append('')
357
+
358
+ return lines
359
+
360
+ def generate_exports(self) -> list[str]:
361
+ """Generate __all__ export list."""
362
+ lines = [
363
+ '# Module exports',
364
+ '__all__ = [',
365
+ ' "BaseDimension",',
366
+ ' "DimensionSignature",',
367
+ ]
368
+
369
+ # Add all dimensions from unit data
370
+ for field_name in sorted(self.unit_data.keys()):
371
+ const_name = field_name.upper()
372
+ lines.append(f' "{const_name}",')
373
+
374
+ lines.extend([
375
+ ']',
376
+ '',
377
+ ])
378
+
379
+ return lines
380
+
381
+ def generate(self) -> None:
382
+ """Generate the complete dimensions.py file."""
383
+ # Extract all dimensions from data
384
+ self.extract_all_dimensions()
385
+
386
+ # Build the file content
387
+ lines = []
388
+ lines.extend(self.generate_header())
389
+ lines.extend(self.generate_base_dimension_class())
390
+ lines.extend(self.generate_dimension_signature_class())
391
+ lines.extend(self.generate_dimension_constants())
392
+ lines.extend(self.generate_exports())
393
+
394
+ # Write the file with newline at end
395
+ content = '\n'.join(lines) + '\n'
396
+ self.output_path.write_text(content, encoding='utf-8')
397
+ print(f"Generated {self.output_path}")
398
+
399
+ # Save metadata to out directory
400
+ metadata = {
401
+ 'total_dimensions': len(self.all_dimensions),
402
+ 'generated_dimensions': sorted(self.unit_data.keys()),
403
+ 'base_dimensions': list(BASE_DIMENSIONS.keys()),
404
+ 'signatures_cached': len(self.common_signatures),
405
+ }
406
+
407
+ metadata_path = self.out_dir / 'dimension_metadata.json'
408
+ with open(metadata_path, 'w', encoding='utf-8') as f:
409
+ json.dump(metadata, f, indent=2)
410
+ print(f"Saved metadata to {metadata_path}")
411
+
412
+
413
+ def main() -> None:
414
+ """Main entry point."""
415
+ # Set up paths
416
+ generator_dir = Path(__file__).parent
417
+ data_path = generator_dir / 'data' / 'unit_data.json'
418
+ output_path = generator_dir.parent.parent / 'generated' / 'dimensions.py'
419
+ out_dir = generator_dir / 'out'
420
+
421
+ # Create output directory if needed
422
+ output_path.parent.mkdir(parents=True, exist_ok=True)
423
+
424
+ # Run generator
425
+ generator = DimensionGenerator(data_path, output_path, out_dir)
426
+ generator.generate()
427
+
428
+ print("\nDimension generation complete!")
429
+ print(f" - Total dimensions: {len(generator.all_dimensions)}")
430
+ print(f" - Cached signatures: {len(generator.common_signatures)}")
431
+
432
+
433
+ if __name__ == "__main__":
434
+ main()
@@ -0,0 +1,141 @@
1
+ """
2
+ Documentation Generation Helper
3
+ ==============================
4
+
5
+ Shared functions for generating consistent documentation across
6
+ quantities.py and quantities.pyi files.
7
+ """
8
+
9
+ try:
10
+ from .data_processor import get_unit_names_and_aliases
11
+ except ImportError:
12
+ # Handle standalone execution
13
+ from .data_processor import get_unit_names_and_aliases
14
+
15
+
16
+ def generate_class_docstring(class_name: str, display_name: str, units: list, is_dimensionless: bool = False) -> list[str]:
17
+ """Generate comprehensive class docstring for quantity classes."""
18
+ # Get example units for documentation
19
+ example_units = []
20
+ for unit in units[:3]: # Take first 3 units as examples
21
+ primary_name, _ = get_unit_names_and_aliases(unit)
22
+ example_units.append(f'"{primary_name}"')
23
+
24
+ unit_examples = ', '.join(example_units) if example_units else '"unit"'
25
+
26
+ lines = [
27
+ ' """',
28
+ f' Type-safe {display_name} quantity with expression capabilities.',
29
+ ' ',
30
+ ]
31
+
32
+ if is_dimensionless:
33
+ lines.extend([
34
+ ' Constructor Options:',
35
+ ' -------------------',
36
+ f' - {class_name}("variable_name") -> Create unknown {display_name}',
37
+ f' - {class_name}(value, "variable_name") -> Create known {display_name}',
38
+ ' ',
39
+ ' Examples:',
40
+ ' ---------',
41
+ f' >>> unknown = {class_name}("efficiency") # Unknown {display_name}',
42
+ f' >>> known = {class_name}(0.85, "thermal_efficiency") # Known {display_name}',
43
+ ])
44
+ else:
45
+ lines.extend([
46
+ ' Constructor Options:',
47
+ ' -------------------',
48
+ f' - {class_name}("variable_name") -> Create unknown {display_name}',
49
+ f' - {class_name}(value, "unit", "variable_name") -> Create known {display_name}',
50
+ ' ',
51
+ ' Examples:',
52
+ ' ---------',
53
+ f' >>> unknown = {class_name}("pressure") # Unknown {display_name}',
54
+ f' >>> known = {class_name}(100, {unit_examples.split(",")[0] if unit_examples else "unit"}, "inlet_pressure") # Known {display_name}',
55
+ ' ',
56
+ f' Available units: {unit_examples}',
57
+ ])
58
+
59
+ lines.append(' """')
60
+ return lines
61
+
62
+
63
+ def generate_init_method(class_name: str, display_name: str, is_dimensionless: bool = False, stub_only: bool = False) -> list[str]:
64
+ """Generate __init__ method with proper type hints."""
65
+ del class_name # Unused but kept for API compatibility
66
+ lines = []
67
+
68
+ if is_dimensionless:
69
+ lines.append(' def __init__(self, name_or_value: str | int | float, name: str | None = None, is_known: bool = True):')
70
+ if not stub_only:
71
+ lines.extend([
72
+ ' """',
73
+ f' Initialize {display_name} quantity.',
74
+ ' ',
75
+ ' Args:',
76
+ ' name_or_value: Variable name (str) if unknown, or value (int/float) if known',
77
+ ' name: Variable name (required if providing value)',
78
+ ' is_known: Whether the variable has a known value',
79
+ ' """',
80
+ ' if name is None:',
81
+ ' # Single argument: name only (unknown variable)',
82
+ ' super().__init__(name_or_value, is_known=is_known)',
83
+ ' else:',
84
+ ' # Two arguments: value and name (known variable)',
85
+ ' super().__init__(name_or_value, name, is_known=is_known)',
86
+ ])
87
+ else:
88
+ lines.append(' ...')
89
+ else:
90
+ lines.append(' def __init__(self, name_or_value: str | int | float, unit: str | None = None, name: str | None = None, is_known: bool = True):')
91
+ if not stub_only:
92
+ lines.extend([
93
+ ' """',
94
+ f' Initialize {display_name} quantity.',
95
+ ' ',
96
+ ' Args:',
97
+ ' name_or_value: Variable name (str) if unknown, or value (int/float) if known',
98
+ ' unit: Unit string (required if providing value)',
99
+ ' name: Variable name (required if providing value)',
100
+ ' is_known: Whether the variable has a known value',
101
+ ' """',
102
+ ' if unit is None and name is None:',
103
+ ' # Single argument: name only (unknown variable)',
104
+ ' super().__init__(name_or_value, is_known=is_known)',
105
+ ' elif unit is not None and name is not None:',
106
+ ' # Three arguments: value, unit, name (known variable)',
107
+ ' super().__init__(name_or_value, unit, name, is_known=is_known)',
108
+ ' else:',
109
+ ' raise ValueError("Must provide either just name (unknown) or value, unit, and name (known)")',
110
+ ])
111
+ else:
112
+ lines.append(' ...')
113
+
114
+ return lines
115
+
116
+
117
+ def generate_set_method(setter_class_name: str, display_name: str, stub_only: bool = False) -> list[str]:
118
+ """Generate set method with comprehensive documentation."""
119
+ lines = [
120
+ f' def set(self, value: int | float) -> ts.{setter_class_name}:',
121
+ ' """',
122
+ f' Create a setter for this {display_name} quantity.',
123
+ ' ',
124
+ ' Args:',
125
+ ' value: The numeric value to set',
126
+ ' ',
127
+ ' Returns:',
128
+ f' {setter_class_name}: A setter with unit properties like .meters, .inches, etc.',
129
+ ' ',
130
+ ' Example:',
131
+ ' >>> length = Length("beam_length")',
132
+ ' >>> length.set(100).millimeters # Sets to 100 mm',
133
+ ' """',
134
+ ]
135
+
136
+ if stub_only:
137
+ lines.append(' ...')
138
+ else:
139
+ lines.append(f' return ts.{setter_class_name}(self, value)')
140
+
141
+ return lines