selene-formats 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.
Files changed (45) hide show
  1. selene_formats-0.1.1/.gitignore +50 -0
  2. selene_formats-0.1.1/CHANGELOG.md +9 -0
  3. selene_formats-0.1.1/PKG-INFO +57 -0
  4. selene_formats-0.1.1/README.md +92 -0
  5. selene_formats-0.1.1/docs/publishing.md +18 -0
  6. selene_formats-0.1.1/docs/trace/0.1.0.md +63 -0
  7. selene_formats-0.1.1/docs/trace/legacy.md +27 -0
  8. selene_formats-0.1.1/docs/versioning.md +9 -0
  9. selene_formats-0.1.1/examples/trace/all-event-types.json +40 -0
  10. selene_formats-0.1.1/examples/trace/legacy.json +25 -0
  11. selene_formats-0.1.1/examples/trace/minimal.json +18 -0
  12. selene_formats-0.1.1/examples/trace/traces.json +22 -0
  13. selene_formats-0.1.1/pyproject.toml +39 -0
  14. selene_formats-0.1.1/python/README.md +40 -0
  15. selene_formats-0.1.1/python/selene_formats/__init__.py +11 -0
  16. selene_formats-0.1.1/python/selene_formats/schema.py +32 -0
  17. selene_formats-0.1.1/python/selene_formats/trace.py +469 -0
  18. selene_formats-0.1.1/python/selene_formats/trace_stream.py +183 -0
  19. selene_formats-0.1.1/python/tests/test_formats_trace.py +278 -0
  20. selene_formats-0.1.1/python/tests/test_legacy_serialization.py +129 -0
  21. selene_formats-0.1.1/python/tests/test_schema.py +306 -0
  22. selene_formats-0.1.1/python/tests/test_trace_stream.py +261 -0
  23. selene_formats-0.1.1/rust/Cargo.toml +24 -0
  24. selene_formats-0.1.1/rust/README.md +34 -0
  25. selene_formats-0.1.1/rust/src/lib.rs +4 -0
  26. selene_formats-0.1.1/rust/src/trace.rs +815 -0
  27. selene_formats-0.1.1/rust/src/trace_stream.rs +671 -0
  28. selene_formats-0.1.1/rust/tests/fixtures/all-event-types.json +40 -0
  29. selene_formats-0.1.1/rust/tests/fixtures/legacy.json +25 -0
  30. selene_formats-0.1.1/rust/tests/fixtures/minimal.json +18 -0
  31. selene_formats-0.1.1/rust/tests/fixtures/trace-stream.msgpack.hex +1 -0
  32. selene_formats-0.1.1/rust/tests/fixtures/traces.json +22 -0
  33. selene_formats-0.1.1/schemas/trace/0.1.0.schema.json +231 -0
  34. selene_formats-0.1.1/schemas/trace/legacy.schema.json +184 -0
  35. selene_formats-0.1.1/scripts/check_package_versions.py +31 -0
  36. selene_formats-0.1.1/scripts/check_schema_immutability.py +151 -0
  37. selene_formats-0.1.1/scripts/validate_schemas.py +44 -0
  38. selene_formats-0.1.1/typescript/README.md +52 -0
  39. selene_formats-0.1.1/typescript/package.json +36 -0
  40. selene_formats-0.1.1/typescript/src/index.ts +3 -0
  41. selene_formats-0.1.1/typescript/src/trace.ts +477 -0
  42. selene_formats-0.1.1/typescript/test/constructors.test.ts +12 -0
  43. selene_formats-0.1.1/typescript/test/protocol.test.mjs +408 -0
  44. selene_formats-0.1.1/typescript/tsconfig.json +12 -0
  45. selene_formats-0.1.1/typescript/tsconfig.test.json +8 -0
@@ -0,0 +1,50 @@
1
+ # Generated by Cargo
2
+ # will have compiled files and executables
3
+ debug/
4
+ target/
5
+
6
+ # These are backup files generated by rustfmt
7
+ **/*.rs.bk
8
+
9
+ # MSVC Windows builds of rustc generate these, which store debugging information
10
+ *.pdb
11
+
12
+ .DS_Store
13
+ 3rdparty/
14
+ .buildsecrets/
15
+ *.whl
16
+ *.pyc
17
+ *.o
18
+ *.so
19
+ *.dll
20
+ *.dll.lib
21
+ *.dll.a
22
+
23
+ venv/
24
+ .venv/
25
+ .vscode/
26
+ .direnv/
27
+ .devenv/
28
+ .hugrenv/
29
+ .devenv.flake.nix
30
+ .pre-commit-config.yaml
31
+ result
32
+
33
+ .envrc
34
+
35
+ __pycache__/
36
+ *.egg-info/
37
+ .ruff_cache/
38
+ .pytest_cache/
39
+ .mypy_cache/
40
+ /devenv.local.nix
41
+ _dist/
42
+
43
+ wheelhouse/
44
+ node_modules/
45
+ .pnpm-store/
46
+ selene-formats/typescript/dist/
47
+ !.cargo
48
+ scratch/
49
+
50
+ .idea
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## [0.1.1](https://github.com/Quantinuum/selene/compare/selene-formats-v0.1.0...selene-formats-v0.1.1) (2026-10-16)
4
+
5
+
6
+ ### Features
7
+
8
+ * add streaming trace format, use it in place of instruction logs ([#235](https://github.com/Quantinuum/selene/issues/235)) ([6f18bda](https://github.com/Quantinuum/selene/commit/6f18bda40783c72c988b0764f3e9578045d29130))
9
+ * Add versioned, cross-language Selene API models ([#212](https://github.com/Quantinuum/selene/issues/212)) ([df33c56](https://github.com/Quantinuum/selene/commit/df33c566cb5e81428c7983e15fe2b1db177c9deb))
@@ -0,0 +1,57 @@
1
+ Metadata-Version: 2.5
2
+ Name: selene-formats
3
+ Version: 0.1.1
4
+ Summary: Selene formats for Python
5
+ Project-URL: repository, https://github.com/quantinuum/selene
6
+ Author: Quantinuum
7
+ License-Expression: Apache-2.0
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Requires-Python: >=3.10
14
+ Requires-Dist: msgpack<2,>=1.1
15
+ Requires-Dist: pydantic~=2.12
16
+ Description-Content-Type: text/markdown
17
+
18
+ # selene-formats for Python
19
+
20
+ Python models for the versioned, serializable Selene trace protocol. See the
21
+ protocol documentation and JSON Schema in the repository's `selene-formats/`
22
+ directory.
23
+
24
+ ```python
25
+ from selene_formats.trace import (
26
+ SCHEMA_VERSION,
27
+ Trace,
28
+ TraceData,
29
+ Traces,
30
+ parse_trace_json,
31
+ )
32
+
33
+ trace = Trace(schema_version=SCHEMA_VERSION, events=[])
34
+ traces = Traces(schema_version=SCHEMA_VERSION, traces=[TraceData(events=[])])
35
+ upgraded = parse_trace_json(versionless_trace_json)
36
+ legacy_json = trace.as_legacy().model_dump_json(indent=2)
37
+ ```
38
+
39
+ `as_legacy()` returns a `LegacyTrace` sharing the current event models and
40
+ validation rules. Its `model_dump()` and `model_dump_json()` omit
41
+ `schema_version` and keep opaque tags numeric. `LegacyTrace` also accepts
42
+ versionless input and defaults its internal version to `0.1.0`. Filtering and
43
+ timing helpers retain legacy serialization. Collections have no legacy format.
44
+
45
+ For trace streams, use the MessagePack API rather than the JSON document parser:
46
+
47
+ ```python
48
+ from selene_formats.trace_stream import iter_trace_stream, write_trace_stream
49
+
50
+ write_trace_stream("trace.msgpack.gz", trace.events)
51
+ for event in iter_trace_stream("trace.msgpack.gz"):
52
+ print(event)
53
+ ```
54
+
55
+ Use `iter_trace_stream_records` to include instruction metadata. Reads are lazy
56
+ and uncached, so keep the source file available until iteration finishes. See
57
+ the [stream format](../README.md#trace-streams) for framing and error handling.
@@ -0,0 +1,92 @@
1
+ # Selene Formats
2
+
3
+ `selene-formats` contains the schemas, documentation, examples, and language
4
+ models for Selene's public serialized traces. It has no dependency on the
5
+ emulator.
6
+
7
+ Selene's `extractor.shots[index].get_trace()` output uses the versionless legacy format and
8
+ `selene_core.trace` models, which use the Python binding's `LegacyTrace` wrapper.
9
+ The language bindings use protocol 0.1.0 and provide adapters for reading and
10
+ serializing legacy traces with the current validation rules.
11
+
12
+ ## APIs
13
+
14
+ | API | Version | Schema | Semantics |
15
+ | --- | --- | --- | --- |
16
+ | Trace documents (`Trace` and `Traces`) | 0.1.0 | [`schemas/trace/0.1.0.schema.json`](schemas/trace/0.1.0.schema.json) | [`docs/trace/0.1.0.md`](docs/trace/0.1.0.md) |
17
+ | Trace (legacy input) | versionless | [`schemas/trace/legacy.schema.json`](schemas/trace/legacy.schema.json) | [`docs/trace/legacy.md`](docs/trace/legacy.md) |
18
+
19
+ Schemas are also published at their canonical `$id` URLs:
20
+
21
+ * [Trace documents 0.1.0](https://quantinuum.github.io/selene/schemas/trace/0.1.0.schema.json)
22
+ * [Trace (legacy input)](https://quantinuum.github.io/selene/schemas/trace/legacy.schema.json)
23
+
24
+ Protocol-wide versioning is described in [`docs/versioning.md`](docs/versioning.md).
25
+
26
+ ## Bindings
27
+
28
+ * Python: [`python/`](python) publishes the `selene-formats` distribution and is
29
+ imported as `selene_formats`.
30
+ * Rust: [`rust/`](rust) publishes the `selene-formats` crate.
31
+ * TypeScript: [`typescript/`](typescript) publishes the `@quantinuum/selene-formats`
32
+ package.
33
+
34
+ The binding release process is described in [`docs/publishing.md`](docs/publishing.md).
35
+
36
+ ## Trace streams
37
+
38
+ `trace_stream` in the Python and Rust APIs implements gzipped MessagePack trace
39
+ streams. This is separate from the complete JSON `Trace` document format, in that
40
+ it is designed to be read and written in a sequential manner.
41
+
42
+ After gzip decompression, the stream consists of consecutive MessagePack objects:
43
+
44
+ 1. A map with `format: "selene.trace.msgpack"`, `format_version: 1`, and
45
+ `schema_version: "0.1.0"`.
46
+ 2. Zero or more record maps.
47
+ 3. A single MessagePack nil (`0xc0`) marking successful completion.
48
+
49
+ Nothing may follow the end marker in the decompressed stream. Readers
50
+ reject missing markers, incomplete records, unsupported headers and bad gzip
51
+ trailers. Exhaust the iterator to check the whole stream; an error cannot retract
52
+ events already yielded.
53
+
54
+ Event records have the existing `source` and `event` fields. Custom payload tags
55
+ use native uint64 values and payload data uses MessagePack binary values, instead
56
+ of hex strings or base64 as used in the JSON format.
57
+
58
+ An event may also contain `instruction: "FutureRead"` or
59
+ `instruction: "MeasureLeakedRequest"`. Batch boundaries are separate maps of the
60
+ form `{"batch_start": {"start_time": 0, "end_time": 0}}`. These are instruction
61
+ metadata, not additional trace events. Event-only readers omit boundaries and
62
+ annotations, so materialising a trace doesn't add gates or change snapshots.
63
+
64
+ Barriers and classical delays use records with `source`, `uint_instruction`,
65
+ `value` and `qubits` fields. `uint_instruction` is `GlobalBarrier`, `LocalBarrier`
66
+ or `ClassicalDelay`, and `value` is a native uint64. Instruction readers preserve
67
+ the full range. Public-event readers and `get_trace()` reject operands above
68
+ `2^53 - 1` with an explicit error because the JSON schema cannot represent them.
69
+ Smaller operands convert to the same gate events as before.
70
+
71
+ Native opaque payload encoding belongs to the stream writer's wrappers. Using
72
+ serde directly on the public Rust models keeps their ordinary representation,
73
+ including hexadecimal tag strings and base64 data, even with a binary serializer.
74
+
75
+ Rust's `TraceStreamWriter` buffers records between caller-selected checkpoints.
76
+ Call `finish()` and check its result before publishing the destination. A failed
77
+ append poisons the writer; it cannot subsequently produce a completed stream.
78
+ `TraceStreamRecordReader` returns records with metadata, while `TraceStreamReader`
79
+ returns public events. The Rust reader adapts native payload fields in memory
80
+ before applying the existing model validation; it does not encode or parse JSON
81
+ text. The Python reader uses `TypeAdapter.validate_python` directly.
82
+
83
+ Python's `write_trace_stream` accepts an iterable and creates a new file without
84
+ overwriting one. `iter_trace_stream_records` and `iter_trace_stream` read lazily
85
+ without caching. Writers use gzip's fast compression setting. Callers own file
86
+ lifetime and removal of failed output; readers never copy or delete artifacts.
87
+ Interactive stacks also leave their run directories in place when closed or
88
+ garbage-collected. Once you're finished reading traces and state dumps, call
89
+ `stack.delete_run_directory()` to close the stack and remove its files. If the
90
+ stack has already been collected, remove the directory yourself using the path
91
+ you saved from `stack.configuration_path.parent`.
92
+ The conventional suffix is `.msgpack.gz`.
@@ -0,0 +1,18 @@
1
+ # Publishing bindings
2
+
3
+ Release Please manages the Python, Rust, and TypeScript bindings as one
4
+ `selene-formats` component. A release PR updates every distribution to the
5
+ same version. Merging it creates this tag:
6
+
7
+ - `selene-formats-v<version>`
8
+
9
+ The formats packages workflow tests and packages all three bindings before it
10
+ publishes all three distributions. Releases are coordinated but not atomic.
11
+
12
+ The binding packages share one version, which remains independent from a trace
13
+ document's `schema_version`.
14
+
15
+ Selene's wheel tests install the Python formats package directly from
16
+ `selene-formats/`, alongside `selene-core/`. This lets us test model changes
17
+ before publishing a new formats release. `just build-ci` uses the same
18
+ installation steps locally through cibuildwheel. Run it inside devenv.
@@ -0,0 +1,63 @@
1
+ # Trace protocol 0.1.0
2
+
3
+ Schema: [`../../schemas/trace/0.1.0.schema.json`](../../schemas/trace/0.1.0.schema.json)
4
+
5
+ ## Documents
6
+
7
+ The schema accepts two top-level document shapes:
8
+
9
+ * `Trace` represents one emulation shot and contains an `events` array.
10
+ * `Traces` represents a collection and contains a `traces` array of
11
+ unversioned `TraceData` objects.
12
+
13
+ Both top-level shapes carry `schema_version`. `TraceData` contains the same
14
+ `events` field as a standalone `Trace`, but does not repeat the collection's
15
+ version. To extract a member as a standalone `Trace`, attach the enclosing
16
+ `schema_version`. A collection may be empty, and its traces retain their given
17
+ order.
18
+
19
+ ## Event ordering and sources
20
+
21
+ `events` is ordered by observation during one emulation shot. An event's
22
+ `source` identifies the stage that emitted it:
23
+
24
+ * `UserProgram`: `index` is that source's zero-based event index.
25
+ * `Runtime`: `start_time` and `end_time` are nanoseconds in the runtime's shot
26
+ timeline.
27
+ * `ErrorModel`: `index` is that source's zero-based event index.
28
+ * `Simulator`: `index` is that source's zero-based event index, and
29
+ `duration_ns` is the wall-clock duration spent in the simulator call.
30
+
31
+ Indices are meaningful within a source only. Consumers must preserve event
32
+ order and must not assume that sources have equal event counts.
33
+
34
+ Indices, qubit IDs, timestamps, and durations are non-negative JSON integers no
35
+ larger than `9007199254740991`, so they can be represented exactly by a
36
+ JavaScript `number`. Opaque-payload tags retain the full unsigned 64-bit range
37
+ and are encoded as canonical hexadecimal strings: a lowercase `0x` prefix
38
+ followed by one to sixteen uppercase hexadecimal digits, with no leading zeroes
39
+ except for `"0x0"`. For example, `"0x11A12517"` is a valid tag, and the maximum
40
+ is `"0xFFFFFFFFFFFFFFFF"`. Gate parameters and values in
41
+ `KeyValuePairPayload` may be signed.
42
+
43
+ ## Encoding
44
+
45
+ `OpaquePayload.data` is base64url-encoded in JSON. Padding may be omitted;
46
+ unused bits in the final Base64 character must be zero. `KeyValuePairPayload` is for
47
+ interoperable, structured custom metadata; consumers that do not understand an
48
+ application-specific key should preserve it when they re-emit the trace.
49
+
50
+ Gate `params` are JSON booleans or numbers. Integer-valued numbers in gate
51
+ parameters and `KeyValuePairPayload` values (including arrays) must be between
52
+ `-9007199254740991` and `9007199254740991`, inclusive. This also applies to
53
+ integer-valued floating-point notation such as `1e20`, which is rejected.
54
+ Fractional numbers remain supported. Gate parameter interpretation is determined
55
+ by `gate_name`.
56
+
57
+ ## Version 0.1.0
58
+
59
+ Version 0.1.0 adds the required `schema_version` field and the `Traces`
60
+ collection document. Every `kind` discriminator is required in a serialized
61
+ trace. Versionless singular traces using the previous numeric opaque-payload
62
+ tag are described by the [`legacy` schema](legacy.md) and can be upgraded by
63
+ the language packages.
@@ -0,0 +1,27 @@
1
+ # Legacy trace format
2
+
3
+ Schema: [`../../schemas/trace/legacy.schema.json`](../../schemas/trace/legacy.schema.json)
4
+
5
+ Selene currently emits trace documents without a `schema_version` through
6
+ `CircuitExtractor` and `selene_core.trace`. These documents use JSON integers
7
+ for indices, qubit IDs, timestamps, durations, and `OpaquePayload.tag`.
8
+
9
+ A missing `schema_version` identifies this format. Producers using the
10
+ `selene-formats` packages emit the versioned format by default and also support
11
+ legacy serialization for singular traces. Their adapters validate legacy input
12
+ and upgrade it to the current in-memory model. Legacy output omits
13
+ `schema_version` and serializes opaque tags as JSON integers; current validation
14
+ bounds still apply. Collections have no legacy format.
15
+
16
+ `OpaquePayload.data` accepts the standard or URL-safe Base64 alphabet, with
17
+ optional padding. Unused bits in the final Base64 character must be zero.
18
+
19
+ The supported legacy schema applies the current JavaScript-safe bounds to
20
+ integer-valued gate parameters and key-value payload values, including arrays:
21
+ `-9007199254740991` through `9007199254740991`. Fractional numbers remain
22
+ supported. Historical traces with values outside those bounds cannot be
23
+ upgraded by these adapters.
24
+
25
+ Legacy opaque tags may exceed JavaScript's safe integer range. TypeScript
26
+ consumers must pass the original JSON text to `parseTraceJson` to preserve such
27
+ tags; parsing them first with `JSON.parse` loses information irreversibly.
@@ -0,0 +1,9 @@
1
+ # Versioning
2
+
3
+ Public schemas use semantic versioning. A document's `schema_version` identifies
4
+ its schema contract, not a Selene release. Schema files use their full version
5
+ in their filenames.
6
+
7
+ The only exception is the versionless legacy trace format. A trace with no
8
+ `schema_version` is interpreted as that legacy format and may be upgraded by
9
+ the model packages. New schemas and producers must always include a version.
@@ -0,0 +1,40 @@
1
+ {
2
+ "schema_version": "0.1.0",
3
+ "events": [
4
+ {
5
+ "source": { "kind": "UserProgram", "index": 0 },
6
+ "event": {
7
+ "kind": "Gate",
8
+ "qubits": [0, 1],
9
+ "gate_name": "RZZ",
10
+ "params": [0.25, true],
11
+ "predicates": [{ "predicate": "ready", "result": true }]
12
+ }
13
+ },
14
+ {
15
+ "source": { "kind": "Runtime", "start_time": 20, "end_time": 50 },
16
+ "event": { "kind": "Measurement", "qubit": 0 }
17
+ },
18
+ {
19
+ "source": { "kind": "ErrorModel", "index": 0 },
20
+ "event": { "kind": "Reset", "qubit": 1 }
21
+ },
22
+ {
23
+ "source": { "kind": "Simulator", "index": 0, "duration_ns": 123 },
24
+ "event": {
25
+ "kind": "Custom",
26
+ "payload": { "kind": "OpaquePayload", "tag": "0x7", "data": "c2VsZW5l" }
27
+ }
28
+ },
29
+ {
30
+ "source": { "kind": "UserProgram", "index": 1 },
31
+ "event": {
32
+ "kind": "Custom",
33
+ "payload": {
34
+ "kind": "KeyValuePairPayload",
35
+ "data": { "name": "analysis", "attempt": 2, "accepted": true }
36
+ }
37
+ }
38
+ }
39
+ ]
40
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "events": [
3
+ {
4
+ "source": { "kind": "UserProgram", "index": 0 },
5
+ "event": {
6
+ "kind": "Gate",
7
+ "qubits": [0],
8
+ "gate_name": "RXY",
9
+ "params": [0.5, 0],
10
+ "predicates": []
11
+ }
12
+ },
13
+ {
14
+ "source": { "kind": "Simulator", "index": 0, "duration_ns": 1 },
15
+ "event": {
16
+ "kind": "Custom",
17
+ "payload": {
18
+ "kind": "OpaquePayload",
19
+ "tag": 11616494188317837126,
20
+ "data": "dHJhY2U="
21
+ }
22
+ }
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "schema_version": "0.1.0",
3
+ "events": [
4
+ {
5
+ "source": {
6
+ "kind": "UserProgram",
7
+ "index": 0
8
+ },
9
+ "event": {
10
+ "kind": "Gate",
11
+ "qubits": [0],
12
+ "gate_name": "RXY",
13
+ "params": [0.5, 0],
14
+ "predicates": []
15
+ }
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "schema_version": "0.1.0",
3
+ "traces": [
4
+ {
5
+ "events": []
6
+ },
7
+ {
8
+ "events": [
9
+ {
10
+ "source": {
11
+ "kind": "UserProgram",
12
+ "index": 0
13
+ },
14
+ "event": {
15
+ "kind": "Measurement",
16
+ "qubit": 0
17
+ }
18
+ }
19
+ ]
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "selene-formats"
7
+ version = "0.1.1"
8
+ description = "Selene formats for Python"
9
+ readme = "python/README.md"
10
+ requires-python = ">=3.10"
11
+ license = "Apache-2.0"
12
+ dependencies = ["pydantic~=2.12", "msgpack>=1.1,<2"]
13
+ authors = [
14
+ { name = "Quantinuum" },
15
+ ]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: Apache Software License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ ]
23
+
24
+ [project.urls]
25
+ repository = "https://github.com/quantinuum/selene"
26
+
27
+ [dependency-groups]
28
+ test = ["jsonschema>=4.23", "pytest>=8.3"]
29
+
30
+ [tool.hatch.build.targets.wheel]
31
+ packages = ["python/selene_formats"]
32
+
33
+ [tool.hatch.build.targets.wheel.force-include]
34
+ "schemas/trace/0.1.0.schema.json" = "selene_formats/schemas/trace/0.1.0.schema.json"
35
+ "schemas/trace/legacy.schema.json" = "selene_formats/schemas/trace/legacy.schema.json"
36
+
37
+ [tool.hatch.build.targets.sdist.force-include]
38
+ "schemas/trace/0.1.0.schema.json" = "schemas/trace/0.1.0.schema.json"
39
+ "schemas/trace/legacy.schema.json" = "schemas/trace/legacy.schema.json"
@@ -0,0 +1,40 @@
1
+ # selene-formats for Python
2
+
3
+ Python models for the versioned, serializable Selene trace protocol. See the
4
+ protocol documentation and JSON Schema in the repository's `selene-formats/`
5
+ directory.
6
+
7
+ ```python
8
+ from selene_formats.trace import (
9
+ SCHEMA_VERSION,
10
+ Trace,
11
+ TraceData,
12
+ Traces,
13
+ parse_trace_json,
14
+ )
15
+
16
+ trace = Trace(schema_version=SCHEMA_VERSION, events=[])
17
+ traces = Traces(schema_version=SCHEMA_VERSION, traces=[TraceData(events=[])])
18
+ upgraded = parse_trace_json(versionless_trace_json)
19
+ legacy_json = trace.as_legacy().model_dump_json(indent=2)
20
+ ```
21
+
22
+ `as_legacy()` returns a `LegacyTrace` sharing the current event models and
23
+ validation rules. Its `model_dump()` and `model_dump_json()` omit
24
+ `schema_version` and keep opaque tags numeric. `LegacyTrace` also accepts
25
+ versionless input and defaults its internal version to `0.1.0`. Filtering and
26
+ timing helpers retain legacy serialization. Collections have no legacy format.
27
+
28
+ For trace streams, use the MessagePack API rather than the JSON document parser:
29
+
30
+ ```python
31
+ from selene_formats.trace_stream import iter_trace_stream, write_trace_stream
32
+
33
+ write_trace_stream("trace.msgpack.gz", trace.events)
34
+ for event in iter_trace_stream("trace.msgpack.gz"):
35
+ print(event)
36
+ ```
37
+
38
+ Use `iter_trace_stream_records` to include instruction metadata. Reads are lazy
39
+ and uncached, so keep the source file available until iteration finishes. See
40
+ the [stream format](../README.md#trace-streams) for framing and error handling.
@@ -0,0 +1,11 @@
1
+ """Selene formats for Python."""
2
+
3
+ from . import trace, trace_stream
4
+ from .schema import get_legacy_trace_schema, get_trace_schema
5
+
6
+ __all__ = [
7
+ "get_legacy_trace_schema",
8
+ "get_trace_schema",
9
+ "trace",
10
+ "trace_stream",
11
+ ]
@@ -0,0 +1,32 @@
1
+ """Access to the JSON Schema for the installed trace-model version."""
2
+
3
+ import json
4
+ from importlib.resources import files
5
+
6
+ try:
7
+ from importlib.resources.abc import Traversable # Python 3.11+
8
+ except ImportError:
9
+ from importlib.abc import Traversable # Python 3.10
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+
14
+ def _get_trace_schema_file(filename: str) -> Traversable:
15
+ schema_file = files("selene_formats").joinpath(f"schemas/trace/{filename}")
16
+ if not schema_file.is_file():
17
+ # The canonical schema lives outside the Python package in a source
18
+ # checkout and is force-included when building a distribution.
19
+ schema_file = Path(__file__).parents[2] / "schemas" / "trace" / filename
20
+ return schema_file
21
+
22
+
23
+ def get_trace_schema() -> dict[str, Any]:
24
+ """Return a new dictionary containing the current trace JSON Schema."""
25
+ schema_file = _get_trace_schema_file("0.1.0.schema.json")
26
+ return json.loads(schema_file.read_text())
27
+
28
+
29
+ def get_legacy_trace_schema() -> dict[str, Any]:
30
+ """Return a new dictionary containing the versionless legacy trace schema."""
31
+ schema_file = _get_trace_schema_file("legacy.schema.json")
32
+ return json.loads(schema_file.read_text())