Skip to content

Commit 9db9f5e

Browse files
authored
docs: add a hosted documentation site (#81)
1 parent e2930a6 commit 9db9f5e

32 files changed

Lines changed: 1860 additions & 185 deletions

.github/workflows/docs.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Documentation
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
12+
13+
jobs:
14+
build:
15+
name: Build documentation
16+
runs-on: ubuntu-24.04
17+
timeout-minutes: 10
18+
19+
steps:
20+
- name: Checkout repository
21+
uses: actions/checkout@v4
22+
23+
- name: Install uv
24+
uses: astral-sh/setup-uv@v5
25+
with:
26+
python-version: "3.12"
27+
28+
- name: Install just
29+
uses: extractions/setup-just@v4
30+
with:
31+
just-version: "1.58.0"
32+
33+
- name: Install documentation dependencies
34+
run: uv sync --locked --group docs --group docs-examples
35+
36+
- name: Build documentation
37+
run: just docs
38+
39+
- name: Run documentation examples
40+
run: just docs-examples
41+
42+
publish:
43+
name: Trigger Read the Docs
44+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
45+
needs: build
46+
runs-on: ubuntu-24.04
47+
timeout-minutes: 5
48+
49+
steps:
50+
- name: Trigger the latest documentation build
51+
env:
52+
RTD_WEBHOOK_TOKEN: ${{ secrets.RTD_WEBHOOK_TOKEN }}
53+
RTD_WEBHOOK_URL: ${{ secrets.RTD_WEBHOOK_URL }}
54+
run: >-
55+
curl --fail-with-body --retry 3
56+
--data-urlencode "token=${RTD_WEBHOOK_TOKEN}"
57+
--data-urlencode "branches=latest"
58+
--data-urlencode "default_branch=main"
59+
"${RTD_WEBHOOK_URL}"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ opendalfs.egg-info/
1212
dist/
1313
.coverage
1414
coverage.xml
15+
docs/_build/

.readthedocs.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
version: 2
2+
3+
build:
4+
os: ubuntu-24.04
5+
tools:
6+
python: "3.12"
7+
8+
sphinx:
9+
configuration: docs/conf.py
10+
fail_on_warning: true
11+
12+
python:
13+
install:
14+
- method: uv
15+
command: sync
16+
groups:
17+
- docs

README.md

Lines changed: 3 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
`opendalfs` is a Python-based interface for file systems that enables interaction with different storage services by [Apache OpenDAL](https://github.com/apache/opendal). Through `opendalfs`, users can utilize fsspec's standard API to operate on all [storage services supported by OpenDAL](https://docs.rs/opendal/latest/opendal/services/index.html).
88

9+
Read the [opendalfs documentation](https://opendalfs.readthedocs.io/en/latest/)
10+
for installation, storage configuration, API details, and tested integrations.
11+
912
## URL Protocols
1013

1114
`opendalfs` registers multiple fsspec protocols in the form of `opendal+<service>`, for example:
@@ -38,70 +41,10 @@ receive their OpenDAL configuration through fsspec storage options.
3841

3942
## Installation
4043

41-
### Basic Installation
42-
4344
```bash
4445
pip install opendalfs
4546
```
4647

47-
### Development Installation
48-
49-
Install [uv](https://docs.astral.sh/uv/) and
50-
[just](https://just.systems/man/en/packages.html), then run:
51-
52-
```bash
53-
just install
54-
```
55-
56-
## Development Setup
57-
58-
This project uses:
59-
60-
- Python 3.12+ for the Python interface
61-
- ruff for code formatting and linting
62-
- ty for type checking
63-
- pytest for testing
64-
65-
Run the local checks and unit tests with:
66-
67-
```bash
68-
just check
69-
just unit
70-
```
71-
72-
For development setup and guidelines, see our [Contributing Guide](https://github.com/fsspec/opendalfs/blob/main/CONTRIBUTING.md).
73-
74-
## Benchmarks
75-
76-
The benchmark script compares Arrow direct, opendalfs (fsspec), and s3fs (fsspec) on MinIO.
77-
78-
```bash
79-
just install
80-
just bench --sizes 16,32,64 --files 4 --workers 4
81-
```
82-
83-
Configure MinIO access via `OPENDAL_S3_ENDPOINT`, `OPENDAL_S3_BUCKET`,
84-
`OPENDAL_S3_REGION`, `OPENDAL_S3_ACCESS_KEY_ID`, and
85-
`OPENDAL_S3_SECRET_ACCESS_KEY`. Compose, tests, and benchmarks read the same
86-
values.
87-
88-
The benchmark target starts MinIO from the root `docker-compose.yml`. Stop it
89-
when you finish:
90-
91-
```bash
92-
just bench-down
93-
```
94-
95-
For profiling, you can install a tool with `uv` (for example `py-spy`) and run:
96-
97-
```bash
98-
uv tool install py-spy
99-
uv tool run py-spy record -o bench.svg -- python bench/bench_read_write.py --sizes 16,32,64 --files 4 --workers 4
100-
```
101-
102-
High write concurrency can stall on some systems. If runs time out, reduce
103-
`--fsspec-workers`.
104-
10548
## Status
10649

10750
See [Tracking issues of 0.1.0 version for opendalfs](https://github.com/fsspec/opendalfs/issues/6)

docs/check_examples.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import re
2+
from pathlib import Path
3+
4+
import pytest
5+
6+
DOCS_ROOT = Path(__file__).parent
7+
PYTHON_BLOCK = re.compile(r"^```python\n(.*?)^```$", re.MULTILINE | re.DOTALL)
8+
9+
10+
def python_example_files():
11+
return [
12+
path
13+
for path in sorted(DOCS_ROOT.rglob("*.md"))
14+
if PYTHON_BLOCK.search(path.read_text())
15+
]
16+
17+
18+
@pytest.mark.parametrize(
19+
"path",
20+
python_example_files(),
21+
ids=lambda path: str(path.relative_to(DOCS_ROOT)),
22+
)
23+
def test_python_examples(path, tmp_path, monkeypatch):
24+
"""Run a page's examples in order, sharing the page's Python namespace."""
25+
monkeypatch.chdir(tmp_path)
26+
namespace = {"__name__": "__docs_example__"}
27+
28+
for index, match in enumerate(PYTHON_BLOCK.finditer(path.read_text()), start=1):
29+
code = compile(match.group(1), f"{path} example {index}", "exec")
30+
exec(code, namespace) # noqa: S102 - executing documentation is the test

docs/concepts/architecture.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Architecture
2+
3+
`opendalfs` connects two existing abstractions. OpenDAL supplies storage
4+
services and operations. fsspec supplies the filesystem interface used by
5+
Python data libraries.
6+
7+
## The request path
8+
9+
When pandas opens an `opendal+s3` URL, the request passes through these layers:
10+
11+
```text
12+
pandas
13+
-> fsspec URL resolution
14+
-> opendalfs filesystem method
15+
-> OpenDAL Python operator
16+
-> storage service
17+
```
18+
19+
The reverse path returns bytes, metadata, directory entries, or a file-like
20+
object in the shape expected by fsspec.
21+
22+
## One filesystem, one service configuration
23+
24+
An {class}`opendalfs.OpendalFileSystem` owns one OpenDAL operator. The operator
25+
is configured for one service and one root. Create another filesystem for a
26+
second bucket, container, or root.
27+
28+
```python
29+
from opendalfs import OpendalFileSystem
30+
31+
incoming = OpendalFileSystem("s3", bucket="incoming", region="us-east-1")
32+
archive = OpendalFileSystem("s3", bucket="archive", region="us-east-1")
33+
```
34+
35+
fsspec may cache instances created with identical arguments. This is useful for
36+
connection reuse, but it does not merge different service configurations.
37+
38+
## What the adapter owns
39+
40+
`opendalfs` is responsible for:
41+
42+
- fsspec method shapes and metadata dictionaries
43+
- sync wrappers around async storage operations
44+
- buffered file behavior
45+
- URL protocol registration
46+
- translating URL authorities and paths into operator-relative paths
47+
- adapter settings such as retries and concurrent writes
48+
49+
OpenDAL remains responsible for service clients, credentials, backend-specific
50+
configuration, and the native capability of each service.
51+
52+
## Why integrations work
53+
54+
Most integrations do not know about OpenDAL. They call fsspec with a URL,
55+
filesystem, mapping, or file-like object. Compatibility depends on how closely
56+
the adapter follows those fsspec contracts. The repository therefore tests
57+
real downstream entry points in addition to its own filesystem methods.

docs/concepts/capabilities.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Capabilities
2+
3+
Compatibility has more than one layer. A method can exist in fsspec while the
4+
selected storage service lacks the native operation needed to implement it.
5+
6+
```text
7+
OpenDAL service capability
8+
-> OpenDAL Python binding
9+
-> opendalfs method
10+
-> downstream library usage
11+
```
12+
13+
## fsspec methods
14+
15+
fsspec defines common filesystem methods such as `open`, `ls`, `find`, `copy`,
16+
and `rm`. It also implements composite methods in terms of smaller operations.
17+
For example, a recursive operation may combine listing, directory creation,
18+
and individual file transfers.
19+
20+
`opendalfs` implements the primitive operations needed by those fsspec flows
21+
and runs the upstream abstract filesystem contract tests against memory and S3.
22+
23+
## Service operations
24+
25+
OpenDAL services do not all support the same operations. Read, write, list,
26+
copy, rename, multipart upload, and metadata behavior can differ. OpenDAL
27+
reports unsupported native operations as errors.
28+
29+
Consult the [OpenDAL service directory](https://opendal.apache.org/services/)
30+
when choosing a backend. Test optional operations against the configured
31+
service before using them in a production workflow.
32+
33+
## Integration evidence
34+
35+
A passing pandas test proves that the tested pandas entry point works with the
36+
locked dependency version and the backends covered by CI. It does not prove
37+
every pandas storage workflow on every OpenDAL service.
38+
39+
The {doc}`../integrations/index` labels this evidence as "CI-tested" and names
40+
the entry form used by each library. This keeps the claim tied to a concrete
41+
test instead of treating compatibility as all or nothing.
42+
43+
## Where to report a mismatch
44+
45+
If a valid fsspec usage fails before reaching an unsupported OpenDAL operation,
46+
report it to `opendalfs`. Include the service, URL or filesystem construction,
47+
operation, and a small reproducer. Backend-specific failures may also require
48+
an OpenDAL issue.

docs/concepts/index.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Concepts
2+
3+
These pages explain where `opendalfs` sits between OpenDAL and fsspec, and why
4+
compatibility depends on more than the presence of a filesystem method.
5+
6+
```{toctree}
7+
8+
architecture
9+
capabilities
10+
```

docs/conf.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from importlib.metadata import version as package_version
2+
3+
project = "opendalfs"
4+
author = "opendalfs contributors"
5+
copyright = "2026, opendalfs contributors" # noqa: A001
6+
7+
release = package_version("opendalfs")
8+
version = release
9+
10+
extensions = [
11+
"myst_parser",
12+
"numpydoc",
13+
"sphinx.ext.autodoc",
14+
"sphinx.ext.autosummary",
15+
"sphinx.ext.intersphinx",
16+
"sphinx.ext.viewcode",
17+
"sphinx_copybutton",
18+
"sphinx_design",
19+
]
20+
21+
autosummary_generate = True
22+
autodoc_typehints = "description"
23+
numpydoc_show_class_members = False
24+
myst_heading_anchors = 3
25+
myst_enable_extensions = ["colon_fence"]
26+
27+
exclude_patterns = ["_build"]
28+
29+
intersphinx_mapping = {
30+
"python": ("https://docs.python.org/3", None),
31+
"fsspec": ("https://filesystem-spec.readthedocs.io/en/latest/", None),
32+
"opendal": ("https://opendal.apache.org/docs/python/", None),
33+
"pandas": ("https://pandas.pydata.org/docs/", None),
34+
"dask": ("https://docs.dask.org/en/stable/", None),
35+
"xarray": ("https://docs.xarray.dev/en/stable/", None),
36+
}
37+
38+
html_theme = "pydata_sphinx_theme"
39+
html_title = "opendalfs"
40+
html_theme_options = {
41+
"github_url": "https://github.com/fsspec/opendalfs",
42+
"navigation_with_keys": True,
43+
"show_toc_level": 2,
44+
"use_edit_page_button": True,
45+
}
46+
html_context = {
47+
"github_user": "fsspec",
48+
"github_repo": "opendalfs",
49+
"github_version": "main",
50+
"doc_path": "docs",
51+
}

0 commit comments

Comments
 (0)