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.
Files changed (124) hide show
  1. remote_store-0.4.0/.github/workflows/ci.yml +79 -0
  2. remote_store-0.4.0/.github/workflows/docs.yml +33 -0
  3. remote_store-0.4.0/.github/workflows/publish.yml +36 -0
  4. remote_store-0.4.0/.gitignore +24 -0
  5. remote_store-0.4.0/.pre-commit-config.yaml +14 -0
  6. remote_store-0.4.0/.readthedocs.yaml +16 -0
  7. remote_store-0.4.0/CHANGELOG.md +90 -0
  8. remote_store-0.4.0/CITATION.cff +22 -0
  9. remote_store-0.4.0/CONTRIBUTING.md +115 -0
  10. remote_store-0.4.0/DEVELOPMENT_STORY.md +294 -0
  11. remote_store-0.4.0/LICENSE +21 -0
  12. remote_store-0.4.0/PKG-INFO +232 -0
  13. remote_store-0.4.0/README.md +157 -0
  14. remote_store-0.4.0/assets/logo.png +0 -0
  15. remote_store-0.4.0/docs/api/backend.md +5 -0
  16. remote_store-0.4.0/docs/api/capabilities.md +9 -0
  17. remote_store-0.4.0/docs/api/config.md +13 -0
  18. remote_store-0.4.0/docs/api/errors.md +29 -0
  19. remote_store-0.4.0/docs/api/index.md +54 -0
  20. remote_store-0.4.0/docs/api/models.md +17 -0
  21. remote_store-0.4.0/docs/api/path.md +5 -0
  22. remote_store-0.4.0/docs/api/registry.md +11 -0
  23. remote_store-0.4.0/docs/api/store.md +5 -0
  24. remote_store-0.4.0/docs/assets/logo.png +0 -0
  25. remote_store-0.4.0/docs/backends/index.md +27 -0
  26. remote_store-0.4.0/docs/backends/local.md +28 -0
  27. remote_store-0.4.0/docs/backends/s3.md +47 -0
  28. remote_store-0.4.0/docs/backends/sftp.md +146 -0
  29. remote_store-0.4.0/docs/changelog.md +3 -0
  30. remote_store-0.4.0/docs/contributing.md +3 -0
  31. remote_store-0.4.0/docs/design/adrs/0001-architecture-store-registry-backends.md +4 -0
  32. remote_store-0.4.0/docs/design/adrs/0002-config-resolution-no-merge.md +4 -0
  33. remote_store-0.4.0/docs/design/adrs/0003-fsspec-is-implementation-detail.md +4 -0
  34. remote_store-0.4.0/docs/design/adrs/0004-empty-path-semantics.md +4 -0
  35. remote_store-0.4.0/docs/design/adrs/index.md +10 -0
  36. remote_store-0.4.0/docs/design/design-spec.md +3 -0
  37. remote_store-0.4.0/docs/design/index.md +28 -0
  38. remote_store-0.4.0/docs/design/process.md +3 -0
  39. remote_store-0.4.0/docs/design/specs/001-store-api.md +4 -0
  40. remote_store-0.4.0/docs/design/specs/002-registry-config.md +4 -0
  41. remote_store-0.4.0/docs/design/specs/003-backend-adapter-contract.md +4 -0
  42. remote_store-0.4.0/docs/design/specs/004-path-model.md +4 -0
  43. remote_store-0.4.0/docs/design/specs/005-error-model.md +4 -0
  44. remote_store-0.4.0/docs/design/specs/006-streaming-io.md +4 -0
  45. remote_store-0.4.0/docs/design/specs/007-atomic-writes.md +4 -0
  46. remote_store-0.4.0/docs/design/specs/008-s3-backend.md +4 -0
  47. remote_store-0.4.0/docs/design/specs/009-sftp-backend.md +4 -0
  48. remote_store-0.4.0/docs/design/specs/index.md +14 -0
  49. remote_store-0.4.0/docs/development-story.md +3 -0
  50. remote_store-0.4.0/docs/examples/atomic-writes.md +7 -0
  51. remote_store-0.4.0/docs/examples/configuration.md +7 -0
  52. remote_store-0.4.0/docs/examples/error-handling.md +7 -0
  53. remote_store-0.4.0/docs/examples/file-operations.md +7 -0
  54. remote_store-0.4.0/docs/examples/index.md +16 -0
  55. remote_store-0.4.0/docs/examples/quickstart.md +7 -0
  56. remote_store-0.4.0/docs/examples/streaming-io.md +7 -0
  57. remote_store-0.4.0/docs/getting-started.md +87 -0
  58. remote_store-0.4.0/docs/index.md +60 -0
  59. remote_store-0.4.0/examples/atomic_writes.py +50 -0
  60. remote_store-0.4.0/examples/configuration.py +70 -0
  61. remote_store-0.4.0/examples/error_handling.py +71 -0
  62. remote_store-0.4.0/examples/file_operations.py +66 -0
  63. remote_store-0.4.0/examples/notebooks/01_getting_started.ipynb +166 -0
  64. remote_store-0.4.0/examples/notebooks/02_file_operations.ipynb +184 -0
  65. remote_store-0.4.0/examples/notebooks/03_configuration.ipynb +165 -0
  66. remote_store-0.4.0/examples/quickstart.py +38 -0
  67. remote_store-0.4.0/examples/streaming_io.py +56 -0
  68. remote_store-0.4.0/mkdocs.yml +117 -0
  69. remote_store-0.4.0/pyproject.toml +141 -0
  70. remote_store-0.4.0/sdd/000-process.md +186 -0
  71. remote_store-0.4.0/sdd/BACKLOG.md +164 -0
  72. remote_store-0.4.0/sdd/DESIGN.md +506 -0
  73. remote_store-0.4.0/sdd/adrs/0001-architecture-store-registry-backends.md +38 -0
  74. remote_store-0.4.0/sdd/adrs/0002-config-resolution-no-merge.md +32 -0
  75. remote_store-0.4.0/sdd/adrs/0003-fsspec-is-implementation-detail.md +34 -0
  76. remote_store-0.4.0/sdd/adrs/0004-empty-path-semantics.md +42 -0
  77. remote_store-0.4.0/sdd/adrs/0005-native-path-resolution.md +98 -0
  78. remote_store-0.4.0/sdd/rfcs/rfc-template.md +43 -0
  79. remote_store-0.4.0/sdd/specs/001-store-api.md +94 -0
  80. remote_store-0.4.0/sdd/specs/002-registry-config.md +86 -0
  81. remote_store-0.4.0/sdd/specs/003-backend-adapter-contract.md +159 -0
  82. remote_store-0.4.0/sdd/specs/004-path-model.md +122 -0
  83. remote_store-0.4.0/sdd/specs/005-error-model.md +87 -0
  84. remote_store-0.4.0/sdd/specs/006-streaming-io.md +40 -0
  85. remote_store-0.4.0/sdd/specs/007-atomic-writes.md +40 -0
  86. remote_store-0.4.0/sdd/specs/008-s3-backend.md +176 -0
  87. remote_store-0.4.0/sdd/specs/009-sftp-backend.md +235 -0
  88. remote_store-0.4.0/sdd/specs/010-native-path-resolution.md +248 -0
  89. remote_store-0.4.0/sdd/specs/011-s3-pyarrow-backend.md +167 -0
  90. remote_store-0.4.0/src/remote_store/__init__.py +51 -0
  91. remote_store-0.4.0/src/remote_store/_backend.py +160 -0
  92. remote_store-0.4.0/src/remote_store/_capabilities.py +75 -0
  93. remote_store-0.4.0/src/remote_store/_config.py +90 -0
  94. remote_store-0.4.0/src/remote_store/_errors.py +91 -0
  95. remote_store-0.4.0/src/remote_store/_models.py +103 -0
  96. remote_store-0.4.0/src/remote_store/_path.py +96 -0
  97. remote_store-0.4.0/src/remote_store/_registry.py +108 -0
  98. remote_store-0.4.0/src/remote_store/_store.py +249 -0
  99. remote_store-0.4.0/src/remote_store/_types.py +10 -0
  100. remote_store-0.4.0/src/remote_store/backends/__init__.py +26 -0
  101. remote_store-0.4.0/src/remote_store/backends/_local.py +282 -0
  102. remote_store-0.4.0/src/remote_store/backends/_s3.py +369 -0
  103. remote_store-0.4.0/src/remote_store/backends/_s3_pyarrow.py +447 -0
  104. remote_store-0.4.0/src/remote_store/backends/_sftp.py +753 -0
  105. remote_store-0.4.0/src/remote_store/ext/__init__.py +1 -0
  106. remote_store-0.4.0/src/remote_store/py.typed +0 -0
  107. remote_store-0.4.0/tests/__init__.py +0 -0
  108. remote_store-0.4.0/tests/backends/__init__.py +0 -0
  109. remote_store-0.4.0/tests/backends/conftest.py +195 -0
  110. remote_store-0.4.0/tests/backends/sftp_server.py +274 -0
  111. remote_store-0.4.0/tests/backends/test_conformance.py +362 -0
  112. remote_store-0.4.0/tests/backends/test_local.py +42 -0
  113. remote_store-0.4.0/tests/backends/test_s3.py +509 -0
  114. remote_store-0.4.0/tests/backends/test_s3_pyarrow.py +513 -0
  115. remote_store-0.4.0/tests/backends/test_sftp.py +583 -0
  116. remote_store-0.4.0/tests/conftest.py +12 -0
  117. remote_store-0.4.0/tests/test_capabilities.py +100 -0
  118. remote_store-0.4.0/tests/test_config.py +119 -0
  119. remote_store-0.4.0/tests/test_coverage_gaps.py +586 -0
  120. remote_store-0.4.0/tests/test_errors.py +153 -0
  121. remote_store-0.4.0/tests/test_models.py +146 -0
  122. remote_store-0.4.0/tests/test_path.py +162 -0
  123. remote_store-0.4.0/tests/test_registry.py +109 -0
  124. 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,16 @@
1
+ version: 2
2
+
3
+ build:
4
+ os: ubuntu-22.04
5
+ tools:
6
+ python: "3.12"
7
+
8
+ mkdocs:
9
+ configuration: mkdocs.yml
10
+
11
+ python:
12
+ install:
13
+ - method: pip
14
+ path: .
15
+ extra_requirements:
16
+ - docs
@@ -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__`.