chronoloompy 0.1.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.
- chronoloompy-0.1.1/LICENSE +21 -0
- chronoloompy-0.1.1/PKG-INFO +75 -0
- chronoloompy-0.1.1/README.md +50 -0
- chronoloompy-0.1.1/pyproject.toml +154 -0
- chronoloompy-0.1.1/rust/Cargo.lock +139 -0
- chronoloompy-0.1.1/rust/Cargo.toml +39 -0
- chronoloompy-0.1.1/rust/src/algebra.rs +11 -0
- chronoloompy-0.1.1/rust/src/lib.rs +19 -0
- chronoloompy-0.1.1/src/chronoloompy/__init__.py +0 -0
- chronoloompy-0.1.1/src/chronoloompy/_core.pyi +7 -0
- chronoloompy-0.1.1/src/chronoloompy/algebra/__init__.py +5 -0
- chronoloompy-0.1.1/src/chronoloompy/algebra/intervals/__init__.py +5 -0
- chronoloompy-0.1.1/src/chronoloompy/algebra/intervals/union.py +19 -0
- chronoloompy-0.1.1/src/chronoloompy/models/__init__.py +5 -0
- chronoloompy-0.1.1/src/chronoloompy/models/primitives/__init__.py +6 -0
- chronoloompy-0.1.1/src/chronoloompy/models/primitives/interval.py +17 -0
- chronoloompy-0.1.1/src/chronoloompy/models/primitives/time_point.py +13 -0
- chronoloompy-0.1.1/src/chronoloompy/models/sequences/__init__.py +0 -0
- chronoloompy-0.1.1/src/chronoloompy/models/sequences/time_point.py +34 -0
- chronoloompy-0.1.1/src/chronoloompy/py.typed +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Amine El Ouassouli
|
|
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,75 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chronoloompy
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Classifier: Development Status :: 3 - Alpha
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: Programming Language :: Rust
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
10
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
11
|
+
Classifier: Typing :: Typed
|
|
12
|
+
Requires-Dist: pydantic>=2.13.4,<3
|
|
13
|
+
Requires-Dist: numpy>=2.5.2,<3
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Summary: Temporal data processing primitives, backed by the chronoloom Rust crate.
|
|
16
|
+
Keywords: time-series,temporal,intervals,streams,rust
|
|
17
|
+
Author-email: Amine El Ouassouli <a.elouassouli@gmail.com>
|
|
18
|
+
License-Expression: MIT
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
21
|
+
Project-URL: Changelog, https://github.com/AElOuassouli/chronoloom/blob/main/chronoloompy/CHANGELOG.md
|
|
22
|
+
Project-URL: Homepage, https://github.com/AElOuassouli/chronoloom
|
|
23
|
+
Project-URL: Repository, https://github.com/AElOuassouli/chronoloom
|
|
24
|
+
|
|
25
|
+
# chronoloompy
|
|
26
|
+
|
|
27
|
+
Temporal data processing primitives for Python, backed by the
|
|
28
|
+
[`chronoloom`](../chronoloom) Rust crate.
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
pip install chronoloompy
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from chronoloompy.models import TimeIntervalEvent
|
|
36
|
+
|
|
37
|
+
event = TimeIntervalEvent(start_timestamp=0, end_timestamp=5)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Intervals are half-open — `start` is included, `end` is excluded.
|
|
41
|
+
|
|
42
|
+
## Relationship to `chronoloom`
|
|
43
|
+
|
|
44
|
+
`chronoloom` is a standalone Rust crate published to
|
|
45
|
+
[crates.io](https://crates.io/crates/chronoloom) and usable from any Rust
|
|
46
|
+
project. `chronoloompy` wraps it through a thin pyo3 binding
|
|
47
|
+
([`rust/`](rust)) and adds a Python-native layer on top — pydantic models
|
|
48
|
+
today, more later.
|
|
49
|
+
|
|
50
|
+
Performance-critical algebra should be implemented once in `chronoloom` and
|
|
51
|
+
exposed here, rather than reimplemented in Python.
|
|
52
|
+
|
|
53
|
+
## Development
|
|
54
|
+
|
|
55
|
+
Requires [uv](https://docs.astral.sh/uv/) and a Rust toolchain (managed
|
|
56
|
+
automatically via the repo's `rust-toolchain.toml` if you have
|
|
57
|
+
[rustup](https://rustup.rs) installed).
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
make install # uv sync -- builds the extension
|
|
61
|
+
make fmt # ruff format + cargo fmt
|
|
62
|
+
make lint # ruff, mypy, cargo fmt --check, clippy
|
|
63
|
+
make test # pytest + cargo test
|
|
64
|
+
make build # release wheel into dist/
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The extension rebuilds automatically when `rust/**/*.rs`, `rust/Cargo.toml`,
|
|
68
|
+
or the `../chronoloom` sources change — see `cache-keys` in `pyproject.toml`.
|
|
69
|
+
|
|
70
|
+
Run `make setup` from the repository root once to install the git hooks.
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
MIT — see [LICENSE](LICENSE).
|
|
75
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# chronoloompy
|
|
2
|
+
|
|
3
|
+
Temporal data processing primitives for Python, backed by the
|
|
4
|
+
[`chronoloom`](../chronoloom) Rust crate.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
pip install chronoloompy
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
from chronoloompy.models import TimeIntervalEvent
|
|
12
|
+
|
|
13
|
+
event = TimeIntervalEvent(start_timestamp=0, end_timestamp=5)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Intervals are half-open — `start` is included, `end` is excluded.
|
|
17
|
+
|
|
18
|
+
## Relationship to `chronoloom`
|
|
19
|
+
|
|
20
|
+
`chronoloom` is a standalone Rust crate published to
|
|
21
|
+
[crates.io](https://crates.io/crates/chronoloom) and usable from any Rust
|
|
22
|
+
project. `chronoloompy` wraps it through a thin pyo3 binding
|
|
23
|
+
([`rust/`](rust)) and adds a Python-native layer on top — pydantic models
|
|
24
|
+
today, more later.
|
|
25
|
+
|
|
26
|
+
Performance-critical algebra should be implemented once in `chronoloom` and
|
|
27
|
+
exposed here, rather than reimplemented in Python.
|
|
28
|
+
|
|
29
|
+
## Development
|
|
30
|
+
|
|
31
|
+
Requires [uv](https://docs.astral.sh/uv/) and a Rust toolchain (managed
|
|
32
|
+
automatically via the repo's `rust-toolchain.toml` if you have
|
|
33
|
+
[rustup](https://rustup.rs) installed).
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
make install # uv sync -- builds the extension
|
|
37
|
+
make fmt # ruff format + cargo fmt
|
|
38
|
+
make lint # ruff, mypy, cargo fmt --check, clippy
|
|
39
|
+
make test # pytest + cargo test
|
|
40
|
+
make build # release wheel into dist/
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The extension rebuilds automatically when `rust/**/*.rs`, `rust/Cargo.toml`,
|
|
44
|
+
or the `../chronoloom` sources change — see `cache-keys` in `pyproject.toml`.
|
|
45
|
+
|
|
46
|
+
Run `make setup` from the repository root once to install the git hooks.
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "chronoloompy"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "Temporal data processing primitives, backed by the chronoloom Rust crate."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
authors = [{ name = "Amine El Ouassouli", email = "a.elouassouli@gmail.com" }]
|
|
10
|
+
keywords = ["time-series", "temporal", "intervals", "streams", "rust"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Programming Language :: Rust",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
"Programming Language :: Python :: 3.13",
|
|
18
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
19
|
+
"Typing :: Typed",
|
|
20
|
+
]
|
|
21
|
+
dependencies = ["pydantic>=2.13.4,<3", "numpy>=2.5.2,<3"]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://github.com/AElOuassouli/chronoloom"
|
|
25
|
+
Repository = "https://github.com/AElOuassouli/chronoloom"
|
|
26
|
+
Changelog = "https://github.com/AElOuassouli/chronoloom/blob/main/chronoloompy/CHANGELOG.md"
|
|
27
|
+
|
|
28
|
+
[build-system]
|
|
29
|
+
# >=1.9.4 sets PYO3_BUILD_EXTENSION_MODULE itself; see [tool.maturin] below.
|
|
30
|
+
requires = ["maturin>=1.14.1,<2.0"]
|
|
31
|
+
build-backend = "maturin"
|
|
32
|
+
|
|
33
|
+
[tool.maturin]
|
|
34
|
+
# Points at the pyo3 binding crate specifically, not a workspace root --
|
|
35
|
+
# maturin needs a concrete [lib] target with crate-type = ["cdylib", ...].
|
|
36
|
+
manifest-path = "rust/Cargo.toml"
|
|
37
|
+
python-source = "src"
|
|
38
|
+
# The last segment must match [lib] name in rust/Cargo.toml and the #[pymodule]
|
|
39
|
+
# fn name, or the built .so cannot be imported under this name.
|
|
40
|
+
module-name = "chronoloompy._core"
|
|
41
|
+
profile = "release"
|
|
42
|
+
# Editable installs (what `uv sync` performs) use the dev profile so the inner
|
|
43
|
+
# loop is not a full optimized Rust build. Requires maturin >= 1.10.
|
|
44
|
+
editable-profile = "dev"
|
|
45
|
+
# NOTE: `features = ["pyo3/extension-module"]` intentionally absent. Since pyo3
|
|
46
|
+
# 0.28 that feature is superseded by PYO3_BUILD_EXTENSION_MODULE, which maturin
|
|
47
|
+
# sets automatically. Re-adding it disables libpython linking for *all* targets
|
|
48
|
+
# and breaks `cargo test` / `cargo clippy --all-targets`.
|
|
49
|
+
|
|
50
|
+
[tool.uv]
|
|
51
|
+
required-version = ">=0.9"
|
|
52
|
+
# uv's default rebuild heuristic only watches pyproject.toml/setup.py/setup.cfg,
|
|
53
|
+
# so a .rs edit would otherwise never trigger a rebuild. Setting cache-keys
|
|
54
|
+
# REPLACES the defaults, hence pyproject.toml is listed again here.
|
|
55
|
+
# Do not use `{ dir = "rust" }` -- it would walk rust/target/ and rebuild always.
|
|
56
|
+
cache-keys = [
|
|
57
|
+
{ file = "pyproject.toml" },
|
|
58
|
+
{ file = "rust/Cargo.toml" },
|
|
59
|
+
{ file = "rust/Cargo.lock" },
|
|
60
|
+
# Scoped to src/, not `rust/**/*.rs` -- that would also match generated
|
|
61
|
+
# build-script output under rust/target/ (e.g. target-lexicon's host.rs),
|
|
62
|
+
# a moving target that self-invalidates the cache on every build.
|
|
63
|
+
{ file = "rust/src/**/*.rs" },
|
|
64
|
+
# No entries for ../chronoloom: the binding depends on the released crate
|
|
65
|
+
# from crates.io, not the sibling directory, so a local core edit cannot
|
|
66
|
+
# change this package's compiled extension. Bumping the requirement in
|
|
67
|
+
# rust/Cargo.toml is what invalidates the cache, and that file is listed
|
|
68
|
+
# above.
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[dependency-groups]
|
|
72
|
+
dev = [
|
|
73
|
+
"maturin>=1.14.1,<2",
|
|
74
|
+
"mypy>=2.3.1",
|
|
75
|
+
"pre-commit>=4.6.2",
|
|
76
|
+
"pytest>=9.1.1",
|
|
77
|
+
"pytest-cov>=7.0",
|
|
78
|
+
"ruff>=0.16.3",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
[tool.ruff]
|
|
82
|
+
target-version = "py312"
|
|
83
|
+
line-length = 88
|
|
84
|
+
src = ["src", "tests"]
|
|
85
|
+
extend-exclude = ["rust/target"]
|
|
86
|
+
|
|
87
|
+
[tool.ruff.lint]
|
|
88
|
+
select = [
|
|
89
|
+
"F", # pyflakes
|
|
90
|
+
"E", # pycodestyle errors
|
|
91
|
+
"W", # pycodestyle warnings
|
|
92
|
+
"I", # isort
|
|
93
|
+
"N", # pep8-naming
|
|
94
|
+
"D", # pydocstyle
|
|
95
|
+
"UP", # pyupgrade
|
|
96
|
+
"ANN", # flake8-annotations
|
|
97
|
+
"S", # flake8-bandit
|
|
98
|
+
"B", # flake8-bugbear
|
|
99
|
+
"A", # flake8-builtins
|
|
100
|
+
"C4", # flake8-comprehensions
|
|
101
|
+
"PT", # flake8-pytest-style
|
|
102
|
+
"RET", # flake8-return
|
|
103
|
+
"SIM", # flake8-simplify
|
|
104
|
+
"TID", # flake8-tidy-imports
|
|
105
|
+
"TC", # flake8-type-checking
|
|
106
|
+
"ARG", # flake8-unused-arguments
|
|
107
|
+
"PTH", # flake8-use-pathlib
|
|
108
|
+
"PL", # pylint
|
|
109
|
+
"NPY", # numpy-specific rules
|
|
110
|
+
"PERF", # perflint
|
|
111
|
+
"FURB", # refurb
|
|
112
|
+
"RUF", # ruff-specific rules
|
|
113
|
+
]
|
|
114
|
+
ignore = [
|
|
115
|
+
"D203", # conflicts with D211 (blank line before class)
|
|
116
|
+
"D213", # conflicts with D212 (multi-line summary position)
|
|
117
|
+
"ANN401", # explicit Any is meaningful for event payloads
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
[tool.ruff.lint.per-file-ignores]
|
|
121
|
+
"__init__.py" = ["D104"]
|
|
122
|
+
"tests/**" = ["D", "S101", "ANN", "PLR2004", "ARG"]
|
|
123
|
+
|
|
124
|
+
[tool.ruff.lint.pydocstyle]
|
|
125
|
+
convention = "google"
|
|
126
|
+
|
|
127
|
+
[tool.ruff.format]
|
|
128
|
+
docstring-code-format = true
|
|
129
|
+
|
|
130
|
+
[tool.mypy]
|
|
131
|
+
python_version = "3.12"
|
|
132
|
+
strict = true
|
|
133
|
+
files = ["src", "tests"]
|
|
134
|
+
mypy_path = ["src"]
|
|
135
|
+
plugins = ["pydantic.mypy"]
|
|
136
|
+
warn_unreachable = true
|
|
137
|
+
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
|
|
138
|
+
|
|
139
|
+
[[tool.mypy.overrides]]
|
|
140
|
+
module = ["tests.*"]
|
|
141
|
+
disallow_untyped_defs = false
|
|
142
|
+
|
|
143
|
+
[tool.pytest.ini_options]
|
|
144
|
+
minversion = "9.0"
|
|
145
|
+
testpaths = ["tests"]
|
|
146
|
+
addopts = "-ra --strict-markers --strict-config"
|
|
147
|
+
filterwarnings = ["error"]
|
|
148
|
+
|
|
149
|
+
[tool.coverage.run]
|
|
150
|
+
source = ["chronoloompy"]
|
|
151
|
+
branch = true
|
|
152
|
+
|
|
153
|
+
[tool.coverage.report]
|
|
154
|
+
exclude_also = ["if TYPE_CHECKING:", "raise NotImplementedError"]
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "chronoloom"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "d6cbfa80d8354d98392aecc2dc769522f2eac756913c36f632e2cf5e1e8a645d"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "chronoloompy-ext"
|
|
13
|
+
version = "0.1.0"
|
|
14
|
+
dependencies = [
|
|
15
|
+
"chronoloom",
|
|
16
|
+
"pyo3",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[[package]]
|
|
20
|
+
name = "heck"
|
|
21
|
+
version = "0.5.0"
|
|
22
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
23
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
24
|
+
|
|
25
|
+
[[package]]
|
|
26
|
+
name = "libc"
|
|
27
|
+
version = "0.2.189"
|
|
28
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
29
|
+
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
|
30
|
+
|
|
31
|
+
[[package]]
|
|
32
|
+
name = "once_cell"
|
|
33
|
+
version = "1.21.4"
|
|
34
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
35
|
+
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
36
|
+
|
|
37
|
+
[[package]]
|
|
38
|
+
name = "portable-atomic"
|
|
39
|
+
version = "1.15.0"
|
|
40
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
41
|
+
checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
|
|
42
|
+
|
|
43
|
+
[[package]]
|
|
44
|
+
name = "proc-macro2"
|
|
45
|
+
version = "1.0.107"
|
|
46
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
47
|
+
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
|
48
|
+
dependencies = [
|
|
49
|
+
"unicode-ident",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[[package]]
|
|
53
|
+
name = "pyo3"
|
|
54
|
+
version = "0.29.2"
|
|
55
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
56
|
+
checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b"
|
|
57
|
+
dependencies = [
|
|
58
|
+
"libc",
|
|
59
|
+
"once_cell",
|
|
60
|
+
"portable-atomic",
|
|
61
|
+
"pyo3-build-config",
|
|
62
|
+
"pyo3-ffi",
|
|
63
|
+
"pyo3-macros",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
[[package]]
|
|
67
|
+
name = "pyo3-build-config"
|
|
68
|
+
version = "0.29.2"
|
|
69
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
70
|
+
checksum = "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9"
|
|
71
|
+
dependencies = [
|
|
72
|
+
"target-lexicon",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
[[package]]
|
|
76
|
+
name = "pyo3-ffi"
|
|
77
|
+
version = "0.29.2"
|
|
78
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
79
|
+
checksum = "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327"
|
|
80
|
+
dependencies = [
|
|
81
|
+
"libc",
|
|
82
|
+
"pyo3-build-config",
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
[[package]]
|
|
86
|
+
name = "pyo3-macros"
|
|
87
|
+
version = "0.29.2"
|
|
88
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
89
|
+
checksum = "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c"
|
|
90
|
+
dependencies = [
|
|
91
|
+
"proc-macro2",
|
|
92
|
+
"pyo3-macros-backend",
|
|
93
|
+
"quote",
|
|
94
|
+
"syn",
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
[[package]]
|
|
98
|
+
name = "pyo3-macros-backend"
|
|
99
|
+
version = "0.29.2"
|
|
100
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
101
|
+
checksum = "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952"
|
|
102
|
+
dependencies = [
|
|
103
|
+
"heck",
|
|
104
|
+
"proc-macro2",
|
|
105
|
+
"quote",
|
|
106
|
+
"syn",
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
[[package]]
|
|
110
|
+
name = "quote"
|
|
111
|
+
version = "1.0.47"
|
|
112
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
113
|
+
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
|
114
|
+
dependencies = [
|
|
115
|
+
"proc-macro2",
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
[[package]]
|
|
119
|
+
name = "syn"
|
|
120
|
+
version = "2.0.119"
|
|
121
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
122
|
+
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
|
123
|
+
dependencies = [
|
|
124
|
+
"proc-macro2",
|
|
125
|
+
"quote",
|
|
126
|
+
"unicode-ident",
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
[[package]]
|
|
130
|
+
name = "target-lexicon"
|
|
131
|
+
version = "0.13.5"
|
|
132
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
133
|
+
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
134
|
+
|
|
135
|
+
[[package]]
|
|
136
|
+
name = "unicode-ident"
|
|
137
|
+
version = "1.0.24"
|
|
138
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
139
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "chronoloompy-ext"
|
|
3
|
+
# NOT the release version. maturin builds the wheel from [project].version in
|
|
4
|
+
# ../pyproject.toml, which is also what the cd-chronoloompy tag guard checks.
|
|
5
|
+
# This one is inert -- the crate is never published anywhere.
|
|
6
|
+
version = "0.1.0"
|
|
7
|
+
edition = "2021"
|
|
8
|
+
rust-version = "1.83"
|
|
9
|
+
# Never published to crates.io: this crate's only export is a #[pymodule], so
|
|
10
|
+
# it is useless to Rust consumers. It ships as a compiled .so inside PyPI
|
|
11
|
+
# wheels, built by maturin. Rust users depend on `chronoloom` directly.
|
|
12
|
+
publish = false
|
|
13
|
+
# Inert given `publish = false`, but `maturin sdist` runs `cargo package --list`,
|
|
14
|
+
# which warns when this metadata is absent. Present only to keep that build
|
|
15
|
+
# output clean. The wheel's real metadata comes from ../pyproject.toml.
|
|
16
|
+
description = "PyO3 bindings for chronoloom. Implementation detail of the chronoloompy Python package."
|
|
17
|
+
license = "MIT"
|
|
18
|
+
repository = "https://github.com/AElOuassouli/chronoloom"
|
|
19
|
+
|
|
20
|
+
[lib]
|
|
21
|
+
# Must exactly match the last segment of `module-name` in ../pyproject.toml and
|
|
22
|
+
# the #[pymodule] fn name in src/lib.rs, or maturin emits a .so that Python
|
|
23
|
+
# cannot import under the expected name.
|
|
24
|
+
name = "_core"
|
|
25
|
+
# rlib is required for `cargo test` / `cargo clippy --all-targets` to link;
|
|
26
|
+
# cdylib is what maturin packages into the Python wheel.
|
|
27
|
+
crate-type = ["cdylib", "rlib"]
|
|
28
|
+
|
|
29
|
+
[dependencies]
|
|
30
|
+
# The released crate, NOT the sibling ../../chronoloom directory. Every wheel
|
|
31
|
+
# therefore contains core code matching a real published version, and the sdist
|
|
32
|
+
# no longer has to vendor a crate from outside this package directory (maturin's
|
|
33
|
+
# most bug-prone path -- PyO3/maturin#579, #838).
|
|
34
|
+
#
|
|
35
|
+
# The trade-off: an edit to ../../chronoloom no longer reaches this binding. To
|
|
36
|
+
# expose new core algebra, release the core to crates.io first, then bump this
|
|
37
|
+
# requirement.
|
|
38
|
+
chronoloom = "0.1"
|
|
39
|
+
pyo3 = { version = "0.29.2", features = ["abi3-py312"] }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//! Adapters exposing `chronoloom::algebra` to Python.
|
|
2
|
+
|
|
3
|
+
use pyo3::prelude::*;
|
|
4
|
+
|
|
5
|
+
/// Intersect two half-open intervals `[start, end)`.
|
|
6
|
+
///
|
|
7
|
+
/// Returns `None` when the intervals do not overlap.
|
|
8
|
+
#[pyfunction]
|
|
9
|
+
pub fn intersection(a: (i64, i64), b: (i64, i64)) -> Option<(i64, i64)> {
|
|
10
|
+
chronoloom::algebra::intersection(a, b)
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//! PyO3 bindings for the [`chronoloom`] crate.
|
|
2
|
+
//!
|
|
3
|
+
//! This crate is an implementation detail of the `chronoloompy` Python
|
|
4
|
+
//! package rather than a library in its own right: its only export is a
|
|
5
|
+
//! `#[pymodule]`. Rust consumers should depend on [`chronoloom`] directly.
|
|
6
|
+
//!
|
|
7
|
+
//! Everything here should stay a thin adapter — real logic belongs in
|
|
8
|
+
//! [`chronoloom`], so that Rust and Python share one implementation.
|
|
9
|
+
|
|
10
|
+
use pyo3::prelude::*;
|
|
11
|
+
|
|
12
|
+
mod algebra;
|
|
13
|
+
|
|
14
|
+
/// Rust extension module, imported from Python as `chronoloompy._core`.
|
|
15
|
+
#[pymodule]
|
|
16
|
+
fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
17
|
+
m.add_function(wrap_pyfunction!(algebra::intersection, m)?)?;
|
|
18
|
+
Ok(())
|
|
19
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Type stubs for the Rust extension module `chronoloompy._core`.
|
|
2
|
+
|
|
3
|
+
Implemented in ../../rust, which is a thin pyo3 adapter over the `chronoloom`
|
|
4
|
+
crate. Keep these signatures in sync with the `#[pyfunction]`s there.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
def intersection(a: tuple[int, int], b: tuple[int, int]) -> tuple[int, int] | None: ...
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Set-algebra operations on time intervals."""
|
|
2
|
+
|
|
3
|
+
from chronoloompy.models import TimeIntervalEvent
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# TODO: implement this function
|
|
7
|
+
def interval_union(
|
|
8
|
+
interval_a: TimeIntervalEvent, interval_b: TimeIntervalEvent
|
|
9
|
+
) -> list[TimeIntervalEvent]:
|
|
10
|
+
"""Return the union of two time intervals."""
|
|
11
|
+
return [interval_a, interval_b]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# TODO: implement this function
|
|
15
|
+
def interval_intersection(
|
|
16
|
+
interval_a: TimeIntervalEvent, interval_b: TimeIntervalEvent
|
|
17
|
+
) -> list[TimeIntervalEvent]:
|
|
18
|
+
"""Return the intersection of two time intervals."""
|
|
19
|
+
return [interval_a, interval_b]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""The interval event primitive: a value attached to a span of time."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TimeIntervalEvent(BaseModel):
|
|
9
|
+
"""A half-open time interval, optionally carrying an attribute."""
|
|
10
|
+
|
|
11
|
+
start_timestamp: int
|
|
12
|
+
end_timestamp: int
|
|
13
|
+
attribute: Any | None = None
|
|
14
|
+
left_open: bool = False
|
|
15
|
+
right_open: bool = True
|
|
16
|
+
|
|
17
|
+
## TODO: add validator to ensure that start_timestamp < end_timestamp
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""The point event primitive: a value observed at a single instant."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, PositiveInt
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TimePointEvent(BaseModel):
|
|
9
|
+
"""A single timestamped observation, optionally carrying an attribute."""
|
|
10
|
+
|
|
11
|
+
timestamp: PositiveInt
|
|
12
|
+
value: Any
|
|
13
|
+
attribute: str | None = None
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Sequence models backed by NumPy structured arrays."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from pydantic import BaseModel, ConfigDict
|
|
7
|
+
|
|
8
|
+
from chronoloompy.models.primitives import TimePointEvent
|
|
9
|
+
|
|
10
|
+
SEQUENCE_DTYPE = np.dtype([("timestamp", np.int64), ("value", np.float64)])
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TimePointSequence(BaseModel):
|
|
14
|
+
"""A time-ordered sequence of point events, stored as a NumPy array."""
|
|
15
|
+
|
|
16
|
+
model_config = ConfigDict(arbitrary_types_allowed=True, frozen=True)
|
|
17
|
+
|
|
18
|
+
sequence: np.ndarray
|
|
19
|
+
attribute: str | None = None
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def from_events(
|
|
23
|
+
cls,
|
|
24
|
+
events: Sequence[TimePointEvent],
|
|
25
|
+
attribute: str | None = None,
|
|
26
|
+
) -> "TimePointSequence":
|
|
27
|
+
"""Build a sequence from a list of point events."""
|
|
28
|
+
return cls(
|
|
29
|
+
sequence=np.array(
|
|
30
|
+
[(event.timestamp, event.value) for event in events],
|
|
31
|
+
dtype=SEQUENCE_DTYPE,
|
|
32
|
+
),
|
|
33
|
+
attribute=attribute,
|
|
34
|
+
)
|
|
File without changes
|