camusdb-py 0.1.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 (38) hide show
  1. camusdb_py-0.1.0/.github/dependabot.yml +11 -0
  2. camusdb_py-0.1.0/.github/scripts/wait-for-camusdb.sh +39 -0
  3. camusdb_py-0.1.0/.github/workflows/ci.yml +229 -0
  4. camusdb_py-0.1.0/.github/workflows/release.yml +67 -0
  5. camusdb_py-0.1.0/.gitignore +12 -0
  6. camusdb_py-0.1.0/LICENSE +21 -0
  7. camusdb_py-0.1.0/PKG-INFO +545 -0
  8. camusdb_py-0.1.0/README.md +519 -0
  9. camusdb_py-0.1.0/pyproject.toml +60 -0
  10. camusdb_py-0.1.0/src/camusdb/__init__.py +101 -0
  11. camusdb_py-0.1.0/src/camusdb/_auth.py +122 -0
  12. camusdb_py-0.1.0/src/camusdb/_base.py +300 -0
  13. camusdb_py-0.1.0/src/camusdb/_codec.py +412 -0
  14. camusdb_py-0.1.0/src/camusdb/_config.py +362 -0
  15. camusdb_py-0.1.0/src/camusdb/_endpoints.py +59 -0
  16. camusdb_py-0.1.0/src/camusdb/_protocol.py +335 -0
  17. camusdb_py-0.1.0/src/camusdb/_sql.py +122 -0
  18. camusdb_py-0.1.0/src/camusdb/_transport.py +217 -0
  19. camusdb_py-0.1.0/src/camusdb/aio/__init__.py +15 -0
  20. camusdb_py-0.1.0/src/camusdb/aio/_transport.py +189 -0
  21. camusdb_py-0.1.0/src/camusdb/aio/connection.py +596 -0
  22. camusdb_py-0.1.0/src/camusdb/codes.py +66 -0
  23. camusdb_py-0.1.0/src/camusdb/connection.py +632 -0
  24. camusdb_py-0.1.0/src/camusdb/errors.py +146 -0
  25. camusdb_py-0.1.0/src/camusdb/py.typed +0 -0
  26. camusdb_py-0.1.0/src/camusdb/retry.py +44 -0
  27. camusdb_py-0.1.0/src/camusdb/rows.py +41 -0
  28. camusdb_py-0.1.0/src/camusdb/types.py +209 -0
  29. camusdb_py-0.1.0/tests/conftest.py +72 -0
  30. camusdb_py-0.1.0/tests/test_codec.py +226 -0
  31. camusdb_py-0.1.0/tests/test_config.py +109 -0
  32. camusdb_py-0.1.0/tests/test_connection_mock.py +354 -0
  33. camusdb_py-0.1.0/tests/test_errors.py +79 -0
  34. camusdb_py-0.1.0/tests/test_integration.py +265 -0
  35. camusdb_py-0.1.0/tests/test_integration_auth.py +120 -0
  36. camusdb_py-0.1.0/tests/test_sql.py +56 -0
  37. camusdb_py-0.1.0/tests/test_version.py +8 -0
  38. camusdb_py-0.1.0/uv.lock +606 -0
@@ -0,0 +1,11 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+
8
+ - package-ecosystem: uv
9
+ directory: /
10
+ schedule:
11
+ interval: weekly
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env bash
2
+ # Wait until a CamusDB container answers /ping, and fail with its logs if it does not.
3
+ #
4
+ # Usage: wait-for-camusdb.sh <container> <base-url>
5
+ #
6
+ # Kestrel binds the port before startup finishes, so a server that fails during startup can answer
7
+ # /ping for a few seconds and then exit. Thus a ping is necessary but not sufficient: the script
8
+ # checks again, a moment later, that the container still runs.
9
+ set -euo pipefail
10
+
11
+ container="$1"
12
+ url="$2"
13
+
14
+ running() {
15
+ [ "$(docker inspect -f '{{.State.Running}}' "$container" 2>/dev/null)" = "true" ]
16
+ }
17
+
18
+ for i in $(seq 1 90); do
19
+ if ! running; then
20
+ echo "The CamusDB container exited during startup." >&2
21
+ docker logs "$container" || true
22
+ exit 1
23
+ fi
24
+ if curl --fail --silent --max-time 2 "$url/ping" >/dev/null 2>&1; then
25
+ sleep 3
26
+ if ! running; then
27
+ echo "CamusDB answered /ping and then exited: it failed during startup." >&2
28
+ docker logs "$container" || true
29
+ exit 1
30
+ fi
31
+ echo "CamusDB is up after ${i}s."
32
+ exit 0
33
+ fi
34
+ sleep 1
35
+ done
36
+
37
+ echo "CamusDB did not become ready in time." >&2
38
+ docker logs "$container" || true
39
+ exit 1
@@ -0,0 +1,229 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ # The release workflow runs this workflow first, and publishes the wheel that it builds.
9
+ workflow_call:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ concurrency:
15
+ group: ${{ github.workflow }}-${{ github.ref }}
16
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
17
+
18
+ env:
19
+ # The server the integration tests run against. 5095 is the REST API.
20
+ CAMUSDB_IMAGE: camusdb/camusdb:latest
21
+
22
+ jobs:
23
+ lint:
24
+ name: Lint & type check
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - uses: actions/checkout@v7
28
+
29
+ - uses: astral-sh/setup-uv@v7
30
+ with:
31
+ python-version: "3.13"
32
+ enable-cache: true
33
+
34
+ - name: Install
35
+ run: uv sync --locked
36
+
37
+ - name: Ruff
38
+ run: uv run ruff check --output-format github .
39
+
40
+ - name: Format
41
+ run: uv run ruff format --check .
42
+
43
+ - name: Mypy
44
+ run: uv run mypy
45
+
46
+ test:
47
+ name: Unit tests (Python ${{ matrix.python-version }})
48
+ runs-on: ubuntu-latest
49
+ strategy:
50
+ fail-fast: false
51
+ matrix:
52
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
53
+ steps:
54
+ - uses: actions/checkout@v7
55
+
56
+ - uses: astral-sh/setup-uv@v7
57
+ with:
58
+ python-version: ${{ matrix.python-version }}
59
+ enable-cache: true
60
+
61
+ - name: Install
62
+ run: uv sync --locked
63
+
64
+ - name: Test
65
+ # Without CAMUSDB_TEST_ENDPOINT the integration tests are skipped.
66
+ run: uv run pytest -ra
67
+
68
+ integration:
69
+ name: Integration tests (Python ${{ matrix.python-version }})
70
+ runs-on: ubuntu-latest
71
+ strategy:
72
+ fail-fast: false
73
+ matrix:
74
+ # The oldest and the newest supported versions.
75
+ python-version: ["3.10", "3.14"]
76
+ steps:
77
+ - uses: actions/checkout@v7
78
+
79
+ - uses: astral-sh/setup-uv@v7
80
+ with:
81
+ python-version: ${{ matrix.python-version }}
82
+ enable-cache: true
83
+
84
+ - name: Start CamusDB
85
+ run: docker run -d -p 5095:5095 -v camus-data:/data --name camusdb "$CAMUSDB_IMAGE"
86
+
87
+ - name: Wait for CamusDB
88
+ run: bash .github/scripts/wait-for-camusdb.sh camusdb http://localhost:5095
89
+
90
+ - name: Install
91
+ run: uv sync --locked
92
+
93
+ - name: Test
94
+ # Each run creates its own database and drops it at the end.
95
+ env:
96
+ CAMUSDB_TEST_ENDPOINT: http://localhost:5095
97
+ run: uv run pytest -ra tests/test_integration.py
98
+
99
+ - name: CamusDB logs
100
+ if: failure()
101
+ run: docker logs camusdb || true
102
+
103
+ - name: Stop CamusDB
104
+ if: always()
105
+ run: docker rm -f camusdb || true
106
+
107
+ integration-auth:
108
+ name: Integration tests (authentication)
109
+ runs-on: ubuntu-latest
110
+ steps:
111
+ - uses: actions/checkout@v7
112
+
113
+ - uses: astral-sh/setup-uv@v7
114
+ with:
115
+ python-version: "3.13"
116
+ enable-cache: true
117
+
118
+ - name: Generate bootstrap credentials
119
+ # The server has no default account: with authentication on, it needs a bootstrap secret.
120
+ run: |
121
+ password="$(openssl rand -base64 24)"
122
+ echo "::add-mask::$password"
123
+ echo "CAMUSDB_BOOTSTRAP_PASSWORD=$password" >> "$GITHUB_ENV"
124
+
125
+ - name: Start CamusDB with authentication on
126
+ # --network host, not -p: with authentication on, the server refuses credentials over clear
127
+ # text except from loopback. Under port mapping the server sees the Docker bridge as the
128
+ # client, so every login would fail with CADB0519. On the host network the client is 127.0.0.1.
129
+ run: |
130
+ docker run -d --network host -v camus-auth-data:/data --name camusdb-auth \
131
+ -e CAMUSDB_AUTH_ENABLED=true \
132
+ -e CAMUSDB_AUTH_TOKEN_KEY="$(openssl rand -hex 32)" \
133
+ -e CAMUSDB_NODE_SECRET="$(openssl rand -hex 32)" \
134
+ -e CAMUSDB_BOOTSTRAP_USER=admin \
135
+ -e CAMUSDB_BOOTSTRAP_PASSWORD="$CAMUSDB_BOOTSTRAP_PASSWORD" \
136
+ "$CAMUSDB_IMAGE"
137
+
138
+ - name: Wait for CamusDB
139
+ run: bash .github/scripts/wait-for-camusdb.sh camusdb-auth http://localhost:5095
140
+
141
+ - name: Check that the image supports authentication
142
+ # An image older than authentication ignores CAMUSDB_AUTH_* and has no /login route. The tests
143
+ # would then fail for a reason outside this repository, so the job skips them with a notice.
144
+ id: auth
145
+ run: |
146
+ status="$(curl --silent --max-time 10 --output /dev/null --write-out '%{http_code}' \
147
+ -X POST http://localhost:5095/login -H "Content-Type: application/json" \
148
+ -d '{"user":"nobody","password":"nobody"}')" || {
149
+ echo "The server did not answer /login." >&2
150
+ docker logs camusdb-auth || true
151
+ exit 1
152
+ }
153
+ if [ "$status" = "404" ]; then
154
+ echo "::notice::$CAMUSDB_IMAGE has no /login route; the authentication tests are skipped."
155
+ echo "supported=false" >> "$GITHUB_OUTPUT"
156
+ else
157
+ echo "supported=true" >> "$GITHUB_OUTPUT"
158
+ fi
159
+
160
+ - name: Wait for the bootstrap superuser
161
+ # The server creates the bootstrap user some seconds after it begins to answer /ping, and a
162
+ # login before that fails with CADB0516. The server permits 20 logins for each account each
163
+ # minute, so the probe tries at most 10 times, 3 s apart.
164
+ if: steps.auth.outputs.supported == 'true'
165
+ run: |
166
+ body="$(jq -n --arg p "$CAMUSDB_BOOTSTRAP_PASSWORD" '{user: "admin", password: $p}')"
167
+ for i in $(seq 1 10); do
168
+ status="$(curl --silent --max-time 10 --output /dev/null --write-out '%{http_code}' \
169
+ -X POST http://localhost:5095/login -H "Content-Type: application/json" -d "$body" || true)"
170
+ if [ "$status" = "200" ]; then
171
+ echo "The bootstrap superuser can log in (attempt $i)."
172
+ exit 0
173
+ fi
174
+ sleep 3
175
+ done
176
+ echo "The bootstrap superuser could not log in (last status: $status)." >&2
177
+ docker logs camusdb-auth || true
178
+ exit 1
179
+
180
+ - name: Install
181
+ if: steps.auth.outputs.supported == 'true'
182
+ run: uv sync --locked
183
+
184
+ - name: Test
185
+ if: steps.auth.outputs.supported == 'true'
186
+ env:
187
+ CAMUSDB_TEST_AUTH_ENDPOINT: http://localhost:5095
188
+ CAMUSDB_TEST_AUTH_USER: admin
189
+ CAMUSDB_TEST_AUTH_PASSWORD: ${{ env.CAMUSDB_BOOTSTRAP_PASSWORD }}
190
+ run: uv run pytest -ra tests/test_integration_auth.py
191
+
192
+ - name: CamusDB logs
193
+ if: failure()
194
+ run: docker logs camusdb-auth || true
195
+
196
+ - name: Stop CamusDB
197
+ if: always()
198
+ run: docker rm -f camusdb-auth || true
199
+
200
+ build:
201
+ name: Build distributions
202
+ runs-on: ubuntu-latest
203
+ needs: [lint, test]
204
+ steps:
205
+ - uses: actions/checkout@v7
206
+
207
+ - uses: astral-sh/setup-uv@v7
208
+ with:
209
+ python-version: "3.13"
210
+
211
+ - name: Build
212
+ run: uv build
213
+
214
+ - name: Check metadata
215
+ run: uvx twine check --strict dist/*
216
+
217
+ - name: Install the wheel and import it
218
+ # Outside the source tree, so the test imports the wheel and not src/.
219
+ run: |
220
+ cd "$RUNNER_TEMP"
221
+ uv venv wheel-check
222
+ uv pip install --python wheel-check "$GITHUB_WORKSPACE"/dist/*.whl
223
+ wheel-check/bin/python -c "import camusdb, camusdb.aio; print('camusdb', camusdb.__version__)"
224
+
225
+ - uses: actions/upload-artifact@v7
226
+ with:
227
+ name: dist
228
+ path: dist/
229
+ if-no-files-found: error
@@ -0,0 +1,67 @@
1
+ name: Release
2
+
3
+ # Push a tag such as v0.2.0 to publish that version to PyPI and to create a GitHub release.
4
+ on:
5
+ push:
6
+ tags: ["v*"]
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ ci:
13
+ name: CI
14
+ uses: ./.github/workflows/ci.yml
15
+
16
+ publish:
17
+ name: Publish to PyPI
18
+ runs-on: ubuntu-latest
19
+ needs: ci
20
+ environment:
21
+ name: pypi
22
+ url: https://pypi.org/project/camusdb-py/
23
+ permissions:
24
+ # Trusted publishing: PyPI accepts this workflow's OIDC token, so no API token is stored.
25
+ id-token: write
26
+ steps:
27
+ - uses: actions/checkout@v7
28
+
29
+ - uses: astral-sh/setup-uv@v7
30
+
31
+ - name: Check that the tag matches the package version
32
+ run: |
33
+ version="$(uv version --short)"
34
+ if [ "v$version" != "$GITHUB_REF_NAME" ]; then
35
+ echo "The tag $GITHUB_REF_NAME does not match the version $version in pyproject.toml." >&2
36
+ exit 1
37
+ fi
38
+
39
+ # The distributions that CI built and tested, not a second build.
40
+ - uses: actions/download-artifact@v8
41
+ with:
42
+ name: dist
43
+ path: dist/
44
+
45
+ - uses: pypa/gh-action-pypi-publish@release/v1
46
+
47
+ github-release:
48
+ name: GitHub release
49
+ runs-on: ubuntu-latest
50
+ needs: publish
51
+ permissions:
52
+ contents: write
53
+ steps:
54
+ - uses: actions/download-artifact@v8
55
+ with:
56
+ name: dist
57
+ path: dist/
58
+
59
+ - name: Create the release
60
+ env:
61
+ GH_TOKEN: ${{ github.token }}
62
+ run: |
63
+ gh release create "$GITHUB_REF_NAME" dist/* \
64
+ --repo "$GITHUB_REPOSITORY" \
65
+ --title "$GITHUB_REF_NAME" \
66
+ --generate-notes \
67
+ --verify-tag
@@ -0,0 +1,12 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .venv/
5
+ build/
6
+ dist/
7
+ .pytest_cache/
8
+ .mypy_cache/
9
+ .ruff_cache/
10
+ .coverage
11
+ htmlcov/
12
+ .DS_Store
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021-2026 Camus DB
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.