hdx-python-country 3.7.8__tar.gz → 3.8.0__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 (36) hide show
  1. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/PKG-INFO +2 -2
  2. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/documentation/main.md +4 -0
  3. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/pyproject.toml +1 -1
  4. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/requirements.txt +15 -17
  5. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/src/hdx/location/_version.py +2 -2
  6. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/src/hdx/location/adminlevel.py +40 -10
  7. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/tests/hdx/location/test_adminlevel.py +53 -12
  8. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/.config/coveragerc +0 -0
  9. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/.config/pre-commit-config.yaml +0 -0
  10. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/.config/pytest.ini +0 -0
  11. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/.config/ruff.toml +0 -0
  12. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/.github/workflows/publish.yaml +0 -0
  13. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/.github/workflows/run-python-tests.yaml +0 -0
  14. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/.gitignore +0 -0
  15. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/CONTRIBUTING.md +0 -0
  16. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/LICENSE +0 -0
  17. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/README.md +0 -0
  18. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/documentation/.readthedocs.yaml +0 -0
  19. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/documentation/pydoc-markdown.yaml +0 -0
  20. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/src/hdx/location/Countries & Territories Taxonomy MVP - C&T Taxonomy with HXL Tags.csv +0 -0
  21. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/src/hdx/location/__init__.py +0 -0
  22. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/src/hdx/location/country.py +0 -0
  23. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/src/hdx/location/currency.py +0 -0
  24. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/src/hdx/location/phonetics.py +0 -0
  25. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/src/hdx/location/wfp_exchangerates.py +0 -0
  26. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/tests/fixtures/adminlevel.yaml +0 -0
  27. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/tests/fixtures/adminlevelparent.yaml +0 -0
  28. /hdx_python_country-3.7.8/tests/fixtures/global_pcode_lengths.csv → /hdx_python_country-3.8.0/tests/fixtures/download-global-pcode-lengths.csv +0 -0
  29. /hdx_python_country-3.7.8/tests/fixtures/global_pcodes_adm_1_2.csv → /hdx_python_country-3.8.0/tests/fixtures/download-global-pcodes-adm-1-2.csv +0 -0
  30. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/tests/fixtures/secondary_historic_rates.csv +0 -0
  31. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/tests/fixtures/secondary_rates.json +0 -0
  32. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/tests/hdx/location/Countries_UZB_Deleted.csv +0 -0
  33. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/tests/hdx/location/__init__.py +0 -0
  34. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/tests/hdx/location/test_country.py +0 -0
  35. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/tests/hdx/location/test_currency.py +0 -0
  36. {hdx_python_country-3.7.8 → hdx_python_country-3.8.0}/tests/hdx/location/test_wfp_exchangerates.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: hdx-python-country
3
- Version: 3.7.8
3
+ Version: 3.8.0
4
4
  Summary: HDX Python country code and exchange rate (fx) utilities
5
5
  Project-URL: Homepage, https://github.com/OCHA-DAP/hdx-python-country
6
6
  Author: Michael Rans
@@ -25,7 +25,7 @@ Classifier: Programming Language :: Python :: 3.11
25
25
  Classifier: Programming Language :: Python :: 3.12
26
26
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
27
  Requires-Python: >=3.8
28
- Requires-Dist: hdx-python-utilities>=3.7.3
28
+ Requires-Dist: hdx-python-utilities>=3.7.4
29
29
  Requires-Dist: libhxl>=5.2.1
30
30
  Requires-Dist: pyphonetics
31
31
  Provides-Extra: dev
@@ -130,6 +130,10 @@ object has been set up with parents). Keys take the form "STRING_TO_REPLACE",
130
130
  *admin_fuzzy_dont* is a list of names for which fuzzy matching should not be
131
131
  tried
132
132
 
133
+ A Retrieve object can be passed in the *retriever* parameter that enables
134
+ saving data downloaded to a file or loading previously saved data depending
135
+ on how the Retrieve object is configured.
136
+
133
137
  Once an AdminLevel object is constructed, one of three setup methods must be
134
138
  called: *setup_from_admin_info*, *setup_from_libhxl_dataset* or
135
139
  *setup_from_url*.
@@ -34,7 +34,7 @@ classifiers = [
34
34
  requires-python = ">=3.8"
35
35
 
36
36
  dependencies = [
37
- "hdx-python-utilities>=3.7.3",
37
+ "hdx-python-utilities>=3.7.4",
38
38
  "libhxl>=5.2.1",
39
39
  "pyphonetics",
40
40
  ]
@@ -24,17 +24,17 @@ distlib==0.3.8
24
24
  # via virtualenv
25
25
  et-xmlfile==1.1.0
26
26
  # via openpyxl
27
- filelock==3.15.4
27
+ filelock==3.16.1
28
28
  # via virtualenv
29
- frictionless==5.17.1
29
+ frictionless==5.18.0
30
30
  # via hdx-python-utilities
31
- hdx-python-utilities==3.7.3
31
+ hdx-python-utilities==3.7.4
32
32
  # via hdx-python-country (pyproject.toml)
33
- humanize==4.10.0
33
+ humanize==4.11.0
34
34
  # via frictionless
35
- identify==2.6.0
35
+ identify==2.6.1
36
36
  # via pre-commit
37
- idna==3.8
37
+ idna==3.10
38
38
  # via requests
39
39
  ijson==3.3.0
40
40
  # via hdx-python-utilities
@@ -74,7 +74,7 @@ packaging==24.1
74
74
  # via pytest
75
75
  petl==1.7.15
76
76
  # via frictionless
77
- platformdirs==4.2.2
77
+ platformdirs==4.3.6
78
78
  # via virtualenv
79
79
  pluggy==1.5.0
80
80
  # via pytest
@@ -82,17 +82,17 @@ ply==3.11
82
82
  # via
83
83
  # jsonpath-ng
84
84
  # libhxl
85
- pre-commit==3.8.0
85
+ pre-commit==4.0.0
86
86
  # via hdx-python-country (pyproject.toml)
87
- pydantic==2.9.0
87
+ pydantic==2.9.2
88
88
  # via frictionless
89
- pydantic-core==2.23.2
89
+ pydantic-core==2.23.4
90
90
  # via pydantic
91
91
  pygments==2.18.0
92
92
  # via rich
93
93
  pyphonetics==0.5.3
94
94
  # via hdx-python-country (pyproject.toml)
95
- pytest==8.3.2
95
+ pytest==8.3.3
96
96
  # via
97
97
  # hdx-python-country (pyproject.toml)
98
98
  # pytest-cov
@@ -127,7 +127,7 @@ requests-file==2.1.0
127
127
  # via hdx-python-utilities
128
128
  rfc3986==2.0.0
129
129
  # via frictionless
130
- rich==13.8.0
130
+ rich==13.9.2
131
131
  # via typer
132
132
  rpds-py==0.20.0
133
133
  # via
@@ -139,7 +139,7 @@ ruamel-yaml-clib==0.2.8
139
139
  # via ruamel-yaml
140
140
  shellingham==1.5.4
141
141
  # via typer
142
- simpleeval==0.9.13
142
+ simpleeval==1.0.0
143
143
  # via frictionless
144
144
  six==1.16.0
145
145
  # via
@@ -163,19 +163,17 @@ typing-extensions==4.12.2
163
163
  # pydantic
164
164
  # pydantic-core
165
165
  # typer
166
- tzdata==2024.1
167
- # via pydantic
168
166
  unidecode==1.3.8
169
167
  # via
170
168
  # libhxl
171
169
  # pyphonetics
172
- urllib3==2.2.2
170
+ urllib3==2.2.3
173
171
  # via
174
172
  # libhxl
175
173
  # requests
176
174
  validators==0.34.0
177
175
  # via frictionless
178
- virtualenv==20.26.3
176
+ virtualenv==20.26.6
179
177
  # via pre-commit
180
178
  wheel==0.44.0
181
179
  # via libhxl
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '3.7.8'
16
- __version_tuple__ = version_tuple = (3, 7, 8)
15
+ __version__ = version = '3.8.0'
16
+ __version_tuple__ = version_tuple = (3, 8, 0)
@@ -8,7 +8,9 @@ from hxl.input import HXLIOException
8
8
 
9
9
  from hdx.location.country import Country
10
10
  from hdx.location.phonetics import Phonetics
11
+ from hdx.utilities.base_downloader import DownloadError
11
12
  from hdx.utilities.dictandlist import dict_of_sets_add
13
+ from hdx.utilities.retriever import Retrieve
12
14
  from hdx.utilities.text import multiple_replace, normalise
13
15
  from hdx.utilities.typehint import ListTuple
14
16
 
@@ -30,10 +32,20 @@ class AdminLevel:
30
32
  admin_fuzzy_dont is a list of names for which fuzzy matching should not be
31
33
  tried
32
34
 
35
+ The admin_level_overrides parameter allows manually overriding the returned
36
+ admin level for given countries. It is a dictionary with iso3s as keys and
37
+ admin level numbers as values.
38
+
39
+ The retriever parameter accepts an object of type Retrieve (or inherited
40
+ classes). It is used to allow either that admin data from urls is saved
41
+ to files or to enable already saved files to be used instead of downloading
42
+ from urls.
43
+
33
44
  Args:
34
45
  admin_config (Dict): Configuration dictionary. Defaults to {}.
35
46
  admin_level (int): Admin level. Defaults to 1.
36
47
  admin_level_overrides (Dict): Countries at other admin levels.
48
+ retriever (Optional[Retrieve]): Retriever object to use for loading/saving files. Defaults to None.
37
49
  """
38
50
 
39
51
  pcode_regex = re.compile(r"^([a-zA-Z]{2,3})(\d+)$")
@@ -47,9 +59,11 @@ class AdminLevel:
47
59
  admin_config: Dict = {},
48
60
  admin_level: int = 1,
49
61
  admin_level_overrides: Dict = {},
62
+ retriever: Optional[Retrieve] = None,
50
63
  ) -> None:
51
64
  self.admin_level = admin_level
52
65
  self.admin_level_overrides = admin_level_overrides
66
+ self.retriever: Optional[Retrieve] = retriever
53
67
  self.countries_fuzzy_try = admin_config.get("countries_fuzzy_try")
54
68
  self.admin_name_mappings = admin_config.get("admin_name_mappings", {})
55
69
  self.admin_name_replacements = admin_config.get(
@@ -101,23 +115,39 @@ class AdminLevel:
101
115
  admin_url = cls._admin_url_default
102
116
  cls._admin_url = admin_url
103
117
 
104
- @classmethod
105
- def get_libhxl_dataset(cls, admin_url: str = _admin_url) -> hxl.Dataset:
118
+ @staticmethod
119
+ def get_libhxl_dataset(
120
+ url: str = _admin_url, retriever: Optional[Retrieve] = None
121
+ ) -> hxl.Dataset:
106
122
  """
107
123
  Get libhxl Dataset object given a URL which defaults to global p-codes
108
124
  dataset on HDX.
109
125
 
110
126
  Args:
111
- admin_url (str): URL from which to load data. Defaults to global p-codes dataset.
127
+ url (str): URL from which to load data. Defaults to internal admin url.
128
+ retriever (Optional[Retrieve]): Retriever object to use for loading file. Defaults to None.
112
129
 
113
130
  Returns:
114
- None
115
- """
131
+ hxl.Dataset: HXL Dataset object
132
+ """
133
+ if retriever:
134
+ try:
135
+ url_to_use = retriever.download_file(url)
136
+ except DownloadError:
137
+ logger.exception(
138
+ f"Setup of libhxl Dataset object with {url} failed!"
139
+ )
140
+ raise
141
+ else:
142
+ url_to_use = url
116
143
  try:
117
- return hxl.data(admin_url, InputOptions(encoding="utf-8"))
118
- except HXLIOException:
144
+ return hxl.data(
145
+ url_to_use,
146
+ InputOptions(InputOptions(allow_local=True, encoding="utf-8")),
147
+ )
148
+ except (FileNotFoundError, HXLIOException):
119
149
  logger.exception(
120
- f"Setup of libhxl Dataset object with {admin_url} failed!"
150
+ f"Setup of libhxl Dataset object with {url} failed!"
121
151
  )
122
152
  raise
123
153
 
@@ -245,7 +275,7 @@ class AdminLevel:
245
275
  Returns:
246
276
  None
247
277
  """
248
- admin_info = self.get_libhxl_dataset(admin_url)
278
+ admin_info = self.get_libhxl_dataset(admin_url, self.retriever)
249
279
  self.setup_from_libhxl_dataset(admin_info, countryiso3s)
250
280
 
251
281
  def load_pcode_formats(self, formats_url: str = _formats_url) -> None:
@@ -258,7 +288,7 @@ class AdminLevel:
258
288
  Returns:
259
289
  None
260
290
  """
261
- formats_info = self.get_libhxl_dataset(formats_url)
291
+ formats_info = self.get_libhxl_dataset(formats_url, self.retriever)
262
292
  for row in formats_info:
263
293
  pcode_format = [int(row.get("#country+len"))]
264
294
  for admin_no in range(1, 4):
@@ -3,28 +3,35 @@
3
3
  from os.path import join
4
4
 
5
5
  import pytest
6
- from hxl.input import HXLIOException
7
6
 
8
7
  from hdx.location.adminlevel import AdminLevel
8
+ from hdx.utilities.base_downloader import DownloadError
9
+ from hdx.utilities.downloader import Download
9
10
  from hdx.utilities.loader import load_yaml
11
+ from hdx.utilities.path import temp_dir
12
+ from hdx.utilities.retriever import Retrieve
10
13
 
11
14
 
12
15
  class TestAdminLevel:
16
+ @pytest.fixture(scope="class")
17
+ def fixtures_dir(self):
18
+ return join("tests", "fixtures")
19
+
13
20
  @pytest.fixture(scope="function")
14
- def config(self):
15
- return load_yaml(join("tests", "fixtures", "adminlevel.yaml"))
21
+ def config(self, fixtures_dir):
22
+ return load_yaml(join(fixtures_dir, "adminlevel.yaml"))
16
23
 
17
24
  @pytest.fixture(scope="function")
18
- def config_parent(self):
19
- return load_yaml(join("tests", "fixtures", "adminlevelparent.yaml"))
25
+ def config_parent(self, fixtures_dir):
26
+ return load_yaml(join(fixtures_dir, "adminlevelparent.yaml"))
20
27
 
21
28
  @pytest.fixture(scope="function")
22
- def url(self):
23
- return "https://raw.githubusercontent.com/OCHA-DAP/hdx-python-country/blank_adm_name/tests/fixtures/global_pcodes_adm_1_2.csv"
29
+ def url(self, fixtures_dir):
30
+ return join(fixtures_dir, "download-global-pcodes-adm-1-2.csv")
24
31
 
25
32
  @pytest.fixture(scope="function")
26
- def formats_url(self):
27
- return "https://raw.githubusercontent.com/OCHA-DAP/hdx-python-country/blank_adm_name/tests/fixtures/global_pcode_lengths.csv"
33
+ def formats_url(self, fixtures_dir):
34
+ return join(fixtures_dir, "download-global-pcode-lengths.csv")
28
35
 
29
36
  def test_adminlevel(self, config):
30
37
  adminone = AdminLevel(config)
@@ -367,16 +374,50 @@ class TestAdminLevel:
367
374
  "MWI", "Blantyre city", parent="MW3", logname="test"
368
375
  ) == (None, False)
369
376
 
370
- def test_adminlevel_with_url(self, config, url):
371
- adminone = AdminLevel(config)
372
- with pytest.raises(HXLIOException):
377
+ def test_adminlevel_with_url(self, config, url, fixtures_dir):
378
+ adminone = AdminLevel(config, admin_level_overrides={"YEM": 5})
379
+ assert adminone.get_admin_level("YEM") == 5
380
+ with pytest.raises(FileNotFoundError):
373
381
  adminone.setup_from_url("fake_url")
382
+ adminone = AdminLevel(config)
374
383
  AdminLevel.set_default_admin_url()
375
384
  assert AdminLevel._admin_url == AdminLevel._admin_url_default
376
385
  AdminLevel.set_default_admin_url(url)
377
386
  assert AdminLevel._admin_url == url
378
387
  adminone.setup_from_url(countryiso3s=("YEM",))
379
388
  assert len(adminone.get_pcode_list()) == 22
389
+
390
+ with temp_dir(
391
+ "TestAdminLevelRetriever",
392
+ delete_on_success=True,
393
+ delete_on_failure=False,
394
+ ) as tempdir:
395
+ with Download(user_agent="test") as downloader:
396
+ retriever = Retrieve(
397
+ downloader,
398
+ tempdir,
399
+ fixtures_dir,
400
+ tempdir,
401
+ save=False,
402
+ use_saved=False,
403
+ )
404
+ adminone = AdminLevel(config, retriever=retriever)
405
+ with pytest.raises(DownloadError):
406
+ adminone.setup_from_url("fake_url")
407
+ retriever = Retrieve(
408
+ downloader,
409
+ tempdir,
410
+ fixtures_dir,
411
+ tempdir,
412
+ save=False,
413
+ use_saved=True,
414
+ )
415
+ adminone = AdminLevel(config, retriever=retriever)
416
+ with pytest.raises(FileNotFoundError):
417
+ adminone.setup_from_url("fake_url")
418
+ adminone.setup_from_url(countryiso3s=("YEM",))
419
+ assert len(adminone.get_pcode_list()) == 22
420
+
380
421
  adminone = AdminLevel(config)
381
422
  adminone.setup_from_url()
382
423
  assert adminone.get_admin_level("YEM") == 1