commodutil 5.5.1__tar.gz → 5.5.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 (83) hide show
  1. {commodutil-5.5.1 → commodutil-5.5.3}/CHANGELOG.md +8 -0
  2. {commodutil-5.5.1 → commodutil-5.5.3}/PKG-INFO +1 -1
  3. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/standards/geography.py +13 -3
  4. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil.egg-info/PKG-INFO +1 -1
  5. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_standards_geography.py +27 -0
  6. {commodutil-5.5.1 → commodutil-5.5.3}/uv.lock +3 -3
  7. {commodutil-5.5.1 → commodutil-5.5.3}/.coveragerc +0 -0
  8. {commodutil-5.5.1 → commodutil-5.5.3}/.github/workflows/1_tests.yml +0 -0
  9. {commodutil-5.5.1 → commodutil-5.5.3}/.github/workflows/2_coverage.yml +0 -0
  10. {commodutil-5.5.1 → commodutil-5.5.3}/.github/workflows/3_linting.yml +0 -0
  11. {commodutil-5.5.1 → commodutil-5.5.3}/.github/workflows/4_release.yml +0 -0
  12. {commodutil-5.5.1 → commodutil-5.5.3}/.gitignore +0 -0
  13. {commodutil-5.5.1 → commodutil-5.5.3}/.pypirc +0 -0
  14. {commodutil-5.5.1 → commodutil-5.5.3}/README.md +0 -0
  15. {commodutil-5.5.1 → commodutil-5.5.3}/azure-build-pipelines.yml +0 -0
  16. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/__init__.py +0 -0
  17. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/arb.py +0 -0
  18. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/convfactors.py +0 -0
  19. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/dates.py +0 -0
  20. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/forward/__init__.py +0 -0
  21. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/forward/calendar.py +0 -0
  22. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/forward/continuous.py +0 -0
  23. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/forward/fly.py +0 -0
  24. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/forward/quarterly.py +0 -0
  25. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/forward/spreads.py +0 -0
  26. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/forward/structure.py +0 -0
  27. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/forward/util.py +0 -0
  28. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/forwards.py +0 -0
  29. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/pandasutil.py +0 -0
  30. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/priceconv.py +0 -0
  31. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/standards/__init__.py +0 -0
  32. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/standards/analysis_types.py +0 -0
  33. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/standards/commodities.py +0 -0
  34. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/standards/commodity_groups.py +0 -0
  35. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/standards/currency.py +0 -0
  36. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/standards/price_unit.py +0 -0
  37. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/standards/trading_hubs.py +0 -0
  38. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/standards/unit_registry.py +0 -0
  39. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/standards/units.py +0 -0
  40. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/stats.py +0 -0
  41. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil/transforms.py +0 -0
  42. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil.egg-info/SOURCES.txt +0 -0
  43. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil.egg-info/dependency_links.txt +0 -0
  44. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil.egg-info/requires.txt +0 -0
  45. {commodutil-5.5.1 → commodutil-5.5.3}/commodutil.egg-info/top_level.txt +0 -0
  46. {commodutil-5.5.1 → commodutil-5.5.3}/pyproject.toml +0 -0
  47. {commodutil-5.5.1 → commodutil-5.5.3}/setup.cfg +0 -0
  48. {commodutil-5.5.1 → commodutil-5.5.3}/tests/__init__.py +0 -0
  49. {commodutil-5.5.1 → commodutil-5.5.3}/tests/conftest.py +0 -0
  50. {commodutil-5.5.1 → commodutil-5.5.3}/tests/forward/__init__.py +0 -0
  51. {commodutil-5.5.1 → commodutil-5.5.3}/tests/forward/conftest.py +0 -0
  52. {commodutil-5.5.1 → commodutil-5.5.3}/tests/forward/test_calendar.py +0 -0
  53. {commodutil-5.5.1 → commodutil-5.5.3}/tests/forward/test_continuous.py +0 -0
  54. {commodutil-5.5.1 → commodutil-5.5.3}/tests/forward/test_fly.py +0 -0
  55. {commodutil-5.5.1 → commodutil-5.5.3}/tests/forward/test_quarterly.py +0 -0
  56. {commodutil-5.5.1 → commodutil-5.5.3}/tests/forward/test_spreads.py +0 -0
  57. {commodutil-5.5.1 → commodutil-5.5.3}/tests/forward/test_structure.py +0 -0
  58. {commodutil-5.5.1 → commodutil-5.5.3}/tests/forward/test_util.py +0 -0
  59. {commodutil-5.5.1 → commodutil-5.5.3}/tests/golden/golden_factors.json +0 -0
  60. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_aliases.py +0 -0
  61. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_align_fx_result.py +0 -0
  62. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_arb.py +0 -0
  63. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_cl.csv +0 -0
  64. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_conv.py +0 -0
  65. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_dates.py +0 -0
  66. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_forwards.py +0 -0
  67. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_golden_factors.py +0 -0
  68. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_pandasutils.py +0 -0
  69. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_price_conv.py +0 -0
  70. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_price_unit.py +0 -0
  71. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_priceconv_reexports.py +0 -0
  72. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_rate_pint.py +0 -0
  73. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_regex_literals.py +0 -0
  74. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_standards_analysis_types.py +0 -0
  75. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_standards_commodities.py +0 -0
  76. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_standards_commodity_groups.py +0 -0
  77. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_standards_currency.py +0 -0
  78. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_standards_trading_hubs.py +0 -0
  79. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_standards_units.py +0 -0
  80. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_stats.py +0 -0
  81. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_transforms.py +0 -0
  82. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_unit_registry.py +0 -0
  83. {commodutil-5.5.1 → commodutil-5.5.3}/tests/test_weekly.csv +0 -0
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.5.3 - 2026-07-28
4
+
5
+ - Fixed country-name resolution for standalone `St`/`St.` abbreviations and the vendor spellings `Taiwan, China` and `Virgin Islands (US)`.
6
+
7
+ ## 5.5.2 - 2026-07-28
8
+
9
+ - Fixed country-name resolution to normalize ampersands to `and`, matching ISO names such as Antigua and Barbuda.
10
+
3
11
  ## 5.5.0 - 2026-07-27
4
12
 
5
13
  - Added `commodutil.standards.geography` with pycountry-backed countries, desk aliases, and membership-defined country groups; added `pycountry` as a production dependency.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commodutil
3
- Version: 5.5.1
3
+ Version: 5.5.3
4
4
  Summary: common commodity/oil analytics utils
5
5
  Author-email: aeorxc <author@example.com>
6
6
  License-Expression: MIT
@@ -15,6 +15,7 @@ memberships are derived from those definitions at import time.
15
15
 
16
16
  from __future__ import annotations
17
17
 
18
+ import re
18
19
  from dataclasses import dataclass
19
20
  from typing import Dict, Optional, Tuple
20
21
 
@@ -69,11 +70,13 @@ COUNTRY_ALIASES: dict[str, str] = {
69
70
  "Turkey": "TR",
70
71
  "Turkiye": "TR",
71
72
  "Republic of China": "TW",
73
+ "Taiwan, China": "TW",
72
74
  "USA": "US",
73
75
  "U.S.": "US",
74
76
  "U.S.A.": "US",
75
77
  "Holy See": "VA",
76
78
  "US Virgin Islands": "VI",
79
+ "Virgin Islands (US)": "VI",
77
80
  # From empirical vendor-vocabulary validation (see commodflows research).
78
81
  # ISO merges Bonaire and Sint Eustatius into BQ; sub-island detail is
79
82
  # lost by design.
@@ -98,6 +101,13 @@ COUNTRY_ALIASES: dict[str, str] = {
98
101
  }
99
102
 
100
103
 
104
+ def _normalize_country_name(name: str) -> str:
105
+ """Return the case-folded lookup key for a country name."""
106
+ normalized = name.replace("&", " and ")
107
+ normalized = re.sub(r"\bSt\.?(?=\s|$)", "Saint", normalized, flags=re.IGNORECASE)
108
+ return " ".join(normalized.split()).casefold()
109
+
110
+
101
111
  def _build_country_name_map(aliases: Dict[str, str]) -> Dict[str, str]:
102
112
  """Build the case-folded name map and reject conflicting aliases."""
103
113
  result: Dict[str, str] = {}
@@ -106,7 +116,7 @@ def _build_country_name_map(aliases: Dict[str, str]) -> Dict[str, str]:
106
116
  name = getattr(country, attribute, None)
107
117
  if name is None:
108
118
  continue
109
- key = name.casefold()
119
+ key = _normalize_country_name(name)
110
120
  existing = result.get(key)
111
121
  if existing is not None and existing != country.alpha_2:
112
122
  raise ValueError(
@@ -120,7 +130,7 @@ def _build_country_name_map(aliases: Dict[str, str]) -> Dict[str, str]:
120
130
  raise ValueError(
121
131
  f"country alias {alias!r} has unknown country code {code!r}"
122
132
  )
123
- key = alias.casefold()
133
+ key = _normalize_country_name(alias)
124
134
  existing = result.get(key)
125
135
  if existing is not None and existing != code:
126
136
  raise ValueError(
@@ -144,7 +154,7 @@ def canonical_country(text: object) -> Optional[str]:
144
154
  code = cleaned.upper()
145
155
  if code in VALID_COUNTRY_CODES:
146
156
  return code
147
- return COUNTRY_NAME_MAP.get(cleaned.casefold())
157
+ return COUNTRY_NAME_MAP.get(_normalize_country_name(cleaned))
148
158
 
149
159
 
150
160
  def is_canonical_country(code: object) -> bool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commodutil
3
- Version: 5.5.1
3
+ Version: 5.5.3
4
4
  Summary: common commodity/oil analytics utils
5
5
  Author-email: aeorxc <author@example.com>
6
6
  License-Expression: MIT
@@ -66,6 +66,33 @@ def test_country_name_map_contains_pycountry_names_and_aliases():
66
66
  assert COUNTRY_NAME_MAP["uk"] == "GB"
67
67
 
68
68
 
69
+ @pytest.mark.parametrize(
70
+ "value,expected",
71
+ [
72
+ ("Antigua & Barbuda", "AG"),
73
+ ("Trinidad & Tobago", "TT"),
74
+ ("Bosnia & Herzegovina", "BA"),
75
+ ("&", None),
76
+ ],
77
+ )
78
+ def test_canonical_country_normalizes_ampersands(value, expected):
79
+ assert canonical_country(value) == expected
80
+
81
+
82
+ @pytest.mark.parametrize(
83
+ "value,expected",
84
+ [
85
+ ("St Kitts & Nevis", "KN"),
86
+ ("St. Lucia", "LC"),
87
+ ("Taiwan, China", "TW"),
88
+ ("Virgin Islands (US)", "VI"),
89
+ ("Stockholm", None),
90
+ ],
91
+ )
92
+ def test_canonical_country_normalizes_live_vendor_spellings(value, expected):
93
+ assert canonical_country(value) == expected
94
+
95
+
69
96
  @pytest.mark.parametrize(
70
97
  "value,expected",
71
98
  [
@@ -1627,7 +1627,7 @@ wheels = [
1627
1627
 
1628
1628
  [[package]]
1629
1629
  name = "twine"
1630
- version = "6.2.0"
1630
+ version = "7.0.0"
1631
1631
  source = { registry = "https://pypi.org/simple" }
1632
1632
  dependencies = [
1633
1633
  { name = "id" },
@@ -1640,9 +1640,9 @@ dependencies = [
1640
1640
  { name = "rich" },
1641
1641
  { name = "urllib3" },
1642
1642
  ]
1643
- sdist = { url = "https://files.pythonhosted.org/packages/e0/a8/949edebe3a82774c1ec34f637f5dd82d1cf22c25e963b7d63771083bbee5/twine-6.2.0.tar.gz", hash = "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf", size = 172262, upload-time = "2025-09-04T15:43:17.255Z" }
1643
+ sdist = { url = "https://files.pythonhosted.org/packages/92/3c/58f808a359700f39a967dffede33efeac809262c03303fa3eec6afff8f49/twine-7.0.0.tar.gz", hash = "sha256:85cdb29c518efef867360ae4acd4b0dfd61c8654a22fca08e6f8539f05022177", size = 215032, upload-time = "2026-07-27T15:59:00.825Z" }
1644
1644
  wheels = [
1645
- { url = "https://files.pythonhosted.org/packages/3a/7a/882d99539b19b1490cac5d77c67338d126e4122c8276bf640e411650c830/twine-6.2.0-py3-none-any.whl", hash = "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8", size = 42727, upload-time = "2025-09-04T15:43:15.994Z" },
1645
+ { url = "https://files.pythonhosted.org/packages/96/08/ddcdc06225eaad6de0e48e1002b06d919dbde20582d0662c7af51308e5d6/twine-7.0.0-py3-none-any.whl", hash = "sha256:b854164df26db268af05f49aa5c0344b10e27a494343ff05b1e0bad3b135f5a7", size = 43204, upload-time = "2026-07-27T15:58:59.26Z" },
1646
1646
  ]
1647
1647
 
1648
1648
  [[package]]
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