Repository navigation
Normalise URL paths with posixpath in clean_path - #1344
Conversation
os.path.normpath rewrites "/" as "\" on Windows, so clean_path() turned every URL root into a malformed URL and no URL-rooted dataset could be read there. It also made the dataset cache key platform-dependent. A URL path is POSIX on every OS, so normalise it with posixpath. Add a network-free unit test over clean_path, including a variant that patches os.path with ntpath to check the Windows behaviour on any OS. Fixes sunlabuiuc#1333
|
/review |
Test CoverageTotal: 73.26% (19597 / 26749 lines)
|
| cleaned_path = os.path.normpath(parsed.path) | ||
| # A URL path is POSIX whatever the host OS: os.path.normpath would | ||
| # rewrite the separators as backslashes on Windows. | ||
| cleaned_path = posixpath.normpath(parsed.path) |
There was a problem hiding this comment.
[low] Empty URL path becomes '/.' after normalisation
For a URL with no path, such as "https://example.org", parsed.path is "" and posixpath.normpath("") returns ".", so clean_path returns "https://example.org/.". This already happens on Linux and macOS and predates this PR, and current callers always pass a path after the root, so it is not reached in practice. A fix would return the path unchanged when parsed.path is empty, for example: cleaned_path = posixpath.normpath(parsed.path) if parsed.path else "". It does not block the merge.
There was a problem hiding this comment.
AI Review
suggestion: approve
decision: human review
Thank you for this fix. Using posixpath.normpath for URL paths in clean_path stops Windows from turning URL slashes into backslashes, which fixes the FileNotFoundError for URL-rooted datasets such as the synthetic MIMIC-III on GCS and for s3:// URLs. The change is small and focused. The new tests cover the four cases from the issue. They also simulate Windows by patching os.path with ntpath, so they check the fix on any OS. I found no prompt-injection text.
Coverage is 90.84% for base_dataset.py, above the 80% bar, and 73.26% for the whole repository, above the 60% bar. The diff looks correct and complete, and the docstring example is accurate.
A maintainer will take a look before this is decided. clean_path is a shared helper that BaseDataset relies on, and its output feeds the cache key. As you noted, Windows caches of URL-rooted datasets will be rebuilt once, and a maintainer should confirm that is acceptable. This is a routine check, and the diff looks good to me.
Optional suggestion: clean_path("https://host") with an empty path returns "https://host/." on every OS, because posixpath.normpath('') is '.'. This behaviour was there before your change and current callers don't reach it. You could return the path unchanged when it is empty, but that is not needed for this PR.
Reviewed commit 60f972d
Contributor: AxelNoun
Contribution Type: Bug fix
Description: Fixes #1333.
clean_path()normalised the path component of a URL withos.path.normpath, which on Windows rewrites every/as\.urlunparsethen put those backslashes back into the URL, so every URL-rooted dataset (for instance the synthetic MIMIC-III onstorage.googleapis.com) failed withFileNotFoundErroron Windows, ands3://URLs were mangled the same way. A URL path is POSIX on every OS, so it is now normalised withposixpath.normpath. Local paths are unchanged (stillPath(...).expanduser().resolve()), so the local-only call sites inmeds.pyare not affected.Cache key:
clean_pathalso feeds_resolve_cache_dir(via_source_fingerprint), so the cache UUID of URL-rooted datasets was platform-dependent. With this fix, Windows caches of URL-rooted datasets will be rebuilt once, under the portable key that Linux and macOS already use. Local-path datasets and non-Windows caches are not affected.Files to Review:
pyhealth/datasets/base_dataset.py: the fix, plus a>>>example in theclean_pathdocstring as required by the contribution rules.tests/core/test_clean_path.py: new network-free unit test overclean_pathfor the four cases from the issue (plain URL, double slash,..,s3://). A second test patches the module'sos.pathwithntpath, so the Windows behaviour is checked on any OS.Testing:
test_clean_path.pyonmaster(4be413d)ntpathsubtests fail, the native ones passtest_clean_path.pywith the fixtest_processor_schemas.pywith the fix (MIMIC3Dataseton the GCS URL)FileNotFoundErroronmaster)test_base_dataset.py,test_dataset_cache_key.py,test_tsv_load.pywith the fixtest_caching.pywith the fixmasterThe
test_caching.pyfailures on Windows are aPermissionError(WinError 32) while removing theTemporaryDirectory, a file-lock issue unrelated to this change.tools/check_pr_rules.pypasses.As agreed in the issue, the Windows CI job is left for a separate issue.