remote-store 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- remote_store-0.4.0/.github/workflows/ci.yml +79 -0
- remote_store-0.4.0/.github/workflows/docs.yml +33 -0
- remote_store-0.4.0/.github/workflows/publish.yml +36 -0
- remote_store-0.4.0/.gitignore +24 -0
- remote_store-0.4.0/.pre-commit-config.yaml +14 -0
- remote_store-0.4.0/.readthedocs.yaml +16 -0
- remote_store-0.4.0/CHANGELOG.md +90 -0
- remote_store-0.4.0/CITATION.cff +22 -0
- remote_store-0.4.0/CONTRIBUTING.md +115 -0
- remote_store-0.4.0/DEVELOPMENT_STORY.md +294 -0
- remote_store-0.4.0/LICENSE +21 -0
- remote_store-0.4.0/PKG-INFO +232 -0
- remote_store-0.4.0/README.md +157 -0
- remote_store-0.4.0/assets/logo.png +0 -0
- remote_store-0.4.0/docs/api/backend.md +5 -0
- remote_store-0.4.0/docs/api/capabilities.md +9 -0
- remote_store-0.4.0/docs/api/config.md +13 -0
- remote_store-0.4.0/docs/api/errors.md +29 -0
- remote_store-0.4.0/docs/api/index.md +54 -0
- remote_store-0.4.0/docs/api/models.md +17 -0
- remote_store-0.4.0/docs/api/path.md +5 -0
- remote_store-0.4.0/docs/api/registry.md +11 -0
- remote_store-0.4.0/docs/api/store.md +5 -0
- remote_store-0.4.0/docs/assets/logo.png +0 -0
- remote_store-0.4.0/docs/backends/index.md +27 -0
- remote_store-0.4.0/docs/backends/local.md +28 -0
- remote_store-0.4.0/docs/backends/s3.md +47 -0
- remote_store-0.4.0/docs/backends/sftp.md +146 -0
- remote_store-0.4.0/docs/changelog.md +3 -0
- remote_store-0.4.0/docs/contributing.md +3 -0
- remote_store-0.4.0/docs/design/adrs/0001-architecture-store-registry-backends.md +4 -0
- remote_store-0.4.0/docs/design/adrs/0002-config-resolution-no-merge.md +4 -0
- remote_store-0.4.0/docs/design/adrs/0003-fsspec-is-implementation-detail.md +4 -0
- remote_store-0.4.0/docs/design/adrs/0004-empty-path-semantics.md +4 -0
- remote_store-0.4.0/docs/design/adrs/index.md +10 -0
- remote_store-0.4.0/docs/design/design-spec.md +3 -0
- remote_store-0.4.0/docs/design/index.md +28 -0
- remote_store-0.4.0/docs/design/process.md +3 -0
- remote_store-0.4.0/docs/design/specs/001-store-api.md +4 -0
- remote_store-0.4.0/docs/design/specs/002-registry-config.md +4 -0
- remote_store-0.4.0/docs/design/specs/003-backend-adapter-contract.md +4 -0
- remote_store-0.4.0/docs/design/specs/004-path-model.md +4 -0
- remote_store-0.4.0/docs/design/specs/005-error-model.md +4 -0
- remote_store-0.4.0/docs/design/specs/006-streaming-io.md +4 -0
- remote_store-0.4.0/docs/design/specs/007-atomic-writes.md +4 -0
- remote_store-0.4.0/docs/design/specs/008-s3-backend.md +4 -0
- remote_store-0.4.0/docs/design/specs/009-sftp-backend.md +4 -0
- remote_store-0.4.0/docs/design/specs/index.md +14 -0
- remote_store-0.4.0/docs/development-story.md +3 -0
- remote_store-0.4.0/docs/examples/atomic-writes.md +7 -0
- remote_store-0.4.0/docs/examples/configuration.md +7 -0
- remote_store-0.4.0/docs/examples/error-handling.md +7 -0
- remote_store-0.4.0/docs/examples/file-operations.md +7 -0
- remote_store-0.4.0/docs/examples/index.md +16 -0
- remote_store-0.4.0/docs/examples/quickstart.md +7 -0
- remote_store-0.4.0/docs/examples/streaming-io.md +7 -0
- remote_store-0.4.0/docs/getting-started.md +87 -0
- remote_store-0.4.0/docs/index.md +60 -0
- remote_store-0.4.0/examples/atomic_writes.py +50 -0
- remote_store-0.4.0/examples/configuration.py +70 -0
- remote_store-0.4.0/examples/error_handling.py +71 -0
- remote_store-0.4.0/examples/file_operations.py +66 -0
- remote_store-0.4.0/examples/notebooks/01_getting_started.ipynb +166 -0
- remote_store-0.4.0/examples/notebooks/02_file_operations.ipynb +184 -0
- remote_store-0.4.0/examples/notebooks/03_configuration.ipynb +165 -0
- remote_store-0.4.0/examples/quickstart.py +38 -0
- remote_store-0.4.0/examples/streaming_io.py +56 -0
- remote_store-0.4.0/mkdocs.yml +117 -0
- remote_store-0.4.0/pyproject.toml +141 -0
- remote_store-0.4.0/sdd/000-process.md +186 -0
- remote_store-0.4.0/sdd/BACKLOG.md +164 -0
- remote_store-0.4.0/sdd/DESIGN.md +506 -0
- remote_store-0.4.0/sdd/adrs/0001-architecture-store-registry-backends.md +38 -0
- remote_store-0.4.0/sdd/adrs/0002-config-resolution-no-merge.md +32 -0
- remote_store-0.4.0/sdd/adrs/0003-fsspec-is-implementation-detail.md +34 -0
- remote_store-0.4.0/sdd/adrs/0004-empty-path-semantics.md +42 -0
- remote_store-0.4.0/sdd/adrs/0005-native-path-resolution.md +98 -0
- remote_store-0.4.0/sdd/rfcs/rfc-template.md +43 -0
- remote_store-0.4.0/sdd/specs/001-store-api.md +94 -0
- remote_store-0.4.0/sdd/specs/002-registry-config.md +86 -0
- remote_store-0.4.0/sdd/specs/003-backend-adapter-contract.md +159 -0
- remote_store-0.4.0/sdd/specs/004-path-model.md +122 -0
- remote_store-0.4.0/sdd/specs/005-error-model.md +87 -0
- remote_store-0.4.0/sdd/specs/006-streaming-io.md +40 -0
- remote_store-0.4.0/sdd/specs/007-atomic-writes.md +40 -0
- remote_store-0.4.0/sdd/specs/008-s3-backend.md +176 -0
- remote_store-0.4.0/sdd/specs/009-sftp-backend.md +235 -0
- remote_store-0.4.0/sdd/specs/010-native-path-resolution.md +248 -0
- remote_store-0.4.0/sdd/specs/011-s3-pyarrow-backend.md +167 -0
- remote_store-0.4.0/src/remote_store/__init__.py +51 -0
- remote_store-0.4.0/src/remote_store/_backend.py +160 -0
- remote_store-0.4.0/src/remote_store/_capabilities.py +75 -0
- remote_store-0.4.0/src/remote_store/_config.py +90 -0
- remote_store-0.4.0/src/remote_store/_errors.py +91 -0
- remote_store-0.4.0/src/remote_store/_models.py +103 -0
- remote_store-0.4.0/src/remote_store/_path.py +96 -0
- remote_store-0.4.0/src/remote_store/_registry.py +108 -0
- remote_store-0.4.0/src/remote_store/_store.py +249 -0
- remote_store-0.4.0/src/remote_store/_types.py +10 -0
- remote_store-0.4.0/src/remote_store/backends/__init__.py +26 -0
- remote_store-0.4.0/src/remote_store/backends/_local.py +282 -0
- remote_store-0.4.0/src/remote_store/backends/_s3.py +369 -0
- remote_store-0.4.0/src/remote_store/backends/_s3_pyarrow.py +447 -0
- remote_store-0.4.0/src/remote_store/backends/_sftp.py +753 -0
- remote_store-0.4.0/src/remote_store/ext/__init__.py +1 -0
- remote_store-0.4.0/src/remote_store/py.typed +0 -0
- remote_store-0.4.0/tests/__init__.py +0 -0
- remote_store-0.4.0/tests/backends/__init__.py +0 -0
- remote_store-0.4.0/tests/backends/conftest.py +195 -0
- remote_store-0.4.0/tests/backends/sftp_server.py +274 -0
- remote_store-0.4.0/tests/backends/test_conformance.py +362 -0
- remote_store-0.4.0/tests/backends/test_local.py +42 -0
- remote_store-0.4.0/tests/backends/test_s3.py +509 -0
- remote_store-0.4.0/tests/backends/test_s3_pyarrow.py +513 -0
- remote_store-0.4.0/tests/backends/test_sftp.py +583 -0
- remote_store-0.4.0/tests/conftest.py +12 -0
- remote_store-0.4.0/tests/test_capabilities.py +100 -0
- remote_store-0.4.0/tests/test_config.py +119 -0
- remote_store-0.4.0/tests/test_coverage_gaps.py +586 -0
- remote_store-0.4.0/tests/test_errors.py +153 -0
- remote_store-0.4.0/tests/test_models.py +146 -0
- remote_store-0.4.0/tests/test_path.py +162 -0
- remote_store-0.4.0/tests/test_registry.py +109 -0
- remote_store-0.4.0/tests/test_store.py +258 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.13"
|
|
17
|
+
- run: pip install -e ".[dev]"
|
|
18
|
+
- run: ruff check src/ tests/ examples/
|
|
19
|
+
- run: ruff format --check src/ tests/ examples/
|
|
20
|
+
|
|
21
|
+
typecheck:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.13"
|
|
28
|
+
- run: pip install -e ".[dev]"
|
|
29
|
+
- run: mypy src/
|
|
30
|
+
|
|
31
|
+
test:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
strategy:
|
|
34
|
+
matrix:
|
|
35
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v4
|
|
38
|
+
- uses: actions/setup-python@v5
|
|
39
|
+
with:
|
|
40
|
+
python-version: ${{ matrix.python-version }}
|
|
41
|
+
- run: pip install -e ".[dev]"
|
|
42
|
+
- run: pytest --cov=remote_store --cov-report=term-missing --cov-fail-under=95
|
|
43
|
+
|
|
44
|
+
examples:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v4
|
|
48
|
+
- uses: actions/setup-python@v5
|
|
49
|
+
with:
|
|
50
|
+
python-version: "3.13"
|
|
51
|
+
- run: pip install -e ".[dev]"
|
|
52
|
+
- run: python examples/quickstart.py
|
|
53
|
+
- run: python examples/file_operations.py
|
|
54
|
+
- run: python examples/streaming_io.py
|
|
55
|
+
- run: python examples/atomic_writes.py
|
|
56
|
+
- run: python examples/configuration.py
|
|
57
|
+
- run: python examples/error_handling.py
|
|
58
|
+
|
|
59
|
+
docs:
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@v4
|
|
63
|
+
- uses: actions/setup-python@v5
|
|
64
|
+
with:
|
|
65
|
+
python-version: "3.13"
|
|
66
|
+
- run: pip install -e ".[docs]"
|
|
67
|
+
- run: mkdocs build --strict
|
|
68
|
+
|
|
69
|
+
package:
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/checkout@v4
|
|
73
|
+
- uses: actions/setup-python@v5
|
|
74
|
+
with:
|
|
75
|
+
python-version: "3.13"
|
|
76
|
+
- run: pip install build
|
|
77
|
+
- run: python -m build
|
|
78
|
+
- run: pip install dist/*.whl
|
|
79
|
+
- run: python -c "import remote_store; print(remote_store.__version__)"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
pages: write
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: pages
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
deploy:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
environment:
|
|
20
|
+
name: github-pages
|
|
21
|
+
url: ${{ steps.deploy.outputs.page_url }}
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: "3.13"
|
|
27
|
+
- run: pip install -e ".[docs]"
|
|
28
|
+
- run: mkdocs build --strict
|
|
29
|
+
- uses: actions/upload-pages-artifact@v3
|
|
30
|
+
with:
|
|
31
|
+
path: site/
|
|
32
|
+
- id: deploy
|
|
33
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.13"
|
|
16
|
+
- run: pip install build
|
|
17
|
+
- run: python -m build
|
|
18
|
+
- uses: actions/upload-artifact@v4
|
|
19
|
+
with:
|
|
20
|
+
name: dist
|
|
21
|
+
path: dist/
|
|
22
|
+
|
|
23
|
+
publish:
|
|
24
|
+
needs: build
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
environment: pypi
|
|
27
|
+
permissions:
|
|
28
|
+
id-token: write
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/download-artifact@v4
|
|
31
|
+
with:
|
|
32
|
+
name: dist
|
|
33
|
+
path: dist/
|
|
34
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
35
|
+
with:
|
|
36
|
+
packages-dir: dist/
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# IDEs & tools
|
|
2
|
+
.venv/
|
|
3
|
+
.vscode/
|
|
4
|
+
.claude/
|
|
5
|
+
|
|
6
|
+
# Caching
|
|
7
|
+
__pycache__/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.mypy_cache/
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
|
|
12
|
+
# Building
|
|
13
|
+
dist/
|
|
14
|
+
build/
|
|
15
|
+
*.egg-info/
|
|
16
|
+
*.whl
|
|
17
|
+
*.tar.gz
|
|
18
|
+
|
|
19
|
+
# Testing & Coverage
|
|
20
|
+
.coverage
|
|
21
|
+
htmlcov/
|
|
22
|
+
|
|
23
|
+
# Documentation
|
|
24
|
+
site/
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.11.2
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
9
|
+
rev: v1.15.0
|
|
10
|
+
hooks:
|
|
11
|
+
- id: mypy
|
|
12
|
+
additional_dependencies: [s3fs, paramiko, tenacity, types-paramiko, pyarrow]
|
|
13
|
+
args: [--config-file=pyproject.toml]
|
|
14
|
+
files: ^src/
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
This project follows [Semantic Versioning](https://semver.org/). Pre-1.0, minor bumps may contain breaking changes.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## [0.4.0] - 2026-02-19
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **S3-PyArrow hybrid backend** -- uses PyArrow's C++ S3 filesystem for reads/writes/copies (higher throughput for large files) and s3fs for listing/metadata/deletion. Drop-in alternative to `S3Backend` with the same constructor signature.
|
|
16
|
+
- Install via `pip install remote-store[s3-pyarrow]`
|
|
17
|
+
- Spec: `sdd/specs/011-s3-pyarrow-backend.md`
|
|
18
|
+
- New optional extra: `s3-pyarrow` (requires `s3fs>=2024.2.0` and `pyarrow>=14.0.0`)
|
|
19
|
+
- Dual `unwrap()` support: returns either `pyarrow.fs.S3FileSystem` or `s3fs.S3FileSystem`
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## [0.3.0] - 2026-02-18
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **`Store.to_key(path)`** — public method to convert backend-native paths to store-relative keys
|
|
28
|
+
- **`Backend.to_key()`** — backend-level native-path-to-key conversion
|
|
29
|
+
- Python 3.14 support — added to CI test matrix and PyPI classifiers
|
|
30
|
+
- **PyPI publish workflow** — trusted publishing (OIDC) via GitHub Actions on `v*` tags (BL-001)
|
|
31
|
+
- **SFTP backend documentation** — `docs/backends/sftp.md` with installation, usage, and API reference (BL-002)
|
|
32
|
+
- **CITATION.cff** — enables GitHub's "Cite this repository" button (BL-005)
|
|
33
|
+
- **Development backlog** — `sdd/BACKLOG.md` for tracking release blockers, prioritized work, and ideas
|
|
34
|
+
- Versioning policy added to SDD process doc (`sdd/000-process.md`)
|
|
35
|
+
- Set up GitHub Pages docs hosting via `actions/deploy-pages` (BL-008)
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- Store round-trip bug: `list()` returned backend-relative paths that included `root_path`, breaking re-use as input to `read()`/`delete()`
|
|
40
|
+
- CI: fixed cross-platform `type: ignore` comments for S3 backend
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- **README rewritten** — approachable, dev-friendly tone with scannable layout (BL-003, BL-004)
|
|
45
|
+
- Pinned minimum versions on public extras: `s3fs>=2024.2.0`, `paramiko>=2.2`, `tenacity>=4.0`
|
|
46
|
+
- Removed `typing-extensions` from core dependencies (unused -- Python 3.10+ covers all needs)
|
|
47
|
+
- Removed `azure` extra (`adlfs`) -- no Azure backend exists yet; will be re-added with the backend
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## [0.2.0] - 2026-02-17
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- **SFTP backend** via pure paramiko with host key policies (STRICT / TOFU / AUTO_ADD), PEM key sanitization, and tenacity retry on transient SSH errors
|
|
56
|
+
- Simulated atomic writes (temp file + rename) with documented orphan-file caveat
|
|
57
|
+
- `HostKeyPolicy` enum and `load_private_key()` utility for key management
|
|
58
|
+
- `_sanitize_pem()` for Azure Key Vault PEM compatibility
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
|
|
62
|
+
- `sftp` optional dependency changed from `paramiko + sshfs` to `paramiko + tenacity`
|
|
63
|
+
- Version bumped to 0.2.0
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## [0.1.0] - 2026-02-14
|
|
68
|
+
|
|
69
|
+
### Added
|
|
70
|
+
|
|
71
|
+
- **Store** — primary user-facing abstraction for folder-scoped file operations
|
|
72
|
+
- **Registry** — backend lifecycle management with lazy instantiation and context manager support
|
|
73
|
+
- **RegistryConfig / BackendConfig / StoreProfile** — declarative, immutable configuration with `from_dict()` for TOML/JSON parsing
|
|
74
|
+
- **RemotePath** — immutable, validated path value object with normalization and safety checks
|
|
75
|
+
- **Local backend** — stdlib-only reference implementation with full capability support
|
|
76
|
+
- **Capability system** — backends declare supported features; unsupported operations fail explicitly
|
|
77
|
+
- **Normalized error hierarchy** — `NotFound`, `AlreadyExists`, `InvalidPath`, `PermissionDenied`, `CapabilityNotSupported`, `BackendUnavailable`
|
|
78
|
+
- **Streaming-first I/O** — `read()` returns `BinaryIO`, `write()` accepts `bytes | BinaryIO`
|
|
79
|
+
- **Atomic writes** — `write_atomic()` via temp-file-and-rename
|
|
80
|
+
- **Empty path support** — `""` resolves to store root for folder/query operations (see ADR-0004)
|
|
81
|
+
- **Full type safety** — mypy strict mode, `py.typed` marker
|
|
82
|
+
- **Spec-driven development** — 7 specifications, 4 ADRs, full test traceability with `@pytest.mark.spec`
|
|
83
|
+
- **Examples** — 6 runnable Python scripts and 3 Jupyter notebooks
|
|
84
|
+
- **CI** — ruff, mypy, pytest (Python 3.10–3.13), example validation
|
|
85
|
+
|
|
86
|
+
### Known Limitations
|
|
87
|
+
|
|
88
|
+
- Only the local filesystem backend is implemented. S3, Azure, and SFTP backends are planned.
|
|
89
|
+
- No glob/pattern matching support yet (`Capability.GLOB` is declared but unused).
|
|
90
|
+
- No async API (sync-only by design; compatible with structured concurrency).
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it using the metadata below."
|
|
3
|
+
title: "remote-store"
|
|
4
|
+
abstract: >-
|
|
5
|
+
One simple API for file storage. Local, S3, SFTP, Azure.
|
|
6
|
+
Same methods, swappable backends, zero reinvention.
|
|
7
|
+
type: software
|
|
8
|
+
version: 0.4.0
|
|
9
|
+
date-released: 2026-02-19
|
|
10
|
+
license: MIT
|
|
11
|
+
repository-code: https://github.com/haalfi/remote-store
|
|
12
|
+
url: https://haalfi.github.io/remote-store/
|
|
13
|
+
authors:
|
|
14
|
+
- family-names: Alferi
|
|
15
|
+
given-names: Harald
|
|
16
|
+
keywords:
|
|
17
|
+
- file-storage
|
|
18
|
+
- object-storage
|
|
19
|
+
- storage-abstraction
|
|
20
|
+
- python
|
|
21
|
+
- s3
|
|
22
|
+
- sftp
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Contributing to Remote Store
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing! This project follows **Spec-Driven Development (SDD)**: every feature starts as a specification before any code is written. See [`sdd/000-process.md`](sdd/000-process.md) for the full methodology.
|
|
4
|
+
|
|
5
|
+
## Spec-First Workflow
|
|
6
|
+
|
|
7
|
+
1. **Propose** — Open a PR with an RFC in `sdd/rfcs/` (see [`rfc-template.md`](sdd/rfcs/rfc-template.md)). No code yet.
|
|
8
|
+
2. **Review** — Maintainers and community review for design fit and completeness.
|
|
9
|
+
3. **Accept** — The RFC graduates to a spec in `sdd/specs/`. It now defines the contract.
|
|
10
|
+
4. **Implement** — Open a follow-up PR with tests (referencing spec IDs) and implementation.
|
|
11
|
+
|
|
12
|
+
## Repository Structure
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
sdd/
|
|
16
|
+
000-process.md # How specs work in this repo
|
|
17
|
+
specs/ # Accepted specifications (source of truth)
|
|
18
|
+
001-store-api.md
|
|
19
|
+
002-registry-config.md
|
|
20
|
+
003-backend-adapter-contract.md
|
|
21
|
+
004-path-model.md
|
|
22
|
+
005-error-model.md
|
|
23
|
+
006-streaming-io.md
|
|
24
|
+
007-atomic-writes.md
|
|
25
|
+
adrs/ # Architecture Decision Records (immutable)
|
|
26
|
+
rfcs/ # Proposals under discussion
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Spec Format
|
|
30
|
+
|
|
31
|
+
Each spec uses numbered section IDs with a module prefix:
|
|
32
|
+
|
|
33
|
+
```markdown
|
|
34
|
+
## <PREFIX>-NNN: <Rule Title>
|
|
35
|
+
**Invariant:** <what must always be true>
|
|
36
|
+
**Preconditions:** <what the caller must ensure>
|
|
37
|
+
**Postconditions:** <what the callee guarantees>
|
|
38
|
+
**Raises:** <error conditions>
|
|
39
|
+
**Example:**
|
|
40
|
+
<short code example>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Prefixes: `STORE`, `MOD` (models), `CFG` (config), `REG` (registry), `BE` (backend), `CAP` (capabilities), `PATH`, `ERR`, `SIO` (streaming I/O), `AW` (atomic writes).
|
|
44
|
+
|
|
45
|
+
## Adding a New Backend
|
|
46
|
+
|
|
47
|
+
1. Write a spec in `sdd/specs/` or as an addendum in `sdd/specs/backends/<name>.md`
|
|
48
|
+
2. Implement `Backend` ABC in `src/remote_store/backends/_<name>.py`
|
|
49
|
+
3. Add a conformance fixture in `tests/backends/conftest.py`
|
|
50
|
+
4. The entire conformance suite (`tests/backends/test_conformance.py`) runs automatically
|
|
51
|
+
|
|
52
|
+
## Adding an Extension
|
|
53
|
+
|
|
54
|
+
1. Write an RFC in `sdd/rfcs/`, get it accepted as a spec
|
|
55
|
+
2. Implement in `src/remote_store/ext/<name>.py`
|
|
56
|
+
3. Add tests in `tests/ext/test_<name>.py`
|
|
57
|
+
4. Use `unwrap()` for native backend access when needed
|
|
58
|
+
|
|
59
|
+
## Third-Party Extensions
|
|
60
|
+
|
|
61
|
+
External packages should:
|
|
62
|
+
|
|
63
|
+
- Use naming convention: `remote-store-<name>`
|
|
64
|
+
- Use `register_backend()` for backend registration
|
|
65
|
+
- Use `unwrap()` for native handle access
|
|
66
|
+
- Reuse the conformance test suite by importing and parameterizing it
|
|
67
|
+
|
|
68
|
+
## Development Setup
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Clone and enter the repo
|
|
72
|
+
git clone https://github.com/haalfi/remote-store.git
|
|
73
|
+
cd remote-store
|
|
74
|
+
|
|
75
|
+
# Create a virtual environment
|
|
76
|
+
python -m venv .venv
|
|
77
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
78
|
+
|
|
79
|
+
# Install with dev dependencies
|
|
80
|
+
pip install -e ".[dev]"
|
|
81
|
+
|
|
82
|
+
# Verify everything works
|
|
83
|
+
hatch run all # or run individual steps:
|
|
84
|
+
hatch run lint
|
|
85
|
+
hatch run typecheck
|
|
86
|
+
hatch run test-cov
|
|
87
|
+
hatch run examples
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
All dev scripts are defined in `pyproject.toml` under `[tool.hatch.envs.default.scripts]`. Run `hatch run` to see available commands.
|
|
91
|
+
|
|
92
|
+
## Code Style
|
|
93
|
+
|
|
94
|
+
See [`sdd/DESIGN.md` Section 11](sdd/DESIGN.md#11-code-style) for the full code style conventions.
|
|
95
|
+
|
|
96
|
+
- **Formatter/linter:** ruff (line-length 120)
|
|
97
|
+
- **Type checking:** mypy strict mode
|
|
98
|
+
- **Tests:** pytest with `@pytest.mark.spec("ID")` markers for spec traceability
|
|
99
|
+
- **Coverage:** Target >= 95%
|
|
100
|
+
|
|
101
|
+
## Test Requirements
|
|
102
|
+
|
|
103
|
+
- Every spec section must have at least one test with `@pytest.mark.spec("ID")`
|
|
104
|
+
- Run `pytest -m spec` to verify all spec-derived tests pass
|
|
105
|
+
- Run `pytest --cov=remote_store` for coverage reports
|
|
106
|
+
|
|
107
|
+
## Examples and Notebooks
|
|
108
|
+
|
|
109
|
+
The `examples/` directory contains runnable Python scripts that are validated in CI. Example scripts must remain self-contained and use `tempfile.TemporaryDirectory` for cleanup.
|
|
110
|
+
|
|
111
|
+
Jupyter notebooks in `examples/notebooks/` are for interactive exploration and are **not** run in CI. They require manual testing when the API changes. This is intentional: notebooks depend on visual output and interactive workflows that don't translate well to automated checks.
|
|
112
|
+
|
|
113
|
+
## Versioning
|
|
114
|
+
|
|
115
|
+
This project follows [Semantic Versioning](https://semver.org/). Pre-1.0, minor bumps may contain breaking changes. The public API surface is everything in `remote_store.__init__.__all__`.
|