Skip to content

Commit 13484c1

Browse files
committed
docs: clarify storage entry points and S3 usage
1 parent 13f9584 commit 13484c1

5 files changed

Lines changed: 138 additions & 17 deletions

File tree

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Find examples backed by the repository's integration tests.
5656
:link: reference/api
5757
:link-type: doc
5858

59-
Inspect the public classes and registration helpers provided by `opendalfs`.
59+
Inspect the public filesystem classes provided by `opendalfs`.
6060
:::
6161
::::
6262

docs/integrations/pandas.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,52 @@ pd.testing.assert_frame_equal(result, frame)
3232
See {doc}`../user-guide/connecting-to-storage` for path and authority handling
3333
with bucket-scoped services.
3434

35+
## Read and write CSV with S3 URLs
36+
37+
Register `S3FileSystem` before using pandas with `s3://` URLs. This selects OpenDAL for subsequent fsspec S3 lookups in the current process; it is not required for `opendal+s3://` URLs.
38+
39+
This example uses the local MinIO service and existing bucket described in {doc}`../user-guide/connecting-to-storage`. Set the environment variables below to connect to your own storage. The default credentials are only for local development.
40+
41+
```python
42+
import os
43+
44+
import fsspec
45+
import pandas as pd
46+
from opendalfs import S3FileSystem
47+
48+
fsspec.register_implementation("s3", S3FileSystem, clobber=True)
49+
50+
bucket = os.environ.get("OPENDAL_S3_BUCKET", "test-bucket")
51+
storage_options = {
52+
"key": os.environ.get("OPENDAL_S3_ACCESS_KEY_ID", "minioadmin"),
53+
"secret": os.environ.get("OPENDAL_S3_SECRET_ACCESS_KEY", "minioadmin"),
54+
"client_kwargs": {
55+
"endpoint_url": os.environ.get("OPENDAL_S3_ENDPOINT", "http://127.0.0.1:9000"),
56+
"region_name": os.environ.get("OPENDAL_S3_REGION", "us-east-1"),
57+
},
58+
}
59+
url = f"s3://{bucket}/docs/scores.csv"
60+
frame = pd.DataFrame({"name": ["Alice", "Bob"], "score": [90, 85]})
61+
62+
frame.to_csv(url, index=False, storage_options=storage_options)
63+
result = pd.read_csv(url, storage_options=storage_options)
64+
pd.testing.assert_frame_equal(result, frame)
65+
66+
fs, path = fsspec.core.url_to_fs(url, **storage_options)
67+
fs.rm(path)
68+
```
69+
70+
Each OpenDAL S3 filesystem is scoped to one bucket. See {doc}`../reference/configuration` for the supported s3fs configuration options; not every s3fs feature is available.
71+
3572
## Test coverage
3673

3774
The repository tests:
3875

3976
- CSV and Parquet URL operations through `opendal+s3`
4077
- Parquet round trips with an explicit filesystem
4178

79+
The documentation example checker also executes the `s3://` CSV round trip above.
80+
4281
The test runs against memory, local filesystem, and MinIO-backed S3 fixtures.
4382
See
4483
[`tests/integration/pandas/test_pandas.py`](https://github.com/fsspec/opendalfs/blob/main/tests/integration/pandas/test_pandas.py).

docs/reference/configuration.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ fs = OpendalFileSystem(
4848
Use the [OpenDAL service directory](https://opendal.apache.org/services/) as the
4949
configuration reference. Option names pass through unchanged.
5050

51-
The `S3FileSystem` adapter accepts these common `s3fs` aliases:
51+
## S3 compatibility options
52+
53+
The `S3FileSystem` adapter for `s3://` accepts the following `s3fs` aliases. These aliases do not apply to `OpendalFileSystem` or the `opendal+...` protocols, which use OpenDAL option names.
5254

5355
| s3fs option | OpenDAL S3 option |
5456
| --- | --- |
@@ -66,11 +68,16 @@ The `S3FileSystem` adapter accepts these common `s3fs` aliases:
6668

6769
OpenDAL option names take precedence when both forms are provided.
6870
Unsupported nested `client_kwargs` raise `TypeError`.
71+
`client_kwargs` must be a mapping. Top-level aliases take precedence over aliases inside `client_kwargs`.
72+
73+
`default_block_size` sets the filesystem's default block size in bytes. It is an adapter setting, not an OpenDAL service option. Other s3fs-specific options and features are not guaranteed to be compatible.
6974

7075
## URL-derived settings
7176

7277
Registered service adapters can derive one setting from the URL authority. For
7378
example, `opendal+s3://my-bucket/path` supplies `bucket="my-bucket"`.
7479
Explicit filesystem construction requires the bucket keyword instead.
7580

81+
For `s3://my-bucket/path`, `S3FileSystem` uses `my-bucket` even when a different `bucket` is present in the storage options. Paths passed to this filesystem include the bucket, such as `my-bucket/path`; the underlying OpenDAL operator receives the bucket-relative key.
82+
7683
Do not provide conflicting values through the URL and keyword arguments.

docs/reference/protocols.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ from opendalfs import S3FileSystem
2929
fsspec.register_implementation("s3", S3FileSystem, clobber=True)
3030
```
3131

32-
This explicit process-wide operation is the only way opendalfs changes the meaning of a standard protocol.
32+
Installing or importing opendalfs does not replace fsspec's S3 implementation. The explicit registration above replaces it for subsequent lookups in the current process.
3333
The adapter accepts the common s3fs constructor options listed in {doc}`configuration`.
34-
Each adapter instance is scoped to one bucket; a multi-path operation spanning buckets raises `ValueError`.
34+
Each adapter instance is scoped to one bucket; paths belonging to another bucket raise `ValueError`. This is not a complete replacement for every s3fs feature.
3535

3636
See {doc}`../user-guide/connecting-to-storage` for complete examples.

docs/user-guide/connecting-to-storage.md

Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ An `opendalfs` filesystem needs an OpenDAL service name and that service's
44
configuration. Choose the construction style that matches the library you are
55
using.
66

7+
| Your application accepts | Use |
8+
| --- | --- |
9+
| A filesystem, mapping, or file-like object | `OpendalFileSystem("service", ...)` |
10+
| An explicit OpenDAL URL | `opendal+s3://`, `opendal+gcs://`, or `opendal+azblob://` |
11+
| An `s3://` URL | Register `S3FileSystem` with fsspec before using it |
12+
713
## Construct the filesystem directly
814

915
Use {class}`opendalfs.OpendalFileSystem` when your code controls the filesystem
@@ -23,6 +29,19 @@ fs = OpendalFileSystem(
2329
The first argument selects the OpenDAL service. Remaining service-specific
2430
keyword arguments are passed to the OpenDAL Python binding.
2531

32+
This also works for services without a URL adapter. For example, the local filesystem service needs no credentials:
33+
34+
```python
35+
from tempfile import TemporaryDirectory
36+
37+
with TemporaryDirectory() as directory:
38+
fs = OpendalFileSystem("fs", root=directory)
39+
fs.pipe_file("hello.txt", b"Hello from OpenDAL!")
40+
assert fs.cat_file("hello.txt") == b"Hello from OpenDAL!"
41+
```
42+
43+
Available services depend on the installed OpenDAL Python binding. Pass the filesystem, its mapping, or an opened file to libraries that accept these objects; no protocol registration is needed.
44+
2645
## Ask fsspec for a registered filesystem
2746

2847
The package installs fsspec entry points for S3, Google Cloud Storage, and Azure
@@ -38,31 +57,70 @@ fs = fsspec.filesystem(
3857
)
3958
```
4059

41-
## Keep an existing S3 URL
60+
## Use S3 URLs
4261

43-
Applications that cannot rewrite existing `s3://` URLs can explicitly replace fsspec's S3 implementation:
62+
Register `S3FileSystem` to use OpenDAL with `s3://` URLs and the supported s3fs configuration options:
4463

4564
```python
4665
import fsspec
4766
from opendalfs import S3FileSystem
4867

4968
fsspec.register_implementation("s3", S3FileSystem, clobber=True)
69+
```
5070

51-
fs, path = fsspec.core.url_to_fs(
52-
"s3://my-bucket/reports/2026.csv",
53-
key="access-key",
54-
secret="secret-key",
55-
client_kwargs={"region_name": "us-east-1"},
56-
)
71+
The registration replaces the S3 implementation for subsequent fsspec lookups in the current process. Run it during application startup, before constructing an S3 filesystem. Installing or importing `opendalfs` does not change `s3://`. Use `opendal+s3://` if you do not want to change the process-wide registration.
72+
73+
### Write and read a file
74+
75+
The following example uses a local MinIO service at `http://127.0.0.1:9000` with an existing `test-bucket` bucket. The default credentials are for local development only. Set the `OPENDAL_S3_*` environment variables to use your own endpoint, credentials, and bucket.
76+
77+
When working from this repository, `podman compose up -d --wait` starts the MinIO service. Create the bucket in the MinIO console at `http://localhost:9001` before running the example; the repository's documentation checks create their test bucket automatically.
78+
79+
```python
80+
import os
81+
82+
bucket = os.environ.get("OPENDAL_S3_BUCKET", "test-bucket")
83+
storage_options = {
84+
"key": os.environ.get("OPENDAL_S3_ACCESS_KEY_ID", "minioadmin"),
85+
"secret": os.environ.get("OPENDAL_S3_SECRET_ACCESS_KEY", "minioadmin"),
86+
"client_kwargs": {
87+
"endpoint_url": os.environ.get("OPENDAL_S3_ENDPOINT", "http://127.0.0.1:9000"),
88+
"region_name": os.environ.get("OPENDAL_S3_REGION", "us-east-1"),
89+
},
90+
}
91+
url = f"s3://{bucket}/docs/hello.txt"
92+
93+
with fsspec.open(url, "wt", **storage_options) as stream:
94+
stream.write("Hello from OpenDAL!")
95+
96+
with fsspec.open(url, "rt", **storage_options) as stream:
97+
assert stream.read() == "Hello from OpenDAL!"
5798
```
5899

59-
The registration call is process-wide and should run during application startup, before constructing an S3 filesystem.
60-
`S3FileSystem` translates common s3fs names such as `key`, `secret`, `token`, `anon`, and supported `client_kwargs`.
61-
Installing `opendalfs` alone never changes `s3://`.
100+
The same `storage_options` can be passed to downstream libraries; see the {doc}`../integrations/pandas` example. The complete list of supported s3fs aliases is in {doc}`../reference/configuration`.
101+
102+
### Bucket scope and compatibility
62103

63104
An OpenDAL S3 operator is scoped to one bucket, so each `S3FileSystem` instance is also scoped to one bucket.
64-
Independent fsspec calls can use different buckets.
65-
A single multi-path operation spanning buckets raises `ValueError` instead of sending a path to the wrong bucket.
105+
`url_to_fs` takes the bucket from the URL and returns a bucket-prefixed path:
106+
107+
```python
108+
fs, path = fsspec.core.url_to_fs(url, **storage_options)
109+
assert path == f"{bucket}/docs/hello.txt"
110+
assert fs.cat_file(path) == b"Hello from OpenDAL!"
111+
112+
try:
113+
fs.cat_file(f"{bucket}-other/docs/hello.txt")
114+
except ValueError:
115+
pass # This filesystem cannot access another bucket.
116+
else:
117+
raise AssertionError("A path in another bucket must be rejected")
118+
119+
```
120+
121+
Independent fsspec calls can use different buckets; create a separate filesystem for each bucket. When constructing `S3FileSystem` directly, pass `bucket=...` explicitly.
122+
123+
The adapter does not implement every s3fs option or feature. Only the aliases listed in the configuration reference are supported as s3fs options; unsupported `client_kwargs` raise `TypeError`.
66124

67125
## Understand OpenDAL URLs
68126

@@ -80,8 +138,25 @@ opendal+gcs://my-bucket/reports/2026.csv
80138
opendal+azblob://my-container/reports/2026.csv
81139
```
82140

141+
These URLs use OpenDAL option names, not s3fs aliases. To read the same MinIO object written above:
142+
143+
```python
144+
with fsspec.open(
145+
f"opendal+s3://{bucket}/docs/hello.txt",
146+
"rt",
147+
endpoint=storage_options["client_kwargs"]["endpoint_url"],
148+
region=storage_options["client_kwargs"]["region_name"],
149+
access_key_id=storage_options["key"],
150+
secret_access_key=storage_options["secret"],
151+
) as stream:
152+
assert stream.read() == "Hello from OpenDAL!"
153+
154+
fs.rm(path)
155+
```
156+
83157
Other OpenDAL services intentionally have no URL adapter.
84158
Construct `OpendalFileSystem` directly and pass the filesystem, mapping, or opened file to the consuming library.
159+
For application-defined URL protocols, use fsspec's `register_implementation()` with a filesystem class that implements the protocol's URL and path rules. Registration alone does not add those rules to `OpendalFileSystem`.
85160

86161
## Find service options
87162

0 commit comments

Comments
 (0)