ducktide 0.0.1__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.
@@ -0,0 +1,126 @@
1
+ ### Python template
2
+ .idea
3
+ .venv
4
+ .ruff_cache
5
+ .ty_cache
6
+
7
+ # HTML outputs from docstring examples (e.g. report.save("output/..."))
8
+ output/
9
+
10
+ ### Don't expose API keys, etc.
11
+ .env
12
+
13
+ __marimo__
14
+
15
+ _tests
16
+ _book
17
+ _pdoc
18
+ docs/notebooks/*.html
19
+ docs/reports
20
+ docs/reports.md
21
+ _mkdocs
22
+ _benchmarks
23
+ _jupyter
24
+ _site
25
+
26
+ # LaTeX build artifacts
27
+ docs/paper/*.aux
28
+ docs/paper/*.fdb_latexmk
29
+ docs/paper/*.fls
30
+ docs/paper/*.log
31
+ docs/paper/*.out
32
+ docs/paper/*.toc
33
+ docs/paper/*.pdf
34
+ docs/paper/*.bbl
35
+ docs/paper/*.blg
36
+
37
+ # temp file used by Junie
38
+ .output.txt
39
+
40
+ # folder used for programs, e.g. uv, uvx, task, etc.
41
+ bin
42
+
43
+ # Byte-compiled / optimized / DLL files
44
+ __pycache__/
45
+ *.py[cod]
46
+ *$py.class
47
+
48
+ # Generated presentation files
49
+ presentation.html
50
+ presentation.pdf
51
+ *.pptx
52
+
53
+ # C extensions
54
+ *.so
55
+
56
+ # .DS_Store
57
+ .DS_Store
58
+
59
+ # Distribution / packaging
60
+ .Python
61
+ build/
62
+ develop-eggs/
63
+ dist/
64
+ downloads/
65
+ eggs/
66
+ .eggs/
67
+ lib/
68
+ lib64/
69
+ parts/
70
+ sdist/
71
+ var/
72
+ wheels/
73
+ share/python-wheels/
74
+ *.egg-info/
75
+ .installed.cfg
76
+ *.egg
77
+ MANIFEST
78
+
79
+ # Installer logs
80
+ pip-log.txt
81
+ pip-delete-this-directory.txt
82
+
83
+ # Unit test / coverage reports
84
+ htmlcov/
85
+ .tox/
86
+ .nox/
87
+ .coverage
88
+ .coverage.*
89
+ .cache
90
+ nosetests.xml
91
+ coverage.xml
92
+ coverage.json
93
+ *.cover
94
+ *.py,cover
95
+ .hypothesis/
96
+ .benchmarks/
97
+ .pytest_cache/
98
+ cover/
99
+
100
+ # Security scanning baselines (regenerate as needed)
101
+ .bandit-baseline.json
102
+
103
+ # Translations
104
+ *.mo
105
+ *.pot
106
+
107
+ # Cython debug symbols
108
+ cython_debug/
109
+
110
+ # Makefile -- `local.mk` is deliberately not ignored. The Makefile is template-owned and
111
+ # overwritten by every sync, so `local.mk` is the only place a repo's own make targets can
112
+ # live, and anything CI invokes has to be committed. rhiza's own `make e2e`, which
113
+ # rhiza_e2e.yml runs in all three language jobs, is exactly that case.
114
+ #
115
+ # `local-setup.sh` is not ignored either, and for the same reason one step further out: it
116
+ # is the hook every layer's `install` runs to provision a native binary the project needs
117
+ # (graphviz, libpq, pandoc), so CI invokes it on every job. A gitignored provisioning script
118
+ # is one that works on its author's machine and nowhere else.
119
+
120
+ .bandit-baseline.json
121
+
122
+ # Rust (rust-core bundle). Kept in core rather than the language layer because
123
+ # .gitignore has one owner and git opens it with O_NOFOLLOW, so it cannot be a
124
+ # per-layer file. These entries are inert in a Python repo.
125
+ target/
126
+ **/*.rs.bk
ducktide-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Thomas Schmelzer
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.
@@ -0,0 +1,283 @@
1
+ Metadata-Version: 2.5
2
+ Name: ducktide
3
+ Version: 0.0.1
4
+ Summary: Immutable Pydantic models and append-fast time series on DuckDB + Polars
5
+ Project-URL: Homepage, https://github.com/Jebel-Quant/ducktide
6
+ Project-URL: Repository, https://github.com/Jebel-Quant/ducktide
7
+ Project-URL: Issues, https://github.com/Jebel-Quant/ducktide/issues
8
+ Author: Thomas Schmelzer
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: duckdb,orm,polars,pydantic,repository-pattern,time-series
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Database
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: duckdb<2,>=1.5.5
22
+ Requires-Dist: polars<2,>=1.44.2
23
+ Requires-Dist: pyarrow>=25.0.0
24
+ Requires-Dist: pydantic<3,>=2.13.5
25
+ Description-Content-Type: text/markdown
26
+
27
+ # ducktide
28
+
29
+ [![Release](https://img.shields.io/github/v/release/Jebel-Quant/ducktide?sort=semver)](https://github.com/Jebel-Quant/ducktide/releases)
30
+
31
+ [![rhiza v1.8.0](https://img.shields.io/badge/rhiza-v1.8.0-blue)](https://github.com/jebel-quant/rhiza/releases/tag/v1.8.0)
32
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
33
+ [![Python versions](https://img.shields.io/badge/Python-3.11 • 3.12 • 3.13 • 3.14-blue?logo=python)](https://www.python.org/)
34
+ [![CI](https://github.com/Jebel-Quant/ducktide/actions/workflows/rhiza_ci.yml/badge.svg?event=push)](https://github.com/Jebel-Quant/ducktide/actions/workflows/rhiza_ci.yml)
35
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg?logo=ruff)](https://github.com/astral-sh/ruff)
36
+ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
37
+ [![CodeFactor](https://www.codefactor.io/repository/github/Jebel-Quant/ducktide/badge)](https://www.codefactor.io/repository/github/Jebel-Quant/ducktide)
38
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Jebel-Quant/ducktide/badge)](https://scorecard.dev/viewer/?uri=github.com/Jebel-Quant/ducktide)
39
+
40
+ Immutable Pydantic models and append-fast time series on DuckDB + Polars.
41
+
42
+ ducktide is a small persistence layer with two halves:
43
+
44
+ - **Entity tables**: frozen Pydantic models persisted through repositories
45
+ (`DB` + `Table`). Models carry no `save()`/`find()`/`delete()` methods;
46
+ all reads and writes go through the table, so domain objects stay plain values.
47
+ - **Time series**: `TimeSeriesDB`, an append-only store for high-volume
48
+ numerical data (prices, volumes, sensor readings). Ingestion only appends rows
49
+ newer than what is already stored, per instrument, so re-ingesting an
50
+ overlapping frame is safe.
51
+
52
+ Both run on DuckDB (in-memory or a single file) and hand data back as Polars
53
+ DataFrames. There is no SQLAlchemy and no server.
54
+
55
+ ## Install
56
+
57
+ ```bash
58
+ pip install ducktide
59
+ ```
60
+
61
+ Requires Python 3.11+.
62
+
63
+ ## Entity tables
64
+
65
+ Define a domain model and its table mapping:
66
+
67
+ ```python
68
+ import tempfile
69
+ from functools import partial
70
+ from pathlib import Path
71
+ from typing import ClassVar
72
+
73
+ from ducktide import DB, Table
74
+ from ducktide.orm import DomainModel, ORMModel
75
+
76
+
77
+ class Sensor(DomainModel):
78
+ table_name: ClassVar[str] = "sensor"
79
+
80
+ id: int
81
+ name: str
82
+ site: str
83
+
84
+
85
+ class SensorORM(ORMModel, Sensor):
86
+ _table_name: ClassVar[str] = "sensor"
87
+ _domain_model: ClassVar[type] = Sensor
88
+ _primary_key: ClassVar[str] = "id"
89
+ _schema: ClassVar[dict[str, str]] = {
90
+ "id": "INTEGER PRIMARY KEY",
91
+ "name": "TEXT NOT NULL",
92
+ "site": "TEXT NOT NULL",
93
+ }
94
+
95
+
96
+ db = DB(tables_map={"sensor": partial(Table, model_class=SensorORM)}) # or db_path="sensors.duckdb"
97
+
98
+ db.sensor.bulk_insert(
99
+ [
100
+ Sensor(id=1, name="north", site="berlin"),
101
+ Sensor(id=2, name="south", site="zurich"),
102
+ ]
103
+ )
104
+
105
+ db.sensor.select(site="berlin") # [SensorORM(id=1, name='north', site='berlin')]
106
+ db.sensor.get(2) # SensorORM(id=2, name='south', site='zurich')
107
+ db.sensor.to_frame() # polars.DataFrame
108
+ db.sensor.to_parquet(Path(tempfile.mkdtemp()) / "sensors.parquet")
109
+ ```
110
+
111
+ ## Time series
112
+
113
+ ```python
114
+ from datetime import date, datetime
115
+
116
+ import polars as pl
117
+
118
+ from ducktide import TimeSeriesDB
119
+
120
+ ts = TimeSeriesDB() # or TimeSeriesDB("prices.duckdb")
121
+
122
+ ts.ingest(
123
+ "prices",
124
+ pl.DataFrame(
125
+ {
126
+ "timestamp": [datetime(2025, 1, 1, 9, 0), datetime(2025, 1, 1, 9, 1)],
127
+ "instrument_id": [1, 1],
128
+ "close": [100.0, 100.5],
129
+ }
130
+ ),
131
+ )
132
+
133
+ ts.get_timeseries_frame("prices", instrument_id=1, start=date(2025, 1, 1))
134
+ ```
135
+
136
+ The table is created on first ingest. The timestamp column defaults to
137
+ `timestamp`; pass `TimeSeriesDB(time_col="ts")` to change it.
138
+
139
+ ## Default database context
140
+
141
+ For notebooks and tests you can scope a default database instead of passing it
142
+ around:
143
+
144
+ ```python
145
+ from ducktide.context import use_db
146
+
147
+ with use_db(db):
148
+ ... # code that calls ducktide.context.get_default_db()
149
+ ```
150
+
151
+ ## Development
152
+
153
+ ```bash
154
+ uv sync --group test
155
+ uv run pytest
156
+ ```
157
+
158
+ Run `make help` to see all available targets:
159
+
160
+ ```text
161
+ task section needs does
162
+ book Book test benchmark build the companion
163
+ stress book
164
+ hypothesis-test
165
+ paper
166
+ book-nav Book check that every
167
+ mkdocs nav entry
168
+ resolves in the
169
+ built book
170
+ marimo Book install start the Marimo
171
+ editor
172
+ marimo-validate Book install check that every
173
+ Marimo notebook runs
174
+ serve Book book build the book and
175
+ serve it on port
176
+ 8000
177
+ clean Dev remove build
178
+ artifacts and stale
179
+ local branches
180
+ doctor Dev check local
181
+ prerequisites
182
+ setup Dev run the repository's
183
+ own environment
184
+ setup hook
185
+ docker-build Docker build the Docker
186
+ image
187
+ docker-clean Docker remove the Docker
188
+ image
189
+ docker-run Docker docker-build run the Docker
190
+ container
191
+ lfs-install Git LFS configure git-lfs
192
+ for this repository
193
+ lfs-pull Git LFS download the LFS
194
+ files for the
195
+ current branch
196
+ lfs-status Git LFS show the status of
197
+ LFS files
198
+ lfs-track Git LFS list the patterns
199
+ tracked by git-lfs
200
+ failed-workflows GitHub Helpers list recent failing
201
+ workflow runs
202
+ latest-release GitHub Helpers show information
203
+ about the latest
204
+ GitHub release
205
+ view-issues GitHub Helpers list open issues
206
+ view-prs GitHub Helpers list open pull
207
+ requests
208
+ whoami GitHub Helpers check github auth
209
+ status
210
+ workflow-status GitHub Helpers show recent runs for
211
+ the release workflow
212
+ paper Paper compile the LaTeX
213
+ paper to PDF
214
+ paper-clean Paper remove the LaTeX
215
+ build artifacts
216
+ presentation Presentation generate the HTML
217
+ slides with Marp
218
+ presentation-pdf Presentation generate the PDF
219
+ slides with Marp
220
+ presentation-serve Presentation serve the slides
221
+ with Marp's live
222
+ preview
223
+ all Python fmt deps test run every gate, as
224
+ docs-coverage CI does
225
+ security license
226
+ typecheck rhiza-test
227
+ coverage Python install measure coverage and
228
+ write
229
+ _tests/coverage.xml
230
+ deps Python install run deptry over the
231
+ contributed folders
232
+ docs-coverage Python install check docstring
233
+ coverage with
234
+ interrogate
235
+ install Python setup create the venv and
236
+ sync dependencies
237
+ license Python install scan for copyleft
238
+ licences
239
+ security Python install run the bandit
240
+ security scan
241
+ test Python install run all tests
242
+ test-lowest Python install run the tests
243
+ against the oldest
244
+ dependencies the
245
+ manifest allows
246
+ typecheck Python install run ty and/or mypy
247
+ (typechecker = ty |
248
+ mypy | both)
249
+ docs-examples Quality install check the fenced
250
+ examples in the docs
251
+ tree
252
+ fmt Quality run the pre-commit
253
+ hooks over all files
254
+ complexity Quality fail on a block
255
+ above the
256
+ cyclomatic-complexi…
257
+ ceiling
258
+ test-pyproject Quality install run the
259
+ pyproject.toml
260
+ structure checks,
261
+ verbosely
262
+ rhiza-test Quality install run the rhiza
263
+ repository checks
264
+ semgrep Quality run the semgrep
265
+ static analysis
266
+ rules
267
+ todos Quality list every TODO,
268
+ FIXME and HACK
269
+ comment
270
+ update Template sync the rhiza
271
+ template into this
272
+ repository
273
+ benchmark Testing extras install run the performance
274
+ benchmarks
275
+ hypothesis-test Testing extras install run the
276
+ property-based tests
277
+ stress Testing extras install run the stress and
278
+ load tests
279
+ ```
280
+
281
+ ## License
282
+
283
+ MIT
@@ -0,0 +1,257 @@
1
+ # ducktide
2
+
3
+ [![Release](https://img.shields.io/github/v/release/Jebel-Quant/ducktide?sort=semver)](https://github.com/Jebel-Quant/ducktide/releases)
4
+
5
+ [![rhiza v1.8.0](https://img.shields.io/badge/rhiza-v1.8.0-blue)](https://github.com/jebel-quant/rhiza/releases/tag/v1.8.0)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
7
+ [![Python versions](https://img.shields.io/badge/Python-3.11 • 3.12 • 3.13 • 3.14-blue?logo=python)](https://www.python.org/)
8
+ [![CI](https://github.com/Jebel-Quant/ducktide/actions/workflows/rhiza_ci.yml/badge.svg?event=push)](https://github.com/Jebel-Quant/ducktide/actions/workflows/rhiza_ci.yml)
9
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg?logo=ruff)](https://github.com/astral-sh/ruff)
10
+ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
11
+ [![CodeFactor](https://www.codefactor.io/repository/github/Jebel-Quant/ducktide/badge)](https://www.codefactor.io/repository/github/Jebel-Quant/ducktide)
12
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Jebel-Quant/ducktide/badge)](https://scorecard.dev/viewer/?uri=github.com/Jebel-Quant/ducktide)
13
+
14
+ Immutable Pydantic models and append-fast time series on DuckDB + Polars.
15
+
16
+ ducktide is a small persistence layer with two halves:
17
+
18
+ - **Entity tables**: frozen Pydantic models persisted through repositories
19
+ (`DB` + `Table`). Models carry no `save()`/`find()`/`delete()` methods;
20
+ all reads and writes go through the table, so domain objects stay plain values.
21
+ - **Time series**: `TimeSeriesDB`, an append-only store for high-volume
22
+ numerical data (prices, volumes, sensor readings). Ingestion only appends rows
23
+ newer than what is already stored, per instrument, so re-ingesting an
24
+ overlapping frame is safe.
25
+
26
+ Both run on DuckDB (in-memory or a single file) and hand data back as Polars
27
+ DataFrames. There is no SQLAlchemy and no server.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ pip install ducktide
33
+ ```
34
+
35
+ Requires Python 3.11+.
36
+
37
+ ## Entity tables
38
+
39
+ Define a domain model and its table mapping:
40
+
41
+ ```python
42
+ import tempfile
43
+ from functools import partial
44
+ from pathlib import Path
45
+ from typing import ClassVar
46
+
47
+ from ducktide import DB, Table
48
+ from ducktide.orm import DomainModel, ORMModel
49
+
50
+
51
+ class Sensor(DomainModel):
52
+ table_name: ClassVar[str] = "sensor"
53
+
54
+ id: int
55
+ name: str
56
+ site: str
57
+
58
+
59
+ class SensorORM(ORMModel, Sensor):
60
+ _table_name: ClassVar[str] = "sensor"
61
+ _domain_model: ClassVar[type] = Sensor
62
+ _primary_key: ClassVar[str] = "id"
63
+ _schema: ClassVar[dict[str, str]] = {
64
+ "id": "INTEGER PRIMARY KEY",
65
+ "name": "TEXT NOT NULL",
66
+ "site": "TEXT NOT NULL",
67
+ }
68
+
69
+
70
+ db = DB(tables_map={"sensor": partial(Table, model_class=SensorORM)}) # or db_path="sensors.duckdb"
71
+
72
+ db.sensor.bulk_insert(
73
+ [
74
+ Sensor(id=1, name="north", site="berlin"),
75
+ Sensor(id=2, name="south", site="zurich"),
76
+ ]
77
+ )
78
+
79
+ db.sensor.select(site="berlin") # [SensorORM(id=1, name='north', site='berlin')]
80
+ db.sensor.get(2) # SensorORM(id=2, name='south', site='zurich')
81
+ db.sensor.to_frame() # polars.DataFrame
82
+ db.sensor.to_parquet(Path(tempfile.mkdtemp()) / "sensors.parquet")
83
+ ```
84
+
85
+ ## Time series
86
+
87
+ ```python
88
+ from datetime import date, datetime
89
+
90
+ import polars as pl
91
+
92
+ from ducktide import TimeSeriesDB
93
+
94
+ ts = TimeSeriesDB() # or TimeSeriesDB("prices.duckdb")
95
+
96
+ ts.ingest(
97
+ "prices",
98
+ pl.DataFrame(
99
+ {
100
+ "timestamp": [datetime(2025, 1, 1, 9, 0), datetime(2025, 1, 1, 9, 1)],
101
+ "instrument_id": [1, 1],
102
+ "close": [100.0, 100.5],
103
+ }
104
+ ),
105
+ )
106
+
107
+ ts.get_timeseries_frame("prices", instrument_id=1, start=date(2025, 1, 1))
108
+ ```
109
+
110
+ The table is created on first ingest. The timestamp column defaults to
111
+ `timestamp`; pass `TimeSeriesDB(time_col="ts")` to change it.
112
+
113
+ ## Default database context
114
+
115
+ For notebooks and tests you can scope a default database instead of passing it
116
+ around:
117
+
118
+ ```python
119
+ from ducktide.context import use_db
120
+
121
+ with use_db(db):
122
+ ... # code that calls ducktide.context.get_default_db()
123
+ ```
124
+
125
+ ## Development
126
+
127
+ ```bash
128
+ uv sync --group test
129
+ uv run pytest
130
+ ```
131
+
132
+ Run `make help` to see all available targets:
133
+
134
+ ```text
135
+ task section needs does
136
+ book Book test benchmark build the companion
137
+ stress book
138
+ hypothesis-test
139
+ paper
140
+ book-nav Book check that every
141
+ mkdocs nav entry
142
+ resolves in the
143
+ built book
144
+ marimo Book install start the Marimo
145
+ editor
146
+ marimo-validate Book install check that every
147
+ Marimo notebook runs
148
+ serve Book book build the book and
149
+ serve it on port
150
+ 8000
151
+ clean Dev remove build
152
+ artifacts and stale
153
+ local branches
154
+ doctor Dev check local
155
+ prerequisites
156
+ setup Dev run the repository's
157
+ own environment
158
+ setup hook
159
+ docker-build Docker build the Docker
160
+ image
161
+ docker-clean Docker remove the Docker
162
+ image
163
+ docker-run Docker docker-build run the Docker
164
+ container
165
+ lfs-install Git LFS configure git-lfs
166
+ for this repository
167
+ lfs-pull Git LFS download the LFS
168
+ files for the
169
+ current branch
170
+ lfs-status Git LFS show the status of
171
+ LFS files
172
+ lfs-track Git LFS list the patterns
173
+ tracked by git-lfs
174
+ failed-workflows GitHub Helpers list recent failing
175
+ workflow runs
176
+ latest-release GitHub Helpers show information
177
+ about the latest
178
+ GitHub release
179
+ view-issues GitHub Helpers list open issues
180
+ view-prs GitHub Helpers list open pull
181
+ requests
182
+ whoami GitHub Helpers check github auth
183
+ status
184
+ workflow-status GitHub Helpers show recent runs for
185
+ the release workflow
186
+ paper Paper compile the LaTeX
187
+ paper to PDF
188
+ paper-clean Paper remove the LaTeX
189
+ build artifacts
190
+ presentation Presentation generate the HTML
191
+ slides with Marp
192
+ presentation-pdf Presentation generate the PDF
193
+ slides with Marp
194
+ presentation-serve Presentation serve the slides
195
+ with Marp's live
196
+ preview
197
+ all Python fmt deps test run every gate, as
198
+ docs-coverage CI does
199
+ security license
200
+ typecheck rhiza-test
201
+ coverage Python install measure coverage and
202
+ write
203
+ _tests/coverage.xml
204
+ deps Python install run deptry over the
205
+ contributed folders
206
+ docs-coverage Python install check docstring
207
+ coverage with
208
+ interrogate
209
+ install Python setup create the venv and
210
+ sync dependencies
211
+ license Python install scan for copyleft
212
+ licences
213
+ security Python install run the bandit
214
+ security scan
215
+ test Python install run all tests
216
+ test-lowest Python install run the tests
217
+ against the oldest
218
+ dependencies the
219
+ manifest allows
220
+ typecheck Python install run ty and/or mypy
221
+ (typechecker = ty |
222
+ mypy | both)
223
+ docs-examples Quality install check the fenced
224
+ examples in the docs
225
+ tree
226
+ fmt Quality run the pre-commit
227
+ hooks over all files
228
+ complexity Quality fail on a block
229
+ above the
230
+ cyclomatic-complexi…
231
+ ceiling
232
+ test-pyproject Quality install run the
233
+ pyproject.toml
234
+ structure checks,
235
+ verbosely
236
+ rhiza-test Quality install run the rhiza
237
+ repository checks
238
+ semgrep Quality run the semgrep
239
+ static analysis
240
+ rules
241
+ todos Quality list every TODO,
242
+ FIXME and HACK
243
+ comment
244
+ update Template sync the rhiza
245
+ template into this
246
+ repository
247
+ benchmark Testing extras install run the performance
248
+ benchmarks
249
+ hypothesis-test Testing extras install run the
250
+ property-based tests
251
+ stress Testing extras install run the stress and
252
+ load tests
253
+ ```
254
+
255
+ ## License
256
+
257
+ MIT