reflet 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- reflet-0.1.0/.github/workflows/ci.yml +57 -0
- reflet-0.1.0/.github/workflows/publish.yml +41 -0
- reflet-0.1.0/.gitignore +9 -0
- reflet-0.1.0/CHANGELOG.md +47 -0
- reflet-0.1.0/LICENSE +28 -0
- reflet-0.1.0/PKG-INFO +656 -0
- reflet-0.1.0/README.md +634 -0
- reflet-0.1.0/examples/clients.csv +5 -0
- reflet-0.1.0/examples/config.json +10 -0
- reflet-0.1.0/examples/declarative_pipeline.json +9 -0
- reflet-0.1.0/examples/declarative_pipeline.py +27 -0
- reflet-0.1.0/examples/example_pipeline.py +64 -0
- reflet-0.1.0/examples/insert_delete.py +64 -0
- reflet-0.1.0/examples/join_example.py +44 -0
- reflet-0.1.0/examples/orders.csv +4 -0
- reflet-0.1.0/examples/provenance_and_validation.py +50 -0
- reflet-0.1.0/examples/sources_example.py +68 -0
- reflet-0.1.0/examples/watch_example.py +42 -0
- reflet-0.1.0/pyproject.toml +45 -0
- reflet-0.1.0/src/reflet/__init__.py +36 -0
- reflet-0.1.0/src/reflet/__main__.py +3 -0
- reflet-0.1.0/src/reflet/cli.py +67 -0
- reflet-0.1.0/src/reflet/declarative.py +133 -0
- reflet-0.1.0/src/reflet/errors.py +72 -0
- reflet-0.1.0/src/reflet/lens.py +113 -0
- reflet-0.1.0/src/reflet/lenses.py +432 -0
- reflet-0.1.0/src/reflet/pipeline.py +218 -0
- reflet-0.1.0/src/reflet/provenance.py +30 -0
- reflet-0.1.0/src/reflet/py.typed +0 -0
- reflet-0.1.0/src/reflet/sources/__init__.py +88 -0
- reflet-0.1.0/src/reflet/sources/csv_source.py +55 -0
- reflet-0.1.0/src/reflet/sources/json_path_source.py +93 -0
- reflet-0.1.0/src/reflet/sources/json_source.py +56 -0
- reflet-0.1.0/src/reflet/sources/sqlite_source.py +83 -0
- reflet-0.1.0/src/reflet/view.py +509 -0
- reflet-0.1.0/tests/conftest.py +35 -0
- reflet-0.1.0/tests/test_cli.py +39 -0
- reflet-0.1.0/tests/test_declarative.py +118 -0
- reflet-0.1.0/tests/test_diff_sync.py +119 -0
- reflet-0.1.0/tests/test_differentiators.py +166 -0
- reflet-0.1.0/tests/test_join.py +64 -0
- reflet-0.1.0/tests/test_lens_laws.py +130 -0
- reflet-0.1.0/tests/test_lenses.py +82 -0
- reflet-0.1.0/tests/test_new_sources.py +81 -0
- reflet-0.1.0/tests/test_pipeline.py +50 -0
- reflet-0.1.0/tests/test_restructure_lenses.py +96 -0
- reflet-0.1.0/tests/test_sources.py +56 -0
- reflet-0.1.0/tests/test_stable_key.py +62 -0
- reflet-0.1.0/tests/test_view_provenance.py +65 -0
- reflet-0.1.0/tests/test_watch.py +43 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_call: # lets publish.yml reuse this as its pre-release gate
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install reflet with dev dependencies
|
|
26
|
+
run: pip install -e ".[dev]"
|
|
27
|
+
|
|
28
|
+
- name: Lint (ruff)
|
|
29
|
+
run: ruff check .
|
|
30
|
+
|
|
31
|
+
- name: Type-check (mypy --strict)
|
|
32
|
+
run: mypy src
|
|
33
|
+
|
|
34
|
+
- name: Test (pytest)
|
|
35
|
+
run: pytest -q
|
|
36
|
+
|
|
37
|
+
build:
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
needs: test
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/checkout@v4
|
|
42
|
+
|
|
43
|
+
- name: Set up Python
|
|
44
|
+
uses: actions/setup-python@v5
|
|
45
|
+
with:
|
|
46
|
+
python-version: "3.12"
|
|
47
|
+
|
|
48
|
+
- name: Build sdist and wheel
|
|
49
|
+
run: |
|
|
50
|
+
pip install build
|
|
51
|
+
python -m build
|
|
52
|
+
|
|
53
|
+
- name: Upload build artifacts
|
|
54
|
+
uses: actions/upload-artifact@v4
|
|
55
|
+
with:
|
|
56
|
+
name: dist
|
|
57
|
+
path: dist/
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
uses: ./.github/workflows/ci.yml
|
|
10
|
+
|
|
11
|
+
publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
needs: test
|
|
14
|
+
environment: pypi
|
|
15
|
+
permissions:
|
|
16
|
+
id-token: write # required for PyPI trusted publishing (OIDC) — no API token needed
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.12"
|
|
25
|
+
|
|
26
|
+
- name: Check release tag matches pyproject.toml version
|
|
27
|
+
run: |
|
|
28
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
29
|
+
version=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
30
|
+
if [ "$tag" != "$version" ]; then
|
|
31
|
+
echo "Release tag v$tag does not match pyproject.toml version $version"
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
- name: Build sdist and wheel
|
|
36
|
+
run: |
|
|
37
|
+
pip install build
|
|
38
|
+
python -m build
|
|
39
|
+
|
|
40
|
+
- name: Publish to PyPI
|
|
41
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
reflet-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-11-01
|
|
11
|
+
|
|
12
|
+
Initial public release.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Core bidirectional lens engine: `Lens.get`/`put`, composition via `>>`,
|
|
17
|
+
`Pipeline`, `View`, `Row` with attribute access, and per-cell `Provenance`
|
|
18
|
+
exposed through `.why()`.
|
|
19
|
+
- Lenses: `rename`, `select`, `filter` (with `defaults=` for `insert()`),
|
|
20
|
+
`map`, `cast`, `split`, `merge`, `constant`, `compute`, `aggregate` (with
|
|
21
|
+
`ConflictPolicy`), `sort`, and `join` (inner join against another
|
|
22
|
+
pipeline; right-hand fields read-only).
|
|
23
|
+
- Sources: CSV, JSON (flat or nested via `rows_at=`), and SQLite
|
|
24
|
+
(`table=`), each with read and atomic write-back, and an optional
|
|
25
|
+
stable `key=` for row identity.
|
|
26
|
+
- `View.diff()` and `sync(dry_run=True)` to preview pending changes before
|
|
27
|
+
writing.
|
|
28
|
+
- `SourceConflictError`: `sync()` detects if the source file changed on
|
|
29
|
+
disk since it was read, and refuses unless `force=True`.
|
|
30
|
+
- `View.undo()`/`redo()` and `View.journal` for edit history and audit.
|
|
31
|
+
- `Pipeline.validate()`: field-level constraints checked at edit time,
|
|
32
|
+
with the field's provenance in the error message.
|
|
33
|
+
- `View.why_not(rid)` and `View.impact(rid, field)` for tracing why a row
|
|
34
|
+
is absent from a view, and what a source cell produced.
|
|
35
|
+
- `View.insert()`/`delete()` with `on_delete` policies (`delete`,
|
|
36
|
+
`tombstone`, `ignore`).
|
|
37
|
+
- `View.watch()`: a polling loop that auto-syncs pending edits and flags
|
|
38
|
+
external source changes via a callback.
|
|
39
|
+
- `reflet.declarative`: build a `Pipeline` from a JSON spec, for the
|
|
40
|
+
lenses that don't need arbitrary Python.
|
|
41
|
+
- CLI: `python -m reflet show|why`, accepting either a `.py` script or a
|
|
42
|
+
declarative `.json` spec.
|
|
43
|
+
- Zero runtime dependencies; typed, `mypy --strict` clean; tested on
|
|
44
|
+
Python 3.10–3.13.
|
|
45
|
+
|
|
46
|
+
[Unreleased]: https://github.com/r-seize/reflet/compare/v0.1.0...HEAD
|
|
47
|
+
[0.1.0]: https://github.com/r-seize/reflet/releases/tag/v0.1.0
|
reflet-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, r-seize
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|