hdx-python-utilities 4.0.7__tar.gz → 4.1.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 (139) hide show
  1. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/.pre-commit-config.yaml +1 -1
  2. hdx_python_utilities-4.1.0/CLAUDE.md +81 -0
  3. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/PKG-INFO +4 -5
  4. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/README.md +0 -1
  5. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/documentation/index.md +3 -91
  6. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/pyproject.toml +3 -3
  7. hdx_python_utilities-4.1.0/src/hdx/utilities/_version.py +24 -0
  8. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/base_downloader.py +0 -4
  9. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/downloader.py +0 -54
  10. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/path.py +10 -23
  11. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/retriever.py +1 -7
  12. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/saver.py +9 -142
  13. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_downloader.py +0 -12
  14. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_retriever.py +0 -16
  15. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_saver.py +55 -152
  16. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_text.py +1 -1
  17. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/uv.lock +132 -98
  18. hdx_python_utilities-4.0.7/src/hdx/utilities/_version.py +0 -34
  19. hdx_python_utilities-4.0.7/tests/fixtures/config/json_csv.yaml +0 -108
  20. hdx_python_utilities-4.0.7/tests/fixtures/retriever/test_hxl.csv +0 -4
  21. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/.github/workflows/publish-test.yaml +0 -0
  22. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/.github/workflows/publish.yaml +0 -0
  23. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/.github/workflows/run-python-tests.yaml +0 -0
  24. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/.gitignore +0 -0
  25. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/LICENSE +0 -0
  26. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/documentation/.readthedocs.yaml +0 -0
  27. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/documentation/mkdocs.yaml +0 -0
  28. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/__init__.py +0 -0
  29. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/compare.py +0 -0
  30. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/dateparse.py +0 -0
  31. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/dictandlist.py +0 -0
  32. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/easy_logging.py +0 -0
  33. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/email.py +0 -0
  34. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/encoding.py +0 -0
  35. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/error_handler.py +0 -0
  36. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/errors_onexit.py +0 -0
  37. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/file_hashing.py +0 -0
  38. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/frictionless_wrapper.py +0 -0
  39. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/html.py +0 -0
  40. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/loader.py +0 -0
  41. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/matching.py +0 -0
  42. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/session.py +0 -0
  43. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/state.py +0 -0
  44. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/text.py +0 -0
  45. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/url.py +0 -0
  46. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/useragent.py +0 -0
  47. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/uuid.py +0 -0
  48. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/src/hdx/utilities/zip_crc.py +0 -0
  49. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/compare/test_csv_processing.csv +0 -0
  50. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/compare/test_csv_processing2.csv +0 -0
  51. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/empty.yaml +0 -0
  52. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/hdx_config.json +0 -0
  53. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/hdx_config.yaml +0 -0
  54. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/hdx_email_configuration.json +0 -0
  55. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/hdx_email_configuration.yaml +0 -0
  56. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/logging_config.json +0 -0
  57. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/logging_config.yaml +0 -0
  58. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/project_configuration.json +0 -0
  59. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/project_configuration.yaml +0 -0
  60. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/smtp_config.json +0 -0
  61. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/smtp_config.yaml +0 -0
  62. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/user_agent_config.yaml +0 -0
  63. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/user_agent_config2.yaml +0 -0
  64. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/user_agent_config3.yaml +0 -0
  65. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/config/user_agent_config_wrong.yaml +0 -0
  66. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/downloader/basicauth.txt +0 -0
  67. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/downloader/bearertoken.txt +0 -0
  68. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/downloader/extra_params.json +0 -0
  69. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/downloader/extra_params.yaml +0 -0
  70. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/downloader/extra_params_tree.yaml +0 -0
  71. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/downloader/test_csv_processing.csv +0 -0
  72. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/downloader/test_csv_processing_blanks.csv +0 -0
  73. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/downloader/test_data.csv +0 -0
  74. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/downloader/test_data.xlsx +0 -0
  75. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/downloader/test_data1.csv/empty.txt +0 -0
  76. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/downloader/test_data2.csv +0 -0
  77. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/downloader/test_json_processing.json +0 -0
  78. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/downloader/test_xls_processing.xls +0 -0
  79. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/downloader/test_xlsx_processing.xlsx +0 -0
  80. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/file_hashing/bad_header.zip +0 -0
  81. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/file_hashing/bad_index.xlsx +0 -0
  82. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/file_hashing/empty.zip +0 -0
  83. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/file_hashing/test.xlsx +0 -0
  84. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/file_hashing/test_shapefile.zip +0 -0
  85. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/file_hashing/valid_sig_invalid_body.zip +0 -0
  86. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/html/response.html +0 -0
  87. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/loader/empty.json +0 -0
  88. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/loader/empty.yaml +0 -0
  89. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/loader/empty_list.json +0 -0
  90. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/retriever/fallbacks/test.csv +0 -0
  91. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/retriever/fallbacks/test.json +0 -0
  92. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/retriever/fallbacks/test.txt +0 -0
  93. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/retriever/fallbacks/test.yaml +0 -0
  94. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/retriever/retriever-test.csv +0 -0
  95. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/retriever/test.csv +0 -0
  96. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/retriever/test.json +0 -0
  97. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/retriever/test.txt +0 -0
  98. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/retriever/test.yaml +0 -0
  99. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/out.csv +0 -0
  100. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/out.json +0 -0
  101. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/out2.csv +0 -0
  102. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/out2.json +0 -0
  103. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/out5.json +0 -0
  104. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/out6.json +0 -0
  105. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/out7.json +0 -0
  106. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/out8.csv +0 -0
  107. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/out8.json +0 -0
  108. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/pretty-false_sortkeys-false.json +0 -0
  109. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/pretty-false_sortkeys-false.yaml +0 -0
  110. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/pretty-false_sortkeys-true.json +0 -0
  111. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/pretty-false_sortkeys-true.yaml +0 -0
  112. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/pretty-true_sortkeys-false.json +0 -0
  113. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/pretty-true_sortkeys-false.yaml +0 -0
  114. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/pretty-true_sortkeys-true.json +0 -0
  115. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/saver/pretty-true_sortkeys-true.yaml +0 -0
  116. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/state/analysis_dates.txt +0 -0
  117. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/state/last_build_date.txt +0 -0
  118. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/test_data.csv +0 -0
  119. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/text/text1.txt +0 -0
  120. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/fixtures/text/text2.txt +0 -0
  121. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/conftest.py +0 -0
  122. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_compare.py +0 -0
  123. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_dateparse.py +0 -0
  124. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_dictandlist.py +0 -0
  125. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_easy_logging.py +0 -0
  126. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_email.py +0 -0
  127. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_encoding.py +0 -0
  128. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_error_handler.py +0 -0
  129. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_file_hashing.py +0 -0
  130. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_html.py +0 -0
  131. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_loader.py +0 -0
  132. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_matching.py +0 -0
  133. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_path.py +0 -0
  134. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_state.py +0 -0
  135. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_url.py +0 -0
  136. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_useragent.py +0 -0
  137. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_uuid.py +0 -0
  138. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/test_zip_crc.py +0 -0
  139. {hdx_python_utilities-4.0.7 → hdx_python_utilities-4.1.0}/tests/hdx/utilities/utils.py +0 -0
@@ -1,5 +1,5 @@
1
1
  default_language_version:
2
- python: python3.13
2
+ python: python3
3
3
 
4
4
  repos:
5
5
  - repo: https://github.com/pre-commit/pre-commit-hooks
@@ -0,0 +1,81 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ **hdx-python-utilities** is a general-purpose Python utility library (not HDX-specific) providing helpers for: downloading files, retrieving tabular data (CSV/JSON/XLSX), date parsing, JSON/YAML I/O, dict/list manipulation, HTML parsing, file hashing, text processing, error handling, and more.
8
+
9
+ ## Commands
10
+
11
+ ```bash
12
+ # Install dependencies
13
+ uv sync
14
+
15
+ # Run all tests with coverage
16
+ uv run pytest
17
+
18
+ # Run a single test file
19
+ uv run pytest tests/hdx/utilities/test_downloader.py
20
+
21
+ # Run a single test by name
22
+ uv run pytest tests/hdx/utilities/test_downloader.py -k "test_download_file"
23
+
24
+ # Lint and format
25
+ uv run ruff check
26
+ pre-commit run --all-files
27
+
28
+ # Build
29
+ uv build
30
+ ```
31
+
32
+ Coverage is written to `coverage.lcov` and JUnit XML to `test-results.xml`.
33
+
34
+ ## Source Layout
35
+
36
+ All source lives under `src/hdx/utilities/`. Tests mirror the layout under `tests/hdx/utilities/` with fixtures in `tests/fixtures/`.
37
+
38
+ Key modules:
39
+ - `base_downloader.py` — `BaseDownload` ABC defining the interface (`download_file`, `download_tabular_rows`, etc.) and `DownloadError`
40
+ - `downloader.py` — `Download(BaseDownload)`: HTTP downloading via `requests` + `frictionless` for tabular parsing; supports auth (basic, bearer, extra params), rate limiting, retries, and streaming rows
41
+ - `retriever.py` — `Retrieve(BaseDownload)`: wraps a `Download`, adding save/load-from-disk and static fallback directories; registered instances in `Retrieve.retrievers` dict
42
+ - `session.py` — `get_session()` builds a `requests.Session` with retry logic and auth headers
43
+ - `frictionless_wrapper.py` — thin wrapper around `frictionless.TableResource` for tabular reads
44
+ - `loader.py` / `saver.py` — JSON and YAML I/O (YAML uses `ruamel.yaml` to preserve order/comments)
45
+ - `dateparse.py` — date/time parsing and timezone utilities
46
+ - `dictandlist.py` — dict/list helpers (merge, flatten, key renaming, etc.)
47
+ - `path.py` — `script_dir()`, temp dir helpers, `NotFoundError`
48
+ - `error_handler.py` / `errors_onexit.py` — structured error accumulation and on-exit reporting
49
+ - `text.py` — text processing including `smart_split`
50
+ - `matching.py` — fuzzy/phonetic matching utilities
51
+ - `state.py` — lightweight on-disk state persistence
52
+ - `useragent.py` — user-agent string construction (reads `~/.useragent.yaml` by default)
53
+
54
+ ## Key Patterns
55
+
56
+ **Download / Retrieve layering** — `Download` handles raw HTTP; `Retrieve` adds a save/use_saved/fallback layer on top. Callers typically use `Retrieve` rather than `Download` directly. Both implement `BaseDownload` so they're interchangeable.
57
+
58
+ **Tabular row iteration** — `download_tabular_rows()` (on both `Download` and `Retrieve`) yields dicts via `frictionless.TableResource`. The `frictionless_wrapper.py` module handles the `FrictionlessException` boundary. Supports CSV, JSON, XLSX, and XLS.
59
+
60
+ **User-agent requirement** — `Download` requires a user agent. Either set a global user agent via `UserAgent.set_global(...)` or pass `user_agent=` directly. Missing user agent raises at construction time.
61
+
62
+ **Context manager usage** — `Download` and `Retrieve` are designed as context managers (`with Download(...) as downloader:`). `__exit__` closes the session.
63
+
64
+ **Auth precedence** — environment variables `EXTRA_PARAMS`, `BASIC_AUTH`, `BEARER_TOKEN` override constructor arguments. Among constructor args: `auth` > `basic_auth`/`basic_auth_file` > `bearer_token`/`bearer_token_file` > `extra_params_dict` > `extra_params_json`/`extra_params_yaml`.
65
+
66
+ ## Code Style
67
+
68
+ - Python ≥ 3.10; type hints throughout using `X | Y` union syntax (PEP 604)
69
+ - Google-style docstrings with `Args:` and `Returns:` sections
70
+ - Formatted and linted with `ruff` (rules: E, F, I, UP; E501 ignored)
71
+ - No inline comments unless the *why* is non-obvious
72
+
73
+ ## Collaboration Style
74
+
75
+ - Be objective, not agreeable. Push back when you disagree, flag tradeoffs honestly.
76
+ - Keep explanations brief and to the point.
77
+ - Don't rely on recalled knowledge for facts that could be stale. Read the actual source first.
78
+
79
+ ## Scope of Changes
80
+
81
+ When fixing a bug or addressing PR feedback, change only what is necessary to resolve the specific issue. Do not refactor surrounding code, rename variables, adjust formatting, or make improvements in the same commit unless they are directly required by the fix.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hdx-python-utilities
3
- Version: 4.0.7
3
+ Version: 4.1.0
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>
@@ -25,8 +25,8 @@ Classifier: Programming Language :: Python :: 3.13
25
25
  Classifier: Programming Language :: Python :: 3.14
26
26
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
27
  Requires-Python: >=3.10
28
- Requires-Dist: frictionless>=5.18.0
29
- Requires-Dist: ijson>=3.2.3
28
+ Requires-Dist: frictionless>=5.19.0
29
+ Requires-Dist: ijson>=3.5.0
30
30
  Requires-Dist: jsonlines>=4.0.0
31
31
  Requires-Dist: loguru
32
32
  Requires-Dist: openpyxl>=3.1.2
@@ -38,7 +38,7 @@ Requires-Dist: ruamel-yaml
38
38
  Requires-Dist: tableschema-to-template>=0.0.13
39
39
  Requires-Dist: typing-extensions
40
40
  Requires-Dist: xlrd>=2.0.1
41
- Requires-Dist: xlsx2csv
41
+ Requires-Dist: xlsx2csv==0.8.4
42
42
  Requires-Dist: xlwt>=1.3.0
43
43
  Provides-Extra: diff
44
44
  Requires-Dist: cydifflib; extra == 'diff'
@@ -63,7 +63,6 @@ Note that these are not specific to HDX.
63
63
  1. Retrieval of data from url with saving to file or from data previously saved
64
64
  1. Date utilities
65
65
  1. Loading and saving JSON and YAML (maintaining order)
66
- 1. Loading and saving HXLated csv and/or JSON
67
66
  1. Dictionary and list utilities
68
67
  1. HTML utilities (inc. BeautifulSoup helper)
69
68
  1. Compare files (eg. for testing)
@@ -10,7 +10,6 @@ Note that these are not specific to HDX.
10
10
  1. Retrieval of data from url with saving to file or from data previously saved
11
11
  1. Date utilities
12
12
  1. Loading and saving JSON and YAML (maintaining order)
13
- 1. Loading and saving HXLated csv and/or JSON
14
13
  1. Dictionary and list utilities
15
14
  1. HTML utilities (inc. BeautifulSoup helper)
16
15
  1. Compare files (eg. for testing)
@@ -9,7 +9,6 @@ Python developers. Note that these are not specific to HDX.
9
9
  1. [Retrieval of data from url with saving to file or from data previously saved](#retrieving-files)
10
10
  1. [Date utilities](#date-utilities)
11
11
  1. [Loading and saving JSON and YAML (maintaining order)](#loading-and-saving-json-and-yaml)
12
- 1. [Loading and saving HXLated csv and/or JSON](#loading-and-saving-hxlated-csv-andor-json)
13
12
  1. [Dictionary and list utilities](#dictionary-and-list-utilities)
14
13
  1. [HTML utilities (inc. BeautifulSoup helper)](#html-utilities)
15
14
  1. [Compare files (eg. for testing)](#comparing-files)
@@ -34,6 +33,9 @@ The code for the library is [here](https://github.com/OCHA-DAP/hdx-python-utilit
34
33
  The library has detailed API documentation which can be found in the menu at the top.
35
34
 
36
35
  ## Breaking Changes
36
+ From 4.1.0, save_hxlated_output and Download.hxl_row removed. Parameter use_hxl removed
37
+ from all calls in Download and Retrieve.
38
+
37
39
  From 4.0.4, get_filename_from_url, get_filename_extension_from_url, get_path_for_url,
38
40
  get_url_for_get, get_url_params_for_post moved to module url
39
41
 
@@ -185,9 +187,6 @@ Other useful functions:
185
187
  headers, iterator = downloader.get_tabular_rows_as_list(url)
186
188
  for row in iterator:
187
189
  ...
188
- # Get hxl row
189
- assert Download.hxl_row(["a", "b", "c"], {"b": "#b", "c": "#c"}, dict_form=True)
190
- # == {"a": "", "b": "#b", "c": "#c"}
191
190
  # Build get url from url and dictionary of parameters
192
191
  Download.get_url_for_get("http://www.lala.com/hdfa?a=3&b=4",
193
192
  OrderedDict([("c", "e"), ("d", "f")]))
@@ -461,93 +460,6 @@ as a csv or Excel.
461
460
 
462
461
  save_iterable(xlfilepath, list_of_lists, headers=["h1", "h2", "h3", "h4"], format="xlsx")
463
462
 
464
- ## Loading and saving HXLated csv and/or JSON
465
-
466
- `save_hxlated_output` is a utility to save HXLated output (currently JSON and/or csv are
467
- supported) based on a given configuration. Here is an example YAML configuration:
468
-
469
- input:
470
- headers:
471
- - "Col1"
472
- - "Col2"
473
- - "Col3"
474
- hxltags:
475
- - "#tag1"
476
- - "#tag2"
477
- - "#tag3"
478
- process:
479
- - header: "tag4"
480
- hxltag: "#tag4"
481
- expression: "#tag1 * 10"
482
- output:
483
- csv:
484
- filename: "out.csv"
485
- hxltags:
486
- - "#tag2"
487
- - "#tag3"
488
- json:
489
- filename: "out.json"
490
- data: "results"
491
- metadata:
492
- "#date": "{{today}}"
493
- "#mytag": 123
494
- hxltags:
495
- - "#tag1"
496
- - "#tag2"
497
-
498
- The `input` section is needed if the rows of data that are passed in are missing either
499
- headers or HXL hashtags. The `output` section defines what files will be created. If
500
- `hxltags` are specified, then only those columns are output. CSV output would look like
501
- this:
502
-
503
- Col2,Col3,tag4
504
- #tag2,#tag3,#tag4
505
- 2,3,10
506
- 5,6,40
507
-
508
-
509
- For JSON output, if no `metadata` or `data` is specified, the output will look like
510
- this:
511
-
512
- [
513
- {"#tag1":1,"#tag2":"2","#tag4":10},
514
- {"#tag1":4,"#tag2":"5","#tag4":40}
515
- ]
516
-
517
- If only `metadata` was specified, not `data`, then output is like this:
518
-
519
- {"metadata":{"#date":"today!","#mytag":123},"data":[
520
- {"#tag1":1,"#tag2":"2","#tag4":10},
521
- {"#tag1":4,"#tag2":"5","#tag4":40}
522
- ]}
523
-
524
- Otherwise, the result is like this:
525
-
526
- {"metadata":{"#date":"today!","#mytag":123},"results":[
527
- {"#tag1":1,"#tag2":"2","#tag4":10},
528
- {"#tag1":4,"#tag2":"5","#tag4":40}
529
- ]}
530
-
531
- The utility is called as follows:
532
-
533
- save_hxlated_output(
534
- configuration,
535
- rows,
536
- includes_header=True,
537
- includes_hxltags=True,
538
- output_dir=output_dir,
539
- today="today!",
540
- )
541
-
542
- The first parameter is the configuration which can come from a YAML file for example.
543
- The second parameter, `rows` is the data. That data can be a list of lists, tuples or
544
- dictionaries. If `includes_header` is `True`, headers are taken from `rows`, otherwise
545
- they must be given by the configuration. If `includes_hxltags` is `True`, HXL hashtags
546
- are taken from `rows`, otherwise they must be given by the configuration. `output_dir`
547
- specifies where the output should go and defaults to "". Any other parameters (such as
548
- `today` in the example above) are used to populate template variables given in the
549
- configuration for the metadata.
550
-
551
463
  ## Dictionary and list utilities
552
464
 
553
465
  Examples:
@@ -39,14 +39,14 @@ requires-python = ">=3.10"
39
39
  # Extras for frictionless[excel,json] added explicitly
40
40
  # for conda-forge compatibility
41
41
  dependencies = [
42
- "frictionless>=5.18.0",
42
+ "frictionless>=5.19.0",
43
43
  # frictionless[excel]
44
44
  "openpyxl>=3.1.2",
45
45
  "tableschema-to-template>=0.0.13",
46
46
  "xlrd>=2.0.1",
47
47
  "xlwt>=1.3.0",
48
48
  # frictionless[json]
49
- "ijson>=3.2.3",
49
+ "ijson>=3.5.0",
50
50
  "jsonlines>=4.0.0",
51
51
  # /end frictionless extras
52
52
  "typing_extensions",
@@ -56,7 +56,7 @@ dependencies = [
56
56
  "ratelimit",
57
57
  "requests-file",
58
58
  "ruamel.yaml",
59
- "xlsx2csv",
59
+ "xlsx2csv==0.8.4",
60
60
  ]
61
61
 
62
62
  [project.optional-dependencies]
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '4.1.0'
22
+ __version_tuple__ = version_tuple = (4, 1, 0)
23
+
24
+ __commit_id__ = commit_id = None
@@ -90,7 +90,6 @@ class BaseDownload(ABC):
90
90
  def get_tabular_rows(
91
91
  self,
92
92
  url: Path | str | Sequence[str],
93
- has_hxl: bool = False,
94
93
  headers: int | Sequence[int] | Sequence[str] = 1,
95
94
  dict_form: bool = False,
96
95
  *args: Any,
@@ -100,8 +99,6 @@ class BaseDownload(ABC):
100
99
  where each row is returned as a list or dictionary depending on the
101
100
  dict_rows argument.
102
101
 
103
- When a list of urls is supplied (in url), then the has_hxl flag indicates if the
104
- files are HXLated so that the HXL row is only included from the first file.
105
102
  The headers argument is either a row number or list of row numbers (in case of
106
103
  multi-line headers) to be considered as headers (rows start counting at 1), or
107
104
  the actual headers defined as a list of strings. It defaults to 1.
@@ -110,7 +107,6 @@ class BaseDownload(ABC):
110
107
 
111
108
  Args:
112
109
  url: A single or list of URLs or paths to read from
113
- has_hxl: Whether files have HXL hashtags. Ignored for single url. Defaults to False.
114
110
  headers: Number of row(s) containing headers or list of headers. Defaults to 1.
115
111
  dict_form: Return dict or list for each row. Defaults to False (list)
116
112
  *args (Any): Positional arguments
@@ -142,28 +142,6 @@ class Download(BaseDownload):
142
142
  preparedrequest = self.session.prepare_request(request)
143
143
  return preparedrequest.url
144
144
 
145
- @staticmethod
146
- def hxl_row(
147
- headers: Sequence[str],
148
- hxltags: dict[str, str],
149
- dict_form: bool = False,
150
- ) -> list[str] | dict[str, str]:
151
- """Return HXL tag row for header row given list of headers and
152
- dictionary with header to HXL hashtag mappings. Return list or
153
- dictionary depending upon the dict_form argument.
154
-
155
- Args:
156
- headers: Headers for which to get HXL hashtags
157
- hxltags: Header to HXL hashtag mapping
158
- dict_form: Return dict or list. Defaults to False (list)
159
-
160
- Returns:
161
- Return either a list or dictionary conating HXL hashtags
162
- """
163
- if dict_form:
164
- return {header: hxltags.get(header, "") for header in headers}
165
- return [hxltags.get(header, "") for header in headers]
166
-
167
145
  def normal_setup(
168
146
  self,
169
147
  url: Path | str,
@@ -674,7 +652,6 @@ class Download(BaseDownload):
674
652
  def get_tabular_rows(
675
653
  self,
676
654
  url: Path | str | Sequence[str],
677
- has_hxl: bool = False,
678
655
  headers: int | Sequence[int] | Sequence[str] = 1,
679
656
  dict_form: bool = False,
680
657
  include_headers: bool = False,
@@ -688,8 +665,6 @@ class Download(BaseDownload):
688
665
  where each row is returned as a list or dictionary depending on the
689
666
  dict_rows argument.
690
667
 
691
- When a list of urls is supplied (in url), then the has_hxl flag indicates if the
692
- files are HXLated so that the HXL row is only included from the first file.
693
668
  The headers argument is either a row number or list of row numbers (in case of
694
669
  multi-line headers) to be considered as headers (rows start counting at 1), or
695
670
  the actual headers defined as a list of strings. It defaults to 1. The dict_form
@@ -705,7 +680,6 @@ class Download(BaseDownload):
705
680
 
706
681
  Args:
707
682
  url: A single or list of URLs or paths to read from
708
- has_hxl: Whether files have HXL hashtags. Ignored for single url. Defaults to False.
709
683
  headers: Number of row(s) containing headers or list of headers. Defaults to 1.
710
684
  dict_form: Return dict or list for each row. Defaults to False (list)
711
685
  include_headers: Whether to include headers in iterator. Defaults to False.
@@ -772,8 +746,6 @@ class Download(BaseDownload):
772
746
  row_function,
773
747
  **temp_kwargs,
774
748
  )
775
- if has_hxl:
776
- next(iterator)
777
749
  yield from iterator
778
750
 
779
751
  return outheaders, make_iterator()
@@ -781,7 +753,6 @@ class Download(BaseDownload):
781
753
  def get_tabular_rows_as_list(
782
754
  self,
783
755
  url: Path | str | Sequence[str],
784
- has_hxl: bool = False,
785
756
  headers: int | Sequence[int] | Sequence[str] = 1,
786
757
  include_headers: bool = True,
787
758
  ignore_blank_rows: bool = True,
@@ -793,8 +764,6 @@ class Download(BaseDownload):
793
764
  """Returns headers and an iterator where each row is returned as a
794
765
  list.
795
766
 
796
- When a list of urls is supplied (in url), then the has_hxl flag indicates if the
797
- files are HXLated so that the HXL row is only included from the first file.
798
767
  The headers argument is either a row number or list of row numbers (in case of
799
768
  multi-line headers) to be considered as headers (rows start counting at 1), or
800
769
  the actual headers defined as a list of strings. It defaults to 1 and cannot be
@@ -809,7 +778,6 @@ class Download(BaseDownload):
809
778
 
810
779
  Args:
811
780
  url: A single or list of URLs or paths to read from
812
- has_hxl: Whether files have HXL hashtags. Ignored for single url. Defaults to False.
813
781
  headers: Number of row(s) containing headers or list of headers. Defaults to 1.
814
782
  include_headers: Whether to include headers in iterator. Defaults to True.
815
783
  ignore_blank_rows: Whether to ignore blank rows. Defaults to True.
@@ -842,7 +810,6 @@ class Download(BaseDownload):
842
810
 
843
811
  headers, iterator = self.get_tabular_rows(
844
812
  url,
845
- has_hxl,
846
813
  headers,
847
814
  False,
848
815
  include_headers,
@@ -857,7 +824,6 @@ class Download(BaseDownload):
857
824
  def get_tabular_rows_as_dict(
858
825
  self,
859
826
  url: Path | str | Sequence[str],
860
- has_hxl: bool = False,
861
827
  headers: int | Sequence[int] | Sequence[str] = 1,
862
828
  ignore_blank_rows: bool = True,
863
829
  infer_types: bool = False,
@@ -868,8 +834,6 @@ class Download(BaseDownload):
868
834
  """Returns headers and an iterator where each row is returned as a
869
835
  dictionary.
870
836
 
871
- When a list of urls is supplied (in url), then the has_hxl flag indicates if the
872
- files are HXLated so that the HXL row is only included from the first file.
873
837
  The headers argument is either a row number or list of row numbers (in case of
874
838
  multi-line headers) to be considered as headers (rows start counting at 1), or
875
839
  the actual headers defined as a list of strings. It defaults to 1 and cannot be
@@ -884,7 +848,6 @@ class Download(BaseDownload):
884
848
 
885
849
  Args:
886
850
  url: A single or list of URLs or paths to read from
887
- has_hxl: Whether files have HXL hashtags. Ignored for single url. Defaults to False.
888
851
  headers: Number of row(s) containing headers or list of headers. Defaults to 1.
889
852
  ignore_blank_rows: Whether to ignore blank rows. Defaults to True.
890
853
  infer_types: Whether to infer types. Defaults to False (strings).
@@ -916,7 +879,6 @@ class Download(BaseDownload):
916
879
 
917
880
  headers, iterator = self.get_tabular_rows(
918
881
  url,
919
- has_hxl,
920
882
  headers,
921
883
  True,
922
884
  False,
@@ -931,7 +893,6 @@ class Download(BaseDownload):
931
893
  def download_tabular_key_value(
932
894
  self,
933
895
  url: Path | str | Sequence[str],
934
- has_hxl: bool = False,
935
896
  headers: int | Sequence[int] | Sequence[str] = 1,
936
897
  include_headers: bool = True,
937
898
  ignore_blank_rows: bool = True,
@@ -943,8 +904,6 @@ class Download(BaseDownload):
943
904
  """Download 2 column csv from url and return a dictionary of keys
944
905
  (first column) and values (second column).
945
906
 
946
- When a list of urls is supplied (in url), then the has_hxl flag indicates if the
947
- files are HXLated so that the HXL row is only included from the first file.
948
907
  The headers argument is either a row number or list of row numbers (in case of
949
908
  multi-line headers) to be considered as headers (rows start counting at 1), or
950
909
  the actual headers defined as a list of strings. It defaults to 1 and cannot be
@@ -957,10 +916,8 @@ class Download(BaseDownload):
957
916
  and row (which will be in dict or list form depending upon the dict_rows
958
917
  argument) and outputs a modified row or None to ignore the row.
959
918
 
960
-
961
919
  Args:
962
920
  url: A single or list of URLs or paths to read from
963
- has_hxl: Whether files have HXL hashtags. Ignored for single url. Defaults to False.
964
921
  headers: Number of row(s) containing headers or list of headers. Defaults to 1.
965
922
  include_headers: Whether to include headers in iterator. Defaults to True.
966
923
  ignore_blank_rows: Whether to ignore blank rows. Defaults to True.
@@ -993,7 +950,6 @@ class Download(BaseDownload):
993
950
  output_dict = {}
994
951
  _, rows = self.get_tabular_rows_as_list(
995
952
  url,
996
- has_hxl,
997
953
  headers,
998
954
  include_headers,
999
955
  ignore_blank_rows,
@@ -1011,7 +967,6 @@ class Download(BaseDownload):
1011
967
  def download_tabular_rows_as_dicts(
1012
968
  self,
1013
969
  url: Path | str | Sequence[str],
1014
- has_hxl: bool = False,
1015
970
  headers: int | Sequence[int] | Sequence[str] = 1,
1016
971
  keycolumn: int = 1,
1017
972
  ignore_blank_rows: bool = True,
@@ -1024,8 +979,6 @@ class Download(BaseDownload):
1024
979
  are first column and values are dictionaries with keys from column
1025
980
  headers and values from columns beneath.
1026
981
 
1027
- When a list of urls is supplied (in url), then the has_hxl flag indicates if the
1028
- files are HXLated so that the HXL row is only included from the first file.
1029
982
  The headers argument is either a row number or list of row numbers (in case of
1030
983
  multi-line headers) to be considered as headers (rows start counting at 1), or
1031
984
  the actual headers defined as a list of strings. It defaults to 1 and cannot be
@@ -1040,7 +993,6 @@ class Download(BaseDownload):
1040
993
 
1041
994
  Args:
1042
995
  url: A single or list of URLs or paths to read from
1043
- has_hxl: Whether files have HXL hashtags. Ignored for single url. Defaults to False.
1044
996
  headers: Number of row(s) containing headers or list of headers. Defaults to 1.
1045
997
  keycolumn: Number of column to be used for key. Defaults to 1.
1046
998
  ignore_blank_rows: Whether to ignore blank rows. Defaults to True.
@@ -1073,7 +1025,6 @@ class Download(BaseDownload):
1073
1025
  """
1074
1026
  headers, iterator = self.get_tabular_rows_as_dict(
1075
1027
  url,
1076
- has_hxl,
1077
1028
  headers,
1078
1029
  ignore_blank_rows,
1079
1030
  infer_types,
@@ -1095,7 +1046,6 @@ class Download(BaseDownload):
1095
1046
  def download_tabular_cols_as_dicts(
1096
1047
  self,
1097
1048
  url: Path | str | Sequence[str],
1098
- has_hxl: bool = False,
1099
1049
  headers: int | Sequence[int] | Sequence[str] = 1,
1100
1050
  keycolumn: int = 1,
1101
1051
  ignore_blank_rows: bool = True,
@@ -1108,8 +1058,6 @@ class Download(BaseDownload):
1108
1058
  are header names and values are dictionaries with keys from first
1109
1059
  column and values from other columns.
1110
1060
 
1111
- When a list of urls is supplied (in url), then the has_hxl flag indicates if the
1112
- files are HXLated so that the HXL row is only included from the first file.
1113
1061
  The headers argument is either a row number or list of row numbers (in case of
1114
1062
  multi-line headers) to be considered as headers (rows start counting at 1), or
1115
1063
  the actual headers defined as a list of strings. It defaults to 1 and cannot be
@@ -1124,7 +1072,6 @@ class Download(BaseDownload):
1124
1072
 
1125
1073
  Args:
1126
1074
  url: A single or list of URLs or paths to read from
1127
- has_hxl: Whether files have HXL hashtags. Ignored for single url. Defaults to False.
1128
1075
  headers: Number of row(s) containing headers or list of headers. Defaults to 1.
1129
1076
  keycolumn: Number of column to be used for key. Defaults to 1.
1130
1077
  ignore_blank_rows: Whether to ignore blank rows. Defaults to True.
@@ -1157,7 +1104,6 @@ class Download(BaseDownload):
1157
1104
  """
1158
1105
  headers, iterator = self.get_tabular_rows_as_dict(
1159
1106
  url,
1160
- has_hxl,
1161
1107
  headers,
1162
1108
  ignore_blank_rows,
1163
1109
  infer_types,
@@ -1,7 +1,6 @@
1
1
  """Directory Path Utilities."""
2
2
 
3
3
  import contextlib
4
- import inspect
5
4
  import logging
6
5
  import sys
7
6
  from collections.abc import Iterable, Iterator, Sequence
@@ -12,7 +11,6 @@ from os.path import (
12
11
  from pathlib import Path
13
12
  from shutil import rmtree
14
13
  from tempfile import gettempdir
15
- from typing import Any
16
14
 
17
15
  from hdx.utilities.loader import load_text
18
16
  from hdx.utilities.saver import save_text
@@ -25,45 +23,34 @@ class NotFoundError(Exception):
25
23
  pass
26
24
 
27
25
 
28
- def script_dir(pyobject: Any, follow_symlinks: bool = True) -> Path:
26
+ def script_dir(*args, **kwargs) -> Path:
29
27
  """Get current script's directory.
30
28
 
31
- Args:
32
- pyobject: Any Python object in the script
33
- follow_symlinks: Follow symlinks or not. Defaults to True.
34
-
35
29
  Returns:
36
30
  Current script's directory
37
31
  """
38
32
  if getattr(sys, "frozen", False):
39
- # Frozen (PyInstaller, etc.): Use the executable path
40
- path = Path(sys.executable) # pragma: no cover
33
+ # PyInstaller bundle
34
+ app_path = Path(sys.executable)
41
35
  else:
42
- # Standard: Use the object's file path
43
- path = Path(inspect.getfile(pyobject))
44
-
45
- # Resolve symlinks if requested, otherwise just make absolute
46
- path = path.resolve() if follow_symlinks else path.absolute()
36
+ # Standard Python script
37
+ app_path = Path(__file__)
47
38
 
48
- return path.parent
39
+ # .resolve() ensures we follow symlinks to the real folder,
40
+ # and makes the path absolute so CWD doesn't matter.
41
+ return app_path.resolve().parent
49
42
 
50
43
 
51
- def script_dir_plus_file(
52
- filename: str, pyobject: Any, follow_symlinks: bool = True
53
- ) -> Path:
44
+ def script_dir_plus_file(filename: str, *args, **kwargs) -> Path:
54
45
  """Get current script's directory and then append a filename.
55
46
 
56
47
  Args:
57
48
  filename: Filename to append to directory path
58
- pyobject: Any Python object in the script
59
- return_path: Whether to return a Path object. Defaults to returning str.
60
-
61
- follow_symlinks: Follow symlinks or not. Defaults to True.
62
49
 
63
50
  Returns:
64
51
  Current script's directory and with filename appended
65
52
  """
66
- return script_dir(pyobject, follow_symlinks) / filename
53
+ return script_dir() / filename
67
54
 
68
55
 
69
56
  def get_temp_dir(
@@ -385,7 +385,6 @@ class Retrieve(BaseDownload):
385
385
  def get_tabular_rows(
386
386
  self,
387
387
  url: Path | str | Sequence[str],
388
- has_hxl: bool = False,
389
388
  headers: int | Sequence[int] | Sequence[str] = 1,
390
389
  dict_form: bool = False,
391
390
  filename: str | None = None,
@@ -397,8 +396,6 @@ class Retrieve(BaseDownload):
397
396
  where each row is returned as a list or dictionary depending on the
398
397
  dict_rows argument.
399
398
 
400
- When a list of urls is supplied (in url), then the has_hxl flag indicates if the
401
- files are HXLated so that the HXL row is only included from the first file.
402
399
  The headers argument is either a row number or list of row numbers (in case of
403
400
  multi-line headers) to be considered as headers (rows start counting at 1), or
404
401
  the actual headers defined as a list of strings. It defaults to 1.
@@ -407,7 +404,6 @@ class Retrieve(BaseDownload):
407
404
 
408
405
  Args:
409
406
  url: A single or list of URLs or paths to read from
410
- has_hxl: Whether files have HXL hashtags. Defaults to False.
411
407
  headers: Number of row(s) containing headers or list of headers. Defaults to 1.
412
408
  dict_form: Return dict or list for each row. Defaults to False (list)
413
409
  filename: Filename of saved file. Defaults to getting from url.
@@ -434,9 +430,7 @@ class Retrieve(BaseDownload):
434
430
  path.append(pth)
435
431
 
436
432
  kwargs.pop("file_prefix", None)
437
- return self.downloader.get_tabular_rows(
438
- path, has_hxl, headers, dict_form, **kwargs
439
- )
433
+ return self.downloader.get_tabular_rows(path, headers, dict_form, **kwargs)
440
434
 
441
435
  @classmethod
442
436
  def generate_retrievers(