fix(cli): honor --unzip for single file downloads and add it to competitions download - #1184
Conversation
…titions Kaggle serves large dataset files inside a zip archive named after the request, so `creditcard.csv` arrives as `creditcard.csv.zip`. dataset_download_cli forwarded --unzip to the full download but not to the single file download, so `datasets download -f <file> --unzip` left the archive on disk and exited 0. Reported since 2019 in Kaggle#158 and Kaggle#9. `competitions download` had no --unzip at all, so the bundle always had to be extracted by hand. Forward unzip to dataset_download_file, add the flag to the competitions parser and thread it through both competition download paths, and share one extraction helper across all four call sites. Extraction is limited to a response named exactly `<requested>.zip`, so a file that is genuinely a .zip inside a dataset is left alone, and a competition bundle served in any format other than zip is left as is. The helper reuses the existing ISSUE_TRACKER_URL rather than two hardcoded variants of the repository URL. Fixes Kaggle#158 Fixes Kaggle#9
| return False | ||
|
|
||
| if unzip and _is_auto_compressed(outfile, file_name): | ||
| _extract_and_remove_zip(outfile, os.path.dirname(outfile)) |
There was a problem hiding this comment.
Could we also handle the case where the ZIP contains the full path that was asked for? For example, if file_name is WICAgencies2014ytd/Food_Costs.csv and the ZIP contains that same path, extracting into dirname(outfile) could result in WICAgencies2014ytd/WICAgencies2014ytd/Food_Costs.csv.
The current nested-path test only uses Food_Costs.csv in the ZIP. Can we add a test for this case, or can we be sure that the API always returns the basename here?
There was a problem hiding this comment.
I had assumed the base name rather than checked it, so I verified it. Reading the local file header of a wrapper over a range request, which avoids pulling the file:
requested 'cord_19_embeddings/cord_19_embeddings_2022-06-02.csv'
served 'cord_19_embeddings_2022-06-02.csv.zip'
member 'cord_19_embeddings_2022-06-02.csv'
So the archive holds the base name today and the double nesting does not happen. That is not something worth depending on, so I removed the dependency in 9196756 rather than documenting it.
_extract_and_remove_zip now takes unwrap_to. When the archive is the single file wrapper, its one member is written to that exact path instead of to the name recorded inside the archive, so the result is the same whichever the server stores. An archive with any other number of members is still extracted as a bundle.
Three tests cover it. One builds an archive holding WICAgencies2014ytd/Food_Costs.csv and asserts it lands at Food_Costs.csv, one asserts end to end that no WICAgencies2014ytd/WICAgencies2014ytd appears, and one covers the multi member fallback. The first two fail against the previous extractall.
The single file wrapper was extracted with extractall(), which writes each member to the name recorded inside the archive. That made the result depend on how the server names the entry: if it ever stored the requested path rather than the base name, a nested request would land at WICAgencies2014ytd/WICAgencies2014ytd/Food_Costs.csv. Write the one member to the path the caller asked for instead, so the result is the same either way. An archive with any other number of members is still extracted as a bundle. Checked what the API does today by reading the local file header of a wrapper over a range request: a request for cord_19_embeddings/cord_19_embeddings_2022-06-02.csv is served as cord_19_embeddings_2022-06-02.csv.zip holding cord_19_embeddings_2022-06-02.csv, so it is the base name. The change removes the dependency on that rather than relying on it.
|
/gcbrun |
Summary
--unzipdid nothing when downloading a single file with-f, andkaggle competitions downloadhad no--unzipat all. Both halves are what #158 asks for, and the single file case is also what is reported in #9.Problem
Kaggle serves large dataset files inside a zip archive named after the request, so
creditcard.csvarrives ascreditcard.csv.zip. That is the case--unzipexists for, and it was the one case the flag did not cover:out/creditcard.csv.zipwas 69 MB and still zipped, and the command exited 0.dataset_download_cliforwardedunziptodataset_download_filesbut not todataset_download_file:Separately,
--unzipwas registered only on the datasets parser, so a competition bundle always had to be extracted by hand.Solution
unzipis forwarded todataset_download_file, the flag is added to the competitions download parser and threaded through both competition download paths, and one_extract_and_remove_zip()helper now serves all four call sites, replacing the extraction block that was inlined indataset_download_files.Extraction of a single file is limited to a response named exactly
<requested>.zip:A download is never renamed, so a file that is genuinely a
.zipinside a dataset does not match and is left alone. A competition bundle served in any format other than zip is also left as is.zipfile.extractall()drops..segments and absolute prefixes from member names, so extraction stays inside the destination. There is a test for that.The helper reuses the existing
ISSUE_TRACKER_URLconstant instead of the two hardcoded variants of the repository URL that were in the old block, one of which pointed at the repository root rather than the issue tracker.Behavior
Without
--unzipnothing changes. An archive that is already on disk is extracted without being downloaded again, which matches how a full dataset download has behaved since #1086.Testing
hatch -e test run pytest tests/unit/test_download_unzip.py -v(26 passed)hatch -e test run pytest tests/unit(1336 passed, 3 skipped)hatch run lint:allcompetitions download --unzipon titanic writes the three csv files and no zip; a full dataset download with--unzipstill writes 68 files and no zip through the shared helper; a cached archive is extracted without a second download; and a plain uncompressed file, a competition bundle download without the flag, and a competition single file with the flag are all unchanged/gcbrunwhen convenientNew tests cover the wrapper rule, a file that is itself a
.zip, an uncompressed response, a nested request, a cached archive, the bundle case with and without the flag, a bundle that is not a zip, extraction that would escape the destination, a corrupted archive, a missing archive, and that both CLI wrappers forward the flag. The eight behavior tests fail against the previous code.Docs updated in
docs/datasets.md,docs/competitions.md,skills/references/datasets.md, andskills/references/competitions.md.Related
Fixes #158
Fixes #9