fletchr-core 0.0.1rc5__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.
- fletchr_core-0.0.1rc5/.gitignore +21 -0
- fletchr_core-0.0.1rc5/CHANGELOG.md +83 -0
- fletchr_core-0.0.1rc5/LICENSE +21 -0
- fletchr_core-0.0.1rc5/PKG-INFO +269 -0
- fletchr_core-0.0.1rc5/README.md +229 -0
- fletchr_core-0.0.1rc5/conftest.py +47 -0
- fletchr_core-0.0.1rc5/pyproject.toml +93 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/__init__.py +193 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/_arrow_ipc.py +101 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/_field.py +144 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/_grammar.py +237 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/_history.py +367 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/_plugins.py +100 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/_registry.py +50 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/_repr.py +266 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/_serialization.py +177 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/_version.py +34 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/containers/__init__.py +13 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/containers/_statistics.py +215 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/containers/framearray.py +998 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/containers/packetlist.py +853 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/containers/packets.py +439 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/dataframes/__init__.py +116 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/dataframes/_pandas.py +49 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/dataframes/_polars.py +58 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/__init__.py +6 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/api.md +82 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/architecture.md +128 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/custom-io.md +119 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/custom-transformers.md +107 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/data-structures.md +270 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/docstrings.md +101 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/index.md +31 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/pipeline.md +105 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/plugins.md +195 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/registries.md +126 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/history.md +113 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/arrow.md +67 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/bits.md +73 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/csv.md +66 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/index.md +49 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/parquet.md +60 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/pickle.md +55 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/txt.md +51 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/wire-format.md +193 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/exceptions.py +101 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/io/__init__.py +68 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/io/_delimited.py +564 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/io/arrow.py +98 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/io/base.py +164 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/io/bits.py +206 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/io/csv.py +160 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/io/parquet.py +169 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/io/pickle.py +62 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/io/txt.py +145 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/py.typed +0 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/skills/README.md +41 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/skills/__init__.py +9 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/skills/__main__.py +123 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-arrow-gotchas/skill.md +100 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-history-and-metadata/skill.md +130 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-io-reader-writer/skill.md +118 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-measurand-spec/skill.md +104 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-plugin-packaging/skill.md +116 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-polars-pandas-interop/skill.md +137 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-subclass-containers/skill.md +85 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-transformer-authoring/skill.md +102 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/transform/__init__.py +52 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/transform/base.py +411 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/transform/clock.py +533 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/transform/frame.py +529 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/transform/measurand.py +213 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/transform/packet.py +511 -0
- fletchr_core-0.0.1rc5/src/fletchr_core/typing.py +30 -0
- fletchr_core-0.0.1rc5/tests/conftest.py +73 -0
- fletchr_core-0.0.1rc5/tests/containers/test_framearray.py +257 -0
- fletchr_core-0.0.1rc5/tests/containers/test_framearray_fields.py +178 -0
- fletchr_core-0.0.1rc5/tests/containers/test_muxframearray.py +279 -0
- fletchr_core-0.0.1rc5/tests/containers/test_packet.py +233 -0
- fletchr_core-0.0.1rc5/tests/containers/test_packetlist.py +237 -0
- fletchr_core-0.0.1rc5/tests/data/sync_test_stream.csv +60 -0
- fletchr_core-0.0.1rc5/tests/dataframes/test_dispatch.py +108 -0
- fletchr_core-0.0.1rc5/tests/dataframes/test_pandas.py +87 -0
- fletchr_core-0.0.1rc5/tests/dataframes/test_polars.py +77 -0
- fletchr_core-0.0.1rc5/tests/integration/test_concatenate.py +139 -0
- fletchr_core-0.0.1rc5/tests/integration/test_edge_cases.py +165 -0
- fletchr_core-0.0.1rc5/tests/integration/test_history_integration.py +235 -0
- fletchr_core-0.0.1rc5/tests/integration/test_properties.py +209 -0
- fletchr_core-0.0.1rc5/tests/io/test_bits.py +340 -0
- fletchr_core-0.0.1rc5/tests/io/test_csv.py +346 -0
- fletchr_core-0.0.1rc5/tests/io/test_filereader.py +238 -0
- fletchr_core-0.0.1rc5/tests/io/test_filewriter.py +143 -0
- fletchr_core-0.0.1rc5/tests/io/test_parquet.py +158 -0
- fletchr_core-0.0.1rc5/tests/io/test_txt.py +288 -0
- fletchr_core-0.0.1rc5/tests/test_grammar.py +147 -0
- fletchr_core-0.0.1rc5/tests/test_history.py +338 -0
- fletchr_core-0.0.1rc5/tests/test_plugins.py +306 -0
- fletchr_core-0.0.1rc5/tests/test_registry_collisions.py +359 -0
- fletchr_core-0.0.1rc5/tests/test_repr.py +399 -0
- fletchr_core-0.0.1rc5/tests/test_serialization.py +117 -0
- fletchr_core-0.0.1rc5/tests/transform/test_clocks.py +531 -0
- fletchr_core-0.0.1rc5/tests/transform/test_convenience_method_docs.py +22 -0
- fletchr_core-0.0.1rc5/tests/transform/test_delimited_packet_extractor.py +243 -0
- fletchr_core-0.0.1rc5/tests/transform/test_frame_filter.py +151 -0
- fletchr_core-0.0.1rc5/tests/transform/test_measurand_group.py +317 -0
- fletchr_core-0.0.1rc5/tests/transform/test_mux_transformers.py +122 -0
- fletchr_core-0.0.1rc5/tests/transform/test_packet_filter.py +174 -0
- fletchr_core-0.0.1rc5/tests/transform/test_packet_select.py +109 -0
- fletchr_core-0.0.1rc5/tests/transform/test_pattern_packet_extractor.py +178 -0
- fletchr_core-0.0.1rc5/tests/transform/test_polarity_fix.py +180 -0
- fletchr_core-0.0.1rc5/tests/transform/test_sync.py +194 -0
- fletchr_core-0.0.1rc5/tests/transform/test_transformer.py +627 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.venv
|
|
2
|
+
__pycache__
|
|
3
|
+
.hypothesis
|
|
4
|
+
.pytest_cache
|
|
5
|
+
.ipynb_checkpoints
|
|
6
|
+
*.ipynb
|
|
7
|
+
_version.py
|
|
8
|
+
|
|
9
|
+
# Build artifacts
|
|
10
|
+
dist/
|
|
11
|
+
build/
|
|
12
|
+
*.egg-info/
|
|
13
|
+
|
|
14
|
+
# Coverage
|
|
15
|
+
.coverage
|
|
16
|
+
htmlcov/
|
|
17
|
+
coverage.xml
|
|
18
|
+
|
|
19
|
+
# Lint / type-check caches
|
|
20
|
+
.ruff_cache/
|
|
21
|
+
.mypy_cache/
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `fletchr-core` are documented here.
|
|
4
|
+
|
|
5
|
+
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
|
|
6
|
+
this project follows [Semantic Versioning](https://semver.org/).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`FrameArray.measurands(specs)`** — convenience method wrapping
|
|
13
|
+
`MeasurandGroup(specs).apply(self)`. Returns a polars `DataFrame`
|
|
14
|
+
with `time`, `ctime`, and one column per spec, plus `_history` when
|
|
15
|
+
the FrameArray carries lineage. Values accept spec strings or
|
|
16
|
+
pre-built `Measurand` objects. Equivalent in output to
|
|
17
|
+
`MeasurandGroup(specs)(frame_array)`; pick the method when you don't
|
|
18
|
+
need the `Transformer` for `Pipeline` composition.
|
|
19
|
+
- **`MeasurandGroup` now accepts `Mapping[str, MeasurandSpec]`**
|
|
20
|
+
(spec strings or pre-built `Measurand` objects), resolved via
|
|
21
|
+
`resolve_measurand`. Strictly more permissive; existing
|
|
22
|
+
`Mapping[str, str]` callers are unaffected.
|
|
23
|
+
- **Claude Code skills bundled as package data.** Skills under
|
|
24
|
+
`src/fletchr_core/skills/<name>/skill.md` ship in the wheel and
|
|
25
|
+
install into the local project's `./.claude/skills/` by default via
|
|
26
|
+
`python -m fletchr_core.skills install`. Use `--user` to elevate to
|
|
27
|
+
`~/.claude/skills/` instead. Eight skills ship in this release:
|
|
28
|
+
`fletchr-subclass-containers` (Packet / FrameArray / PacketList
|
|
29
|
+
subclassing), `fletchr-measurand-spec` (measurand DSL reference — `Field` and programmatic `Measurand` both use it),
|
|
30
|
+
`fletchr-transformer-authoring` (writing and composing
|
|
31
|
+
Transformers), `fletchr-io-reader-writer` (custom file formats),
|
|
32
|
+
`fletchr-plugin-packaging` (downstream entry-point packages),
|
|
33
|
+
`fletchr-polars-pandas-interop` (DataFrame conversion),
|
|
34
|
+
`fletchr-arrow-gotchas` (recurring extension-type kernel issues),
|
|
35
|
+
and `fletchr-history-and-metadata` (lineage / `metadata` /
|
|
36
|
+
container-mutation idioms).
|
|
37
|
+
- **`Field` is now exported from the top-level `fletchr_core` namespace.**
|
|
38
|
+
`from fletchr_core import Field` works directly; downstream packages no
|
|
39
|
+
longer need to reach into the private `fletchr_core._field` module to
|
|
40
|
+
declare `Packet.fields` / `FrameArray.fields` ClassVars on subclasses.
|
|
41
|
+
- **`Field` specs now accept the full measurand grammar**
|
|
42
|
+
(`parameter ";" encoding ";" euc ";" sampling-strategy`), not just the
|
|
43
|
+
parameter grammar. Existing raw-extraction specs (`Field("[1+2]")`)
|
|
44
|
+
parse as a measurand with no encoding/EUC and behave unchanged.
|
|
45
|
+
Decoded views like `Field("[3+4];2c;EUC[0.1]")` apply Level 1 + Level 2
|
|
46
|
+
at construction time, materializing the decoded column directly.
|
|
47
|
+
- `Field.parsed` (cached_property) and `Field.materialize(table)` are
|
|
48
|
+
the canonical accessors; the framework's class-init machinery
|
|
49
|
+
(`_parsed_fields` ClassVar) is gone — Field owns its own parse cache.
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- **Default output dtype for `Field` is no longer `int64`.** When
|
|
54
|
+
`dtype=None`, no cast is applied — the natural output of the
|
|
55
|
+
measurand chain flows through (`fletchr.uintn(bits=N)` for raw
|
|
56
|
+
extraction, signed integers for `;2c`, `float64` with EUC, etc.).
|
|
57
|
+
Visible impact: `HeaderPacket.fields = {"pid": Field("[1]"), "updl":
|
|
58
|
+
Field("[2]")}` previously produced `int64` columns; now produces
|
|
59
|
+
`fletchr.uintn(bits=word_size)` columns. Callers using
|
|
60
|
+
`pl.pid.tolist()` / iteration are unaffected; callers doing
|
|
61
|
+
`pl.pid.type == pa.int64()` or `pl.pid[0] == 1` (scalar equality)
|
|
62
|
+
need updates — see `pa.ExtensionScalar.as_py()` for the latter.
|
|
63
|
+
- `_clock_decode` (internal helper in `transform/clock.py`) now unwraps
|
|
64
|
+
`ExtensionArray.storage` before applying `pc.fill_null`, to accommodate
|
|
65
|
+
the `fletchr.uintn` extension type now flowing through identity-decoded
|
|
66
|
+
`MeasurandResult.values` (per fletchr-measurand's matching change).
|
|
67
|
+
No observable behavior change for callers.
|
|
68
|
+
- Internal: `int(extracted["pid"][0])` patterns in
|
|
69
|
+
`Packet._extract_pid_pdl_from_header` use `.as_py()` for scalar
|
|
70
|
+
unwrapping, since extracted columns may now be `pa.ExtensionScalar`.
|
|
71
|
+
|
|
72
|
+
### Removed
|
|
73
|
+
|
|
74
|
+
- `Packet._parsed_fields` and `FrameArray._parsed_fields` ClassVars
|
|
75
|
+
(internal). Replaced by per-Field caching via
|
|
76
|
+
`Field.parsed` (cached_property). Subclasses that read
|
|
77
|
+
`cls._parsed_fields` directly (no in-tree callers) should switch to
|
|
78
|
+
`cls.fields[name].parsed`.
|
|
79
|
+
|
|
80
|
+
## Links
|
|
81
|
+
|
|
82
|
+
- [Source](https://github.com/fletchr-labs/fletchr/tree/main/fletchr-core)
|
|
83
|
+
- [PyPI](https://pypi.org/project/fletchr-core/)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jonathan Olsten
|
|
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,269 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fletchr-core
|
|
3
|
+
Version: 0.0.1rc5
|
|
4
|
+
Summary: Arrow-backed frame and packet containers, transformers, pipeline, and I/O for binary protocol data.
|
|
5
|
+
Project-URL: Homepage, https://github.com/fletchr-labs/fletchr
|
|
6
|
+
Project-URL: Repository, https://github.com/fletchr-labs/fletchr
|
|
7
|
+
Project-URL: Issues, https://github.com/fletchr-labs/fletchr/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/fletchr-labs/fletchr/blob/main/fletchr-core/CHANGELOG.md
|
|
9
|
+
Author-email: Jonathan Olsten <jolsten@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: arrow,binary-protocol,framework,pipeline,pyarrow
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Requires-Dist: attrs>=22.1.0
|
|
28
|
+
Requires-Dist: escapement>=0.2.0
|
|
29
|
+
Requires-Dist: eval-type-backport>=0.2.0; python_version < '3.10'
|
|
30
|
+
Requires-Dist: fletchr-measurand>=0.0.1rc4
|
|
31
|
+
Requires-Dist: fletchr-uintn>=0.0.1rc3
|
|
32
|
+
Requires-Dist: numpy>=2.0
|
|
33
|
+
Requires-Dist: ormsgpack>=1.5.0
|
|
34
|
+
Requires-Dist: polars>=0.20
|
|
35
|
+
Requires-Dist: pyarrow>=16
|
|
36
|
+
Requires-Dist: varuintarray>=1.3.0
|
|
37
|
+
Provides-Extra: pandas
|
|
38
|
+
Requires-Dist: pandas>=2.0; extra == 'pandas'
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# fletchr-core
|
|
42
|
+
|
|
43
|
+
Arrow-backed frame and packet containers, transformers, pipeline, and
|
|
44
|
+
I/O for binary protocol data. The integration layer of the
|
|
45
|
+
[`fletchr`](https://github.com/fletchr-labs/fletchr) framework — built
|
|
46
|
+
on [`fletchr-uintn`](https://github.com/fletchr-labs/fletchr/tree/main/fletchr-uintn)
|
|
47
|
+
for bit-precise storage and
|
|
48
|
+
[`fletchr-measurand`](https://github.com/fletchr-labs/fletchr/tree/main/fletchr-measurand)
|
|
49
|
+
for bit-fragment extraction and decoding.
|
|
50
|
+
|
|
51
|
+
## Why?
|
|
52
|
+
|
|
53
|
+
Binary-protocol pipelines repeat the same shape across projects:
|
|
54
|
+
|
|
55
|
+
- **Containers** for fixed-width frames (rectangular) and variable-length
|
|
56
|
+
packets (jagged).
|
|
57
|
+
- **Transformers** to filter, subframe, invert, synchronize, and
|
|
58
|
+
decode-on-the-fly.
|
|
59
|
+
- **I/O** for the formats raw data arrives in (CSV, packed bits, msgpack,
|
|
60
|
+
pickle) and the formats it leaves in (Arrow IPC, Parquet).
|
|
61
|
+
- **A plugin model** so domain-specific packet types and transformers
|
|
62
|
+
plug in cleanly without forking the framework.
|
|
63
|
+
|
|
64
|
+
`fletchr-core` provides each of these as one Arrow-native, null-aware
|
|
65
|
+
unit. Containers are backed by `pa.Table` with
|
|
66
|
+
`fletchr.uintn(bits=N)` columns (no out-of-band schema metadata, nulls
|
|
67
|
+
propagate end-to-end); the Measurand build chain consumes those tables
|
|
68
|
+
directly; transformers compose into pipelines with provenance tracking
|
|
69
|
+
built in.
|
|
70
|
+
|
|
71
|
+
## Features
|
|
72
|
+
|
|
73
|
+
- **Containers** — `FrameArray` (column-major fixed-width frames),
|
|
74
|
+
`MuxFrameArray` (multiplexed variants), `PacketList` (variable-length
|
|
75
|
+
packets, homogeneous), `Packet` / `GenericPacket` / `HeaderPacket` /
|
|
76
|
+
`define_packet` for declaring per-protocol packet types.
|
|
77
|
+
- **`fields` declarations** — `Packet` and `FrameArray` subclasses
|
|
78
|
+
declare data-derived columns via `fields: ClassVar[dict[str, Field]]`
|
|
79
|
+
using the measurand parameter DSL. The framework parses specs at
|
|
80
|
+
class-definition time, materializes columns at construction, and
|
|
81
|
+
exposes them via `__getattr__`. One-line column additions, no codec
|
|
82
|
+
hooks to write.
|
|
83
|
+
- **Transformers + Pipeline** — `FrameFilter`, `MuxFilter`, `MuxSelect`,
|
|
84
|
+
`FrameSynchronizer`, `Subframe`, `Reverse`, `Invert`, `PolarityFix`,
|
|
85
|
+
`PacketFilter`, `PacketSelect`, `PatternPacketExtractor`,
|
|
86
|
+
`DelimitedPacketExtractor`, clock transformers, plus the `Pipeline`
|
|
87
|
+
container for composition.
|
|
88
|
+
- **I/O** — `read_arrow` / `write_arrow` (Arrow IPC), `read_parquet` /
|
|
89
|
+
`write_parquet`, `read_csv` / `write_csv`, `read_bits` / `write_bits`,
|
|
90
|
+
`read_txt` / `write_txt`, `read_pickle` / `write_pickle`, plus the
|
|
91
|
+
generic `read_file` / `write_file` dispatch and `FileReader` /
|
|
92
|
+
`FileWriter` extension points.
|
|
93
|
+
- **Polars + pandas adapters** — `add_columns`, `merge`, `stack` for
|
|
94
|
+
interop with the common DataFrame libraries.
|
|
95
|
+
- **History / provenance** — optional processing-history DAG attached to
|
|
96
|
+
containers; tracks slice / concat / transform operations. Toggle with
|
|
97
|
+
`enable_history` / `disable_history`.
|
|
98
|
+
- **Plugin registry** — downstream packages register custom `Packet`
|
|
99
|
+
subclasses, `Transformer`s, and I/O readers/writers via Python entry
|
|
100
|
+
points. `register_plugin_group("my_pkg.plugins")` to add a new
|
|
101
|
+
discovery namespace.
|
|
102
|
+
- **Custom exception hierarchy** — `FletchrCoreError` root with subsystem
|
|
103
|
+
branches (`ContainerError`, `TransformError`, `CodecError`,
|
|
104
|
+
`IODispatchError`, `RegistryError`, `PluginError`, `GrammarError`).
|
|
105
|
+
Each leaf also multi-inherits the matching builtin (`ValueError` /
|
|
106
|
+
`TypeError` / `KeyError` / `ImportError`) so existing
|
|
107
|
+
`except ValueError:` callers keep working.
|
|
108
|
+
|
|
109
|
+
## Install
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
uv add fletchr-core # or: pip install fletchr-core
|
|
113
|
+
uv add 'fletchr-core[pandas]' # with pandas adapter (polars is required)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Requires Python 3.9+. Pulls in `fletchr-uintn`, `fletchr-measurand`,
|
|
117
|
+
`pyarrow >= 16`, `numpy >= 2.0`, `polars >= 0.20`, `attrs`, `escapement`,
|
|
118
|
+
`ormsgpack`, `varuintarray`.
|
|
119
|
+
|
|
120
|
+
## Quickstart
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
import numpy as np
|
|
124
|
+
from varuintarray import VarUIntArray
|
|
125
|
+
from fletchr_core import FrameArray, write_arrow, read_arrow
|
|
126
|
+
from fletchr_measurand import Measurand, parameter_parser
|
|
127
|
+
|
|
128
|
+
# Build a 2-row, 3-word FrameArray with 8-bit words.
|
|
129
|
+
time = np.array(["2026-01-01", "2026-01-02"], dtype="datetime64[ns]")
|
|
130
|
+
data = VarUIntArray(
|
|
131
|
+
np.array([[10, 20, 30], [40, 50, 60]], dtype=np.uint8),
|
|
132
|
+
word_size=8,
|
|
133
|
+
)
|
|
134
|
+
frame = FrameArray(time=time, ctime=time, data=data)
|
|
135
|
+
|
|
136
|
+
frame.shape # (2, 3)
|
|
137
|
+
frame.table.column_names # ['time', 'ctime', 'c0', 'c1', 'c2']
|
|
138
|
+
|
|
139
|
+
# Compute a measurand directly against the FrameArray's Arrow table.
|
|
140
|
+
# Parameter "[1+2]" reads word 1 + word 2 as a single 16-bit value.
|
|
141
|
+
m = Measurand(parameter=parameter_parser.parse("[1+2]"))
|
|
142
|
+
m.build(frame.table).values.to_pylist()
|
|
143
|
+
# [2580, 10290] = [10*256 + 20, 40*256 + 50]
|
|
144
|
+
|
|
145
|
+
# Round-trip through Arrow IPC.
|
|
146
|
+
write_arrow(frame, "frame.arrow")
|
|
147
|
+
back = read_arrow("frame.arrow")
|
|
148
|
+
assert back.shape == frame.shape
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Declarative fields on a subclass
|
|
152
|
+
|
|
153
|
+
`FrameArray` (and `Packet`) subclasses can declare data-derived columns
|
|
154
|
+
using the [`fletchr-measurand`](https://github.com/fletchr-labs/fletchr/tree/main/fletchr-measurand)
|
|
155
|
+
parameter DSL. The framework parses specs at class-definition time,
|
|
156
|
+
materializes the columns at construction, and exposes them as
|
|
157
|
+
attributes:
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from typing import ClassVar
|
|
161
|
+
import numpy as np
|
|
162
|
+
from varuintarray import VarUIntArray
|
|
163
|
+
from fletchr_core import FrameArray, Field
|
|
164
|
+
|
|
165
|
+
class NumberedFrame(FrameArray):
|
|
166
|
+
fields: ClassVar[dict[str, Field]] = {
|
|
167
|
+
"counter": Field("[1+2]"), # raw 16-bit (uintn(bits=16))
|
|
168
|
+
"counter_halved": Field("[1+2];u;EUC[0.5]"), # decoded float64, scaled ×0.5
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
time = np.array(["2026-01-01", "2026-01-02"], dtype="datetime64[ns]")
|
|
172
|
+
data = VarUIntArray(
|
|
173
|
+
np.array([[0, 1, 100], [1, 0, 200]], dtype=np.uint8),
|
|
174
|
+
word_size=8,
|
|
175
|
+
)
|
|
176
|
+
fa = NumberedFrame(time=time, ctime=time, data=data)
|
|
177
|
+
list(fa.counter) # [1, 256]
|
|
178
|
+
list(fa.counter_halved) # [0.5, 128.0]
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Field specs use the [`fletchr-measurand`](https://github.com/fletchr-labs/fletchr/tree/main/fletchr-measurand)
|
|
182
|
+
grammar — `parameter ";" encoding ";" euc ";" sampling-strategy`,
|
|
183
|
+
encoding/euc/ss independently optional. Parameter-only specs like
|
|
184
|
+
`[1+2]` extract raw bits (natural output: `fletchr.uintn(bits=N)`);
|
|
185
|
+
full measurand specs like `[1+2];2c;EUC[0.1]` apply Level 1 (encoding)
|
|
186
|
+
and Level 2 (engineering-unit conversion) at construction time. When
|
|
187
|
+
`dtype=` is omitted, the column's type is whatever the measurand chain
|
|
188
|
+
naturally produces.
|
|
189
|
+
|
|
190
|
+
## Public API
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
from fletchr_core import (
|
|
194
|
+
# Containers
|
|
195
|
+
FrameArray, MuxFrameArray,
|
|
196
|
+
PacketList, Packet, GenericPacket, HeaderPacket, define_packet,
|
|
197
|
+
# Pipeline
|
|
198
|
+
Transformer, Pipeline, stack,
|
|
199
|
+
# I/O (generic + per-format)
|
|
200
|
+
read_file, write_file, FileReader, FileWriter,
|
|
201
|
+
read_arrow, write_arrow,
|
|
202
|
+
read_parquet, write_parquet,
|
|
203
|
+
read_csv, write_csv,
|
|
204
|
+
read_bits, write_bits,
|
|
205
|
+
read_txt, write_txt,
|
|
206
|
+
read_pickle, write_pickle,
|
|
207
|
+
# DataFrame interop
|
|
208
|
+
add_columns, merge,
|
|
209
|
+
# History
|
|
210
|
+
History, HistoryGraph, HistoryNode,
|
|
211
|
+
enable_history, disable_history, is_history_enabled, reset_history_graph,
|
|
212
|
+
# Plugins
|
|
213
|
+
load_plugins, register_plugin_group,
|
|
214
|
+
# Exceptions
|
|
215
|
+
FletchrCoreError, ContainerError, TransformError,
|
|
216
|
+
HomogeneityError, BitWidthError, SchemaError,
|
|
217
|
+
PipelineError, TransformConfigError,
|
|
218
|
+
CodecError, IODispatchError,
|
|
219
|
+
RegistryError, RegistryCollisionError,
|
|
220
|
+
PluginError, GrammarError,
|
|
221
|
+
)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Concrete transformers (`FrameFilter`, `Invert`, `MuxSelect`, etc.) live
|
|
225
|
+
under `fletchr_core.transform`.
|
|
226
|
+
|
|
227
|
+
## Extensibility
|
|
228
|
+
|
|
229
|
+
`fletchr-core` is a **framework**, not a finished application. Domain
|
|
230
|
+
packages plug in by registering subclasses through Python entry points:
|
|
231
|
+
|
|
232
|
+
```toml
|
|
233
|
+
# in your downstream package's pyproject.toml
|
|
234
|
+
[project.entry-points."fletchr_core.plugins"]
|
|
235
|
+
my_protocol = "my_pkg.fletchr_plugins"
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Modules in that entry-point group are imported lazily on first registry
|
|
239
|
+
access; any `Packet` / `FrameArray` / `Transformer` / `FileReader` /
|
|
240
|
+
`FileWriter` subclasses they define land in the same global registries
|
|
241
|
+
as the built-ins. Use `register_plugin_group("my_namespace.plugins")` to
|
|
242
|
+
add a new discovery namespace.
|
|
243
|
+
|
|
244
|
+
## Claude Code skills
|
|
245
|
+
|
|
246
|
+
`fletchr-core` ships [Claude Code](https://docs.claude.com/claude-code)
|
|
247
|
+
skills as bundled package data. Install them into your user Claude
|
|
248
|
+
skills directory:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
python -m fletchr_core.skills list # show bundled skills
|
|
252
|
+
python -m fletchr_core.skills install # copy to ./.claude/skills/ (project)
|
|
253
|
+
python -m fletchr_core.skills install --user # copy to ~/.claude/skills/
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Project-level is the default (skills are typically scoped to the
|
|
257
|
+
project that needs them); `--user` elevates to user-level.
|
|
258
|
+
|
|
259
|
+
The shipped skills travel versioned with the code they describe — when
|
|
260
|
+
you upgrade `fletchr-core`, re-run `install --force` to refresh.
|
|
261
|
+
|
|
262
|
+
## Links
|
|
263
|
+
|
|
264
|
+
- Source: <https://github.com/fletchr-labs/fletchr>
|
|
265
|
+
- Issues: <https://github.com/fletchr-labs/fletchr/issues>
|
|
266
|
+
|
|
267
|
+
## License
|
|
268
|
+
|
|
269
|
+
MIT.
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# fletchr-core
|
|
2
|
+
|
|
3
|
+
Arrow-backed frame and packet containers, transformers, pipeline, and
|
|
4
|
+
I/O for binary protocol data. The integration layer of the
|
|
5
|
+
[`fletchr`](https://github.com/fletchr-labs/fletchr) framework — built
|
|
6
|
+
on [`fletchr-uintn`](https://github.com/fletchr-labs/fletchr/tree/main/fletchr-uintn)
|
|
7
|
+
for bit-precise storage and
|
|
8
|
+
[`fletchr-measurand`](https://github.com/fletchr-labs/fletchr/tree/main/fletchr-measurand)
|
|
9
|
+
for bit-fragment extraction and decoding.
|
|
10
|
+
|
|
11
|
+
## Why?
|
|
12
|
+
|
|
13
|
+
Binary-protocol pipelines repeat the same shape across projects:
|
|
14
|
+
|
|
15
|
+
- **Containers** for fixed-width frames (rectangular) and variable-length
|
|
16
|
+
packets (jagged).
|
|
17
|
+
- **Transformers** to filter, subframe, invert, synchronize, and
|
|
18
|
+
decode-on-the-fly.
|
|
19
|
+
- **I/O** for the formats raw data arrives in (CSV, packed bits, msgpack,
|
|
20
|
+
pickle) and the formats it leaves in (Arrow IPC, Parquet).
|
|
21
|
+
- **A plugin model** so domain-specific packet types and transformers
|
|
22
|
+
plug in cleanly without forking the framework.
|
|
23
|
+
|
|
24
|
+
`fletchr-core` provides each of these as one Arrow-native, null-aware
|
|
25
|
+
unit. Containers are backed by `pa.Table` with
|
|
26
|
+
`fletchr.uintn(bits=N)` columns (no out-of-band schema metadata, nulls
|
|
27
|
+
propagate end-to-end); the Measurand build chain consumes those tables
|
|
28
|
+
directly; transformers compose into pipelines with provenance tracking
|
|
29
|
+
built in.
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- **Containers** — `FrameArray` (column-major fixed-width frames),
|
|
34
|
+
`MuxFrameArray` (multiplexed variants), `PacketList` (variable-length
|
|
35
|
+
packets, homogeneous), `Packet` / `GenericPacket` / `HeaderPacket` /
|
|
36
|
+
`define_packet` for declaring per-protocol packet types.
|
|
37
|
+
- **`fields` declarations** — `Packet` and `FrameArray` subclasses
|
|
38
|
+
declare data-derived columns via `fields: ClassVar[dict[str, Field]]`
|
|
39
|
+
using the measurand parameter DSL. The framework parses specs at
|
|
40
|
+
class-definition time, materializes columns at construction, and
|
|
41
|
+
exposes them via `__getattr__`. One-line column additions, no codec
|
|
42
|
+
hooks to write.
|
|
43
|
+
- **Transformers + Pipeline** — `FrameFilter`, `MuxFilter`, `MuxSelect`,
|
|
44
|
+
`FrameSynchronizer`, `Subframe`, `Reverse`, `Invert`, `PolarityFix`,
|
|
45
|
+
`PacketFilter`, `PacketSelect`, `PatternPacketExtractor`,
|
|
46
|
+
`DelimitedPacketExtractor`, clock transformers, plus the `Pipeline`
|
|
47
|
+
container for composition.
|
|
48
|
+
- **I/O** — `read_arrow` / `write_arrow` (Arrow IPC), `read_parquet` /
|
|
49
|
+
`write_parquet`, `read_csv` / `write_csv`, `read_bits` / `write_bits`,
|
|
50
|
+
`read_txt` / `write_txt`, `read_pickle` / `write_pickle`, plus the
|
|
51
|
+
generic `read_file` / `write_file` dispatch and `FileReader` /
|
|
52
|
+
`FileWriter` extension points.
|
|
53
|
+
- **Polars + pandas adapters** — `add_columns`, `merge`, `stack` for
|
|
54
|
+
interop with the common DataFrame libraries.
|
|
55
|
+
- **History / provenance** — optional processing-history DAG attached to
|
|
56
|
+
containers; tracks slice / concat / transform operations. Toggle with
|
|
57
|
+
`enable_history` / `disable_history`.
|
|
58
|
+
- **Plugin registry** — downstream packages register custom `Packet`
|
|
59
|
+
subclasses, `Transformer`s, and I/O readers/writers via Python entry
|
|
60
|
+
points. `register_plugin_group("my_pkg.plugins")` to add a new
|
|
61
|
+
discovery namespace.
|
|
62
|
+
- **Custom exception hierarchy** — `FletchrCoreError` root with subsystem
|
|
63
|
+
branches (`ContainerError`, `TransformError`, `CodecError`,
|
|
64
|
+
`IODispatchError`, `RegistryError`, `PluginError`, `GrammarError`).
|
|
65
|
+
Each leaf also multi-inherits the matching builtin (`ValueError` /
|
|
66
|
+
`TypeError` / `KeyError` / `ImportError`) so existing
|
|
67
|
+
`except ValueError:` callers keep working.
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
uv add fletchr-core # or: pip install fletchr-core
|
|
73
|
+
uv add 'fletchr-core[pandas]' # with pandas adapter (polars is required)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Requires Python 3.9+. Pulls in `fletchr-uintn`, `fletchr-measurand`,
|
|
77
|
+
`pyarrow >= 16`, `numpy >= 2.0`, `polars >= 0.20`, `attrs`, `escapement`,
|
|
78
|
+
`ormsgpack`, `varuintarray`.
|
|
79
|
+
|
|
80
|
+
## Quickstart
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
import numpy as np
|
|
84
|
+
from varuintarray import VarUIntArray
|
|
85
|
+
from fletchr_core import FrameArray, write_arrow, read_arrow
|
|
86
|
+
from fletchr_measurand import Measurand, parameter_parser
|
|
87
|
+
|
|
88
|
+
# Build a 2-row, 3-word FrameArray with 8-bit words.
|
|
89
|
+
time = np.array(["2026-01-01", "2026-01-02"], dtype="datetime64[ns]")
|
|
90
|
+
data = VarUIntArray(
|
|
91
|
+
np.array([[10, 20, 30], [40, 50, 60]], dtype=np.uint8),
|
|
92
|
+
word_size=8,
|
|
93
|
+
)
|
|
94
|
+
frame = FrameArray(time=time, ctime=time, data=data)
|
|
95
|
+
|
|
96
|
+
frame.shape # (2, 3)
|
|
97
|
+
frame.table.column_names # ['time', 'ctime', 'c0', 'c1', 'c2']
|
|
98
|
+
|
|
99
|
+
# Compute a measurand directly against the FrameArray's Arrow table.
|
|
100
|
+
# Parameter "[1+2]" reads word 1 + word 2 as a single 16-bit value.
|
|
101
|
+
m = Measurand(parameter=parameter_parser.parse("[1+2]"))
|
|
102
|
+
m.build(frame.table).values.to_pylist()
|
|
103
|
+
# [2580, 10290] = [10*256 + 20, 40*256 + 50]
|
|
104
|
+
|
|
105
|
+
# Round-trip through Arrow IPC.
|
|
106
|
+
write_arrow(frame, "frame.arrow")
|
|
107
|
+
back = read_arrow("frame.arrow")
|
|
108
|
+
assert back.shape == frame.shape
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Declarative fields on a subclass
|
|
112
|
+
|
|
113
|
+
`FrameArray` (and `Packet`) subclasses can declare data-derived columns
|
|
114
|
+
using the [`fletchr-measurand`](https://github.com/fletchr-labs/fletchr/tree/main/fletchr-measurand)
|
|
115
|
+
parameter DSL. The framework parses specs at class-definition time,
|
|
116
|
+
materializes the columns at construction, and exposes them as
|
|
117
|
+
attributes:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from typing import ClassVar
|
|
121
|
+
import numpy as np
|
|
122
|
+
from varuintarray import VarUIntArray
|
|
123
|
+
from fletchr_core import FrameArray, Field
|
|
124
|
+
|
|
125
|
+
class NumberedFrame(FrameArray):
|
|
126
|
+
fields: ClassVar[dict[str, Field]] = {
|
|
127
|
+
"counter": Field("[1+2]"), # raw 16-bit (uintn(bits=16))
|
|
128
|
+
"counter_halved": Field("[1+2];u;EUC[0.5]"), # decoded float64, scaled ×0.5
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
time = np.array(["2026-01-01", "2026-01-02"], dtype="datetime64[ns]")
|
|
132
|
+
data = VarUIntArray(
|
|
133
|
+
np.array([[0, 1, 100], [1, 0, 200]], dtype=np.uint8),
|
|
134
|
+
word_size=8,
|
|
135
|
+
)
|
|
136
|
+
fa = NumberedFrame(time=time, ctime=time, data=data)
|
|
137
|
+
list(fa.counter) # [1, 256]
|
|
138
|
+
list(fa.counter_halved) # [0.5, 128.0]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Field specs use the [`fletchr-measurand`](https://github.com/fletchr-labs/fletchr/tree/main/fletchr-measurand)
|
|
142
|
+
grammar — `parameter ";" encoding ";" euc ";" sampling-strategy`,
|
|
143
|
+
encoding/euc/ss independently optional. Parameter-only specs like
|
|
144
|
+
`[1+2]` extract raw bits (natural output: `fletchr.uintn(bits=N)`);
|
|
145
|
+
full measurand specs like `[1+2];2c;EUC[0.1]` apply Level 1 (encoding)
|
|
146
|
+
and Level 2 (engineering-unit conversion) at construction time. When
|
|
147
|
+
`dtype=` is omitted, the column's type is whatever the measurand chain
|
|
148
|
+
naturally produces.
|
|
149
|
+
|
|
150
|
+
## Public API
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from fletchr_core import (
|
|
154
|
+
# Containers
|
|
155
|
+
FrameArray, MuxFrameArray,
|
|
156
|
+
PacketList, Packet, GenericPacket, HeaderPacket, define_packet,
|
|
157
|
+
# Pipeline
|
|
158
|
+
Transformer, Pipeline, stack,
|
|
159
|
+
# I/O (generic + per-format)
|
|
160
|
+
read_file, write_file, FileReader, FileWriter,
|
|
161
|
+
read_arrow, write_arrow,
|
|
162
|
+
read_parquet, write_parquet,
|
|
163
|
+
read_csv, write_csv,
|
|
164
|
+
read_bits, write_bits,
|
|
165
|
+
read_txt, write_txt,
|
|
166
|
+
read_pickle, write_pickle,
|
|
167
|
+
# DataFrame interop
|
|
168
|
+
add_columns, merge,
|
|
169
|
+
# History
|
|
170
|
+
History, HistoryGraph, HistoryNode,
|
|
171
|
+
enable_history, disable_history, is_history_enabled, reset_history_graph,
|
|
172
|
+
# Plugins
|
|
173
|
+
load_plugins, register_plugin_group,
|
|
174
|
+
# Exceptions
|
|
175
|
+
FletchrCoreError, ContainerError, TransformError,
|
|
176
|
+
HomogeneityError, BitWidthError, SchemaError,
|
|
177
|
+
PipelineError, TransformConfigError,
|
|
178
|
+
CodecError, IODispatchError,
|
|
179
|
+
RegistryError, RegistryCollisionError,
|
|
180
|
+
PluginError, GrammarError,
|
|
181
|
+
)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Concrete transformers (`FrameFilter`, `Invert`, `MuxSelect`, etc.) live
|
|
185
|
+
under `fletchr_core.transform`.
|
|
186
|
+
|
|
187
|
+
## Extensibility
|
|
188
|
+
|
|
189
|
+
`fletchr-core` is a **framework**, not a finished application. Domain
|
|
190
|
+
packages plug in by registering subclasses through Python entry points:
|
|
191
|
+
|
|
192
|
+
```toml
|
|
193
|
+
# in your downstream package's pyproject.toml
|
|
194
|
+
[project.entry-points."fletchr_core.plugins"]
|
|
195
|
+
my_protocol = "my_pkg.fletchr_plugins"
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Modules in that entry-point group are imported lazily on first registry
|
|
199
|
+
access; any `Packet` / `FrameArray` / `Transformer` / `FileReader` /
|
|
200
|
+
`FileWriter` subclasses they define land in the same global registries
|
|
201
|
+
as the built-ins. Use `register_plugin_group("my_namespace.plugins")` to
|
|
202
|
+
add a new discovery namespace.
|
|
203
|
+
|
|
204
|
+
## Claude Code skills
|
|
205
|
+
|
|
206
|
+
`fletchr-core` ships [Claude Code](https://docs.claude.com/claude-code)
|
|
207
|
+
skills as bundled package data. Install them into your user Claude
|
|
208
|
+
skills directory:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
python -m fletchr_core.skills list # show bundled skills
|
|
212
|
+
python -m fletchr_core.skills install # copy to ./.claude/skills/ (project)
|
|
213
|
+
python -m fletchr_core.skills install --user # copy to ~/.claude/skills/
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Project-level is the default (skills are typically scoped to the
|
|
217
|
+
project that needs them); `--user` elevates to user-level.
|
|
218
|
+
|
|
219
|
+
The shipped skills travel versioned with the code they describe — when
|
|
220
|
+
you upgrade `fletchr-core`, re-run `install --force` to refresh.
|
|
221
|
+
|
|
222
|
+
## Links
|
|
223
|
+
|
|
224
|
+
- Source: <https://github.com/fletchr-labs/fletchr>
|
|
225
|
+
- Issues: <https://github.com/fletchr-labs/fletchr/issues>
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
MIT.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Root conftest.
|
|
2
|
+
|
|
3
|
+
Lives at the repo root so that fixtures apply to doctests collected from
|
|
4
|
+
`src/fletchr_core/**/*.py` and `**/*.md`, in addition to the regular
|
|
5
|
+
`tests/`. Per-test-suite fixtures stay in `tests/conftest.py`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
import pytest
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from collections.abc import Iterator
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture(autouse=True)
|
|
20
|
+
def _doctest_namespace(doctest_namespace: dict[str, object]) -> Iterator[None]:
|
|
21
|
+
"""Inject common names + pin numpy print options for doctest stability.
|
|
22
|
+
|
|
23
|
+
Doctests can reference ``np``, ``VarUIntArray``, and any symbol in
|
|
24
|
+
``fletchr_core.__all__`` (top-level public API) or
|
|
25
|
+
``fletchr_core.transform.__all__`` (concrete transformers) without
|
|
26
|
+
importing — same convention numpy/pandas/polars use for their own
|
|
27
|
+
doctests. Sourcing from ``__all__`` keeps the namespace
|
|
28
|
+
auto-in-sync with the public API.
|
|
29
|
+
|
|
30
|
+
``legacy="1.25"`` pins numpy's array repr to the pre-2.0 format so
|
|
31
|
+
examples are stable across numpy versions.
|
|
32
|
+
"""
|
|
33
|
+
from varuintarray import VarUIntArray
|
|
34
|
+
|
|
35
|
+
import fletchr_core
|
|
36
|
+
import fletchr_core.transform
|
|
37
|
+
|
|
38
|
+
saved = np.get_printoptions()
|
|
39
|
+
np.set_printoptions(legacy="1.25") # type: ignore[arg-type] # numpy stubs lag the runtime accepted values
|
|
40
|
+
doctest_namespace["np"] = np
|
|
41
|
+
doctest_namespace["VarUIntArray"] = VarUIntArray
|
|
42
|
+
for name in fletchr_core.__all__:
|
|
43
|
+
doctest_namespace[name] = getattr(fletchr_core, name)
|
|
44
|
+
for name in fletchr_core.transform.__all__:
|
|
45
|
+
doctest_namespace[name] = getattr(fletchr_core.transform, name)
|
|
46
|
+
yield
|
|
47
|
+
np.set_printoptions(**saved)
|