commodutil 4.0.0__tar.gz → 4.0.1__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. {commodutil-4.0.0 → commodutil-4.0.1}/PKG-INFO +1 -1
  2. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/__init__.py +6 -5
  3. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/convfactors.py +21 -42
  4. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil.egg-info/PKG-INFO +1 -1
  5. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_conv.py +9 -9
  6. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_standards_currency.py +29 -18
  7. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_standards_units.py +0 -20
  8. {commodutil-4.0.0 → commodutil-4.0.1}/.coveragerc +0 -0
  9. {commodutil-4.0.0 → commodutil-4.0.1}/.github/workflows/1_tests.yml +0 -0
  10. {commodutil-4.0.0 → commodutil-4.0.1}/.github/workflows/2_coverage.yml +0 -0
  11. {commodutil-4.0.0 → commodutil-4.0.1}/.github/workflows/3_linting.yml +0 -0
  12. {commodutil-4.0.0 → commodutil-4.0.1}/.github/workflows/4_release.yml +0 -0
  13. {commodutil-4.0.0 → commodutil-4.0.1}/.gitignore +0 -0
  14. {commodutil-4.0.0 → commodutil-4.0.1}/.pypirc +0 -0
  15. {commodutil-4.0.0 → commodutil-4.0.1}/azure-build-pipelines.yml +0 -0
  16. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/arb.py +0 -0
  17. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/dates.py +0 -0
  18. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/forward/__init__.py +0 -0
  19. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/forward/calendar.py +0 -0
  20. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/forward/continuous.py +0 -0
  21. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/forward/fly.py +0 -0
  22. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/forward/quarterly.py +0 -0
  23. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/forward/spreads.py +0 -0
  24. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/forward/structure.py +0 -0
  25. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/forward/util.py +0 -0
  26. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/forwards.py +0 -0
  27. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/pandasutil.py +0 -0
  28. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/standards/__init__.py +0 -0
  29. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/standards/analysis_types.py +0 -0
  30. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/standards/commodities.py +0 -0
  31. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/standards/commodity_groups.py +0 -0
  32. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/standards/currency.py +0 -0
  33. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/standards/regions.py +0 -0
  34. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/standards/units.py +0 -0
  35. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/stats.py +0 -0
  36. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil/transforms.py +0 -0
  37. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil.egg-info/SOURCES.txt +0 -0
  38. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil.egg-info/dependency_links.txt +0 -0
  39. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil.egg-info/requires.txt +0 -0
  40. {commodutil-4.0.0 → commodutil-4.0.1}/commodutil.egg-info/top_level.txt +0 -0
  41. {commodutil-4.0.0 → commodutil-4.0.1}/pyproject.toml +0 -0
  42. {commodutil-4.0.0 → commodutil-4.0.1}/requirements-test.txt +0 -0
  43. {commodutil-4.0.0 → commodutil-4.0.1}/requirements.txt +0 -0
  44. {commodutil-4.0.0 → commodutil-4.0.1}/requirements_dev.txt +0 -0
  45. {commodutil-4.0.0 → commodutil-4.0.1}/setup.cfg +0 -0
  46. {commodutil-4.0.0 → commodutil-4.0.1}/tests/__init__.py +0 -0
  47. {commodutil-4.0.0 → commodutil-4.0.1}/tests/conftest.py +0 -0
  48. {commodutil-4.0.0 → commodutil-4.0.1}/tests/forward/__init__.py +0 -0
  49. {commodutil-4.0.0 → commodutil-4.0.1}/tests/forward/conftest.py +0 -0
  50. {commodutil-4.0.0 → commodutil-4.0.1}/tests/forward/test_calendar.py +0 -0
  51. {commodutil-4.0.0 → commodutil-4.0.1}/tests/forward/test_continuous.py +0 -0
  52. {commodutil-4.0.0 → commodutil-4.0.1}/tests/forward/test_fly.py +0 -0
  53. {commodutil-4.0.0 → commodutil-4.0.1}/tests/forward/test_quarterly.py +0 -0
  54. {commodutil-4.0.0 → commodutil-4.0.1}/tests/forward/test_spreads.py +0 -0
  55. {commodutil-4.0.0 → commodutil-4.0.1}/tests/forward/test_structure.py +0 -0
  56. {commodutil-4.0.0 → commodutil-4.0.1}/tests/forward/test_util.py +0 -0
  57. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_arb.py +0 -0
  58. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_cl.csv +0 -0
  59. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_dates.py +0 -0
  60. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_forwards.py +0 -0
  61. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_pandasutils.py +0 -0
  62. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_price_conv.py +0 -0
  63. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_standards_analysis_types.py +0 -0
  64. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_standards_commodities.py +0 -0
  65. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_standards_commodity_groups.py +0 -0
  66. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_standards_regions.py +0 -0
  67. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_stats.py +0 -0
  68. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_transforms.py +0 -0
  69. {commodutil-4.0.0 → commodutil-4.0.1}/tests/test_weekly.csv +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commodutil
3
- Version: 4.0.0
3
+ Version: 4.0.1
4
4
  Summary: common commodity/oil analytics utils
5
5
  Author-email: aeorxc <author@example.com>
6
6
  Project-URL: Homepage, https://dev.azure.com/RWEST-MFI-TE/Oil/_git/commodutil
@@ -25,16 +25,17 @@ _LAZY_EXPORTS = {
25
25
  "ALIASES": "commodutil.convfactors",
26
26
  "COMMODITIES": "commodutil.convfactors",
27
27
  "Commodity": "commodutil.convfactors",
28
- "FRACTIONAL_TO_MAJOR": "commodutil.convfactors",
29
- "VALID_CURRENCY_TOKENS": "commodutil.convfactors",
30
28
  "convert": "commodutil.convfactors",
31
29
  "convert_price": "commodutil.convfactors",
32
30
  "convfactor": "commodutil.convfactors",
33
- "fractional_to_major": "commodutil.convfactors",
34
- "is_fractional_currency": "commodutil.convfactors",
35
31
  "list_commodities": "commodutil.convfactors",
36
32
  "list_units": "commodutil.convfactors",
37
- "split_currency_unit": "commodutil.convfactors",
33
+ # standards.currency (stdlib-only -- cheap, no pint)
34
+ "FRACTIONAL_TO_MAJOR": "commodutil.standards.currency",
35
+ "VALID_CURRENCY_TOKENS": "commodutil.standards.currency",
36
+ "fractional_to_major": "commodutil.standards.currency",
37
+ "is_fractional_currency": "commodutil.standards.currency",
38
+ "split_currency_unit": "commodutil.standards.currency",
38
39
  # dates
39
40
  "curmon": "commodutil.dates",
40
41
  "curmonyear": "commodutil.dates",
@@ -240,8 +240,8 @@ class CommodityConverter:
240
240
  convert(series, 'kt/month', 'bbl/day', commodity='gasoline')
241
241
  """
242
242
  # Normalize and parse units to handle daily/monthly rates
243
- from_unit = self._normalize_unit(from_unit)
244
- to_unit = self._normalize_unit(to_unit)
243
+ from_unit = _to_pint_token(from_unit)
244
+ to_unit = _to_pint_token(to_unit)
245
245
  from_rate = self._parse_rate_unit(from_unit)
246
246
  to_rate = self._parse_rate_unit(to_unit)
247
247
 
@@ -273,8 +273,8 @@ class CommodityConverter:
273
273
  self, value: float, from_unit: str, to_unit: str, commodity: Optional[str]
274
274
  ) -> float:
275
275
  """Convert a scalar value across mass/volume/energy using commodity context when needed."""
276
- from_unit = self._normalize_unit(from_unit)
277
- to_unit = self._normalize_unit(to_unit)
276
+ from_unit = _to_pint_token(from_unit)
277
+ to_unit = _to_pint_token(to_unit)
278
278
  qty = value * self.ureg(from_unit)
279
279
 
280
280
  # Try direct conversion first
@@ -404,10 +404,10 @@ class CommodityConverter:
404
404
  """Parse units like 'bbl/day' or 'kt/month'."""
405
405
  if "/" in unit:
406
406
  base, period = unit.split("/", 1)
407
- base = self._normalize_unit(base)
407
+ base = _to_pint_token(base)
408
408
  period = period.strip().lower().rstrip("s") # day(s), month(s), year(s)
409
409
  return {"base": base, "period": period}
410
- return {"base": self._normalize_unit(unit), "period": None}
410
+ return {"base": _to_pint_token(unit), "period": None}
411
411
 
412
412
  def _rate_factor_scalar(
413
413
  self, from_period: Optional[str], to_period: Optional[str]
@@ -469,15 +469,6 @@ class CommodityConverter:
469
469
  except DimensionalityError:
470
470
  return False
471
471
 
472
- def _normalize_unit(self, unit: str) -> str:
473
- """Normalize a unit string into a pint-parseable token.
474
-
475
- Thin shim around :func:`commodutil.standards.units.to_pint_token`.
476
- Kept as a bound method because it has six internal call sites and
477
- is exercised by the public test surface (``converter._normalize_unit``).
478
- """
479
- return _to_pint_token(unit)
480
-
481
472
  @property
482
473
  def available_commodities(self) -> list:
483
474
  """List all available commodities"""
@@ -545,28 +536,14 @@ def convfactor(from_unit: str, to_unit: str, commodity: Optional[str] = None) ->
545
536
 
546
537
  # ---- Currency-aware price conversion helpers ----
547
538
  #
548
- # Vocabulary moved to commodutil.standards.currency (2026-05) so it's
549
- # importable without dragging in pint / pandas. convfactors still owns the
550
- # integrated unit + currency `convert_price` math (which depends on the
551
- # pint registry above). Names are re-exported for backwards compatibility
552
- # `from commodutil.convfactors import VALID_CURRENCY_TOKENS` still works.
539
+ # Currency vocabulary lives in commodutil.standards.currency (importable
540
+ # without dragging in pint / pandas). convfactors owns only the integrated
541
+ # unit + currency `convert_price` math (which depends on the pint registry
542
+ # above) and reads currency vocabulary directly from _currency. Callers
543
+ # wanting currency vocabulary should import from commodutil.standards.currency.
553
544
 
554
545
  from commodutil.standards import currency as _currency
555
546
 
556
- _FRACTIONAL_CURRENCY_DIVISORS = _currency.FRACTIONAL_CURRENCY_DIVISORS
557
- _FRACTIONAL_TO_MAJOR = _currency.FRACTIONAL_TO_MAJOR
558
- _VALID_CURRENCY_TOKENS = _currency.VALID_CURRENCY_TOKENS
559
-
560
- # Public re-exports — preserve every existing public symbol so that
561
- # `from commodutil.convfactors import VALID_CURRENCY_TOKENS, fractional_to_major, ...`
562
- # continues to resolve for downstream packages (pyoilprice etc.).
563
- VALID_CURRENCY_TOKENS = _VALID_CURRENCY_TOKENS
564
- FRACTIONAL_TO_MAJOR = _FRACTIONAL_TO_MAJOR
565
- fractional_to_major = _currency.fractional_to_major
566
- is_fractional_currency = _currency.is_fractional_currency
567
- split_currency_unit = _currency.split_currency_unit
568
- _split_currency_unit = split_currency_unit
569
-
570
547
 
571
548
  def convert_price(
572
549
  value: Union[float, pd.Series],
@@ -626,15 +603,17 @@ def convert_price(
626
603
  fx_series = pd.Series([1.07, 1.08, 1.06], index=p.index)
627
604
  convert_price(p, 'EUR/MWh', 'USD/MMBtu', fx=fx_series)
628
605
  """
629
- from_ccy, from_bare_unit = split_currency_unit(from_unit)
630
- to_ccy, to_bare_unit = split_currency_unit(to_unit)
606
+ from_ccy, from_bare_unit = _currency.split_currency_unit(from_unit)
607
+ to_ccy, to_bare_unit = _currency.split_currency_unit(to_unit)
631
608
 
632
609
  # Resolve the underlying "major" currency on each side for same-base detection
633
610
  # (e.g. USc and USD share major USD — pure scale, no FX needed).
634
- from_major = _FRACTIONAL_TO_MAJOR.get(
611
+ from_major = _currency.FRACTIONAL_TO_MAJOR.get(
635
612
  from_ccy, from_ccy.upper() if from_ccy else ""
636
613
  )
637
- to_major = _FRACTIONAL_TO_MAJOR.get(to_ccy, to_ccy.upper() if to_ccy else "")
614
+ to_major = _currency.FRACTIONAL_TO_MAJOR.get(
615
+ to_ccy, to_ccy.upper() if to_ccy else ""
616
+ )
638
617
  # Treat '$' as 'USD' for the purpose of major-currency comparison.
639
618
  if from_major == "$":
640
619
  from_major = "USD"
@@ -663,8 +642,8 @@ def convert_price(
663
642
  # needed even though the literal currency tokens differ. Handle BEFORE the
664
643
  # `fx is None` raise below.
665
644
  if same_base_fractional:
666
- from_div = _FRACTIONAL_CURRENCY_DIVISORS.get(from_ccy, 1.0)
667
- to_div = _FRACTIONAL_CURRENCY_DIVISORS.get(to_ccy, 1.0)
645
+ from_div = _currency.FRACTIONAL_CURRENCY_DIVISORS.get(from_ccy, 1.0)
646
+ to_div = _currency.FRACTIONAL_CURRENCY_DIVISORS.get(to_ccy, 1.0)
668
647
  # value is in source-currency units; divide by from_div to get majors,
669
648
  # multiply by to_div to get target-currency units.
670
649
  return unit_converted * (to_div / from_div)
@@ -680,7 +659,7 @@ def convert_price(
680
659
  f"(source currency '{from_ccy}' is non-USD)"
681
660
  )
682
661
 
683
- fractional_divisor = _FRACTIONAL_CURRENCY_DIVISORS.get(from_ccy, 1.0)
662
+ fractional_divisor = _currency.FRACTIONAL_CURRENCY_DIVISORS.get(from_ccy, 1.0)
684
663
 
685
664
  if isinstance(unit_converted, pd.Series) and isinstance(fx, pd.Series):
686
665
  target_idx = unit_converted.index
@@ -752,7 +731,7 @@ def list_commodities():
752
731
  def list_units():
753
732
  """List common units"""
754
733
  # Return normalized forms to avoid encoding issues
755
- return [converter._normalize_unit(u) for u in converter.available_units]
734
+ return [_to_pint_token(u) for u in converter.available_units]
756
735
 
757
736
 
758
737
  # Example usage
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commodutil
3
- Version: 4.0.0
3
+ Version: 4.0.1
4
4
  Summary: common commodity/oil analytics utils
5
5
  Author-email: aeorxc <author@example.com>
6
6
  Project-URL: Homepage, https://dev.azure.com/RWEST-MFI-TE/Oil/_git/commodutil
@@ -217,21 +217,21 @@ class TestUtils:
217
217
  ) # 7450 bbl/kt / 30.4375 days/month
218
218
 
219
219
  def test_normalize_unit(self):
220
- """Test unit normalization"""
221
- from commodutil.convfactors import converter
220
+ """Test unit normalization (delegates to standards.units.to_pint_token)."""
221
+ from commodutil.standards.units import to_pint_token
222
222
 
223
223
  # Test cubic meter variations
224
- assert converter._normalize_unit("m³") == "m^3"
225
- assert converter._normalize_unit("m**3") == "m^3"
226
- assert converter._normalize_unit("cubic_meter") == "m^3"
227
- assert converter._normalize_unit("CUBIC_METER") == "m^3"
224
+ assert to_pint_token("m³") == "m^3"
225
+ assert to_pint_token("m**3") == "m^3"
226
+ assert to_pint_token("cubic_meter") == "m^3"
227
+ assert to_pint_token("CUBIC_METER") == "m^3"
228
228
 
229
229
  # Test energy unit normalization
230
- assert converter._normalize_unit("BTU") == "Btu"
231
- assert converter._normalize_unit("MMBTU") == "MMBtu"
230
+ assert to_pint_token("BTU") == "Btu"
231
+ assert to_pint_token("MMBTU") == "MMBtu"
232
232
 
233
233
  # Test whitespace handling
234
- assert converter._normalize_unit(" bbl ") == "bbl"
234
+ assert to_pint_token(" bbl ") == "bbl"
235
235
 
236
236
  def test_commodity_properties(self):
237
237
  """Test commodity property access and helper functions"""
@@ -134,22 +134,33 @@ def test_currency_map_known_entries():
134
134
  assert CURRENCY_MAP["canadian dollars"] == "CAD"
135
135
 
136
136
 
137
- # ---- Legacy import-path regression tests ----
138
-
139
-
140
- def test_legacy_imports_from_convfactors():
141
- """Downstream callers must still be able to import the moved names from
142
- commodutil.convfactors (backwards-compat re-exports)."""
143
- from commodutil.convfactors import (
144
- FRACTIONAL_TO_MAJOR as cf_FRACTIONAL_TO_MAJOR,
145
- VALID_CURRENCY_TOKENS as cf_VALID_CURRENCY_TOKENS,
146
- fractional_to_major as cf_fractional_to_major,
147
- is_fractional_currency as cf_is_fractional_currency,
148
- split_currency_unit as cf_split_currency_unit,
137
+ # ---- Shim-removal regression guard ----
138
+
139
+
140
+ def test_legacy_convfactors_currency_shims_are_gone():
141
+ """The 4.0 standards consolidation kept temporary back-compat re-exports
142
+ of the currency vocabulary on commodutil.convfactors. They were removed
143
+ in 4.1 after an in-repo consumer sweep across the stack confirmed zero
144
+ live downstream callers. This test is a positive guard against
145
+ accidental re-introduction of the shims -- importing any of these names
146
+ from commodutil.convfactors must fail. The canonical home is
147
+ commodutil.standards.currency.
148
+ """
149
+ import pytest
150
+
151
+ import commodutil.convfactors as cf
152
+
153
+ legacy_names = (
154
+ "VALID_CURRENCY_TOKENS",
155
+ "FRACTIONAL_TO_MAJOR",
156
+ "fractional_to_major",
157
+ "is_fractional_currency",
158
+ "split_currency_unit",
159
+ "_split_currency_unit",
160
+ "_FRACTIONAL_CURRENCY_DIVISORS",
161
+ "_FRACTIONAL_TO_MAJOR",
162
+ "_VALID_CURRENCY_TOKENS",
149
163
  )
150
-
151
- assert cf_VALID_CURRENCY_TOKENS is VALID_CURRENCY_TOKENS
152
- assert cf_FRACTIONAL_TO_MAJOR is FRACTIONAL_TO_MAJOR
153
- assert cf_is_fractional_currency("USc") is True
154
- assert cf_fractional_to_major("GBp") == "GBP"
155
- assert cf_split_currency_unit("EUR/MWh") == ("EUR", "MWh")
164
+ for name in legacy_names:
165
+ with pytest.raises(AttributeError):
166
+ getattr(cf, name)
@@ -129,26 +129,6 @@ def test_to_pint_token_exposed_via_standards_facade():
129
129
  assert to_pint_token("m³") == "m^3"
130
130
 
131
131
 
132
- def test_converter_normalize_unit_delegates_to_to_pint_token():
133
- """CommodityConverter._normalize_unit is a thin shim around the
134
- standalone normaliser. Regression guard against accidental drift."""
135
- from commodutil.convfactors import converter
136
- from commodutil.standards.units import to_pint_token
137
-
138
- for sample in [
139
- "m³",
140
- "m**3",
141
- "cubic_meter",
142
- "CUBIC_METER",
143
- "BTU",
144
- "MMBTU",
145
- " bbl ",
146
- "m3/day",
147
- None,
148
- ]:
149
- assert converter._normalize_unit(sample) == to_pint_token(sample)
150
-
151
-
152
132
  def test_unit_map_parity_with_curvemetadata():
153
133
  """Regression guard: UNIT_MAP must match curvemetadata's previous local
154
134
  copy byte-for-byte until curvemetadata's re-export PR lands. Skipped if
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