diff --git a/dev/fsspec_inspector/generate_flavours.py b/dev/fsspec_inspector/generate_flavours.py index 324f007a..2d58e1c3 100644 --- a/dev/fsspec_inspector/generate_flavours.py +++ b/dev/fsspec_inspector/generate_flavours.py @@ -142,6 +142,15 @@ def _fix_azure_blob_file_system(x: str) -> str: return x +def _fix_data_file_system(x: str) -> str: + return re.sub( + "sep = '/'", + "sep = '' # type: ignore[assignment]\n " + "altsep = ' ' # type: ignore[assignment]", + x, + ) + + def _fix_memfs_file_system(x: str) -> str: return re.sub( "_MemFS", @@ -184,6 +193,7 @@ def _fix_xrootd_file_system(x: str) -> str: FIX_SOURCE = { "AbstractFileSystem": _fix_abstract_file_system, "AzureBlobFileSystem": _fix_azure_blob_file_system, + "DataFileSystem": _fix_data_file_system, "MemFS": _fix_memfs_file_system, "MemoryFileSystem": _fix_memory_file_system, "OSSFileSystem": _fix_oss_file_system, diff --git a/upath/_flavour.py b/upath/_flavour.py index 19eb62a3..3274bcb2 100644 --- a/upath/_flavour.py +++ b/upath/_flavour.py @@ -274,7 +274,7 @@ def sep(self) -> str: # type: ignore[override] @property def altsep(self) -> str | None: # type: ignore[override] - return None + return getattr(self._spec, "altsep", None) def isabs(self, path: JoinablePathLike) -> bool: path = self.strip_protocol(path) diff --git a/upath/_flavour_sources.py b/upath/_flavour_sources.py index 457551c0..32366a4e 100644 --- a/upath/_flavour_sources.py +++ b/upath/_flavour_sources.py @@ -341,7 +341,8 @@ class DataFileSystemFlavour(AbstractFileSystemFlavour): __orig_version__ = '2025.10.0' protocol = ('data',) root_marker = '' - sep = '/' + sep = "" # type: ignore[assignment] + altsep = " " # type: ignore[assignment] class DatabricksFileSystemFlavour(AbstractFileSystemFlavour): diff --git a/upath/implementations/data.py b/upath/implementations/data.py index 83dce046..0b8dfdc1 100644 --- a/upath/implementations/data.py +++ b/upath/implementations/data.py @@ -1,9 +1,12 @@ from __future__ import annotations import sys +from collections.abc import Iterator from collections.abc import Sequence from typing import TYPE_CHECKING +from urllib.parse import quote_plus +from upath._protocol import get_upath_protocol from upath.core import UnsupportedOperation from upath.core import UPath from upath.types import JoinablePathLike @@ -45,10 +48,49 @@ def __str__(self) -> str: return self.parser.join(*self._raw_urlpaths) def with_segments(self, *pathsegments: JoinablePathLike) -> Self: - raise UnsupportedOperation("path operation not supported by DataPath") + try: + (segment,) = pathsegments + except ValueError: + raise UnsupportedOperation("join not supported by DataPath") + if get_upath_protocol(segment) != "data": + raise ValueError(f"requires a data URI, got: {segment!r}") + return type(self)(segment, protocol="data", **self.storage_options) + + @property + def name(self) -> str: + return quote_plus(self.path) + + @property + def stem(self) -> str: + return quote_plus(self.path) + + @property + def suffix(self) -> str: + return "" + + @property + def suffixes(self) -> list[str]: + return [] + + def with_name(self, name: str) -> Self: + raise UnsupportedOperation("with_name not supported by DataPath") def with_suffix(self, suffix: str) -> Self: - raise UnsupportedOperation("path operation not supported by DataPath") + raise UnsupportedOperation("with_suffix not supported by DataPath") + + def with_stem(self, stem: str) -> Self: + raise UnsupportedOperation("with_stem not supported by DataPath") + + @property + def parent(self) -> Self: + return self + + @property + def parents(self) -> Sequence[Self]: + return [] + + def full_match(self, pattern, *, case_sensitive: bool | None = None) -> bool: + return super().full_match(pattern, case_sensitive=case_sensitive) def mkdir( self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False @@ -66,3 +108,19 @@ def write_text( newline: str | None = None, ) -> int: raise UnsupportedOperation("DataPath does not support writing") + + def iterdir(self) -> Iterator[Self]: + raise NotADirectoryError + + def glob( + self, pattern, *, case_sensitive=None, recurse_symlinks=False + ) -> Iterator[Self]: + return iter([]) + + def rglob( + self, pattern, *, case_sensitive=None, recurse_symlinks=False + ) -> Iterator[Self]: + return iter([]) + + def unlink(self, missing_ok: bool = False) -> None: + raise UnsupportedOperation