h5t 0.2.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.
- h5t-0.2.0/LICENSE +21 -0
- h5t-0.2.0/PKG-INFO +162 -0
- h5t-0.2.0/README.md +131 -0
- h5t-0.2.0/pyproject.toml +96 -0
- h5t-0.2.0/pyproject.toml.orig +76 -0
- h5t-0.2.0/src/h5t/__init__.py +20 -0
- h5t-0.2.0/src/h5t/_cli.py +66 -0
- h5t-0.2.0/src/h5t/_compile.py +840 -0
- h5t-0.2.0/src/h5t/_errors.py +24 -0
- h5t-0.2.0/src/h5t/_spec.py +91 -0
- h5t-0.2.0/src/h5t/py.typed +0 -0
h5t-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 binado
|
|
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.
|
h5t-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: h5t
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Load HDF5 groups as detached, typed Python records.
|
|
5
|
+
Keywords: hdf5,h5py,schema,pydantic,typing,validation
|
|
6
|
+
Author: binado
|
|
7
|
+
Author-email: binado <bernardopveronese@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Dist: h5py>=3.10
|
|
23
|
+
Requires-Dist: numpy>=1.26
|
|
24
|
+
Requires-Dist: pydantic>=2.4,<3
|
|
25
|
+
Requires-Python: >=3.11
|
|
26
|
+
Project-URL: Homepage, https://github.com/binado/h5t
|
|
27
|
+
Project-URL: Repository, https://github.com/binado/h5t
|
|
28
|
+
Project-URL: Issues, https://github.com/binado/h5t/issues
|
|
29
|
+
Project-URL: Changelog, https://github.com/binado/h5t/blob/main/CHANGELOG.md
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# h5t
|
|
33
|
+
|
|
34
|
+
[](https://pypi.org/project/h5t/)
|
|
35
|
+
[](https://pypi.org/project/h5t/)
|
|
36
|
+
[](https://github.com/binado/h5t/actions/workflows/ci.yml)
|
|
37
|
+
[](https://github.com/binado/h5t/blob/main/LICENSE)
|
|
38
|
+
|
|
39
|
+
`h5t` loads HDF5 groups into detached, typed Python records. Group attributes and
|
|
40
|
+
ordinary NumPy-array fields are materialized while the file is open. Typed datasets
|
|
41
|
+
keep snapshot metadata and can read their payload lazily without retaining an open
|
|
42
|
+
file descriptor.
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install h5t # or: uv add h5t
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Requires Python 3.11+. `h5py`, `numpy`, and Pydantic v2 are installed automatically.
|
|
51
|
+
|
|
52
|
+
## Example
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import json
|
|
56
|
+
from pathlib import Path
|
|
57
|
+
from typing import Annotated, Any
|
|
58
|
+
|
|
59
|
+
import numpy as np
|
|
60
|
+
|
|
61
|
+
import h5t
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class Measurement(h5t.Dataset, extras="forbid"):
|
|
65
|
+
unit: str
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Nested(h5t.Group):
|
|
69
|
+
label: str
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class Result(h5t.Group, extras="ignore"):
|
|
73
|
+
version: int # implicit HDF5 attribute
|
|
74
|
+
title: Annotated[str, h5t.Name("name")] # renamed attribute
|
|
75
|
+
config: Annotated[
|
|
76
|
+
dict[str, Any],
|
|
77
|
+
h5t.Attr(converter=json.loads),
|
|
78
|
+
]
|
|
79
|
+
array_attr: Annotated[np.ndarray, h5t.Attr()]
|
|
80
|
+
values: np.ndarray # eager dataset payload
|
|
81
|
+
measurement: Measurement # lazy detached dataset
|
|
82
|
+
eager_measurement: Annotated[Measurement, h5t.Eager()]
|
|
83
|
+
nested: Nested # recursively loaded group
|
|
84
|
+
note: str | None # absent becomes None
|
|
85
|
+
revision: int = 1 # absent uses a validated default
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
result = Result.from_file(Path("result.h5"), root="/")
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`result.attrs` and `result.measurement.attrs` are immutable mappings keyed by their
|
|
92
|
+
on-disk HDF5 names. Declared attributes contain their Pydantic-processed values;
|
|
93
|
+
undeclared attributes retained under `extras="ignore"` contain the raw h5py values.
|
|
94
|
+
|
|
95
|
+
Typed datasets expose snapshot metadata and explicit data access:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
dataset = result.measurement
|
|
99
|
+
dataset.path, dataset.shape, dataset.dtype, dataset.ndim
|
|
100
|
+
|
|
101
|
+
complete = dataset.data # first access reads and caches an ndarray
|
|
102
|
+
assert dataset.read() is complete
|
|
103
|
+
|
|
104
|
+
with dataset.open() as live:
|
|
105
|
+
first_hundred = live[:100] # fresh file view, useful for slices
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Both `from_file()` and `Dataset.open()` close every handle on normal and exceptional
|
|
109
|
+
exits. Schema objects cannot be directly constructed, written, or serialized by h5t.
|
|
110
|
+
|
|
111
|
+
## Field rules
|
|
112
|
+
|
|
113
|
+
| Annotation | HDF5 representation | Loading behavior |
|
|
114
|
+
| --- | --- | --- |
|
|
115
|
+
| `Group` subclass | child group | recursively materialized |
|
|
116
|
+
| `Dataset` or subclass | child dataset | metadata/attrs snapshot, payload lazy |
|
|
117
|
+
| `Annotated[DatasetSubtype, Eager()]` | child dataset | complete payload cached during loading |
|
|
118
|
+
| `np.ndarray` | child dataset | complete payload loaded as an ndarray |
|
|
119
|
+
| `Annotated[T, Attr(...)]` | attribute | converter, then Pydantic validation |
|
|
120
|
+
| scalar or `Literal[...]` | attribute | Pydantic validation |
|
|
121
|
+
|
|
122
|
+
`Name("stored-name")` renames any field kind. `Attr` is valid only for attributes and
|
|
123
|
+
`Eager` only for typed datasets. A parameterized alias such as
|
|
124
|
+
`numpy.typing.NDArray[np.floating]` is accepted wherever `np.ndarray` is; the dtype
|
|
125
|
+
parameter is not validated. Unsupported collection-shaped child annotations raise
|
|
126
|
+
`SchemaError`; dynamic collections are not yet supported. Declarations are compiled at
|
|
127
|
+
the `class` statement, so a `SchemaError` surfaces there. A schema class that names a
|
|
128
|
+
class defined later in its module instead compiles on first use.
|
|
129
|
+
|
|
130
|
+
Each `Group` and `Dataset` subclass accepts `extras="ignore"` (the default) or
|
|
131
|
+
`extras="forbid"`. A group policy applies to its immediate child and attribute
|
|
132
|
+
names. A typed dataset policy applies to its attributes. Nested schemas keep their own
|
|
133
|
+
policy, while a plain `np.ndarray` field never checks the dataset's attributes.
|
|
134
|
+
|
|
135
|
+
## Snapshot consistency
|
|
136
|
+
|
|
137
|
+
A loaded model is a snapshot, with one deliberate exception:
|
|
138
|
+
|
|
139
|
+
- Group and dataset attributes, dataset shape/dtype/path, eager datasets, and plain
|
|
140
|
+
arrays reflect the file during `from_file()`.
|
|
141
|
+
- A lazy dataset's first `.data`/`.read()` observes the file at that later moment and
|
|
142
|
+
caches the resulting array permanently.
|
|
143
|
+
- `.open()` always opens the current file and current dataset. It may therefore observe
|
|
144
|
+
replacements or fail if the source was changed or deleted.
|
|
145
|
+
|
|
146
|
+
## CLI
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
h5t check result.h5 --schema mypackage.schemas:Result --root /results/latest
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Exit status is 0 on success, 1 for the first file/schema mismatch, and 2 for import,
|
|
153
|
+
declaration, usage, or I/O errors.
|
|
154
|
+
|
|
155
|
+
## Development
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
uv sync
|
|
159
|
+
uv run pytest
|
|
160
|
+
uv run ty check
|
|
161
|
+
uv run ruff check .
|
|
162
|
+
```
|
h5t-0.2.0/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# h5t
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/h5t/)
|
|
4
|
+
[](https://pypi.org/project/h5t/)
|
|
5
|
+
[](https://github.com/binado/h5t/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/binado/h5t/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
`h5t` loads HDF5 groups into detached, typed Python records. Group attributes and
|
|
9
|
+
ordinary NumPy-array fields are materialized while the file is open. Typed datasets
|
|
10
|
+
keep snapshot metadata and can read their payload lazily without retaining an open
|
|
11
|
+
file descriptor.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install h5t # or: uv add h5t
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Requires Python 3.11+. `h5py`, `numpy`, and Pydantic v2 are installed automatically.
|
|
20
|
+
|
|
21
|
+
## Example
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
import json
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import Annotated, Any
|
|
27
|
+
|
|
28
|
+
import numpy as np
|
|
29
|
+
|
|
30
|
+
import h5t
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Measurement(h5t.Dataset, extras="forbid"):
|
|
34
|
+
unit: str
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Nested(h5t.Group):
|
|
38
|
+
label: str
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Result(h5t.Group, extras="ignore"):
|
|
42
|
+
version: int # implicit HDF5 attribute
|
|
43
|
+
title: Annotated[str, h5t.Name("name")] # renamed attribute
|
|
44
|
+
config: Annotated[
|
|
45
|
+
dict[str, Any],
|
|
46
|
+
h5t.Attr(converter=json.loads),
|
|
47
|
+
]
|
|
48
|
+
array_attr: Annotated[np.ndarray, h5t.Attr()]
|
|
49
|
+
values: np.ndarray # eager dataset payload
|
|
50
|
+
measurement: Measurement # lazy detached dataset
|
|
51
|
+
eager_measurement: Annotated[Measurement, h5t.Eager()]
|
|
52
|
+
nested: Nested # recursively loaded group
|
|
53
|
+
note: str | None # absent becomes None
|
|
54
|
+
revision: int = 1 # absent uses a validated default
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
result = Result.from_file(Path("result.h5"), root="/")
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`result.attrs` and `result.measurement.attrs` are immutable mappings keyed by their
|
|
61
|
+
on-disk HDF5 names. Declared attributes contain their Pydantic-processed values;
|
|
62
|
+
undeclared attributes retained under `extras="ignore"` contain the raw h5py values.
|
|
63
|
+
|
|
64
|
+
Typed datasets expose snapshot metadata and explicit data access:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
dataset = result.measurement
|
|
68
|
+
dataset.path, dataset.shape, dataset.dtype, dataset.ndim
|
|
69
|
+
|
|
70
|
+
complete = dataset.data # first access reads and caches an ndarray
|
|
71
|
+
assert dataset.read() is complete
|
|
72
|
+
|
|
73
|
+
with dataset.open() as live:
|
|
74
|
+
first_hundred = live[:100] # fresh file view, useful for slices
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Both `from_file()` and `Dataset.open()` close every handle on normal and exceptional
|
|
78
|
+
exits. Schema objects cannot be directly constructed, written, or serialized by h5t.
|
|
79
|
+
|
|
80
|
+
## Field rules
|
|
81
|
+
|
|
82
|
+
| Annotation | HDF5 representation | Loading behavior |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| `Group` subclass | child group | recursively materialized |
|
|
85
|
+
| `Dataset` or subclass | child dataset | metadata/attrs snapshot, payload lazy |
|
|
86
|
+
| `Annotated[DatasetSubtype, Eager()]` | child dataset | complete payload cached during loading |
|
|
87
|
+
| `np.ndarray` | child dataset | complete payload loaded as an ndarray |
|
|
88
|
+
| `Annotated[T, Attr(...)]` | attribute | converter, then Pydantic validation |
|
|
89
|
+
| scalar or `Literal[...]` | attribute | Pydantic validation |
|
|
90
|
+
|
|
91
|
+
`Name("stored-name")` renames any field kind. `Attr` is valid only for attributes and
|
|
92
|
+
`Eager` only for typed datasets. A parameterized alias such as
|
|
93
|
+
`numpy.typing.NDArray[np.floating]` is accepted wherever `np.ndarray` is; the dtype
|
|
94
|
+
parameter is not validated. Unsupported collection-shaped child annotations raise
|
|
95
|
+
`SchemaError`; dynamic collections are not yet supported. Declarations are compiled at
|
|
96
|
+
the `class` statement, so a `SchemaError` surfaces there. A schema class that names a
|
|
97
|
+
class defined later in its module instead compiles on first use.
|
|
98
|
+
|
|
99
|
+
Each `Group` and `Dataset` subclass accepts `extras="ignore"` (the default) or
|
|
100
|
+
`extras="forbid"`. A group policy applies to its immediate child and attribute
|
|
101
|
+
names. A typed dataset policy applies to its attributes. Nested schemas keep their own
|
|
102
|
+
policy, while a plain `np.ndarray` field never checks the dataset's attributes.
|
|
103
|
+
|
|
104
|
+
## Snapshot consistency
|
|
105
|
+
|
|
106
|
+
A loaded model is a snapshot, with one deliberate exception:
|
|
107
|
+
|
|
108
|
+
- Group and dataset attributes, dataset shape/dtype/path, eager datasets, and plain
|
|
109
|
+
arrays reflect the file during `from_file()`.
|
|
110
|
+
- A lazy dataset's first `.data`/`.read()` observes the file at that later moment and
|
|
111
|
+
caches the resulting array permanently.
|
|
112
|
+
- `.open()` always opens the current file and current dataset. It may therefore observe
|
|
113
|
+
replacements or fail if the source was changed or deleted.
|
|
114
|
+
|
|
115
|
+
## CLI
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
h5t check result.h5 --schema mypackage.schemas:Result --root /results/latest
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Exit status is 0 on success, 1 for the first file/schema mismatch, and 2 for import,
|
|
122
|
+
declaration, usage, or I/O errors.
|
|
123
|
+
|
|
124
|
+
## Development
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
uv sync
|
|
128
|
+
uv run pytest
|
|
129
|
+
uv run ty check
|
|
130
|
+
uv run ruff check .
|
|
131
|
+
```
|
h5t-0.2.0/pyproject.toml
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "h5t"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Load HDF5 groups as detached, typed Python records."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
requires-python = ">=3.11"
|
|
9
|
+
keywords = [
|
|
10
|
+
"hdf5",
|
|
11
|
+
"h5py",
|
|
12
|
+
"schema",
|
|
13
|
+
"pydantic",
|
|
14
|
+
"typing",
|
|
15
|
+
"validation",
|
|
16
|
+
]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 4 - Beta",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Intended Audience :: Science/Research",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Programming Language :: Python :: 3.14",
|
|
27
|
+
"Topic :: Scientific/Engineering",
|
|
28
|
+
"Topic :: Software Development :: Libraries",
|
|
29
|
+
"Typing :: Typed",
|
|
30
|
+
]
|
|
31
|
+
dependencies = [
|
|
32
|
+
"h5py>=3.10",
|
|
33
|
+
"numpy>=1.26",
|
|
34
|
+
"pydantic>=2.4,<3",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[[project.authors]]
|
|
38
|
+
name = "binado"
|
|
39
|
+
email = "bernardopveronese@gmail.com"
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://github.com/binado/h5t"
|
|
43
|
+
Repository = "https://github.com/binado/h5t"
|
|
44
|
+
Issues = "https://github.com/binado/h5t/issues"
|
|
45
|
+
Changelog = "https://github.com/binado/h5t/blob/main/CHANGELOG.md"
|
|
46
|
+
|
|
47
|
+
[project.scripts]
|
|
48
|
+
h5t = "h5t._cli:main"
|
|
49
|
+
|
|
50
|
+
[dependency-groups]
|
|
51
|
+
dev = [
|
|
52
|
+
"pytest>=8",
|
|
53
|
+
"hypothesis>=6",
|
|
54
|
+
"ty>=0.0.74",
|
|
55
|
+
"ruff>=0.6",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[build-system]
|
|
59
|
+
requires = ["uv_build>=0.12.5,<0.13.0"]
|
|
60
|
+
build-backend = "uv_build"
|
|
61
|
+
|
|
62
|
+
[tool.ruff]
|
|
63
|
+
line-length = 100
|
|
64
|
+
target-version = "py311"
|
|
65
|
+
|
|
66
|
+
[tool.ruff.lint]
|
|
67
|
+
select = [
|
|
68
|
+
"E",
|
|
69
|
+
"F",
|
|
70
|
+
"I",
|
|
71
|
+
"UP",
|
|
72
|
+
"B",
|
|
73
|
+
"D",
|
|
74
|
+
]
|
|
75
|
+
ignore = [
|
|
76
|
+
"D105",
|
|
77
|
+
"D107",
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
[tool.ruff.lint.pydocstyle]
|
|
81
|
+
convention = "numpy"
|
|
82
|
+
|
|
83
|
+
[tool.ruff.lint.per-file-ignores]
|
|
84
|
+
"tests/**" = [
|
|
85
|
+
"D",
|
|
86
|
+
"B018",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
[tool.ty.src]
|
|
90
|
+
include = [
|
|
91
|
+
"src/h5t",
|
|
92
|
+
"tests/acceptance_example.py",
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
[tool.pytest.ini_options]
|
|
96
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "h5t"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Load HDF5 groups as detached, typed Python records."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "binado", email = "bernardopveronese@gmail.com" }
|
|
10
|
+
]
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
keywords = ["hdf5", "h5py", "schema", "pydantic", "typing", "validation"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Intended Audience :: Science/Research",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Programming Language :: Python :: 3.14",
|
|
23
|
+
"Topic :: Scientific/Engineering",
|
|
24
|
+
"Topic :: Software Development :: Libraries",
|
|
25
|
+
"Typing :: Typed",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"h5py>=3.10",
|
|
29
|
+
"numpy>=1.26",
|
|
30
|
+
"pydantic>=2.4,<3",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/binado/h5t"
|
|
35
|
+
Repository = "https://github.com/binado/h5t"
|
|
36
|
+
Issues = "https://github.com/binado/h5t/issues"
|
|
37
|
+
Changelog = "https://github.com/binado/h5t/blob/main/CHANGELOG.md"
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
h5t = "h5t._cli:main"
|
|
41
|
+
|
|
42
|
+
[dependency-groups]
|
|
43
|
+
dev = [
|
|
44
|
+
"pytest>=8",
|
|
45
|
+
"hypothesis>=6",
|
|
46
|
+
"ty>=0.0.74",
|
|
47
|
+
"ruff>=0.6",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[build-system]
|
|
51
|
+
requires = ["uv_build>=0.12.5,<0.13.0"]
|
|
52
|
+
build-backend = "uv_build"
|
|
53
|
+
|
|
54
|
+
[tool.ruff]
|
|
55
|
+
line-length = 100
|
|
56
|
+
target-version = "py311"
|
|
57
|
+
|
|
58
|
+
[tool.ruff.lint]
|
|
59
|
+
select = ["E", "F", "I", "UP", "B", "D"]
|
|
60
|
+
ignore = [
|
|
61
|
+
"D105", # missing docstring in magic method
|
|
62
|
+
"D107", # missing docstring in __init__
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint.pydocstyle]
|
|
66
|
+
convention = "numpy"
|
|
67
|
+
|
|
68
|
+
[tool.ruff.lint.per-file-ignores]
|
|
69
|
+
# B018 fires on attribute access inside pytest.raises blocks.
|
|
70
|
+
"tests/**" = ["D", "B018"]
|
|
71
|
+
|
|
72
|
+
[tool.ty.src]
|
|
73
|
+
include = ["src/h5t", "tests/acceptance_example.py"]
|
|
74
|
+
|
|
75
|
+
[tool.pytest.ini_options]
|
|
76
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Detached, typed records for read-only HDF5 access."""
|
|
2
|
+
|
|
3
|
+
from h5t._compile import Dataset, Group
|
|
4
|
+
from h5t._errors import ConversionError, H5TError, SchemaError, ValidationError
|
|
5
|
+
from h5t._spec import Attr, Eager, Name
|
|
6
|
+
|
|
7
|
+
__version__ = "0.2.0"
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"Attr",
|
|
11
|
+
"ConversionError",
|
|
12
|
+
"Dataset",
|
|
13
|
+
"Eager",
|
|
14
|
+
"Group",
|
|
15
|
+
"H5TError",
|
|
16
|
+
"Name",
|
|
17
|
+
"SchemaError",
|
|
18
|
+
"ValidationError",
|
|
19
|
+
"__version__",
|
|
20
|
+
]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""The ``h5t check`` command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import importlib
|
|
7
|
+
import sys
|
|
8
|
+
from typing import NoReturn
|
|
9
|
+
|
|
10
|
+
from h5t._compile import Group
|
|
11
|
+
from h5t._errors import SchemaError, ValidationError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _fail(message: str) -> NoReturn:
|
|
15
|
+
print(f"error: {message}", file=sys.stderr)
|
|
16
|
+
raise SystemExit(2)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _load_schema(ref: str) -> type[Group]:
|
|
20
|
+
module_name, separator, class_name = ref.partition(":")
|
|
21
|
+
if not separator or not module_name or not class_name:
|
|
22
|
+
_fail(f"--schema must look like pkg.module:ClassName, got {ref!r}")
|
|
23
|
+
try:
|
|
24
|
+
module = importlib.import_module(module_name)
|
|
25
|
+
except Exception as exc:
|
|
26
|
+
_fail(f"could not import module {module_name!r}: {exc}")
|
|
27
|
+
try:
|
|
28
|
+
schema = getattr(module, class_name)
|
|
29
|
+
except AttributeError:
|
|
30
|
+
_fail(f"module {module_name!r} has no attribute {class_name!r}")
|
|
31
|
+
if not (isinstance(schema, type) and issubclass(schema, Group)):
|
|
32
|
+
_fail(f"{ref!r} is not an h5t.Group schema class")
|
|
33
|
+
return schema
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _cmd_check(args: argparse.Namespace) -> int:
|
|
37
|
+
schema = _load_schema(args.schema)
|
|
38
|
+
try:
|
|
39
|
+
schema.from_file(args.file, root=args.root)
|
|
40
|
+
except ValidationError as exc:
|
|
41
|
+
print(f"invalid: {args.file} against {schema.__name__}: {exc}")
|
|
42
|
+
return 1
|
|
43
|
+
except (OSError, ValueError, TypeError, SchemaError) as exc:
|
|
44
|
+
_fail(f"could not check {args.file!r}: {exc}")
|
|
45
|
+
print(f"ok: {args.file} validates against {schema.__name__} at {args.root}")
|
|
46
|
+
return 0
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def main(argv: list[str] | None = None) -> int:
|
|
50
|
+
"""Run the command line interface and return its process exit code."""
|
|
51
|
+
parser = argparse.ArgumentParser(
|
|
52
|
+
prog="h5t",
|
|
53
|
+
description="Validate and materialize an HDF5 group schema.",
|
|
54
|
+
)
|
|
55
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
56
|
+
check = subparsers.add_parser("check", help="check an HDF5 file against a Group schema")
|
|
57
|
+
check.add_argument("file", help="path to the HDF5 file")
|
|
58
|
+
check.add_argument("--schema", required=True, help="schema reference, e.g. pkg.schemas:Result")
|
|
59
|
+
check.add_argument("--root", default="/", help="absolute HDF5 group path (default: /)")
|
|
60
|
+
check.set_defaults(func=_cmd_check)
|
|
61
|
+
args = parser.parse_args(argv)
|
|
62
|
+
return int(args.func(args))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
sys.exit(main())
|