Skip to content

Normalise URL paths with posixpath in clean_path - #1344

Merged
Logiquo merged 1 commit into
sunlabuiuc:masterfrom
AxelNoun:fix/clean-path-url-posix
Oct 28, 2026
Merged

Logiquo merged 1 commit into
sunlabuiuc:masterfrom
AxelNoun:fix/clean-path-url-posix

Conversation

@AxelNoun

Copy link
Copy Markdown
Contributor

Contributor: AxelNoun

Contribution Type: Bug fix

Description: Fixes #1333. clean_path() normalised the path component of a URL with os.path.normpath, which on Windows rewrites every / as \. urlunparse then put those backslashes back into the URL, so every URL-rooted dataset (for instance the synthetic MIMIC-III on storage.googleapis.com) failed with FileNotFoundError on Windows, and s3:// URLs were mangled the same way. A URL path is POSIX on every OS, so it is now normalised with posixpath.normpath. Local paths are unchanged (still Path(...).expanduser().resolve()), so the local-only call sites in meds.py are not affected.

Cache key: clean_path also 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 the clean_path docstring as required by the contribution rules.
  • tests/core/test_clean_path.py: new network-free unit test over clean_path for the four cases from the issue (plain URL, double slash, .., s3://). A second test patches the module's os.path with ntpath, so the Windows behaviour is checked on any OS.

Testing:

Windows 11, Python 3.12.10 Ubuntu 24.04 (WSL2), Python 3.12.3
test_clean_path.py on master (4be413d) 8 of 8 subtests fail the 4 ntpath subtests fail, the native ones pass
test_clean_path.py with the fix all pass all pass
test_processor_schemas.py with the fix (MIMIC3Dataset on the GCS URL) 5 passed (FileNotFoundError on master) 5 passed
test_base_dataset.py, test_dataset_cache_key.py, test_tsv_load.py with the fix pass pass
test_caching.py with the fix 5 failures, identical on master pass

The test_caching.py failures on Windows are a PermissionError (WinError 32) while removing the TemporaryDirectory, a file-lock issue unrelated to this change. tools/check_pr_rules.py passes.

As agreed in the issue, the Windows CI job is left for a separate issue.

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
@Logiquo Logiquo added the status: automatic The PR will go through automatic review process. label Oct 28, 2026
@Logiquo

Logiquo commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator

/review

@github-actions

Copy link
Copy Markdown

Test Coverage

Total: 73.26% (19597 / 26749 lines)

Modified file Coverage Lines
pyhealth/datasets/base_dataset.py 90.84% 476 / 524

Workflow run · 60f972d

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed commit 60f972d

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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

@github-actions github-actions Bot added status: manual Pending maintainer's review and removed status: automatic The PR will go through automatic review process. labels Oct 28, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@Logiquo Logiquo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks.

@Logiquo
Logiquo merged commit 45a8db9 into sunlabuiuc:master Oct 28, 2026
2 checks passed
@AxelNoun
AxelNoun deleted the fix/clean-path-url-posix branch October 28, 2026 22:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: manual Pending maintainer's review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

clean_path() normalises URL paths with os.path.normpath, breaking every URL-rooted dataset on Windows

2 participants