pyrosome 0.1.11__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.
- pyrosome-0.1.11/LICENSE +21 -0
- pyrosome-0.1.11/PKG-INFO +174 -0
- pyrosome-0.1.11/README.md +159 -0
- pyrosome-0.1.11/pyproject.toml +161 -0
- pyrosome-0.1.11/pyproject.toml.orig +160 -0
- pyrosome-0.1.11/src/pyrosome/__init__.py +14 -0
- pyrosome-0.1.11/src/pyrosome/api.py +262 -0
- pyrosome-0.1.11/src/pyrosome/dal.py +770 -0
- pyrosome-0.1.11/src/pyrosome/datasets.py +224 -0
- pyrosome-0.1.11/src/pyrosome/py.typed +0 -0
- pyrosome-0.1.11/src/pyrosome/simple_file_lock.py +157 -0
pyrosome-0.1.11/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ian Green
|
|
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.
|
pyrosome-0.1.11/PKG-INFO
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyrosome
|
|
3
|
+
Version: 0.1.11
|
|
4
|
+
Summary: Versioned, append-only storage for polars data frames, safe for concurrent processes
|
|
5
|
+
Author: ian
|
|
6
|
+
Author-email: ian <dev@iangreen.dev>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: polars>=2.0.0
|
|
10
|
+
Requires-Dist: pandas>=3.1.0 ; extra == 'pandas'
|
|
11
|
+
Requires-Dist: pyarrow>=26.0.0 ; extra == 'pandas'
|
|
12
|
+
Requires-Python: >=3.14
|
|
13
|
+
Provides-Extra: pandas
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# pyrosome
|
|
17
|
+
|
|
18
|
+
Versioned, append-only storage for [polars](https://pola.rs) data frames.
|
|
19
|
+
|
|
20
|
+
pyrosome stores data frames on disk as parquet (or CSV) files, keeps every version you write, and reads them back as lazy frames. Several processes can read, write and append to the same data at once without overwriting each other or seeing half-written data.
|
|
21
|
+
|
|
22
|
+
## Purpose
|
|
23
|
+
|
|
24
|
+
Saving data frames to files is easy. Keeping those files trustworthy is harder:
|
|
25
|
+
|
|
26
|
+
- overwriting a file loses the previous data, and a crash halfway through can leave it corrupt;
|
|
27
|
+
- two processes writing at once can clobber each other;
|
|
28
|
+
- a reader can pick up a file that is still being written.
|
|
29
|
+
|
|
30
|
+
pyrosome handles this with a small, predictable layout on the filesystem. There is no database or server to run.
|
|
31
|
+
|
|
32
|
+
## Project summary
|
|
33
|
+
|
|
34
|
+
- **Versioned**: every `write` creates a new version (`v0`, `v1`, …). Older versions stay on disk until you remove them, so rollbacks and audits are simple.
|
|
35
|
+
- **Append-only**: `append` adds a new file to the latest version and never rewrites existing files. Columns are reordered and cast to match the stored data.
|
|
36
|
+
- **Safe with several processes**: versions are claimed with an atomic `mkdir`, appends claim their index by exclusively creating a marker file so they never overwrite each other, and the manifest naming the latest version is replaced in one step.
|
|
37
|
+
- **Lazy**: `read` returns a `polars.LazyFrame`, so filters and column selections are pushed down into the file scan.
|
|
38
|
+
- **Parquet or CSV**: `ParquetDal` stores parquet; `CsvDal` has the same API and stores CSV for people and tools that need to open the files directly.
|
|
39
|
+
|
|
40
|
+
Data is organised into *profiles*, named by one or more keys:
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
data/
|
|
44
|
+
└── sales/
|
|
45
|
+
└── orders/ # profile for keys ("sales", "orders")
|
|
46
|
+
├── manifest.json # {"latest_data_dir": "v1"}
|
|
47
|
+
├── v0/
|
|
48
|
+
│ ├── 0.parquet # written by write()
|
|
49
|
+
│ └── 1.parquet # added by append()
|
|
50
|
+
└── v1/
|
|
51
|
+
└── 0.parquet
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Platform support
|
|
55
|
+
|
|
56
|
+
pyrosome supports Windows, Linux and macOS, with the data directory on a local disk or an NFS share (NFSv3 or later).
|
|
57
|
+
|
|
58
|
+
SMB shares are supported as a lower priority. pyrosome aims to work on them, but they get less attention and testing, and some SMB setups have limitations. For example, they may ignore the read-only permission pyrosome sets on finished data files.
|
|
59
|
+
|
|
60
|
+
## Install
|
|
61
|
+
|
|
62
|
+
pyrosome requires Python 3.14 or newer.
|
|
63
|
+
|
|
64
|
+
Add it to a uv project:
|
|
65
|
+
|
|
66
|
+
```console
|
|
67
|
+
uv add pyrosome
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or install it into the current environment with pip:
|
|
71
|
+
|
|
72
|
+
```console
|
|
73
|
+
pip install pyrosome
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Development setup
|
|
77
|
+
|
|
78
|
+
```console
|
|
79
|
+
uv sync --all-groups
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This installs the development tools (pytest, ruff, ty, mypy, pylint) and the documentation tools (Sphinx, furo).
|
|
83
|
+
|
|
84
|
+
| Task | Command |
|
|
85
|
+
| --- | --- |
|
|
86
|
+
| Run tests, including every example in the docs and this README | `uv run pytest` |
|
|
87
|
+
| Lint and format | `uv run ruff check` and `uv run ruff format` |
|
|
88
|
+
| Type check | `uv run ty check` and `uv run mypy src` |
|
|
89
|
+
| Pylint | `uv run pylint src` |
|
|
90
|
+
| Build docs | `uv run sphinx-build -W -b html docs docs/_build/html` |
|
|
91
|
+
|
|
92
|
+
## Quickstart
|
|
93
|
+
|
|
94
|
+
`write` creates a new version and returns its directory:
|
|
95
|
+
|
|
96
|
+
```pycon
|
|
97
|
+
>>> from pathlib import Path
|
|
98
|
+
>>> import polars as pl
|
|
99
|
+
>>> from pyrosome import ParquetDal
|
|
100
|
+
>>> dal = ParquetDal(Path("data"))
|
|
101
|
+
>>> orders = pl.DataFrame(
|
|
102
|
+
... {"order_id": [1, 2, 3], "region": ["eu", "eu", "us"], "total": [9.5, 20.0, 7.25]}
|
|
103
|
+
... )
|
|
104
|
+
>>> dal.write(orders, "sales", "orders").as_posix()
|
|
105
|
+
'data/sales/orders/v0'
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`append` adds a file to the latest version. Column order doesn't matter:
|
|
110
|
+
|
|
111
|
+
```pycon
|
|
112
|
+
>>> more = pl.DataFrame({"total": [12.0], "order_id": [4], "region": ["us"]})
|
|
113
|
+
>>> dal.append(more, "sales", "orders").as_posix()
|
|
114
|
+
'data/sales/orders/v0/1.parquet'
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`read` returns a `LazyFrame` over the latest version:
|
|
119
|
+
|
|
120
|
+
```pycon
|
|
121
|
+
>>> (
|
|
122
|
+
... dal.read("sales", "orders")
|
|
123
|
+
... .filter(pl.col("region") == "eu")
|
|
124
|
+
... .select(pl.col("total").sum())
|
|
125
|
+
... .collect()
|
|
126
|
+
... .item()
|
|
127
|
+
... )
|
|
128
|
+
29.5
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Writing again creates `v1`, and `read` now returns only the new data. The earlier version stays on disk:
|
|
133
|
+
|
|
134
|
+
```pycon
|
|
135
|
+
>>> _ = dal.write(pl.DataFrame({"order_id": [10], "status": ["open"]}), "sales", "orders")
|
|
136
|
+
>>> dal.read("sales", "orders").collect().columns
|
|
137
|
+
['order_id', 'status']
|
|
138
|
+
>>> sorted(path.as_posix() for path in Path("data").rglob("[!.]*.*"))
|
|
139
|
+
['data/sales/orders/manifest.json', 'data/sales/orders/v0/0.parquet', 'data/sales/orders/v0/1.parquet', 'data/sales/orders/v1/0.parquet']
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
To store CSV instead, use `CsvDal` with the same methods. Keep each format in its own data directory, because a CSV write to a parquet profile hides the parquet data from `ParquetDal`:
|
|
144
|
+
|
|
145
|
+
```pycon
|
|
146
|
+
>>> from pyrosome import CsvDal
|
|
147
|
+
>>> CsvDal(Path("exports")).write(orders, "sales", "orders").as_posix()
|
|
148
|
+
'exports/sales/orders/v0'
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`read` raises `FileNotFoundError` for a profile with no data, and `append` raises `ValueError` if the columns don't match the stored data:
|
|
153
|
+
|
|
154
|
+
```pycon
|
|
155
|
+
>>> dal.read("sales", "returns")
|
|
156
|
+
Traceback (most recent call last):
|
|
157
|
+
...
|
|
158
|
+
FileNotFoundError: Cannot find files for ('sales', 'returns')
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Documentation
|
|
163
|
+
|
|
164
|
+
The full documentation is in `docs/` and covers concepts, 14 recipes (compaction, rollbacks, pruning old versions, multi-process ingest and more), and the API reference. Build it with:
|
|
165
|
+
|
|
166
|
+
```console
|
|
167
|
+
uv run sphinx-build -W -b html docs docs/_build/html
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Then open `docs/_build/html/index.html`.
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# pyrosome
|
|
2
|
+
|
|
3
|
+
Versioned, append-only storage for [polars](https://pola.rs) data frames.
|
|
4
|
+
|
|
5
|
+
pyrosome stores data frames on disk as parquet (or CSV) files, keeps every version you write, and reads them back as lazy frames. Several processes can read, write and append to the same data at once without overwriting each other or seeing half-written data.
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Saving data frames to files is easy. Keeping those files trustworthy is harder:
|
|
10
|
+
|
|
11
|
+
- overwriting a file loses the previous data, and a crash halfway through can leave it corrupt;
|
|
12
|
+
- two processes writing at once can clobber each other;
|
|
13
|
+
- a reader can pick up a file that is still being written.
|
|
14
|
+
|
|
15
|
+
pyrosome handles this with a small, predictable layout on the filesystem. There is no database or server to run.
|
|
16
|
+
|
|
17
|
+
## Project summary
|
|
18
|
+
|
|
19
|
+
- **Versioned**: every `write` creates a new version (`v0`, `v1`, …). Older versions stay on disk until you remove them, so rollbacks and audits are simple.
|
|
20
|
+
- **Append-only**: `append` adds a new file to the latest version and never rewrites existing files. Columns are reordered and cast to match the stored data.
|
|
21
|
+
- **Safe with several processes**: versions are claimed with an atomic `mkdir`, appends claim their index by exclusively creating a marker file so they never overwrite each other, and the manifest naming the latest version is replaced in one step.
|
|
22
|
+
- **Lazy**: `read` returns a `polars.LazyFrame`, so filters and column selections are pushed down into the file scan.
|
|
23
|
+
- **Parquet or CSV**: `ParquetDal` stores parquet; `CsvDal` has the same API and stores CSV for people and tools that need to open the files directly.
|
|
24
|
+
|
|
25
|
+
Data is organised into *profiles*, named by one or more keys:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
data/
|
|
29
|
+
└── sales/
|
|
30
|
+
└── orders/ # profile for keys ("sales", "orders")
|
|
31
|
+
├── manifest.json # {"latest_data_dir": "v1"}
|
|
32
|
+
├── v0/
|
|
33
|
+
│ ├── 0.parquet # written by write()
|
|
34
|
+
│ └── 1.parquet # added by append()
|
|
35
|
+
└── v1/
|
|
36
|
+
└── 0.parquet
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Platform support
|
|
40
|
+
|
|
41
|
+
pyrosome supports Windows, Linux and macOS, with the data directory on a local disk or an NFS share (NFSv3 or later).
|
|
42
|
+
|
|
43
|
+
SMB shares are supported as a lower priority. pyrosome aims to work on them, but they get less attention and testing, and some SMB setups have limitations. For example, they may ignore the read-only permission pyrosome sets on finished data files.
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
pyrosome requires Python 3.14 or newer.
|
|
48
|
+
|
|
49
|
+
Add it to a uv project:
|
|
50
|
+
|
|
51
|
+
```console
|
|
52
|
+
uv add pyrosome
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or install it into the current environment with pip:
|
|
56
|
+
|
|
57
|
+
```console
|
|
58
|
+
pip install pyrosome
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Development setup
|
|
62
|
+
|
|
63
|
+
```console
|
|
64
|
+
uv sync --all-groups
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This installs the development tools (pytest, ruff, ty, mypy, pylint) and the documentation tools (Sphinx, furo).
|
|
68
|
+
|
|
69
|
+
| Task | Command |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| Run tests, including every example in the docs and this README | `uv run pytest` |
|
|
72
|
+
| Lint and format | `uv run ruff check` and `uv run ruff format` |
|
|
73
|
+
| Type check | `uv run ty check` and `uv run mypy src` |
|
|
74
|
+
| Pylint | `uv run pylint src` |
|
|
75
|
+
| Build docs | `uv run sphinx-build -W -b html docs docs/_build/html` |
|
|
76
|
+
|
|
77
|
+
## Quickstart
|
|
78
|
+
|
|
79
|
+
`write` creates a new version and returns its directory:
|
|
80
|
+
|
|
81
|
+
```pycon
|
|
82
|
+
>>> from pathlib import Path
|
|
83
|
+
>>> import polars as pl
|
|
84
|
+
>>> from pyrosome import ParquetDal
|
|
85
|
+
>>> dal = ParquetDal(Path("data"))
|
|
86
|
+
>>> orders = pl.DataFrame(
|
|
87
|
+
... {"order_id": [1, 2, 3], "region": ["eu", "eu", "us"], "total": [9.5, 20.0, 7.25]}
|
|
88
|
+
... )
|
|
89
|
+
>>> dal.write(orders, "sales", "orders").as_posix()
|
|
90
|
+
'data/sales/orders/v0'
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`append` adds a file to the latest version. Column order doesn't matter:
|
|
95
|
+
|
|
96
|
+
```pycon
|
|
97
|
+
>>> more = pl.DataFrame({"total": [12.0], "order_id": [4], "region": ["us"]})
|
|
98
|
+
>>> dal.append(more, "sales", "orders").as_posix()
|
|
99
|
+
'data/sales/orders/v0/1.parquet'
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`read` returns a `LazyFrame` over the latest version:
|
|
104
|
+
|
|
105
|
+
```pycon
|
|
106
|
+
>>> (
|
|
107
|
+
... dal.read("sales", "orders")
|
|
108
|
+
... .filter(pl.col("region") == "eu")
|
|
109
|
+
... .select(pl.col("total").sum())
|
|
110
|
+
... .collect()
|
|
111
|
+
... .item()
|
|
112
|
+
... )
|
|
113
|
+
29.5
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Writing again creates `v1`, and `read` now returns only the new data. The earlier version stays on disk:
|
|
118
|
+
|
|
119
|
+
```pycon
|
|
120
|
+
>>> _ = dal.write(pl.DataFrame({"order_id": [10], "status": ["open"]}), "sales", "orders")
|
|
121
|
+
>>> dal.read("sales", "orders").collect().columns
|
|
122
|
+
['order_id', 'status']
|
|
123
|
+
>>> sorted(path.as_posix() for path in Path("data").rglob("[!.]*.*"))
|
|
124
|
+
['data/sales/orders/manifest.json', 'data/sales/orders/v0/0.parquet', 'data/sales/orders/v0/1.parquet', 'data/sales/orders/v1/0.parquet']
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
To store CSV instead, use `CsvDal` with the same methods. Keep each format in its own data directory, because a CSV write to a parquet profile hides the parquet data from `ParquetDal`:
|
|
129
|
+
|
|
130
|
+
```pycon
|
|
131
|
+
>>> from pyrosome import CsvDal
|
|
132
|
+
>>> CsvDal(Path("exports")).write(orders, "sales", "orders").as_posix()
|
|
133
|
+
'exports/sales/orders/v0'
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`read` raises `FileNotFoundError` for a profile with no data, and `append` raises `ValueError` if the columns don't match the stored data:
|
|
138
|
+
|
|
139
|
+
```pycon
|
|
140
|
+
>>> dal.read("sales", "returns")
|
|
141
|
+
Traceback (most recent call last):
|
|
142
|
+
...
|
|
143
|
+
FileNotFoundError: Cannot find files for ('sales', 'returns')
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Documentation
|
|
148
|
+
|
|
149
|
+
The full documentation is in `docs/` and covers concepts, 14 recipes (compaction, rollbacks, pruning old versions, multi-process ingest and more), and the API reference. Build it with:
|
|
150
|
+
|
|
151
|
+
```console
|
|
152
|
+
uv run sphinx-build -W -b html docs docs/_build/html
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Then open `docs/_build/html/index.html`.
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pyrosome"
|
|
3
|
+
version = "0.1.11"
|
|
4
|
+
description = "Versioned, append-only storage for polars data frames, safe for concurrent processes"
|
|
5
|
+
license = "MIT"
|
|
6
|
+
license-files = ["LICENSE"]
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
requires-python = ">=3.14"
|
|
9
|
+
dependencies = ["polars>=2.0.0"]
|
|
10
|
+
|
|
11
|
+
[[project.authors]]
|
|
12
|
+
name = "ian"
|
|
13
|
+
email = "dev@iangreen.dev"
|
|
14
|
+
|
|
15
|
+
[project.optional-dependencies]
|
|
16
|
+
pandas = [
|
|
17
|
+
"pandas>=3.1.0",
|
|
18
|
+
"pyarrow>=26.0.0",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["uv_build>=0.13.5,<0.14.0"]
|
|
23
|
+
build-backend = "uv_build"
|
|
24
|
+
|
|
25
|
+
[dependency-groups]
|
|
26
|
+
dev = [
|
|
27
|
+
"mypy>=2.4.0",
|
|
28
|
+
"pandas>=3.1.0",
|
|
29
|
+
"pandas-stubs>=3.0.5.260914",
|
|
30
|
+
"pyarrow>=26.0.0",
|
|
31
|
+
"pylint>=4.1.3",
|
|
32
|
+
"pytest>=9.1.1",
|
|
33
|
+
"ruff>=0.17.2",
|
|
34
|
+
"ty>=0.0.91",
|
|
35
|
+
]
|
|
36
|
+
docs = [
|
|
37
|
+
"furo>=2025.12.19",
|
|
38
|
+
"sphinx>=9.2.0",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[tool.ruff]
|
|
42
|
+
target-version = "py314"
|
|
43
|
+
preview = true
|
|
44
|
+
|
|
45
|
+
[tool.ruff.lint]
|
|
46
|
+
select = ["ALL"]
|
|
47
|
+
ignore = [
|
|
48
|
+
"missing-trailing-comma",
|
|
49
|
+
"missing-copyright-notice",
|
|
50
|
+
"docstring-extraneous-returns",
|
|
51
|
+
"docstring-extraneous-exception",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint.per-file-ignores]
|
|
55
|
+
"tests/**" = ["assert"]
|
|
56
|
+
"docs/conf.py" = ["implicit-namespace-package"]
|
|
57
|
+
"docs/examples/**" = [
|
|
58
|
+
"implicit-namespace-package",
|
|
59
|
+
"print",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[tool.ruff.lint.pydocstyle]
|
|
63
|
+
convention = "google"
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint.flake8-type-checking]
|
|
66
|
+
strict = true
|
|
67
|
+
|
|
68
|
+
[tool.ruff.lint.flake8-tidy-imports]
|
|
69
|
+
ban-relative-imports = "all"
|
|
70
|
+
|
|
71
|
+
[tool.ty.rules]
|
|
72
|
+
all = "error"
|
|
73
|
+
|
|
74
|
+
[tool.ty.terminal]
|
|
75
|
+
error-on-warning = true
|
|
76
|
+
|
|
77
|
+
[tool.mypy]
|
|
78
|
+
python_version = "3.14"
|
|
79
|
+
strict = true
|
|
80
|
+
warn_unreachable = true
|
|
81
|
+
local_partial_types = true
|
|
82
|
+
strict_bytes = true
|
|
83
|
+
disallow_any_unimported = true
|
|
84
|
+
disallow_any_expr = true
|
|
85
|
+
disallow_any_decorated = true
|
|
86
|
+
disallow_any_explicit = true
|
|
87
|
+
enable_error_code = [
|
|
88
|
+
"deprecated",
|
|
89
|
+
"exhaustive-match",
|
|
90
|
+
"explicit-override",
|
|
91
|
+
"ignore-without-code",
|
|
92
|
+
"mutable-override",
|
|
93
|
+
"possibly-undefined",
|
|
94
|
+
"redundant-expr",
|
|
95
|
+
"redundant-self",
|
|
96
|
+
"truthy-bool",
|
|
97
|
+
"truthy-iterable",
|
|
98
|
+
"unimported-reveal",
|
|
99
|
+
"unused-awaitable",
|
|
100
|
+
"unused-ignore",
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
[tool.pylint.main]
|
|
104
|
+
py-version = "3.14"
|
|
105
|
+
fail-on = ["all"]
|
|
106
|
+
load-plugins = [
|
|
107
|
+
"pylint.extensions.bad_builtin",
|
|
108
|
+
"pylint.extensions.broad_try_clause",
|
|
109
|
+
"pylint.extensions.check_elif",
|
|
110
|
+
"pylint.extensions.code_style",
|
|
111
|
+
"pylint.extensions.comparison_placement",
|
|
112
|
+
"pylint.extensions.confusing_elif",
|
|
113
|
+
"pylint.extensions.consider_refactoring_into_while_condition",
|
|
114
|
+
"pylint.extensions.consider_ternary_expression",
|
|
115
|
+
"pylint.extensions.dict_init_mutate",
|
|
116
|
+
"pylint.extensions.docparams",
|
|
117
|
+
"pylint.extensions.docstyle",
|
|
118
|
+
"pylint.extensions.dunder",
|
|
119
|
+
"pylint.extensions.empty_comment",
|
|
120
|
+
"pylint.extensions.eq_without_hash",
|
|
121
|
+
"pylint.extensions.for_any_all",
|
|
122
|
+
"pylint.extensions.magic_value",
|
|
123
|
+
"pylint.extensions.mccabe",
|
|
124
|
+
"pylint.extensions.no_self_use",
|
|
125
|
+
"pylint.extensions.overlapping_exceptions",
|
|
126
|
+
"pylint.extensions.private_import",
|
|
127
|
+
"pylint.extensions.redefined_loop_name",
|
|
128
|
+
"pylint.extensions.redefined_variable_type",
|
|
129
|
+
"pylint.extensions.set_membership",
|
|
130
|
+
"pylint.extensions.typing",
|
|
131
|
+
"pylint.extensions.while_used",
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
[tool.pylint."messages control"]
|
|
135
|
+
enable = ["all"]
|
|
136
|
+
disable = ["redundant-returns-doc"]
|
|
137
|
+
|
|
138
|
+
[tool.pylint.parameter_documentation]
|
|
139
|
+
accept-no-param-doc = false
|
|
140
|
+
accept-no-raise-doc = false
|
|
141
|
+
accept-no-return-doc = false
|
|
142
|
+
accept-no-yields-doc = false
|
|
143
|
+
default-docstring-type = "google"
|
|
144
|
+
|
|
145
|
+
[tool.pylint.typing]
|
|
146
|
+
runtime-typing = false
|
|
147
|
+
|
|
148
|
+
[tool.pytest]
|
|
149
|
+
testpaths = [
|
|
150
|
+
"tests",
|
|
151
|
+
"src",
|
|
152
|
+
"docs",
|
|
153
|
+
"README.md",
|
|
154
|
+
]
|
|
155
|
+
addopts = [
|
|
156
|
+
"--doctest-modules",
|
|
157
|
+
"--doctest-glob=*.rst",
|
|
158
|
+
"--doctest-glob=*.md",
|
|
159
|
+
]
|
|
160
|
+
doctest_optionflags = ["ELLIPSIS"]
|
|
161
|
+
usefixtures = ["run_in_tmp_path"]
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pyrosome"
|
|
3
|
+
version = "0.1.11"
|
|
4
|
+
description = "Versioned, append-only storage for polars data frames, safe for concurrent processes"
|
|
5
|
+
license = "MIT"
|
|
6
|
+
license-files = ["LICENSE"]
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "ian", email = "dev@iangreen.dev" }
|
|
10
|
+
]
|
|
11
|
+
requires-python = ">=3.14"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"polars>=2.0.0",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.optional-dependencies]
|
|
17
|
+
pandas = [
|
|
18
|
+
"pandas>=3.1.0",
|
|
19
|
+
"pyarrow>=26.0.0",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[build-system]
|
|
23
|
+
requires = ["uv_build>=0.13.5,<0.14.0"]
|
|
24
|
+
build-backend = "uv_build"
|
|
25
|
+
|
|
26
|
+
[dependency-groups]
|
|
27
|
+
dev = [
|
|
28
|
+
"mypy>=2.4.0",
|
|
29
|
+
"pandas>=3.1.0",
|
|
30
|
+
"pandas-stubs>=3.0.5.260914",
|
|
31
|
+
"pyarrow>=26.0.0",
|
|
32
|
+
"pylint>=4.1.3",
|
|
33
|
+
"pytest>=9.1.1",
|
|
34
|
+
"ruff>=0.17.2",
|
|
35
|
+
"ty>=0.0.91",
|
|
36
|
+
]
|
|
37
|
+
docs = [
|
|
38
|
+
"furo>=2025.12.19",
|
|
39
|
+
"sphinx>=9.2.0",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[tool.ruff]
|
|
43
|
+
target-version = "py314"
|
|
44
|
+
preview = true
|
|
45
|
+
|
|
46
|
+
[tool.ruff.lint]
|
|
47
|
+
select = ["ALL"]
|
|
48
|
+
ignore = [
|
|
49
|
+
# conflicts with `ruff format`
|
|
50
|
+
"missing-trailing-comma",
|
|
51
|
+
# no copyright headers in this project
|
|
52
|
+
"missing-copyright-notice",
|
|
53
|
+
# every docstring documents its returns and raises, including None and
|
|
54
|
+
# exceptions raised by functions it calls
|
|
55
|
+
"docstring-extraneous-returns",
|
|
56
|
+
"docstring-extraneous-exception",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
[tool.ruff.lint.per-file-ignores]
|
|
60
|
+
# pytest relies on bare asserts
|
|
61
|
+
"tests/**" = ["assert"]
|
|
62
|
+
# Sphinx requires conf.py to sit in docs/ without an __init__.py
|
|
63
|
+
"docs/conf.py" = ["implicit-namespace-package"]
|
|
64
|
+
# standalone scripts included in the docs, which print their results
|
|
65
|
+
"docs/examples/**" = ["implicit-namespace-package", "print"]
|
|
66
|
+
|
|
67
|
+
[tool.ruff.lint.pydocstyle]
|
|
68
|
+
convention = "google"
|
|
69
|
+
|
|
70
|
+
[tool.ruff.lint.flake8-type-checking]
|
|
71
|
+
strict = true
|
|
72
|
+
|
|
73
|
+
[tool.ruff.lint.flake8-tidy-imports]
|
|
74
|
+
ban-relative-imports = "all"
|
|
75
|
+
|
|
76
|
+
[tool.ty.rules]
|
|
77
|
+
all = "error"
|
|
78
|
+
|
|
79
|
+
[tool.ty.terminal]
|
|
80
|
+
error-on-warning = true
|
|
81
|
+
|
|
82
|
+
[tool.mypy]
|
|
83
|
+
python_version = "3.14"
|
|
84
|
+
strict = true
|
|
85
|
+
warn_unreachable = true
|
|
86
|
+
local_partial_types = true
|
|
87
|
+
strict_bytes = true
|
|
88
|
+
disallow_any_unimported = true
|
|
89
|
+
disallow_any_expr = true
|
|
90
|
+
disallow_any_decorated = true
|
|
91
|
+
disallow_any_explicit = true
|
|
92
|
+
enable_error_code = [
|
|
93
|
+
"deprecated",
|
|
94
|
+
"exhaustive-match",
|
|
95
|
+
"explicit-override",
|
|
96
|
+
"ignore-without-code",
|
|
97
|
+
"mutable-override",
|
|
98
|
+
"possibly-undefined",
|
|
99
|
+
"redundant-expr",
|
|
100
|
+
"redundant-self",
|
|
101
|
+
"truthy-bool",
|
|
102
|
+
"truthy-iterable",
|
|
103
|
+
"unimported-reveal",
|
|
104
|
+
"unused-awaitable",
|
|
105
|
+
"unused-ignore",
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
[tool.pylint.main]
|
|
109
|
+
py-version = "3.14"
|
|
110
|
+
fail-on = ["all"]
|
|
111
|
+
load-plugins = [
|
|
112
|
+
"pylint.extensions.bad_builtin",
|
|
113
|
+
"pylint.extensions.broad_try_clause",
|
|
114
|
+
"pylint.extensions.check_elif",
|
|
115
|
+
"pylint.extensions.code_style",
|
|
116
|
+
"pylint.extensions.comparison_placement",
|
|
117
|
+
"pylint.extensions.confusing_elif",
|
|
118
|
+
"pylint.extensions.consider_refactoring_into_while_condition",
|
|
119
|
+
"pylint.extensions.consider_ternary_expression",
|
|
120
|
+
"pylint.extensions.dict_init_mutate",
|
|
121
|
+
"pylint.extensions.docparams",
|
|
122
|
+
"pylint.extensions.docstyle",
|
|
123
|
+
"pylint.extensions.dunder",
|
|
124
|
+
"pylint.extensions.empty_comment",
|
|
125
|
+
"pylint.extensions.eq_without_hash",
|
|
126
|
+
"pylint.extensions.for_any_all",
|
|
127
|
+
"pylint.extensions.magic_value",
|
|
128
|
+
"pylint.extensions.mccabe",
|
|
129
|
+
"pylint.extensions.no_self_use",
|
|
130
|
+
"pylint.extensions.overlapping_exceptions",
|
|
131
|
+
"pylint.extensions.private_import",
|
|
132
|
+
"pylint.extensions.redefined_loop_name",
|
|
133
|
+
"pylint.extensions.redefined_variable_type",
|
|
134
|
+
"pylint.extensions.set_membership",
|
|
135
|
+
"pylint.extensions.typing",
|
|
136
|
+
"pylint.extensions.while_used",
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
[tool.pylint."messages control"]
|
|
140
|
+
enable = ["all"]
|
|
141
|
+
# every docstring documents its returns, including None
|
|
142
|
+
disable = ["redundant-returns-doc"]
|
|
143
|
+
|
|
144
|
+
[tool.pylint.parameter_documentation]
|
|
145
|
+
accept-no-param-doc = false
|
|
146
|
+
accept-no-raise-doc = false
|
|
147
|
+
accept-no-return-doc = false
|
|
148
|
+
accept-no-yields-doc = false
|
|
149
|
+
default-docstring-type = "google"
|
|
150
|
+
|
|
151
|
+
[tool.pylint.typing]
|
|
152
|
+
runtime-typing = false
|
|
153
|
+
|
|
154
|
+
[tool.pytest]
|
|
155
|
+
testpaths = ["tests", "src", "docs", "README.md"]
|
|
156
|
+
# run the examples in docstrings, docs pages and the README as doctests
|
|
157
|
+
addopts = ["--doctest-modules", "--doctest-glob=*.rst", "--doctest-glob=*.md"]
|
|
158
|
+
doctest_optionflags = ["ELLIPSIS"]
|
|
159
|
+
# defined in conftest.py
|
|
160
|
+
usefixtures = ["run_in_tmp_path"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""File IO layer."""
|
|
2
|
+
|
|
3
|
+
from pyrosome.api import SimpleIo
|
|
4
|
+
from pyrosome.dal import CsvDal, Dal, ParquetDal, get_accessor
|
|
5
|
+
from pyrosome.datasets import Dataset
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"CsvDal",
|
|
9
|
+
"Dal",
|
|
10
|
+
"Dataset",
|
|
11
|
+
"ParquetDal",
|
|
12
|
+
"SimpleIo",
|
|
13
|
+
"get_accessor",
|
|
14
|
+
]
|