hdx-python-utilities 4.1.1__tar.gz → 4.1.2__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.
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/PKG-INFO +1 -1
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/documentation/index.md +11 -1
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/pyproject.toml +1 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/_version.py +2 -2
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/downloader.py +81 -17
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/retriever.py +7 -1
- hdx_python_utilities-4.1.2/tests/fixtures/downloader/resume_test.dat +9 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_downloader.py +156 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_retriever.py +21 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/uv.lock +14 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/.github/workflows/publish-test.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/.github/workflows/publish.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/.github/workflows/run-python-tests.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/.gitignore +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/.pre-commit-config.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/CLAUDE.md +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/LICENSE +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/README.md +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/documentation/.readthedocs.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/documentation/mkdocs.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/__init__.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/base_downloader.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/compare.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/dateparse.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/dictandlist.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/easy_logging.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/email.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/encoding.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/error_handler.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/errors_onexit.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/file_hashing.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/frictionless_wrapper.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/html.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/loader.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/matching.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/path.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/saver.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/session.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/state.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/text.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/url.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/useragent.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/uuid.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/zip_crc.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/compare/test_csv_processing.csv +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/compare/test_csv_processing2.csv +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/empty.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/hdx_config.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/hdx_config.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/hdx_email_configuration.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/hdx_email_configuration.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/logging_config.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/logging_config.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/project_configuration.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/project_configuration.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/smtp_config.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/smtp_config.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/user_agent_config.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/user_agent_config2.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/user_agent_config3.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/user_agent_config_wrong.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/basicauth.txt +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/bearertoken.txt +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/extra_params.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/extra_params.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/extra_params_tree.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/test_csv_processing.csv +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/test_csv_processing_blanks.csv +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/test_data.csv +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/test_data.xlsx +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/test_data1.csv/empty.txt +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/test_data2.csv +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/test_json_processing.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/test_xls_processing.xls +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/test_xlsx_processing.xlsx +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/file_hashing/bad_header.zip +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/file_hashing/bad_index.xlsx +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/file_hashing/empty.zip +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/file_hashing/test.xlsx +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/file_hashing/test_shapefile.zip +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/file_hashing/valid_sig_invalid_body.zip +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/html/response.html +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/loader/empty.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/loader/empty.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/loader/empty_list.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/retriever/fallbacks/test.csv +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/retriever/fallbacks/test.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/retriever/fallbacks/test.txt +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/retriever/fallbacks/test.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/retriever/retriever-test.csv +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/retriever/test.csv +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/retriever/test.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/retriever/test.txt +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/retriever/test.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/out.csv +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/out.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/out2.csv +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/out2.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/out5.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/out6.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/out7.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/out8.csv +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/out8.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/pretty-false_sortkeys-false.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/pretty-false_sortkeys-false.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/pretty-false_sortkeys-true.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/pretty-false_sortkeys-true.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/pretty-true_sortkeys-false.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/pretty-true_sortkeys-false.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/pretty-true_sortkeys-true.json +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/saver/pretty-true_sortkeys-true.yaml +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/state/analysis_dates.txt +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/state/last_build_date.txt +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/test_data.csv +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/text/text1.txt +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/text/text2.txt +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/conftest.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_compare.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_dateparse.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_dictandlist.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_easy_logging.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_email.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_encoding.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_error_handler.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_file_hashing.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_html.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_loader.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_matching.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_path.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_saver.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_state.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_text.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_url.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_useragent.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_uuid.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_zip_crc.py +0 -0
- {hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hdx-python-utilities
|
|
3
|
-
Version: 4.1.
|
|
3
|
+
Version: 4.1.2
|
|
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>
|
|
@@ -205,7 +205,15 @@ Other useful functions:
|
|
|
205
205
|
# if no folder supplied
|
|
206
206
|
# path = Download.get_path_for_url(url, folder)
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
### Resumable downloads
|
|
209
|
+
|
|
210
|
+
Pass `resume=True` and a fixed `path` to continue an interrupted download using
|
|
211
|
+
HTTP Range requests. If the server returns 200 instead of 206, the file is
|
|
212
|
+
downloaded from scratch. Add `retries` to automatically retry mid-stream
|
|
213
|
+
failures; each retry continues from the current end of the partial file.
|
|
214
|
+
`retries` has no effect without `resume=True`.
|
|
215
|
+
|
|
216
|
+
path = downloader.download_file(url, path=mypath, resume=True, retries=3)
|
|
209
217
|
|
|
210
218
|
## Retrieving files
|
|
211
219
|
|
|
@@ -261,6 +269,8 @@ Examples:
|
|
|
261
269
|
retriever = Retrieve(downloader, fallback_dir, saved_dir, temp_dir, save=False, use_saved=True)
|
|
262
270
|
data = retriever.download_json(url, filename, logstr="test json", fallback=False, log_level=logging.DEBUG)
|
|
263
271
|
|
|
272
|
+
`Retrieve.download_file` also accepts `resume=True` and `retries=N` — see [Resumable downloads](#resumable-downloads).
|
|
273
|
+
|
|
264
274
|
## Date utilities
|
|
265
275
|
|
|
266
276
|
There are utilities to parse dates. By default, *no timezone information will be parsed
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '4.1.
|
|
22
|
-
__version_tuple__ = version_tuple = (4, 1,
|
|
21
|
+
__version__ = version = '4.1.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (4, 1, 2)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -250,13 +250,16 @@ class Download(BaseDownload):
|
|
|
250
250
|
f"Download of {url} failed in retrieval of stream!" % url
|
|
251
251
|
)
|
|
252
252
|
|
|
253
|
-
def stream_path(
|
|
253
|
+
def stream_path(
|
|
254
|
+
self, path: Path | str, errormsg: str, append: bool = False
|
|
255
|
+
) -> Path:
|
|
254
256
|
"""Stream file from url and store in provided path. Must call setup
|
|
255
257
|
method first.
|
|
256
258
|
|
|
257
259
|
Args:
|
|
258
260
|
path: Path for downloaded file
|
|
259
261
|
errormsg: Error message to display if there is a problem
|
|
262
|
+
append: Whether to append to an existing file. Defaults to False.
|
|
260
263
|
|
|
261
264
|
Returns:
|
|
262
265
|
Path of downloaded file
|
|
@@ -264,7 +267,7 @@ class Download(BaseDownload):
|
|
|
264
267
|
f = None
|
|
265
268
|
try:
|
|
266
269
|
path = Path(path)
|
|
267
|
-
f = path.open("wb")
|
|
270
|
+
f = path.open("ab" if append else "wb")
|
|
268
271
|
for chunk in self.response.iter_content(chunk_size=10240):
|
|
269
272
|
if chunk: # filter out keep-alive new chunks
|
|
270
273
|
f.write(chunk)
|
|
@@ -325,6 +328,8 @@ class Download(BaseDownload):
|
|
|
325
328
|
path (str): Full path to use for downloaded file instead of folder and filename.
|
|
326
329
|
overwrite (bool): Whether to overwrite existing file. Defaults to False.
|
|
327
330
|
keep (bool): Whether to keep already downloaded file. Defaults to False.
|
|
331
|
+
resume (bool): Whether to resume a partial download using Range requests where the server supports it. Defaults to False.
|
|
332
|
+
retries (int): Number of times to retry a mid-stream failure. Only effective when resume=True so that each retry can continue from the partial file. Defaults to 0.
|
|
328
333
|
post (bool): Whether to use POST instead of GET. Defaults to False.
|
|
329
334
|
parameters (dict): Parameters to pass. Defaults to None.
|
|
330
335
|
timeout (float): Timeout for connecting to URL. Defaults to None (no timeout).
|
|
@@ -340,25 +345,84 @@ class Download(BaseDownload):
|
|
|
340
345
|
path = kwargs.get("path")
|
|
341
346
|
overwrite = kwargs.get("overwrite", False)
|
|
342
347
|
keep = kwargs.get("keep", False)
|
|
348
|
+
resume = kwargs.get("resume", False)
|
|
349
|
+
retries = kwargs.get("retries", 0)
|
|
343
350
|
try:
|
|
344
|
-
|
|
351
|
+
# When resuming, skip uniqueness renaming so we get back the exact target path
|
|
352
|
+
path = get_path_for_url(
|
|
353
|
+
url, folder, filename, path, overwrite, keep or resume
|
|
354
|
+
)
|
|
345
355
|
except ValueError as ex:
|
|
346
356
|
raise DownloadError(ex) from ex
|
|
347
|
-
if keep and exists(path):
|
|
357
|
+
if keep and not resume and exists(path):
|
|
348
358
|
return path
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
359
|
+
errormsg = f"Download of {url} failed in retrieval of stream!"
|
|
360
|
+
setup_headers = dict(kwargs.get("headers") or {})
|
|
361
|
+
append = False
|
|
362
|
+
if resume and path.exists() and path.stat().st_size > 0:
|
|
363
|
+
setup_headers["Range"] = f"bytes={path.stat().st_size}-"
|
|
364
|
+
# Range bytes must come from the raw stream: partial gzip streams
|
|
365
|
+
# lack the header and cannot be decoded, so request uncompressed.
|
|
366
|
+
setup_headers.setdefault("Accept-Encoding", "identity")
|
|
367
|
+
try:
|
|
368
|
+
self.setup(
|
|
369
|
+
url,
|
|
370
|
+
stream=True,
|
|
371
|
+
post=kwargs.get("post", False),
|
|
372
|
+
parameters=kwargs.get("parameters"),
|
|
373
|
+
timeout=kwargs.get("timeout"),
|
|
374
|
+
headers=setup_headers,
|
|
375
|
+
encoding=kwargs.get("encoding"),
|
|
376
|
+
json_string=kwargs.get("json_string", False),
|
|
377
|
+
)
|
|
378
|
+
except DownloadError:
|
|
379
|
+
if self.response is not None and self.response.status_code == 416:
|
|
380
|
+
return path
|
|
381
|
+
raise
|
|
382
|
+
append = self.response.status_code == 206
|
|
383
|
+
else:
|
|
384
|
+
self.setup(
|
|
385
|
+
url,
|
|
386
|
+
stream=True,
|
|
387
|
+
post=kwargs.get("post", False),
|
|
388
|
+
parameters=kwargs.get("parameters"),
|
|
389
|
+
timeout=kwargs.get("timeout"),
|
|
390
|
+
headers=setup_headers or None,
|
|
391
|
+
encoding=kwargs.get("encoding"),
|
|
392
|
+
json_string=kwargs.get("json_string", False),
|
|
393
|
+
)
|
|
394
|
+
attempts_remaining = retries
|
|
395
|
+
while True:
|
|
396
|
+
try:
|
|
397
|
+
return self.stream_path(path, errormsg, append=append)
|
|
398
|
+
except DownloadError:
|
|
399
|
+
if not resume or attempts_remaining == 0:
|
|
400
|
+
raise
|
|
401
|
+
attempts_remaining -= 1
|
|
402
|
+
logger.warning(
|
|
403
|
+
"Download of %s interrupted, retrying (attempt %d of %d)...",
|
|
404
|
+
url,
|
|
405
|
+
retries - attempts_remaining,
|
|
406
|
+
retries,
|
|
407
|
+
)
|
|
408
|
+
setup_headers["Range"] = f"bytes={path.stat().st_size}-"
|
|
409
|
+
setup_headers.setdefault("Accept-Encoding", "identity")
|
|
410
|
+
try:
|
|
411
|
+
self.setup(
|
|
412
|
+
url,
|
|
413
|
+
stream=True,
|
|
414
|
+
post=kwargs.get("post", False),
|
|
415
|
+
parameters=kwargs.get("parameters"),
|
|
416
|
+
timeout=kwargs.get("timeout"),
|
|
417
|
+
headers=setup_headers,
|
|
418
|
+
encoding=kwargs.get("encoding"),
|
|
419
|
+
json_string=kwargs.get("json_string", False),
|
|
420
|
+
)
|
|
421
|
+
except DownloadError:
|
|
422
|
+
if self.response is not None and self.response.status_code == 416:
|
|
423
|
+
return path
|
|
424
|
+
raise
|
|
425
|
+
append = self.response.status_code == 206
|
|
362
426
|
|
|
363
427
|
def download(self, url: Path | str, **kwargs: Any) -> requests.Response:
|
|
364
428
|
"""Download url.
|
|
@@ -184,6 +184,8 @@ class Retrieve(BaseDownload):
|
|
|
184
184
|
filename: str | None = None,
|
|
185
185
|
logstr: str | None = None,
|
|
186
186
|
fallback: bool = False,
|
|
187
|
+
resume: bool = False,
|
|
188
|
+
retries: int = 0,
|
|
187
189
|
log_level: int = None,
|
|
188
190
|
**kwargs: Any,
|
|
189
191
|
) -> Path:
|
|
@@ -194,6 +196,8 @@ class Retrieve(BaseDownload):
|
|
|
194
196
|
filename: Filename of saved file. Defaults to getting from url.
|
|
195
197
|
logstr: Text to use in log string to describe download. Defaults to filename.
|
|
196
198
|
fallback: Whether to use static fallback if download fails. Defaults to False.
|
|
199
|
+
resume: Whether to resume a partial download using Range requests where the server supports it. Defaults to False.
|
|
200
|
+
retries: Number of times to retry a mid-stream failure. Only effective when resume=True. Defaults to 0.
|
|
197
201
|
log_level: Level at which to log messages. Overrides level from constructor.
|
|
198
202
|
**kwargs: Parameters to pass to download_file call
|
|
199
203
|
|
|
@@ -219,7 +223,9 @@ class Retrieve(BaseDownload):
|
|
|
219
223
|
log_level,
|
|
220
224
|
f"Downloading {logstr} from {self.get_url_logstr(url)} into {output_path}",
|
|
221
225
|
)
|
|
222
|
-
return self.downloader.download_file(
|
|
226
|
+
return self.downloader.download_file(
|
|
227
|
+
url, path=output_path, resume=resume, retries=retries, **kwargs
|
|
228
|
+
)
|
|
223
229
|
except DownloadError:
|
|
224
230
|
if not fallback:
|
|
225
231
|
raise
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_downloader.py
RENAMED
|
@@ -1314,3 +1314,159 @@ class TestDownloader:
|
|
|
1314
1314
|
downloader1 = Download.get_downloader()
|
|
1315
1315
|
downloader2 = Download.get_downloader("test")
|
|
1316
1316
|
assert_downloaders(downloader1, downloader2, downloaders)
|
|
1317
|
+
|
|
1318
|
+
|
|
1319
|
+
class TestResumableDownload:
|
|
1320
|
+
url = "https://example.com/testfile.bin"
|
|
1321
|
+
|
|
1322
|
+
def _make_response(self, mocker, status_code, content=b""):
|
|
1323
|
+
import requests
|
|
1324
|
+
|
|
1325
|
+
mock_resp = mocker.MagicMock()
|
|
1326
|
+
mock_resp.status_code = status_code
|
|
1327
|
+
if status_code >= 400:
|
|
1328
|
+
mock_resp.raise_for_status.side_effect = requests.exceptions.HTTPError(
|
|
1329
|
+
response=mock_resp
|
|
1330
|
+
)
|
|
1331
|
+
else:
|
|
1332
|
+
mock_resp.raise_for_status.return_value = None
|
|
1333
|
+
mock_resp.iter_content.return_value = iter([content] if content else [])
|
|
1334
|
+
return mock_resp
|
|
1335
|
+
|
|
1336
|
+
def test_resume_206_appends(self, tmp_path, mocker):
|
|
1337
|
+
partial = tmp_path / "file.bin"
|
|
1338
|
+
partial.write_bytes(b"hello ")
|
|
1339
|
+
mock_resp = self._make_response(mocker, 206, b"world")
|
|
1340
|
+
with Download(user_agent="test") as downloader:
|
|
1341
|
+
mocker.patch.object(downloader.session, "get", return_value=mock_resp)
|
|
1342
|
+
result = downloader.download_file(self.url, path=partial, resume=True)
|
|
1343
|
+
assert result == partial
|
|
1344
|
+
assert partial.read_bytes() == b"hello world"
|
|
1345
|
+
sent_headers = downloader.session.get.call_args.kwargs["headers"]
|
|
1346
|
+
assert sent_headers["Range"] == "bytes=6-"
|
|
1347
|
+
|
|
1348
|
+
def test_resume_200_overwrites(self, tmp_path, mocker):
|
|
1349
|
+
partial = tmp_path / "file.bin"
|
|
1350
|
+
partial.write_bytes(b"partial")
|
|
1351
|
+
mock_resp = self._make_response(mocker, 200, b"fullcontent")
|
|
1352
|
+
with Download(user_agent="test") as downloader:
|
|
1353
|
+
mocker.patch.object(downloader.session, "get", return_value=mock_resp)
|
|
1354
|
+
downloader.download_file(self.url, path=partial, resume=True)
|
|
1355
|
+
assert partial.read_bytes() == b"fullcontent"
|
|
1356
|
+
|
|
1357
|
+
def test_resume_416_returns_existing(self, tmp_path, mocker):
|
|
1358
|
+
partial = tmp_path / "file.bin"
|
|
1359
|
+
partial.write_bytes(b"complete")
|
|
1360
|
+
mock_resp = self._make_response(mocker, 416)
|
|
1361
|
+
with Download(user_agent="test") as downloader:
|
|
1362
|
+
mocker.patch.object(downloader.session, "get", return_value=mock_resp)
|
|
1363
|
+
result = downloader.download_file(self.url, path=partial, resume=True)
|
|
1364
|
+
assert result == partial
|
|
1365
|
+
assert partial.read_bytes() == b"complete"
|
|
1366
|
+
|
|
1367
|
+
def test_resume_no_partial_file_does_full_download(self, tmp_path, mocker):
|
|
1368
|
+
path = tmp_path / "newfile.bin"
|
|
1369
|
+
mock_resp = self._make_response(mocker, 200, b"fullcontent")
|
|
1370
|
+
with Download(user_agent="test") as downloader:
|
|
1371
|
+
mocker.patch.object(downloader.session, "get", return_value=mock_resp)
|
|
1372
|
+
downloader.download_file(self.url, path=path, resume=True)
|
|
1373
|
+
assert path.read_bytes() == b"fullcontent"
|
|
1374
|
+
sent_headers = downloader.session.get.call_args.kwargs.get("headers")
|
|
1375
|
+
assert sent_headers is None or "Range" not in sent_headers
|
|
1376
|
+
|
|
1377
|
+
def test_no_resume_overwrites_partial(self, tmp_path, mocker):
|
|
1378
|
+
partial = tmp_path / "file.bin"
|
|
1379
|
+
partial.write_bytes(b"staledata")
|
|
1380
|
+
mock_resp = self._make_response(mocker, 200, b"freshcontent")
|
|
1381
|
+
with Download(user_agent="test") as downloader:
|
|
1382
|
+
mocker.patch.object(downloader.session, "get", return_value=mock_resp)
|
|
1383
|
+
downloader.download_file(self.url, path=partial, overwrite=True)
|
|
1384
|
+
assert partial.read_bytes() == b"freshcontent"
|
|
1385
|
+
sent_headers = downloader.session.get.call_args.kwargs.get("headers")
|
|
1386
|
+
assert sent_headers is None or "Range" not in sent_headers
|
|
1387
|
+
|
|
1388
|
+
def test_resume_real_file(self, tmp_path, fixturesfolder):
|
|
1389
|
+
fixture = fixturesfolder / "downloader" / "resume_test.dat"
|
|
1390
|
+
expected = fixture.read_bytes()
|
|
1391
|
+
split_at = 40
|
|
1392
|
+
partial = tmp_path / "resume_test.dat"
|
|
1393
|
+
partial.write_bytes(expected[:split_at])
|
|
1394
|
+
url = "https://raw.githubusercontent.com/OCHA-DAP/hdx-python-utilities/main/tests/fixtures/downloader/resume_test.dat"
|
|
1395
|
+
with Download(user_agent="test") as downloader:
|
|
1396
|
+
result = downloader.download_file(url, path=partial, resume=True)
|
|
1397
|
+
assert result == partial
|
|
1398
|
+
assert partial.read_bytes() == expected
|
|
1399
|
+
|
|
1400
|
+
def test_retry_succeeds_on_second_attempt(self, tmp_path, mocker):
|
|
1401
|
+
partial = tmp_path / "file.bin"
|
|
1402
|
+
partial.write_bytes(b"hello ")
|
|
1403
|
+
first_resp = self._make_response(mocker, 206, b"wo")
|
|
1404
|
+
first_resp.iter_content.side_effect = Exception("connection reset")
|
|
1405
|
+
second_resp = self._make_response(mocker, 206, b"world")
|
|
1406
|
+
with Download(user_agent="test") as downloader:
|
|
1407
|
+
mocker.patch.object(
|
|
1408
|
+
downloader.session, "get", side_effect=[first_resp, second_resp]
|
|
1409
|
+
)
|
|
1410
|
+
result = downloader.download_file(
|
|
1411
|
+
self.url, path=partial, resume=True, retries=1
|
|
1412
|
+
)
|
|
1413
|
+
assert result == partial
|
|
1414
|
+
assert partial.read_bytes() == b"hello world"
|
|
1415
|
+
second_call_headers = downloader.session.get.call_args_list[1].kwargs["headers"]
|
|
1416
|
+
assert second_call_headers["Range"] == "bytes=6-"
|
|
1417
|
+
|
|
1418
|
+
def test_retry_exhausted_raises(self, tmp_path, mocker):
|
|
1419
|
+
partial = tmp_path / "file.bin"
|
|
1420
|
+
partial.write_bytes(b"hello ")
|
|
1421
|
+
failing_resp = self._make_response(mocker, 206, b"world")
|
|
1422
|
+
failing_resp.iter_content.side_effect = Exception("connection reset")
|
|
1423
|
+
with Download(user_agent="test") as downloader:
|
|
1424
|
+
mocker.patch.object(
|
|
1425
|
+
downloader.session,
|
|
1426
|
+
"get",
|
|
1427
|
+
return_value=failing_resp,
|
|
1428
|
+
)
|
|
1429
|
+
with pytest.raises(DownloadError):
|
|
1430
|
+
downloader.download_file(self.url, path=partial, resume=True, retries=2)
|
|
1431
|
+
assert downloader.session.get.call_count == 3
|
|
1432
|
+
|
|
1433
|
+
def test_retry_not_attempted_without_resume(self, tmp_path, mocker):
|
|
1434
|
+
path = tmp_path / "file.bin"
|
|
1435
|
+
failing_resp = self._make_response(mocker, 200, b"content")
|
|
1436
|
+
failing_resp.iter_content.side_effect = Exception("connection reset")
|
|
1437
|
+
with Download(user_agent="test") as downloader:
|
|
1438
|
+
mocker.patch.object(downloader.session, "get", return_value=failing_resp)
|
|
1439
|
+
with pytest.raises(DownloadError):
|
|
1440
|
+
downloader.download_file(self.url, path=path, retries=3)
|
|
1441
|
+
assert downloader.session.get.call_count == 1
|
|
1442
|
+
|
|
1443
|
+
def test_retry_updates_range_from_partial_bytes(self, tmp_path, mocker):
|
|
1444
|
+
partial = tmp_path / "file.bin"
|
|
1445
|
+
partial.write_bytes(b"hello ")
|
|
1446
|
+
|
|
1447
|
+
def make_partial_resp(content, fail_after):
|
|
1448
|
+
resp = self._make_response(mocker, 206, b"")
|
|
1449
|
+
chunks = [content[i : i + 1] for i in range(len(content))]
|
|
1450
|
+
|
|
1451
|
+
def iter_content_side_effect(chunk_size=1):
|
|
1452
|
+
for i, chunk in enumerate(chunks):
|
|
1453
|
+
if i == fail_after:
|
|
1454
|
+
raise Exception("connection reset")
|
|
1455
|
+
yield chunk
|
|
1456
|
+
|
|
1457
|
+
resp.iter_content.side_effect = iter_content_side_effect
|
|
1458
|
+
return resp
|
|
1459
|
+
|
|
1460
|
+
first_resp = make_partial_resp(b"wor", fail_after=2)
|
|
1461
|
+
second_resp = self._make_response(mocker, 206, b"rld")
|
|
1462
|
+
with Download(user_agent="test") as downloader:
|
|
1463
|
+
mocker.patch.object(
|
|
1464
|
+
downloader.session, "get", side_effect=[first_resp, second_resp]
|
|
1465
|
+
)
|
|
1466
|
+
result = downloader.download_file(
|
|
1467
|
+
self.url, path=partial, resume=True, retries=1
|
|
1468
|
+
)
|
|
1469
|
+
assert result == partial
|
|
1470
|
+
assert partial.read_bytes() == b"hello world"
|
|
1471
|
+
second_call_headers = downloader.session.get.call_args_list[1].kwargs["headers"]
|
|
1472
|
+
assert second_call_headers["Range"] == "bytes=8-"
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_retriever.py
RENAMED
|
@@ -462,6 +462,27 @@ class TestRetriever:
|
|
|
462
462
|
# Uses saved file
|
|
463
463
|
assert headers == ["header1", "header2", "header3", "header4"]
|
|
464
464
|
|
|
465
|
+
def test_resume_forwarded_to_downloader(self, dirs, fallback_dir, mocker):
|
|
466
|
+
saved_dir, temp_dir = dirs
|
|
467
|
+
downloader = mocker.MagicMock(spec=Download)
|
|
468
|
+
filename = "test.txt"
|
|
469
|
+
expected_path = temp_dir / filename
|
|
470
|
+
downloader.download_file.return_value = expected_path
|
|
471
|
+
with Retrieve(
|
|
472
|
+
downloader,
|
|
473
|
+
fallback_dir,
|
|
474
|
+
saved_dir,
|
|
475
|
+
temp_dir,
|
|
476
|
+
save=False,
|
|
477
|
+
use_saved=False,
|
|
478
|
+
) as retriever:
|
|
479
|
+
url = "http://example.com/test.txt"
|
|
480
|
+
path = retriever.download_file(url, filename, resume=True)
|
|
481
|
+
assert path == expected_path
|
|
482
|
+
downloader.download_file.assert_called_once_with(
|
|
483
|
+
url, path=expected_path, resume=True, retries=0
|
|
484
|
+
)
|
|
485
|
+
|
|
465
486
|
def test_generate_retrievers(self, downloaders, dirs, fallback_dir):
|
|
466
487
|
saved_dir, temp_dir = dirs
|
|
467
488
|
Retrieve.generate_retrievers(
|
|
@@ -565,6 +565,7 @@ dev = [
|
|
|
565
565
|
{ name = "pytest" },
|
|
566
566
|
{ name = "pytest-cov" },
|
|
567
567
|
{ name = "pytest-loguru" },
|
|
568
|
+
{ name = "pytest-mock" },
|
|
568
569
|
{ name = "ruff" },
|
|
569
570
|
]
|
|
570
571
|
|
|
@@ -599,6 +600,7 @@ dev = [
|
|
|
599
600
|
{ name = "pytest" },
|
|
600
601
|
{ name = "pytest-cov" },
|
|
601
602
|
{ name = "pytest-loguru" },
|
|
603
|
+
{ name = "pytest-mock" },
|
|
602
604
|
{ name = "ruff", specifier = "==0.14.13" },
|
|
603
605
|
]
|
|
604
606
|
|
|
@@ -1335,6 +1337,18 @@ wheels = [
|
|
|
1335
1337
|
{ url = "https://files.pythonhosted.org/packages/33/ef/b0c2e96e3508bca8d1874e39789d541cd7f4731b38bcf9c7098f0b882001/pytest_loguru-0.4.0-py3-none-any.whl", hash = "sha256:3cc7b9c6b22cb158209ccbabf0d678dacd3f3c7497d6f46f1c338c13bee1ac77", size = 3886, upload-time = "2024-03-20T00:52:12.72Z" },
|
|
1336
1338
|
]
|
|
1337
1339
|
|
|
1340
|
+
[[package]]
|
|
1341
|
+
name = "pytest-mock"
|
|
1342
|
+
version = "3.15.1"
|
|
1343
|
+
source = { registry = "https://pypi.org/simple" }
|
|
1344
|
+
dependencies = [
|
|
1345
|
+
{ name = "pytest" },
|
|
1346
|
+
]
|
|
1347
|
+
sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" }
|
|
1348
|
+
wheels = [
|
|
1349
|
+
{ url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" },
|
|
1350
|
+
]
|
|
1351
|
+
|
|
1338
1352
|
[[package]]
|
|
1339
1353
|
name = "python-dateutil"
|
|
1340
1354
|
version = "2.9.0.post0"
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/.github/workflows/publish-test.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/.github/workflows/run-python-tests.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/base_downloader.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/error_handler.py
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/errors_onexit.py
RENAMED
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/src/hdx/utilities/frictionless_wrapper.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/hdx_config.json
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/hdx_config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/logging_config.json
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/logging_config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/smtp_config.json
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/config/smtp_config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/basicauth.txt
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/bearertoken.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/test_data.csv
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/test_data.xlsx
RENAMED
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/downloader/test_data2.csv
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/file_hashing/bad_header.zip
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/file_hashing/bad_index.xlsx
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/file_hashing/empty.zip
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/file_hashing/test.xlsx
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/loader/empty_list.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/retriever/test.json
RENAMED
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/retriever/test.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/state/analysis_dates.txt
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/fixtures/state/last_build_date.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_compare.py
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_dateparse.py
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_dictandlist.py
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_easy_logging.py
RENAMED
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_encoding.py
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_error_handler.py
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_file_hashing.py
RENAMED
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_loader.py
RENAMED
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_matching.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_useragent.py
RENAMED
|
File without changes
|
|
File without changes
|
{hdx_python_utilities-4.1.1 → hdx_python_utilities-4.1.2}/tests/hdx/utilities/test_zip_crc.py
RENAMED
|
File without changes
|
|
File without changes
|