Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/source/features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,9 @@ reads the same zip-file, but extracts the CSV files and stores them locally in t
**For developers**: this "chaining" methods works by formatting the arguments passed to ``open_*``
into ``target_protocol`` (a simple string) and ``target_options`` (a dict) and also optionally
``fo`` (target path, if a specific file is required). In order for an implementation to chain
successfully like this, it must look for exactly those named arguments.
successfully like this, it must look for exactly those named arguments. Implementations that
require access to the target path of their nested targets should inherit from ``ChainedFileSystem``,
which will trigger pass-through of the nested path automatically.

Caching Files Locally
---------------------
Expand Down
4 changes: 2 additions & 2 deletions fsspec/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ def open_files(

def _un_chain(path, kwargs):
# Avoid a circular import
from fsspec.implementations.cached import CachingFileSystem
from fsspec.implementations.chained import ChainedFileSystem

if "::" in path:
x = re.compile(".*[^a-z]+.*") # test for non protocol-like single word
Expand Down Expand Up @@ -358,7 +358,7 @@ def _un_chain(path, kwargs):
**kws,
)
bit = cls._strip_protocol(bit)
if "target_protocol" not in kw and issubclass(cls, CachingFileSystem):
if "target_protocol" not in kw and issubclass(cls, ChainedFileSystem):
bit = previous_bit
out.append((bit, protocol, kw))
previous_bit = bit
Expand Down
5 changes: 3 additions & 2 deletions fsspec/implementations/cached.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
from shutil import rmtree
from typing import TYPE_CHECKING, Any, Callable, ClassVar

from fsspec import AbstractFileSystem, filesystem
from fsspec import filesystem
from fsspec.callbacks import DEFAULT_CALLBACK
from fsspec.compression import compr
from fsspec.core import BaseCache, MMapCache
from fsspec.exceptions import BlocksizeMismatchError
from fsspec.implementations.cache_mapper import create_cache_mapper
from fsspec.implementations.cache_metadata import CacheMetadata
from fsspec.implementations.chained import ChainedFileSystem
from fsspec.implementations.local import LocalFileSystem
from fsspec.spec import AbstractBufferedFile
from fsspec.transaction import Transaction
Expand All @@ -39,7 +40,7 @@ def complete(self, commit=True):
self.fs = None # break cycle


class CachingFileSystem(AbstractFileSystem):
class CachingFileSystem(ChainedFileSystem):
"""Locally caching filesystem, layer over any other FS

This class implements chunk-wise local storage of remote files, for quick
Expand Down
9 changes: 9 additions & 0 deletions fsspec/implementations/chained.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from typing import ClassVar

from fsspec import AbstractFileSystem

__all__ = ("ChainedFileSystem",)


class ChainedFileSystem(AbstractFileSystem):
chained: ClassVar[str] = "chained"
37 changes: 37 additions & 0 deletions fsspec/tests/test_chained.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import pytest

from fsspec import AbstractFileSystem, filesystem
from fsspec import url_to_fs, register_implementation
from fsspec.implementations.cached import ChainedFileSystem


class MyChainedFS(ChainedFileSystem):
protocol = "mychain"

def __init__(self, target_protocol="", target_options=None, **kwargs):
super().__init__(**kwargs)
self.fs = filesystem(target_protocol, **target_options)

class MyNonChainedFS(AbstractFileSystem):
protocol = "mynonchain"

@pytest.fixture(scope="module")
def register_fs():
register_implementation(MyChainedFS.protocol, MyChainedFS)
register_implementation(MyNonChainedFS.protocol, MyNonChainedFS)
yield

def test_token_passthrough_to_chained(register_fs):
# First, run a sanity check
fs, rest = url_to_fs("mynonchain://path/to/file")
assert isinstance(fs, MyNonChainedFS)
assert fs.protocol == "mynonchain"
assert rest == "path/to/file"

# Now test that the chained FS works
fs, rest = url_to_fs("mychain::mynonchain://path/to/file")
assert isinstance(fs, MyChainedFS)
assert fs.protocol == "mychain"
assert rest == "path/to/file"
assert isinstance(fs.fs, MyNonChainedFS)
assert fs.fs.protocol == "mynonchain"
Loading