hdx-python-utilities 4.0.2__tar.gz → 4.0.4__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 (145) hide show
  1. hdx_python_utilities-4.0.4/.github/workflows/publish-test.yaml +33 -0
  2. hdx_python_utilities-4.0.4/.github/workflows/publish.yaml +34 -0
  3. hdx_python_utilities-4.0.4/.github/workflows/run-python-tests.yaml +49 -0
  4. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/.pre-commit-config.yaml +8 -9
  5. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/PKG-INFO +5 -9
  6. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/README.md +1 -0
  7. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/documentation/index.md +32 -9
  8. hdx_python_utilities-4.0.4/pyproject.toml +159 -0
  9. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/_version.py +2 -2
  10. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/base_downloader.py +3 -1
  11. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/downloader.py +14 -97
  12. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/path.py +1 -57
  13. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/retriever.py +1 -1
  14. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/saver.py +17 -8
  15. hdx_python_utilities-4.0.4/src/hdx/utilities/url.py +141 -0
  16. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/useragent.py +1 -1
  17. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/conftest.py +5 -0
  18. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_downloader.py +6 -41
  19. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_path.py +0 -29
  20. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_saver.py +9 -0
  21. hdx_python_utilities-4.0.4/tests/hdx/utilities/test_url.py +63 -0
  22. hdx_python_utilities-4.0.4/uv.lock +1463 -0
  23. hdx_python_utilities-4.0.2/.coveragerc +0 -17
  24. hdx_python_utilities-4.0.2/.github/workflows/publish-test.yaml +0 -38
  25. hdx_python_utilities-4.0.2/.github/workflows/publish.yaml +0 -37
  26. hdx_python_utilities-4.0.2/.github/workflows/run-python-tests.yaml +0 -53
  27. hdx_python_utilities-4.0.2/hatch.toml +0 -34
  28. hdx_python_utilities-4.0.2/pyproject.toml +0 -72
  29. hdx_python_utilities-4.0.2/pytest.ini +0 -4
  30. hdx_python_utilities-4.0.2/requirements.txt +0 -131
  31. hdx_python_utilities-4.0.2/ruff.toml +0 -10
  32. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/.gitignore +0 -0
  33. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/CONTRIBUTING.md +0 -0
  34. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/LICENSE +0 -0
  35. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/documentation/.readthedocs.yaml +0 -0
  36. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/documentation/mkdocs.yaml +0 -0
  37. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/__init__.py +0 -0
  38. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/compare.py +0 -0
  39. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/dateparse.py +0 -0
  40. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/dictandlist.py +0 -0
  41. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/easy_logging.py +0 -0
  42. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/email.py +0 -0
  43. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/encoding.py +0 -0
  44. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/error_handler.py +0 -0
  45. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/errors_onexit.py +0 -0
  46. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/file_hashing.py +0 -0
  47. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/frictionless_wrapper.py +0 -0
  48. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/html.py +0 -0
  49. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/loader.py +0 -0
  50. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/matching.py +0 -0
  51. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/session.py +0 -0
  52. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/state.py +0 -0
  53. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/text.py +0 -0
  54. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/uuid.py +0 -0
  55. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/src/hdx/utilities/zip_crc.py +0 -0
  56. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/compare/test_csv_processing.csv +0 -0
  57. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/compare/test_csv_processing2.csv +0 -0
  58. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/empty.yaml +0 -0
  59. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/hdx_config.json +0 -0
  60. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/hdx_config.yaml +0 -0
  61. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/hdx_email_configuration.json +0 -0
  62. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/hdx_email_configuration.yaml +0 -0
  63. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/json_csv.yaml +0 -0
  64. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/logging_config.json +0 -0
  65. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/logging_config.yaml +0 -0
  66. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/project_configuration.json +0 -0
  67. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/project_configuration.yaml +0 -0
  68. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/smtp_config.json +0 -0
  69. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/smtp_config.yaml +0 -0
  70. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/user_agent_config.yaml +0 -0
  71. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/user_agent_config2.yaml +0 -0
  72. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/user_agent_config3.yaml +0 -0
  73. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/config/user_agent_config_wrong.yaml +0 -0
  74. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/downloader/basicauth.txt +0 -0
  75. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/downloader/bearertoken.txt +0 -0
  76. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/downloader/extra_params.json +0 -0
  77. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/downloader/extra_params.yaml +0 -0
  78. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/downloader/extra_params_tree.yaml +0 -0
  79. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/downloader/test_csv_processing.csv +0 -0
  80. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/downloader/test_csv_processing_blanks.csv +0 -0
  81. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/downloader/test_data.csv +0 -0
  82. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/downloader/test_data.xlsx +0 -0
  83. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/downloader/test_data1.csv/empty.txt +0 -0
  84. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/downloader/test_data2.csv +0 -0
  85. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/downloader/test_json_processing.json +0 -0
  86. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/downloader/test_xls_processing.xls +0 -0
  87. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/downloader/test_xlsx_processing.xlsx +0 -0
  88. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/file_hashing/bad_header.zip +0 -0
  89. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/file_hashing/bad_index.xlsx +0 -0
  90. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/file_hashing/empty.zip +0 -0
  91. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/file_hashing/test.xlsx +0 -0
  92. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/file_hashing/test_shapefile.zip +0 -0
  93. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/file_hashing/valid_sig_invalid_body.zip +0 -0
  94. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/html/response.html +0 -0
  95. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/loader/empty.json +0 -0
  96. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/loader/empty.yaml +0 -0
  97. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/loader/empty_list.json +0 -0
  98. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/retriever/fallbacks/test.csv +0 -0
  99. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/retriever/fallbacks/test.json +0 -0
  100. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/retriever/fallbacks/test.txt +0 -0
  101. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/retriever/fallbacks/test.yaml +0 -0
  102. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/retriever/retriever-test.csv +0 -0
  103. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/retriever/test.csv +0 -0
  104. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/retriever/test.json +0 -0
  105. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/retriever/test.txt +0 -0
  106. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/retriever/test.yaml +0 -0
  107. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/retriever/test_hxl.csv +0 -0
  108. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/out.csv +0 -0
  109. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/out.json +0 -0
  110. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/out2.csv +0 -0
  111. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/out2.json +0 -0
  112. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/out5.json +0 -0
  113. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/out6.json +0 -0
  114. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/out7.json +0 -0
  115. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/out8.csv +0 -0
  116. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/out8.json +0 -0
  117. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/pretty-false_sortkeys-false.json +0 -0
  118. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/pretty-false_sortkeys-false.yaml +0 -0
  119. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/pretty-false_sortkeys-true.json +0 -0
  120. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/pretty-false_sortkeys-true.yaml +0 -0
  121. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/pretty-true_sortkeys-false.json +0 -0
  122. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/pretty-true_sortkeys-false.yaml +0 -0
  123. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/pretty-true_sortkeys-true.json +0 -0
  124. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/saver/pretty-true_sortkeys-true.yaml +0 -0
  125. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/state/analysis_dates.txt +0 -0
  126. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/state/last_build_date.txt +0 -0
  127. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/fixtures/test_data.csv +0 -0
  128. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_compare.py +0 -0
  129. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_dateparse.py +0 -0
  130. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_dictandlist.py +0 -0
  131. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_easy_logging.py +0 -0
  132. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_email.py +0 -0
  133. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_encoding.py +0 -0
  134. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_error_handler.py +0 -0
  135. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_file_hashing.py +0 -0
  136. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_html.py +0 -0
  137. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_loader.py +0 -0
  138. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_matching.py +0 -0
  139. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_retriever.py +0 -0
  140. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_state.py +0 -0
  141. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_text.py +0 -0
  142. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_useragent.py +0 -0
  143. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_uuid.py +0 -0
  144. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/test_zip_crc.py +0 -0
  145. {hdx_python_utilities-4.0.2 → hdx_python_utilities-4.0.4}/tests/hdx/utilities/utils.py +0 -0
@@ -0,0 +1,33 @@
1
+ name: Publish to TestPyPI
2
+
3
+ on:
4
+ workflow_dispatch: # add run button in github
5
+
6
+ jobs:
7
+ publish:
8
+ runs-on: ubuntu-latest
9
+
10
+ environment:
11
+ name: testpypi
12
+ url: https://test.pypi.org/p/hdx-python-utilities
13
+
14
+ permissions:
15
+ id-token: write
16
+ contents: read
17
+
18
+ steps:
19
+ - uses: actions/checkout@v6
20
+
21
+ - name: Get history and tags for versioning to work
22
+ run: |
23
+ git fetch --prune --unshallow
24
+ git fetch --depth=1 origin +refs/tags/*:refs/tags/*
25
+
26
+ - name: Install uv
27
+ uses: astral-sh/setup-uv@v7
28
+
29
+ - name: Build with uv
30
+ run: uv build
31
+
32
+ - name: Publish distribution 📦 to TestPyPI
33
+ run: uv publish --publish-url https://test.pypi.org/legacy/
@@ -0,0 +1,34 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+
11
+ environment:
12
+ name: pypi
13
+ url: https://pypi.org/p/hdx-python-utilities
14
+
15
+ permissions:
16
+ id-token: write
17
+ contents: read
18
+
19
+ steps:
20
+ - uses: actions/checkout@v6
21
+
22
+ - name: Get history and tags for versioning to work
23
+ run: |
24
+ git fetch --prune --unshallow
25
+ git fetch --depth=1 origin +refs/tags/*:refs/tags/*
26
+
27
+ - name: Install uv
28
+ uses: astral-sh/setup-uv@v7
29
+
30
+ - name: Build with uv
31
+ run: uv build
32
+
33
+ - name: Publish distribution 📦 to PyPI
34
+ run: uv publish
@@ -0,0 +1,49 @@
1
+ name: Run tests
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches-ignore: [gh-pages, "dependabot/**"]
7
+ pull_request:
8
+ branches-ignore: [gh-pages]
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ contents: read
15
+ checks: write
16
+ pull-requests: write
17
+
18
+ steps:
19
+ - uses: actions/checkout@v6
20
+
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@v7
23
+ with:
24
+ enable-cache: true
25
+ python-version: "3.13"
26
+
27
+ - name: Install dependencies
28
+ run: uv sync --frozen
29
+
30
+ - name: Check styling
31
+ run: |
32
+ uv run ruff format --check
33
+ uv run ruff check
34
+
35
+ - name: Test with pytest
36
+ run: uv run --extra html --extra diff --extra email pytest
37
+
38
+ - name: Publish Unit Test Results
39
+ uses: EnricoMi/publish-unit-test-result-action@v2
40
+ if: always()
41
+ with:
42
+ files: test-results.xml
43
+
44
+ - name: Publish in Coveralls
45
+ uses: coverallsapp/github-action@v2
46
+ if: always()
47
+ with:
48
+ flag-name: tests
49
+ format: lcov
@@ -1,5 +1,6 @@
1
1
  default_language_version:
2
- python: python3.13
2
+ python: python3.13
3
+
3
4
  repos:
4
5
  - repo: https://github.com/pre-commit/pre-commit-hooks
5
6
  rev: v6.0.0
@@ -9,20 +10,18 @@ repos:
9
10
  - id: end-of-file-fixer
10
11
  exclude: (test_csv_processing_blanks.csv|test.txt)
11
12
  - id: check-ast
13
+
12
14
  - repo: https://github.com/astral-sh/ruff-pre-commit
13
- rev: v0.14.10
15
+ rev: v0.14.13
14
16
  hooks:
15
17
  # Run the linter.
16
18
  - id: ruff-check
17
19
  args: [ --fix ]
18
20
  # Run the formatter.
19
21
  - id: ruff-format
22
+
20
23
  - repo: https://github.com/astral-sh/uv-pre-commit
21
- rev: 0.9.22
24
+ rev: 0.9.25
22
25
  hooks:
23
- # Run the pip compile
24
- - id: pip-compile
25
- name: pip-compile requirements.txt
26
- files: pyproject.toml
27
- args: [ pyproject.toml, --resolver=backtracking, --upgrade, -q,
28
- -o, requirements.txt ]
26
+ # Ensure the lockfile is up-to-date with pyproject.toml
27
+ - id: uv-lock
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hdx-python-utilities
3
- Version: 4.0.2
3
+ Version: 4.0.4
4
4
  Summary: HDX Python Utilities for streaming tabular data, date and time handling and other helpful functions
5
5
  Project-URL: Homepage, https://github.com/OCHA-DAP/hdx-python-utilities
6
6
  Author-email: Michael Rans <rans@email.com>
@@ -18,11 +18,11 @@ Classifier: Operating System :: Unix
18
18
  Classifier: Programming Language :: Python
19
19
  Classifier: Programming Language :: Python :: 3
20
20
  Classifier: Programming Language :: Python :: 3 :: Only
21
- Classifier: Programming Language :: Python :: 3.8
22
- Classifier: Programming Language :: Python :: 3.9
23
21
  Classifier: Programming Language :: Python :: 3.10
24
22
  Classifier: Programming Language :: Python :: 3.11
25
23
  Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
26
26
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
27
  Requires-Python: >=3.10
28
28
  Requires-Dist: frictionless>=5.18.0
@@ -36,11 +36,10 @@ Requires-Dist: ratelimit
36
36
  Requires-Dist: requests-file
37
37
  Requires-Dist: ruamel-yaml
38
38
  Requires-Dist: tableschema-to-template>=0.0.13
39
+ Requires-Dist: typing-extensions
39
40
  Requires-Dist: xlrd>=2.0.1
40
41
  Requires-Dist: xlsx2csv
41
42
  Requires-Dist: xlwt>=1.3.0
42
- Provides-Extra: dev
43
- Requires-Dist: pre-commit; extra == 'dev'
44
43
  Provides-Extra: diff
45
44
  Requires-Dist: cydifflib; extra == 'diff'
46
45
  Provides-Extra: docs
@@ -50,10 +49,6 @@ Requires-Dist: email-validator; extra == 'email'
50
49
  Provides-Extra: html
51
50
  Requires-Dist: beautifulsoup4; extra == 'html'
52
51
  Requires-Dist: html5lib; extra == 'html'
53
- Provides-Extra: test
54
- Requires-Dist: pytest; extra == 'test'
55
- Requires-Dist: pytest-cov; extra == 'test'
56
- Requires-Dist: pytest-loguru; extra == 'test'
57
52
  Description-Content-Type: text/markdown
58
53
 
59
54
  [![Build Status](https://github.com/OCHA-DAP/hdx-python-utilities/actions/workflows/run-python-tests.yaml/badge.svg)](https://github.com/OCHA-DAP/hdx-python-utilities/actions/workflows/run-python-tests.yaml)
@@ -76,6 +71,7 @@ Note that these are not specific to HDX.
76
71
  1. Easy logging setup and error logging
77
72
  1. State utility
78
73
  1. Path utilities
74
+ 1. URL utilities
79
75
  1. Text processing
80
76
  1. Stable file hashing
81
77
  1. Matching utilities
@@ -18,6 +18,7 @@ Note that these are not specific to HDX.
18
18
  1. Easy logging setup and error logging
19
19
  1. State utility
20
20
  1. Path utilities
21
+ 1. URL utilities
21
22
  1. Text processing
22
23
  1. Stable file hashing
23
24
  1. Matching utilities
@@ -17,6 +17,7 @@ Python developers. Note that these are not specific to HDX.
17
17
  1. [Easy logging setup and error logging](#logging)
18
18
  1. [State utility](#state-utility)
19
19
  1. [Path utilities](#path-utilities)
20
+ 1. [URL utilities](#path-utilities)
20
21
  1. [Text processing](#text-processing)
21
22
  1. [Stable file hashing](#stable-file-hashing)
22
23
  1. [Matching utilities](#matching-utilities)
@@ -33,6 +34,9 @@ The code for the library is [here](https://github.com/OCHA-DAP/hdx-python-utilit
33
34
  The library has detailed API documentation which can be found in the menu at the top.
34
35
 
35
36
  ## Breaking Changes
37
+ From 4.0.4, get_filename_from_url, get_filename_extension_from_url, get_path_for_url,
38
+ get_url_for_get, get_url_params_for_post moved to module url
39
+
36
40
  From 4.0.1, Library is Path aware. The following methods return Path not str:
37
41
  download_file, get_path_for_url, stream_path, get_temp_dir, script_dir_plus_file,
38
42
  script_dir. The context managers in hdx.utilities.path where they yield a path
@@ -829,15 +833,6 @@ Get current directory of script with filename appended
829
833
 
830
834
  path = script_dir_plus_file("myfile.txt", ANY_PYTHON_OBJECT_IN_SCRIPT)
831
835
 
832
- Get filename or (filename, extension) from url
833
-
834
- url = "https://raw.githubusercontent.com/OCHA-DAP/hdx-python-utilities/master/tests/fixtures/test_data.csv"
835
- filename = get_filename_from_url(fixtureurl)
836
- assert filename == "test_data.csv"
837
- filename, extension = get_filename_extension_from_url(fixtureurl)
838
- assert filename == "test_data"
839
- assert extension == ".csv"
840
-
841
836
  Gets temporary directory from environment variable `TEMP_DIR` and falls back to
842
837
  the temporary folder created by the os function `gettempdir`.
843
838
 
@@ -890,6 +885,34 @@ The batch code can be passed into `wheretostart_tempdir_batch` in the `batch`
890
885
  parameter. If not given, the batch code is generated. The folder to use will be
891
886
  a generated temporary folder unless `tempdir` is given.
892
887
 
888
+ ## URL utilities
889
+
890
+ Examples:
891
+
892
+ Get filename or (filename, extension) or path from url
893
+
894
+ url = "https://raw.githubusercontent.com/OCHA-DAP/hdx-python-utilities/master/tests/fixtures/test_data.csv"
895
+ filename = get_filename_from_url(fixtureurl)
896
+ assert filename == "test_data.csv"
897
+ filename, extension = get_filename_extension_from_url(fixtureurl)
898
+ assert filename == "test_data"
899
+ assert extension == ".csv"
900
+ # Get unique filename from url and join to provided folder or temporary folder
901
+ # if no folder supplied
902
+ path = get_path_for_url(url, folder)
903
+
904
+ Conversion between POST dictionary and GET parameters and the reverse:
905
+
906
+ # Build get url from url and dictionary of parameters
907
+ get_url_for_get("http://www.lala.com/hdfa?a=3&b=4",
908
+ OrderedDict([("c", "e"), ("d", "f")]))
909
+ # == "http://www.lala.com/hdfa?a=3&b=4&c=e&d=f"
910
+
911
+ # Extract url and dictionary of parameters from get url
912
+ get_url_params_for_post("http://www.lala.com/hdfa?a=3&b=4",
913
+ OrderedDict([("c", "e"), ("d", "f")]))
914
+
915
+
893
916
  ## Text processing
894
917
 
895
918
  Examples:
@@ -0,0 +1,159 @@
1
+ #########################
2
+ # Project Configuration #
3
+ #########################
4
+
5
+ [build-system]
6
+ requires = ["hatchling", "hatch-vcs"]
7
+ build-backend = "hatchling.build"
8
+
9
+ [project]
10
+ name = "hdx-python-utilities"
11
+ description = "HDX Python Utilities for streaming tabular data, date and time handling and other helpful functions"
12
+ authors = [{name = "Michael Rans", email = "rans@email.com"}]
13
+ license = {text = "MIT"}
14
+ keywords = ["HDX", "utilities", "library", "streaming", "tabular data", "datetime", "date", "time", "timezone", "dict",
15
+ "list", "json", "yaml"]
16
+ classifiers = [
17
+ "Development Status :: 5 - Production/Stable",
18
+ "Topic :: Software Development :: Libraries :: Python Modules",
19
+ "Programming Language :: Python",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Programming Language :: Python :: 3.14",
27
+ "Intended Audience :: Developers",
28
+ "License :: OSI Approved :: MIT License",
29
+ "Natural Language :: English",
30
+ "Operating System :: POSIX :: Linux",
31
+ "Operating System :: Unix",
32
+ "Operating System :: MacOS",
33
+ "Operating System :: Microsoft :: Windows",
34
+ ]
35
+ readme = "README.md"
36
+ dynamic = ["version"]
37
+ requires-python = ">=3.10"
38
+
39
+ # Extras for frictionless[excel,json] added explicitly
40
+ # for conda-forge compatibility
41
+ dependencies = [
42
+ "frictionless>=5.18.0",
43
+ # frictionless[excel]
44
+ "openpyxl>=3.1.2",
45
+ "tableschema-to-template>=0.0.13",
46
+ "xlrd>=2.0.1",
47
+ "xlwt>=1.3.0",
48
+ # frictionless[json]
49
+ "ijson>=3.2.3",
50
+ "jsonlines>=4.0.0",
51
+ # /end frictionless extras
52
+ "typing_extensions",
53
+ "loguru",
54
+ "pyphonetics",
55
+ "python-dateutil>=2.9.0, <2.9.1",
56
+ "ratelimit",
57
+ "requests-file",
58
+ "ruamel.yaml",
59
+ "xlsx2csv",
60
+ ]
61
+
62
+ [project.optional-dependencies]
63
+ html = ["beautifulsoup4", "html5lib"]
64
+ diff = ["cydifflib"]
65
+ email = ["email_validator"]
66
+ docs = ["mkapi"]
67
+
68
+ [dependency-groups]
69
+ dev = [
70
+ "pytest",
71
+ "pytest-cov",
72
+ "pytest-loguru",
73
+ "pre-commit",
74
+ "ruff==0.14.13",
75
+ ]
76
+
77
+ [project.urls]
78
+ Homepage = "https://github.com/OCHA-DAP/hdx-python-utilities"
79
+
80
+ # ----------------------------------------------------------------------------
81
+ # Hatchling (Build & Versioning)
82
+ # ----------------------------------------------------------------------------
83
+
84
+ [tool.hatch.version]
85
+ source = "vcs"
86
+
87
+ [tool.hatch.version.raw-options]
88
+ local_scheme = "no-local-version"
89
+ version_scheme = "python-simplified-semver"
90
+
91
+ [tool.hatch.build.hooks.vcs]
92
+ version-file = "src/hdx/utilities/_version.py"
93
+
94
+ [tool.hatch.build.targets.wheel]
95
+ packages = ["src/hdx"]
96
+
97
+ [tool.hatch.metadata]
98
+ allow-direct-references = true
99
+
100
+ # ----------------------------------------------------------------------------
101
+ # Ruff (Linting & Formatting)
102
+ # ----------------------------------------------------------------------------
103
+
104
+ [tool.ruff]
105
+ target-version = "py310"
106
+ src = ["src"]
107
+ exclude = ["_version.py"]
108
+
109
+ [tool.ruff.lint]
110
+ # Defaults are E (pycodestyle) and F (pyflakes). We extend them:
111
+ extend-select = [
112
+ "I", # isort
113
+ "UP", # pyupgrade
114
+ ]
115
+ ignore = [
116
+ "E501", # Line too long
117
+ ]
118
+
119
+ [tool.ruff.lint.isort]
120
+ known-local-folder = ["hdx.utilities"]
121
+
122
+ # ----------------------------------------------------------------------------
123
+ # Pytest (Testing)
124
+ # ----------------------------------------------------------------------------
125
+
126
+ [tool.pytest.ini_options]
127
+ pythonpath = "src"
128
+ log_cli = true
129
+ addopts = """
130
+ --color=yes
131
+ --rootdir=.
132
+ --junitxml=test-results.xml
133
+ --cov
134
+ --no-cov-on-fail
135
+ --cov-report=lcov
136
+ --cov-report=term-missing
137
+ """
138
+
139
+ # ----------------------------------------------------------------------------
140
+ # Coverage (Reporting)
141
+ # ----------------------------------------------------------------------------
142
+
143
+ [tool.coverage.run]
144
+ source = ["src"]
145
+ omit = ["*/_version.py"]
146
+
147
+ [tool.coverage.report]
148
+ exclude_also = [
149
+ "from ._version",
150
+ "def __repr__",
151
+ "if self.debug:",
152
+ "if settings.DEBUG",
153
+ "raise AssertionError",
154
+ "raise NotImplementedError",
155
+ "if 0:",
156
+ "if __name__ == .__main__.:",
157
+ "if TYPE_CHECKING:",
158
+ "@(abc\\.)?abstractmethod",
159
+ ]
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '4.0.2'
32
- __version_tuple__ = version_tuple = (4, 0, 2)
31
+ __version__ = version = '4.0.4'
32
+ __version_tuple__ = version_tuple = (4, 0, 4)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -3,6 +3,8 @@ from collections.abc import Iterator, Sequence
3
3
  from pathlib import Path
4
4
  from typing import Any
5
5
 
6
+ from typing_extensions import Self
7
+
6
8
 
7
9
  class DownloadError(Exception):
8
10
  pass
@@ -12,7 +14,7 @@ class BaseDownload(ABC):
12
14
  """Base download class with various download operations that subclasses
13
15
  should implement."""
14
16
 
15
- def __enter__(self) -> "BaseDownload":
17
+ def __enter__(self) -> Self:
16
18
  """Allow usage of with.
17
19
 
18
20
  Returns:
@@ -4,11 +4,10 @@ import hashlib
4
4
  import logging
5
5
  from collections.abc import Callable, Iterator, Sequence
6
6
  from copy import deepcopy
7
- from os import remove
8
- from os.path import exists, isfile, split, splitext
7
+ from os.path import exists, isfile
9
8
  from pathlib import Path
10
9
  from typing import Any
11
- from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
10
+ from urllib.parse import urlsplit, urlunsplit
12
11
 
13
12
  import requests
14
13
  from frictionless import FrictionlessException
@@ -21,8 +20,8 @@ from xlsx2csv import Xlsx2csv
21
20
 
22
21
  from hdx.utilities.base_downloader import BaseDownload, DownloadError
23
22
  from hdx.utilities.frictionless_wrapper import get_frictionless_tableresource
24
- from hdx.utilities.path import get_filename_from_url, get_temp_dir
25
23
  from hdx.utilities.session import get_session
24
+ from hdx.utilities.url import get_path_for_url, get_url_for_get, get_url_params_for_post
26
25
 
27
26
  logger = logging.getLogger(__name__)
28
27
 
@@ -130,56 +129,8 @@ class Download(BaseDownload):
130
129
  """
131
130
  self.close()
132
131
 
133
- @staticmethod
134
- def get_path_for_url(
135
- url: str,
136
- folder: Path | str | None = None,
137
- filename: str | None = None,
138
- path: Path | str | None = None,
139
- overwrite: bool = False,
140
- keep: bool = False,
141
- ) -> Path:
142
- """Get filename from url and join to provided folder or temporary
143
- folder if no folder supplied, ensuring uniqueness.
144
-
145
- Args:
146
- url: URL to download
147
- folder: Folder to download it to. Defaults to None (temporary folder).
148
- filename: Filename to use for downloaded file. Defaults to None (derive from the url).
149
- path: Full path to use for downloaded file. Defaults to None (use folder and filename).
150
- overwrite: Whether to overwrite existing file. Defaults to False.
151
- keep: Whether to keep already downloaded file. Defaults to False.
152
-
153
- Returns:
154
- Path of downloaded file
155
- """
156
- if path:
157
- if folder or filename:
158
- raise DownloadError(
159
- "Cannot use folder or filename and path arguments together!"
160
- )
161
- folder, filename = split(path)
162
- if not filename:
163
- filename = get_filename_from_url(url)
164
- filename, extension = splitext(filename)
165
- if not folder:
166
- folder = get_temp_dir()
167
- folder = Path(folder)
168
- path = folder / f"{filename}{extension}"
169
- if overwrite:
170
- try:
171
- remove(path)
172
- except OSError:
173
- pass
174
- elif not keep:
175
- count = 0
176
- while exists(path):
177
- count += 1
178
- path = folder / f"{filename}{count}{extension}"
179
- return path
180
-
181
132
  def get_full_url(self, url: str) -> str:
182
- """Get full url including any additional parameters.
133
+ """Get full url including any additional parameters added to the session.
183
134
 
184
135
  Args:
185
136
  url: URL for which to get full url
@@ -191,46 +142,6 @@ class Download(BaseDownload):
191
142
  preparedrequest = self.session.prepare_request(request)
192
143
  return preparedrequest.url
193
144
 
194
- @staticmethod
195
- def get_url_for_get(url: str, parameters: dict | None = None) -> str:
196
- """Get full url for GET request including parameters.
197
-
198
- Args:
199
- url: URL to download
200
- parameters: Parameters to pass. Defaults to None.
201
-
202
- Returns:
203
- Full url
204
- """
205
- spliturl = urlsplit(url)
206
- getparams = dict(parse_qsl(spliturl.query))
207
- if parameters is not None:
208
- getparams.update(parameters)
209
- spliturl = spliturl._replace(query=urlencode(getparams))
210
- return urlunsplit(spliturl)
211
-
212
- @staticmethod
213
- def get_url_params_for_post(
214
- url: str, parameters: dict | None = None
215
- ) -> tuple[str, dict]:
216
- """Get full url for POST request and all parameters including any in
217
- the url.
218
-
219
- Args:
220
- url: URL to download
221
- parameters: Parameters to pass. Defaults to None.
222
-
223
- Returns:
224
- (Full url, parameters)
225
- """
226
- spliturl = urlsplit(url)
227
- getparams = dict(parse_qsl(spliturl.query))
228
- if parameters is not None:
229
- getparams.update(parameters)
230
- spliturl = spliturl._replace(query="")
231
- full_url = urlunsplit(spliturl)
232
- return full_url, getparams
233
-
234
145
  @staticmethod
235
146
  def hxl_row(
236
147
  headers: Sequence[str],
@@ -291,7 +202,7 @@ class Download(BaseDownload):
291
202
  spliturl = spliturl._replace(scheme="https")
292
203
  url = urlunsplit(spliturl)
293
204
  if post:
294
- full_url, parameters = self.get_url_params_for_post(url, parameters)
205
+ full_url, parameters = get_url_params_for_post(url, parameters)
295
206
  if json_string:
296
207
  self.response = self.session.post(
297
208
  full_url,
@@ -310,7 +221,7 @@ class Download(BaseDownload):
310
221
  )
311
222
  else:
312
223
  self.response = self.session.get(
313
- self.get_url_for_get(url, parameters),
224
+ get_url_for_get(url, parameters),
314
225
  stream=stream,
315
226
  timeout=timeout,
316
227
  headers=headers,
@@ -408,7 +319,10 @@ class Download(BaseDownload):
408
319
  Returns:
409
320
  Path of downloaded file
410
321
  """
411
- path = self.get_path_for_url(url, folder, filename, path, overwrite, keep)
322
+ try:
323
+ path = get_path_for_url(url, folder, filename, path, overwrite, keep)
324
+ except ValueError as ex:
325
+ raise DownloadError(ex) from ex
412
326
  if keep and exists(path):
413
327
  return path
414
328
  return self.stream_path(
@@ -446,7 +360,10 @@ class Download(BaseDownload):
446
360
  path = kwargs.get("path")
447
361
  overwrite = kwargs.get("overwrite", False)
448
362
  keep = kwargs.get("keep", False)
449
- path = self.get_path_for_url(url, folder, filename, path, overwrite, keep)
363
+ try:
364
+ path = get_path_for_url(url, folder, filename, path, overwrite, keep)
365
+ except ValueError as ex:
366
+ raise DownloadError(ex) from ex
450
367
  if keep and exists(path):
451
368
  return path
452
369
  self.setup(