Skip to content

improvement: support custom cloudpathlib providers in path normalization#8929

Merged
mscolnick merged 4 commits intomainfrom
ms/extending-cloudpath
Mar 31, 2026
Merged

improvement: support custom cloudpathlib providers in path normalization#8929
mscolnick merged 4 commits intomainfrom
ms/extending-cloudpath

Conversation

@mscolnick
Copy link
Copy Markdown
Contributor

Closes #8868

Custom cloudpathlib providers (e.g., SMBPath) that subclass CloudPath but live in external packages had their URI schemes corrupted (smb://smb:/) because the cloud-path check used __module__.startswith("cloudpathlib"), which only matched built-in providers.

Added an is_cloudpath() utility that uses isinstance(path, CloudPath) when cloudpathlib is installed, with a module-name fallback when it isn't. Replaced both call sites (normalize_path and file_browser limit defaulting) with the new utility.

Closes #8868

Custom cloudpathlib providers (e.g., SMBPath) that subclass `CloudPath` but live in external packages had their URI schemes corrupted (`smb://` → `smb:/`) because the cloud-path check used `__module__.startswith("cloudpathlib")`, which only matched built-in providers.

Added an `is_cloudpath()` utility that uses `isinstance(path, CloudPath)` when cloudpathlib is installed, with a module-name fallback when it isn't. Replaced both call sites (`normalize_path` and `file_browser` limit defaulting) with the new utility.
Copilot AI review requested due to automatic review settings March 30, 2026 17:42
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Mar 30, 2026 8:16pm

Request Review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes URI scheme corruption for custom cloudpathlib providers by improving cloud-path detection during path normalization and in the file browser’s default limit selection.

Changes:

  • Add is_cloudpath() utility to detect CloudPath instances (including external subclasses).
  • Update normalize_path() and file_browser limit defaulting to use is_cloudpath().
  • Expand/adjust tests for cloud path detection and normalization behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
marimo/_utils/paths.py Adds is_cloudpath() and switches normalize_path() to use it for skipping cloud URI normalization.
marimo/_plugins/ui/_impl/file_browser.py Uses is_cloudpath() to choose a conservative default file listing limit for cloud storage.
marimo/_dependencies/dependencies.py Registers cloudpathlib in DependencyManager for dependency introspection.
tests/_utils/test_paths.py Adds tests covering is_cloudpath() and custom provider normalization behavior.

Comment on lines +107 to +116
# Return early if cloudpathlib is not installed
if not DependencyManager.cloudpathlib.imported():
return False

try:
from cloudpathlib import CloudPath

return isinstance(path, CloudPath)
except ImportError:
return path.__class__.__module__.startswith("cloudpathlib")
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

is_cloudpath() short-circuits on DependencyManager.cloudpathlib.imported(), which only checks cloudpathlib in sys.modules. This makes the docstring’s “fallback to module-name check when cloudpathlib isn't installed” ineffective when the package is simply unavailable (i.e., not in sys.modules), and it also prevents the try/except ImportError logic from running in that case. Consider removing the imported() guard and instead try: import cloudpathlib (or use DependencyManager.cloudpathlib.has(quiet=True)) and fall back to the __module__ prefix check on ImportError.

Copilot uses AI. Check for mistakes.
Comment on lines +96 to +112
def test_builtin_cloudpath_detected_via_isinstance(self) -> None:
"""Built-in cloudpathlib paths are detected via isinstance."""
from cloudpathlib import S3Path

assert is_cloudpath(S3Path("s3://bucket/key"))

def test_custom_cloudpath_subclass_detected(self) -> None:
"""Custom CloudPath subclasses from external packages are detected.

This is the core scenario from issue #8868: a user creates a
custom provider whose __module__ does NOT start with 'cloudpathlib'.
We use virtual subclass registration to avoid cloudpathlib's
metaclass issues with direct subclassing.
"""
from cloudpathlib import CloudPath

class FakeSMBPath:
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

These tests import cloudpathlib unconditionally (from cloudpathlib import S3Path/CloudPath). CI runs the core test suite with the test dependency group (without optional deps like cloudpathlib), so this will raise ModuleNotFoundError and fail the core test job. Use pytest.importorskip("cloudpathlib") (or a skipif based on DependencyManager.cloudpathlib.has()) around cloudpathlib-dependent tests.

Copilot uses AI. Check for mistakes.
Comment on lines +152 to +156
from cloudpathlib import S3Path

cloud_path = S3Path("s3://bucket/folder/file.txt")
result = normalize_path(cloud_path)
assert result is cloud_path
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

This test imports cloudpathlib directly (from cloudpathlib import S3Path). In the core CI test run (dependency group test), cloudpathlib isn’t installed (it’s in test-optional), so this test should be skipped when the dependency is missing (e.g., pytest.importorskip("cloudpathlib")).

Copilot uses AI. Check for mistakes.
Comment on lines +152 to +161
from cloudpathlib import S3Path

cloud_path = S3Path("s3://bucket/folder/file.txt")
result = normalize_path(cloud_path)
assert result is cloud_path


def test_normalize_path_skips_custom_cloudpath_subclass() -> None:
"""Custom CloudPath subclasses should also skip normalization (#8868)."""
from cloudpathlib import CloudPath
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

This test imports cloudpathlib directly (from cloudpathlib import CloudPath). In the core CI test run (dependency group test), cloudpathlib isn’t installed (it’s in test-optional), so this test should be skipped when the dependency is missing (e.g., pytest.importorskip("cloudpathlib")).

Suggested change
from cloudpathlib import S3Path
cloud_path = S3Path("s3://bucket/folder/file.txt")
result = normalize_path(cloud_path)
assert result is cloud_path
def test_normalize_path_skips_custom_cloudpath_subclass() -> None:
"""Custom CloudPath subclasses should also skip normalization (#8868)."""
from cloudpathlib import CloudPath
cloudpathlib = pytest.importorskip("cloudpathlib")
cloud_path = cloudpathlib.S3Path("s3://bucket/folder/file.txt")
result = normalize_path(cloud_path)
assert result is cloud_path
def test_normalize_path_skips_custom_cloudpath_subclass() -> None:
"""Custom CloudPath subclasses should also skip normalization (#8868)."""
cloudpathlib = pytest.importorskip("cloudpathlib")
CloudPath = cloudpathlib.CloudPath

Copilot uses AI. Check for mistakes.
@mscolnick mscolnick added bug Something isn't working enhancement New feature or request and removed bug Something isn't working labels Mar 30, 2026
@mscolnick mscolnick changed the title fix: support custom cloudpathlib providers in path normalization improvement: support custom cloudpathlib providers in path normalization Mar 30, 2026
…loudpath

Tests that import cloudpathlib directly now use pytest.importorskip so
they are skipped on core-deps CI where cloudpathlib is not installed.

Restructured is_cloudpath() to: (1) use a fast module-name heuristic
first, (2) skip the import when cloudpathlib hasn't been loaded yet,
and (3) fall back to isinstance for virtual subclasses registered via
CloudPath.register().
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

@mscolnick mscolnick merged commit ed0ec3b into main Mar 31, 2026
43 checks passed
@mscolnick mscolnick deleted the ms/extending-cloudpath branch March 31, 2026 14:49
@github-actions
Copy link
Copy Markdown

🚀 Development release published. You may be able to view the changes at https://marimo.app?v=0.21.2-dev99

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Not possible to use Path extended from cloudpathlib

2 participants