ucon 0.6.2__tar.gz → 0.6.3__tar.gz

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 (69) hide show
  1. {ucon-0.6.2 → ucon-0.6.3}/PKG-INFO +1 -1
  2. {ucon-0.6.2 → ucon-0.6.3}/ROADMAP.md +1 -0
  3. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_unit_parsing.py +43 -0
  4. {ucon-0.6.2 → ucon-0.6.3}/ucon/units.py +16 -3
  5. {ucon-0.6.2 → ucon-0.6.3}/ucon.egg-info/PKG-INFO +1 -1
  6. {ucon-0.6.2 → ucon-0.6.3}/.github/workflows/publish.yaml +0 -0
  7. {ucon-0.6.2 → ucon-0.6.3}/.github/workflows/tests.yaml +0 -0
  8. {ucon-0.6.2 → ucon-0.6.3}/.gitignore +0 -0
  9. {ucon-0.6.2 → ucon-0.6.3}/LICENSE +0 -0
  10. {ucon-0.6.2 → ucon-0.6.3}/Makefile +0 -0
  11. {ucon-0.6.2 → ucon-0.6.3}/NOTICE +0 -0
  12. {ucon-0.6.2 → ucon-0.6.3}/README.md +0 -0
  13. {ucon-0.6.2 → ucon-0.6.3}/docs/decisions/001-unity-distance-metric-for-nearest-scale.md +0 -0
  14. {ucon-0.6.2 → ucon-0.6.3}/docs/decisions/002-composite-units.md +0 -0
  15. {ucon-0.6.2 → ucon-0.6.3}/docs/decisions/003-composable-unit-algebra.md +0 -0
  16. {ucon-0.6.2 → ucon-0.6.3}/docs/decisions/004-unit-algebra-naming.md +0 -0
  17. {ucon-0.6.2 → ucon-0.6.3}/docs/decisions/005-pseudo-dimension-tuple-values.md +0 -0
  18. {ucon-0.6.2 → ucon-0.6.3}/docs/decisions/006-pydantic-integration-pattern.md +0 -0
  19. {ucon-0.6.2 → ucon-0.6.3}/docs/examples/basis-transform-fantasy-units.md +0 -0
  20. {ucon-0.6.2 → ucon-0.6.3}/docs/explainers/exponent-scale-relationship.md +0 -0
  21. {ucon-0.6.2 → ucon-0.6.3}/docs/explainers/type-operation-matrix.md +0 -0
  22. {ucon-0.6.2 → ucon-0.6.3}/docs/explainers/why-algebraic-closure-matters.md +0 -0
  23. {ucon-0.6.2 → ucon-0.6.3}/docs/explainers/why-type-safety-matters.md +0 -0
  24. {ucon-0.6.2 → ucon-0.6.3}/docs/proposals/interface-unifying-the-value-layer.md +0 -0
  25. {ucon-0.6.2 → ucon-0.6.3}/docs/proposals/project_unified-algebraic-core.md +0 -0
  26. {ucon-0.6.2 → ucon-0.6.3}/docs/proposals/support-for-fractional-exponents.md +0 -0
  27. {ucon-0.6.2 → ucon-0.6.3}/docs/proposals/unified-unit-presentation.md +0 -0
  28. {ucon-0.6.2 → ucon-0.6.3}/pyproject.toml +0 -0
  29. {ucon-0.6.2 → ucon-0.6.3}/requirements.txt +0 -0
  30. {ucon-0.6.2 → ucon-0.6.3}/setup.cfg +0 -0
  31. {ucon-0.6.2 → ucon-0.6.3}/setup.py +0 -0
  32. {ucon-0.6.2 → ucon-0.6.3}/tests/__init__.py +0 -0
  33. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/__init__.py +0 -0
  34. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/conversion/__init__.py +0 -0
  35. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/conversion/test_graph.py +0 -0
  36. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/conversion/test_map.py +0 -0
  37. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/mcp/__init__.py +0 -0
  38. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/mcp/test_server.py +0 -0
  39. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_algebra.py +0 -0
  40. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_basis_transform.py +0 -0
  41. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_core.py +0 -0
  42. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_default_graph_conversions.py +0 -0
  43. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_dimensionless_units.py +0 -0
  44. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_graph_basis_transform.py +0 -0
  45. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_logmap.py +0 -0
  46. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_nines.py +0 -0
  47. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_pickle.py +0 -0
  48. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_pydantic.py +0 -0
  49. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_quantity.py +0 -0
  50. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_rebased_unit.py +0 -0
  51. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_uncertainty.py +0 -0
  52. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_unit_system.py +0 -0
  53. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_units.py +0 -0
  54. {ucon-0.6.2 → ucon-0.6.3}/tests/ucon/test_vector_fraction.py +0 -0
  55. {ucon-0.6.2 → ucon-0.6.3}/ucon/__init__.py +0 -0
  56. {ucon-0.6.2 → ucon-0.6.3}/ucon/algebra.py +0 -0
  57. {ucon-0.6.2 → ucon-0.6.3}/ucon/core.py +0 -0
  58. {ucon-0.6.2 → ucon-0.6.3}/ucon/graph.py +0 -0
  59. {ucon-0.6.2 → ucon-0.6.3}/ucon/maps.py +0 -0
  60. {ucon-0.6.2 → ucon-0.6.3}/ucon/mcp/__init__.py +0 -0
  61. {ucon-0.6.2 → ucon-0.6.3}/ucon/mcp/server.py +0 -0
  62. {ucon-0.6.2 → ucon-0.6.3}/ucon/pydantic.py +0 -0
  63. {ucon-0.6.2 → ucon-0.6.3}/ucon/quantity.py +0 -0
  64. {ucon-0.6.2 → ucon-0.6.3}/ucon.egg-info/SOURCES.txt +0 -0
  65. {ucon-0.6.2 → ucon-0.6.3}/ucon.egg-info/dependency_links.txt +0 -0
  66. {ucon-0.6.2 → ucon-0.6.3}/ucon.egg-info/entry_points.txt +0 -0
  67. {ucon-0.6.2 → ucon-0.6.3}/ucon.egg-info/requires.txt +0 -0
  68. {ucon-0.6.2 → ucon-0.6.3}/ucon.egg-info/top_level.txt +0 -0
  69. {ucon-0.6.2 → ucon-0.6.3}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ucon
3
- Version: 0.6.2
3
+ Version: 0.6.3
4
4
  Summary: A tool for dimensional analysis: a 'Unit CONverter'
5
5
  Home-page: https://github.com/withtwoemms/ucon
6
6
  Author: Emmanuel I. Obi
@@ -244,6 +244,7 @@ Building on v0.5.x baseline:
244
244
  - [x] `parse("kg * m / s^2")` → `UnitProduct` (completed in v0.6.0 via `get_unit_by_name()`)
245
245
  - [x] Alias resolution (`meters`, `metre`, `m` all work) (completed in v0.6.0)
246
246
  - [ ] Uncertainty parsing: `parse("1.234 ± 0.005 m")`
247
+ - [ ] Revisit priority alias architecture (v0.6.x uses `_PRIORITY_ALIASES` / `_PRIORITY_SCALED_ALIASES` for `min`, `mcg`; consider "exact match first" or longest-match strategy if list grows)
247
248
 
248
249
  **Outcomes:**
249
250
  - Human-friendly unit input for interactive and configuration use cases
@@ -329,5 +329,48 @@ class TestPriorityAliases(unittest.TestCase):
329
329
  self.assertEqual(result, units.inch)
330
330
 
331
331
 
332
+ class TestPriorityScaledAliases(unittest.TestCase):
333
+ """Test priority scaled aliases for domain-specific conventions.
334
+
335
+ Some domains use non-standard abbreviations that include an implicit
336
+ scale, like 'mcg' for microgram in medical contexts.
337
+ """
338
+
339
+ def test_mcg_is_microgram(self):
340
+ """'mcg' should parse as microgram (medical convention)."""
341
+ from ucon.core import Dimension
342
+ result = get_unit_by_name("mcg")
343
+ self.assertIsInstance(result, UnitProduct)
344
+ self.assertEqual(result.dimension, Dimension.mass)
345
+ self.assertAlmostEqual(result.fold_scale(), 1e-6, places=15)
346
+
347
+ def test_mcg_to_mg(self):
348
+ """Conversion from mcg to mg should work."""
349
+ from ucon.core import Number
350
+ dose = Number(500, unit=get_unit_by_name("mcg"))
351
+ result = dose.to(get_unit_by_name("mg"))
352
+ self.assertAlmostEqual(result.quantity, 0.5, places=9)
353
+
354
+ def test_mcg_to_ug(self):
355
+ """mcg and µg should be equivalent."""
356
+ from ucon.core import Number
357
+ dose = Number(1, unit=get_unit_by_name("mcg"))
358
+ result = dose.to(get_unit_by_name("µg"))
359
+ self.assertAlmostEqual(result.quantity, 1.0, places=9)
360
+
361
+ def test_mcg_in_composite(self):
362
+ """'mcg' should work in composite units."""
363
+ result = get_unit_by_name("mcg/mL")
364
+ self.assertIsInstance(result, UnitProduct)
365
+ from ucon.core import Dimension
366
+ self.assertEqual(result.dimension, Dimension.density)
367
+
368
+ def test_mcg_per_kg_per_min(self):
369
+ """'mcg/kg/min' style dosing units (requires chained division support)."""
370
+ # This tests mcg works; chained division is a separate issue
371
+ result = get_unit_by_name("mcg")
372
+ self.assertIsInstance(result, UnitProduct)
373
+
374
+
332
375
  if __name__ == '__main__':
333
376
  unittest.main()
@@ -208,6 +208,11 @@ _UNIT_REGISTRY_CASE_SENSITIVE: Dict[str, Unit] = {}
208
208
  # Prevents ambiguous parses like "min" -> milli-inch instead of minute.
209
209
  _PRIORITY_ALIASES: set = {'min'}
210
210
 
211
+ # Priority scaled aliases that map to a specific (unit, scale) tuple.
212
+ # Used for medical conventions like "mcg" -> (gram, Scale.micro).
213
+ # Populated by _build_registry() after units are defined.
214
+ _PRIORITY_SCALED_ALIASES: Dict[str, Tuple[Unit, Scale]] = {}
215
+
211
216
  # Scale prefix mapping (shorthand -> Scale)
212
217
  # Sorted by length descending for greedy matching
213
218
  _SCALE_PREFIXES: Dict[str, Scale] = {
@@ -260,6 +265,9 @@ def _build_registry() -> None:
260
265
  _UNIT_REGISTRY[alias.lower()] = obj
261
266
  _UNIT_REGISTRY_CASE_SENSITIVE[alias] = obj
262
267
 
268
+ # Register priority scaled aliases (medical conventions)
269
+ _PRIORITY_SCALED_ALIASES['mcg'] = (gram, Scale.micro) # microgram
270
+
263
271
 
264
272
  def _parse_exponent(s: str) -> Tuple[str, float]:
265
273
  """
@@ -299,8 +307,8 @@ def _lookup_factor(s: str) -> Tuple[Unit, Scale]:
299
307
  Look up a single unit factor, handling scale prefixes.
300
308
 
301
309
  Prioritizes prefix+unit interpretation over direct unit lookup,
302
- except for priority aliases (like 'min') which are checked first
303
- to avoid ambiguous parses.
310
+ except for priority aliases (like 'min', 'mcg') which are checked first
311
+ to avoid ambiguous parses or to handle domain-specific conventions.
304
312
 
305
313
  This means "kg" returns (gram, Scale.kilo) rather than (kilogram, Scale.one).
306
314
 
@@ -311,6 +319,7 @@ def _lookup_factor(s: str) -> Tuple[Unit, Scale]:
311
319
  - 'kg' -> (gram, Scale.kilo)
312
320
  - 'mL' -> (liter, Scale.milli)
313
321
  - 'min' -> (minute, Scale.one) # priority alias, not milli-inch
322
+ - 'mcg' -> (gram, Scale.micro) # medical convention for microgram
314
323
 
315
324
  Returns:
316
325
  Tuple of (unit, scale).
@@ -318,7 +327,11 @@ def _lookup_factor(s: str) -> Tuple[Unit, Scale]:
318
327
  Raises:
319
328
  UnknownUnitError: If the unit cannot be resolved.
320
329
  """
321
- # Check priority aliases first (prevents "min" -> milli-inch)
330
+ # Check priority scaled aliases first (e.g., "mcg" -> microgram)
331
+ if s in _PRIORITY_SCALED_ALIASES:
332
+ return _PRIORITY_SCALED_ALIASES[s]
333
+
334
+ # Check priority aliases (prevents "min" -> milli-inch)
322
335
  if s in _PRIORITY_ALIASES:
323
336
  if s in _UNIT_REGISTRY_CASE_SENSITIVE:
324
337
  return _UNIT_REGISTRY_CASE_SENSITIVE[s], Scale.one
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ucon
3
- Version: 0.6.2
3
+ Version: 0.6.3
4
4
  Summary: A tool for dimensional analysis: a 'Unit CONverter'
5
5
  Home-page: https://github.com/withtwoemms/ucon
6
6
  Author: Emmanuel I. Obi
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes