ucon 0.6.1__tar.gz → 0.6.2__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.1 → ucon-0.6.2}/PKG-INFO +1 -1
  2. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_unit_parsing.py +43 -0
  3. {ucon-0.6.1 → ucon-0.6.2}/ucon/units.py +18 -2
  4. {ucon-0.6.1 → ucon-0.6.2}/ucon.egg-info/PKG-INFO +1 -1
  5. {ucon-0.6.1 → ucon-0.6.2}/.github/workflows/publish.yaml +0 -0
  6. {ucon-0.6.1 → ucon-0.6.2}/.github/workflows/tests.yaml +0 -0
  7. {ucon-0.6.1 → ucon-0.6.2}/.gitignore +0 -0
  8. {ucon-0.6.1 → ucon-0.6.2}/LICENSE +0 -0
  9. {ucon-0.6.1 → ucon-0.6.2}/Makefile +0 -0
  10. {ucon-0.6.1 → ucon-0.6.2}/NOTICE +0 -0
  11. {ucon-0.6.1 → ucon-0.6.2}/README.md +0 -0
  12. {ucon-0.6.1 → ucon-0.6.2}/ROADMAP.md +0 -0
  13. {ucon-0.6.1 → ucon-0.6.2}/docs/decisions/001-unity-distance-metric-for-nearest-scale.md +0 -0
  14. {ucon-0.6.1 → ucon-0.6.2}/docs/decisions/002-composite-units.md +0 -0
  15. {ucon-0.6.1 → ucon-0.6.2}/docs/decisions/003-composable-unit-algebra.md +0 -0
  16. {ucon-0.6.1 → ucon-0.6.2}/docs/decisions/004-unit-algebra-naming.md +0 -0
  17. {ucon-0.6.1 → ucon-0.6.2}/docs/decisions/005-pseudo-dimension-tuple-values.md +0 -0
  18. {ucon-0.6.1 → ucon-0.6.2}/docs/decisions/006-pydantic-integration-pattern.md +0 -0
  19. {ucon-0.6.1 → ucon-0.6.2}/docs/examples/basis-transform-fantasy-units.md +0 -0
  20. {ucon-0.6.1 → ucon-0.6.2}/docs/explainers/exponent-scale-relationship.md +0 -0
  21. {ucon-0.6.1 → ucon-0.6.2}/docs/explainers/type-operation-matrix.md +0 -0
  22. {ucon-0.6.1 → ucon-0.6.2}/docs/explainers/why-algebraic-closure-matters.md +0 -0
  23. {ucon-0.6.1 → ucon-0.6.2}/docs/explainers/why-type-safety-matters.md +0 -0
  24. {ucon-0.6.1 → ucon-0.6.2}/docs/proposals/interface-unifying-the-value-layer.md +0 -0
  25. {ucon-0.6.1 → ucon-0.6.2}/docs/proposals/project_unified-algebraic-core.md +0 -0
  26. {ucon-0.6.1 → ucon-0.6.2}/docs/proposals/support-for-fractional-exponents.md +0 -0
  27. {ucon-0.6.1 → ucon-0.6.2}/docs/proposals/unified-unit-presentation.md +0 -0
  28. {ucon-0.6.1 → ucon-0.6.2}/pyproject.toml +0 -0
  29. {ucon-0.6.1 → ucon-0.6.2}/requirements.txt +0 -0
  30. {ucon-0.6.1 → ucon-0.6.2}/setup.cfg +0 -0
  31. {ucon-0.6.1 → ucon-0.6.2}/setup.py +0 -0
  32. {ucon-0.6.1 → ucon-0.6.2}/tests/__init__.py +0 -0
  33. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/__init__.py +0 -0
  34. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/conversion/__init__.py +0 -0
  35. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/conversion/test_graph.py +0 -0
  36. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/conversion/test_map.py +0 -0
  37. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/mcp/__init__.py +0 -0
  38. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/mcp/test_server.py +0 -0
  39. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_algebra.py +0 -0
  40. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_basis_transform.py +0 -0
  41. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_core.py +0 -0
  42. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_default_graph_conversions.py +0 -0
  43. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_dimensionless_units.py +0 -0
  44. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_graph_basis_transform.py +0 -0
  45. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_logmap.py +0 -0
  46. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_nines.py +0 -0
  47. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_pickle.py +0 -0
  48. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_pydantic.py +0 -0
  49. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_quantity.py +0 -0
  50. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_rebased_unit.py +0 -0
  51. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_uncertainty.py +0 -0
  52. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_unit_system.py +0 -0
  53. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_units.py +0 -0
  54. {ucon-0.6.1 → ucon-0.6.2}/tests/ucon/test_vector_fraction.py +0 -0
  55. {ucon-0.6.1 → ucon-0.6.2}/ucon/__init__.py +0 -0
  56. {ucon-0.6.1 → ucon-0.6.2}/ucon/algebra.py +0 -0
  57. {ucon-0.6.1 → ucon-0.6.2}/ucon/core.py +0 -0
  58. {ucon-0.6.1 → ucon-0.6.2}/ucon/graph.py +0 -0
  59. {ucon-0.6.1 → ucon-0.6.2}/ucon/maps.py +0 -0
  60. {ucon-0.6.1 → ucon-0.6.2}/ucon/mcp/__init__.py +0 -0
  61. {ucon-0.6.1 → ucon-0.6.2}/ucon/mcp/server.py +0 -0
  62. {ucon-0.6.1 → ucon-0.6.2}/ucon/pydantic.py +0 -0
  63. {ucon-0.6.1 → ucon-0.6.2}/ucon/quantity.py +0 -0
  64. {ucon-0.6.1 → ucon-0.6.2}/ucon.egg-info/SOURCES.txt +0 -0
  65. {ucon-0.6.1 → ucon-0.6.2}/ucon.egg-info/dependency_links.txt +0 -0
  66. {ucon-0.6.1 → ucon-0.6.2}/ucon.egg-info/entry_points.txt +0 -0
  67. {ucon-0.6.1 → ucon-0.6.2}/ucon.egg-info/requires.txt +0 -0
  68. {ucon-0.6.1 → ucon-0.6.2}/ucon.egg-info/top_level.txt +0 -0
  69. {ucon-0.6.1 → ucon-0.6.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ucon
3
- Version: 0.6.1
3
+ Version: 0.6.2
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
@@ -286,5 +286,48 @@ class TestWhitespaceHandling(unittest.TestCase):
286
286
  self.assertEqual(result, units.meter)
287
287
 
288
288
 
289
+ class TestPriorityAliases(unittest.TestCase):
290
+ """Test that priority aliases are matched before prefix decomposition.
291
+
292
+ Some aliases like 'min' could be misinterpreted as prefix+unit
293
+ (e.g., 'm' + 'in' = milli-inch). Priority aliases ensure these
294
+ are matched exactly first.
295
+ """
296
+
297
+ def test_min_is_minute_not_milli_inch(self):
298
+ """'min' should parse as minute (time), not milli-inch (length)."""
299
+ from ucon.core import Dimension
300
+ result = get_unit_by_name("min")
301
+ self.assertEqual(result, units.minute)
302
+ self.assertEqual(result.dimension, Dimension.time)
303
+
304
+ def test_min_in_composite(self):
305
+ """'min' should work correctly in composite units."""
306
+ result = get_unit_by_name("mL/min")
307
+ self.assertIsInstance(result, UnitProduct)
308
+ # Volume / time dimension
309
+ from ucon.core import Dimension
310
+ expected_dim = Dimension.volume / Dimension.time
311
+ self.assertEqual(result.dimension, expected_dim)
312
+
313
+ def test_mL_per_min_conversion(self):
314
+ """Conversion using 'min' should work correctly."""
315
+ from ucon.core import Number
316
+ rate_per_hour = Number(120, unit=get_unit_by_name("mL/h"))
317
+ rate_per_min = rate_per_hour.to(get_unit_by_name("mL/min"))
318
+ self.assertAlmostEqual(rate_per_min.quantity, 2.0, places=9)
319
+
320
+ def test_milli_prefix_still_works(self):
321
+ """Normal milli- prefix parsing should still work."""
322
+ result = get_unit_by_name("mL")
323
+ self.assertIsInstance(result, UnitProduct)
324
+ self.assertAlmostEqual(result.fold_scale(), 0.001, places=10)
325
+
326
+ def test_inch_still_works(self):
327
+ """Inch unit should still be accessible."""
328
+ result = get_unit_by_name("in")
329
+ self.assertEqual(result, units.inch)
330
+
331
+
289
332
  if __name__ == '__main__':
290
333
  unittest.main()
@@ -204,6 +204,10 @@ def have(name: str) -> bool:
204
204
  _UNIT_REGISTRY: Dict[str, Unit] = {}
205
205
  _UNIT_REGISTRY_CASE_SENSITIVE: Dict[str, Unit] = {}
206
206
 
207
+ # Priority aliases that must match exactly before prefix decomposition.
208
+ # Prevents ambiguous parses like "min" -> milli-inch instead of minute.
209
+ _PRIORITY_ALIASES: set = {'min'}
210
+
207
211
  # Scale prefix mapping (shorthand -> Scale)
208
212
  # Sorted by length descending for greedy matching
209
213
  _SCALE_PREFIXES: Dict[str, Scale] = {
@@ -294,7 +298,10 @@ def _lookup_factor(s: str) -> Tuple[Unit, Scale]:
294
298
  """
295
299
  Look up a single unit factor, handling scale prefixes.
296
300
 
297
- Prioritizes prefix+unit interpretation over direct unit lookup.
301
+ Prioritizes prefix+unit interpretation over direct unit lookup,
302
+ except for priority aliases (like 'min') which are checked first
303
+ to avoid ambiguous parses.
304
+
298
305
  This means "kg" returns (gram, Scale.kilo) rather than (kilogram, Scale.one).
299
306
 
300
307
  Examples:
@@ -303,6 +310,7 @@ def _lookup_factor(s: str) -> Tuple[Unit, Scale]:
303
310
  - 'km' -> (meter, Scale.kilo)
304
311
  - 'kg' -> (gram, Scale.kilo)
305
312
  - 'mL' -> (liter, Scale.milli)
313
+ - 'min' -> (minute, Scale.one) # priority alias, not milli-inch
306
314
 
307
315
  Returns:
308
316
  Tuple of (unit, scale).
@@ -310,7 +318,15 @@ def _lookup_factor(s: str) -> Tuple[Unit, Scale]:
310
318
  Raises:
311
319
  UnknownUnitError: If the unit cannot be resolved.
312
320
  """
313
- # Try scale prefix + unit first (prioritize decomposition)
321
+ # Check priority aliases first (prevents "min" -> milli-inch)
322
+ if s in _PRIORITY_ALIASES:
323
+ if s in _UNIT_REGISTRY_CASE_SENSITIVE:
324
+ return _UNIT_REGISTRY_CASE_SENSITIVE[s], Scale.one
325
+ s_lower = s.lower()
326
+ if s_lower in _UNIT_REGISTRY:
327
+ return _UNIT_REGISTRY[s_lower], Scale.one
328
+
329
+ # Try scale prefix + unit (prioritize decomposition)
314
330
  # Only case-sensitive matching for remainder (e.g., "fT" = femto-tesla, "ft" = foot)
315
331
  for prefix in _SCALE_PREFIXES_SORTED:
316
332
  if s.startswith(prefix) and len(s) > len(prefix):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ucon
3
- Version: 0.6.1
3
+ Version: 0.6.2
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
File without changes