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.
Files changed (112) hide show
  1. fletchr_core-0.0.1rc5/.gitignore +21 -0
  2. fletchr_core-0.0.1rc5/CHANGELOG.md +83 -0
  3. fletchr_core-0.0.1rc5/LICENSE +21 -0
  4. fletchr_core-0.0.1rc5/PKG-INFO +269 -0
  5. fletchr_core-0.0.1rc5/README.md +229 -0
  6. fletchr_core-0.0.1rc5/conftest.py +47 -0
  7. fletchr_core-0.0.1rc5/pyproject.toml +93 -0
  8. fletchr_core-0.0.1rc5/src/fletchr_core/__init__.py +193 -0
  9. fletchr_core-0.0.1rc5/src/fletchr_core/_arrow_ipc.py +101 -0
  10. fletchr_core-0.0.1rc5/src/fletchr_core/_field.py +144 -0
  11. fletchr_core-0.0.1rc5/src/fletchr_core/_grammar.py +237 -0
  12. fletchr_core-0.0.1rc5/src/fletchr_core/_history.py +367 -0
  13. fletchr_core-0.0.1rc5/src/fletchr_core/_plugins.py +100 -0
  14. fletchr_core-0.0.1rc5/src/fletchr_core/_registry.py +50 -0
  15. fletchr_core-0.0.1rc5/src/fletchr_core/_repr.py +266 -0
  16. fletchr_core-0.0.1rc5/src/fletchr_core/_serialization.py +177 -0
  17. fletchr_core-0.0.1rc5/src/fletchr_core/_version.py +34 -0
  18. fletchr_core-0.0.1rc5/src/fletchr_core/containers/__init__.py +13 -0
  19. fletchr_core-0.0.1rc5/src/fletchr_core/containers/_statistics.py +215 -0
  20. fletchr_core-0.0.1rc5/src/fletchr_core/containers/framearray.py +998 -0
  21. fletchr_core-0.0.1rc5/src/fletchr_core/containers/packetlist.py +853 -0
  22. fletchr_core-0.0.1rc5/src/fletchr_core/containers/packets.py +439 -0
  23. fletchr_core-0.0.1rc5/src/fletchr_core/dataframes/__init__.py +116 -0
  24. fletchr_core-0.0.1rc5/src/fletchr_core/dataframes/_pandas.py +49 -0
  25. fletchr_core-0.0.1rc5/src/fletchr_core/dataframes/_polars.py +58 -0
  26. fletchr_core-0.0.1rc5/src/fletchr_core/docs/__init__.py +6 -0
  27. fletchr_core-0.0.1rc5/src/fletchr_core/docs/api.md +82 -0
  28. fletchr_core-0.0.1rc5/src/fletchr_core/docs/architecture.md +128 -0
  29. fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/custom-io.md +119 -0
  30. fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/custom-transformers.md +107 -0
  31. fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/data-structures.md +270 -0
  32. fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/docstrings.md +101 -0
  33. fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/index.md +31 -0
  34. fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/pipeline.md +105 -0
  35. fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/plugins.md +195 -0
  36. fletchr_core-0.0.1rc5/src/fletchr_core/docs/developers/registries.md +126 -0
  37. fletchr_core-0.0.1rc5/src/fletchr_core/docs/history.md +113 -0
  38. fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/arrow.md +67 -0
  39. fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/bits.md +73 -0
  40. fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/csv.md +66 -0
  41. fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/index.md +49 -0
  42. fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/parquet.md +60 -0
  43. fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/pickle.md +55 -0
  44. fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/txt.md +51 -0
  45. fletchr_core-0.0.1rc5/src/fletchr_core/docs/io/wire-format.md +193 -0
  46. fletchr_core-0.0.1rc5/src/fletchr_core/exceptions.py +101 -0
  47. fletchr_core-0.0.1rc5/src/fletchr_core/io/__init__.py +68 -0
  48. fletchr_core-0.0.1rc5/src/fletchr_core/io/_delimited.py +564 -0
  49. fletchr_core-0.0.1rc5/src/fletchr_core/io/arrow.py +98 -0
  50. fletchr_core-0.0.1rc5/src/fletchr_core/io/base.py +164 -0
  51. fletchr_core-0.0.1rc5/src/fletchr_core/io/bits.py +206 -0
  52. fletchr_core-0.0.1rc5/src/fletchr_core/io/csv.py +160 -0
  53. fletchr_core-0.0.1rc5/src/fletchr_core/io/parquet.py +169 -0
  54. fletchr_core-0.0.1rc5/src/fletchr_core/io/pickle.py +62 -0
  55. fletchr_core-0.0.1rc5/src/fletchr_core/io/txt.py +145 -0
  56. fletchr_core-0.0.1rc5/src/fletchr_core/py.typed +0 -0
  57. fletchr_core-0.0.1rc5/src/fletchr_core/skills/README.md +41 -0
  58. fletchr_core-0.0.1rc5/src/fletchr_core/skills/__init__.py +9 -0
  59. fletchr_core-0.0.1rc5/src/fletchr_core/skills/__main__.py +123 -0
  60. fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-arrow-gotchas/skill.md +100 -0
  61. fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-history-and-metadata/skill.md +130 -0
  62. fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-io-reader-writer/skill.md +118 -0
  63. fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-measurand-spec/skill.md +104 -0
  64. fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-plugin-packaging/skill.md +116 -0
  65. fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-polars-pandas-interop/skill.md +137 -0
  66. fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-subclass-containers/skill.md +85 -0
  67. fletchr_core-0.0.1rc5/src/fletchr_core/skills/fletchr-transformer-authoring/skill.md +102 -0
  68. fletchr_core-0.0.1rc5/src/fletchr_core/transform/__init__.py +52 -0
  69. fletchr_core-0.0.1rc5/src/fletchr_core/transform/base.py +411 -0
  70. fletchr_core-0.0.1rc5/src/fletchr_core/transform/clock.py +533 -0
  71. fletchr_core-0.0.1rc5/src/fletchr_core/transform/frame.py +529 -0
  72. fletchr_core-0.0.1rc5/src/fletchr_core/transform/measurand.py +213 -0
  73. fletchr_core-0.0.1rc5/src/fletchr_core/transform/packet.py +511 -0
  74. fletchr_core-0.0.1rc5/src/fletchr_core/typing.py +30 -0
  75. fletchr_core-0.0.1rc5/tests/conftest.py +73 -0
  76. fletchr_core-0.0.1rc5/tests/containers/test_framearray.py +257 -0
  77. fletchr_core-0.0.1rc5/tests/containers/test_framearray_fields.py +178 -0
  78. fletchr_core-0.0.1rc5/tests/containers/test_muxframearray.py +279 -0
  79. fletchr_core-0.0.1rc5/tests/containers/test_packet.py +233 -0
  80. fletchr_core-0.0.1rc5/tests/containers/test_packetlist.py +237 -0
  81. fletchr_core-0.0.1rc5/tests/data/sync_test_stream.csv +60 -0
  82. fletchr_core-0.0.1rc5/tests/dataframes/test_dispatch.py +108 -0
  83. fletchr_core-0.0.1rc5/tests/dataframes/test_pandas.py +87 -0
  84. fletchr_core-0.0.1rc5/tests/dataframes/test_polars.py +77 -0
  85. fletchr_core-0.0.1rc5/tests/integration/test_concatenate.py +139 -0
  86. fletchr_core-0.0.1rc5/tests/integration/test_edge_cases.py +165 -0
  87. fletchr_core-0.0.1rc5/tests/integration/test_history_integration.py +235 -0
  88. fletchr_core-0.0.1rc5/tests/integration/test_properties.py +209 -0
  89. fletchr_core-0.0.1rc5/tests/io/test_bits.py +340 -0
  90. fletchr_core-0.0.1rc5/tests/io/test_csv.py +346 -0
  91. fletchr_core-0.0.1rc5/tests/io/test_filereader.py +238 -0
  92. fletchr_core-0.0.1rc5/tests/io/test_filewriter.py +143 -0
  93. fletchr_core-0.0.1rc5/tests/io/test_parquet.py +158 -0
  94. fletchr_core-0.0.1rc5/tests/io/test_txt.py +288 -0
  95. fletchr_core-0.0.1rc5/tests/test_grammar.py +147 -0
  96. fletchr_core-0.0.1rc5/tests/test_history.py +338 -0
  97. fletchr_core-0.0.1rc5/tests/test_plugins.py +306 -0
  98. fletchr_core-0.0.1rc5/tests/test_registry_collisions.py +359 -0
  99. fletchr_core-0.0.1rc5/tests/test_repr.py +399 -0
  100. fletchr_core-0.0.1rc5/tests/test_serialization.py +117 -0
  101. fletchr_core-0.0.1rc5/tests/transform/test_clocks.py +531 -0
  102. fletchr_core-0.0.1rc5/tests/transform/test_convenience_method_docs.py +22 -0
  103. fletchr_core-0.0.1rc5/tests/transform/test_delimited_packet_extractor.py +243 -0
  104. fletchr_core-0.0.1rc5/tests/transform/test_frame_filter.py +151 -0
  105. fletchr_core-0.0.1rc5/tests/transform/test_measurand_group.py +317 -0
  106. fletchr_core-0.0.1rc5/tests/transform/test_mux_transformers.py +122 -0
  107. fletchr_core-0.0.1rc5/tests/transform/test_packet_filter.py +174 -0
  108. fletchr_core-0.0.1rc5/tests/transform/test_packet_select.py +109 -0
  109. fletchr_core-0.0.1rc5/tests/transform/test_pattern_packet_extractor.py +178 -0
  110. fletchr_core-0.0.1rc5/tests/transform/test_polarity_fix.py +180 -0
  111. fletchr_core-0.0.1rc5/tests/transform/test_sync.py +194 -0
  112. 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)