fixtureqa 0.9.2__tar.gz → 0.9.4__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.
- {fixtureqa-0.9.2/fixtureqa.egg-info → fixtureqa-0.9.4}/PKG-INFO +2 -2
- fixtureqa-0.9.4/fixture/core/fix_builder.py +69 -0
- fixtureqa-0.9.4/fixture/static/assets/index-BhnT--gk.js +102 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/static/index.html +1 -1
- {fixtureqa-0.9.2 → fixtureqa-0.9.4/fixtureqa.egg-info}/PKG-INFO +2 -2
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixtureqa.egg-info/SOURCES.txt +1 -1
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixtureqa.egg-info/requires.txt +1 -1
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/pyproject.toml +2 -2
- fixtureqa-0.9.2/fixture/core/fix_builder.py +0 -132
- fixtureqa-0.9.2/fixture/static/assets/index-WJ1w221m.js +0 -102
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/LICENSE +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/README.md +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/__init__.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/__main__.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/__init__.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/app.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/connection_manager.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/deps.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/__init__.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/admin.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/auth.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/branding.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/custom_tags.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/fix_spec.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/messages.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/perf.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/scenarios.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/sessions.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/setup.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/spec_overlay.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/specs.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/templates.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/ws.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/schemas.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/config/__init__.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/__init__.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/atomic_io.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/auth.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/config_store.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/custom_tag_store.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/db_migrations.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/events.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/fix_application.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/fix_parser.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/fix_spec_parser.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/fix_tags.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/fix_time.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/housekeeping.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/inbound.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/json_store.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/latency_csv_writer.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/message_log.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/message_store.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/models.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/perf_charts.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/perf_engine.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/perf_models.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/perf_payload.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/perf_stats.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/perf_store.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/perf_writer.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/scenario_runner.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/scenario_store.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/session.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/session_manager.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/spec_overlay_store.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/spec_store.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/template_store.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/user_store.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/value_gen.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/venue_responses.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/server.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/static/assets/ag-grid-_QKprVdm.js +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/static/assets/index-B-hpSZWZ.css +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/static/assets/index-CyNOPa0n.js +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/static/assets/react-vendor-2eF0YfZT.js +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/static/favicon.svg +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/ui/__init__.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixtureqa.egg-info/dependency_links.txt +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixtureqa.egg-info/entry_points.txt +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixtureqa.egg-info/top_level.txt +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/setup.cfg +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_atomic_io.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_auth.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_config_store.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_connection_manager.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_db_migrations.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_fix_builder.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_health.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_inbound.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_inbound_validation.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_message_store.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_non_stop_session.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_perf_api.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_perf_engine.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_perf_models.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_perf_payload.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_perf_rehydrate.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_scenarios.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_session_auto_response.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_session_lifecycle.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_session_manager_concurrency.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_sessions.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_specs.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_templates.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_timezone_data.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_value_gen.py +0 -0
- {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_ws.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fixtureqa
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.4
|
|
4
4
|
Summary: FIXture — FIX Protocol Testing Tool
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
License-File: LICENSE
|
|
7
|
-
Requires-Dist: fixcore-engine==0.9.
|
|
7
|
+
Requires-Dist: fixcore-engine==0.9.2
|
|
8
8
|
Requires-Dist: fastapi>=0.111.0
|
|
9
9
|
Requires-Dist: uvicorn[standard]>=0.29.0
|
|
10
10
|
Requires-Dist: websockets>=12.0
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Group-aware construction of outbound FIX messages from a raw string.
|
|
3
|
+
|
|
4
|
+
The naive path — parse_raw() into a {tag: value} dict, then set_field() per tag —
|
|
5
|
+
collapses repeating groups: duplicate member tags (e.g. three NoAllocs rows) all
|
|
6
|
+
overwrite down to the last value, while the NoXxx count survives as a literal. So
|
|
7
|
+
the wire ends up claiming N instances but carrying only one.
|
|
8
|
+
|
|
9
|
+
This builder instead keeps fields in order and hands them to fixcore's
|
|
10
|
+
``Message.from_fields`` (the lenient, fields-based counterpart to ``decode``),
|
|
11
|
+
which splits repeating groups using each group's spec definition when a
|
|
12
|
+
DataDictionary is supplied — the same code path the engine uses to decode
|
|
13
|
+
inbound messages, so the two can't drift. Header / seqnum / checksum tags are
|
|
14
|
+
dropped here; the session stamps them on send.
|
|
15
|
+
|
|
16
|
+
Without a DataDictionary (or for a count tag the spec doesn't define) it falls
|
|
17
|
+
back to flat fields — no worse than before, but standard groups now round-trip.
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from typing import Optional
|
|
22
|
+
|
|
23
|
+
from fixcore.message import DataDictionary, Message
|
|
24
|
+
|
|
25
|
+
SOH = "\x01"
|
|
26
|
+
|
|
27
|
+
# Tags the transport owns; never copied from the user's raw into the body.
|
|
28
|
+
CONTROL_TAGS = frozenset({8, 9, 10, 34, 49, 52, 56})
|
|
29
|
+
_MSG_TYPE = 35
|
|
30
|
+
_TRANSACT_TIME = 60
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _ordered_pairs(raw: str) -> list[tuple[int, str]]:
|
|
34
|
+
"""Parse raw FIX into an ORDERED list of (tag, value), preserving duplicates."""
|
|
35
|
+
norm = raw if SOH in raw else raw.replace("|", SOH)
|
|
36
|
+
pairs: list[tuple[int, str]] = []
|
|
37
|
+
for part in norm.split(SOH):
|
|
38
|
+
if "=" not in part:
|
|
39
|
+
continue
|
|
40
|
+
tag_str, _, value = part.partition("=")
|
|
41
|
+
try:
|
|
42
|
+
pairs.append((int(tag_str), value))
|
|
43
|
+
except ValueError:
|
|
44
|
+
continue
|
|
45
|
+
return pairs
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def build_message_from_raw(
|
|
49
|
+
raw: str,
|
|
50
|
+
data_dictionary: Optional[DataDictionary] = None,
|
|
51
|
+
*,
|
|
52
|
+
transact_time: Optional[str] = None,
|
|
53
|
+
) -> Message:
|
|
54
|
+
"""Build a fixcore Message from raw FIX, preserving repeating-group structure
|
|
55
|
+
when *data_dictionary* is supplied. Injects TransactTime(60) when
|
|
56
|
+
*transact_time* is given and absent. Raises ValueError if MsgType is missing."""
|
|
57
|
+
pairs = _ordered_pairs(raw)
|
|
58
|
+
msg_idx = next((i for i, (t, _) in enumerate(pairs) if t == _MSG_TYPE), None)
|
|
59
|
+
if msg_idx is None:
|
|
60
|
+
raise ValueError("Missing MsgType (tag 35)")
|
|
61
|
+
|
|
62
|
+
# Drop transport-owned tags; MsgType (35) stays — from_fields routes it to
|
|
63
|
+
# the header. The session stamps the control tags on send.
|
|
64
|
+
fields = [(t, v) for t, v in pairs if t not in CONTROL_TAGS]
|
|
65
|
+
if transact_time is not None and not any(t == _TRANSACT_TIME for t, _ in fields):
|
|
66
|
+
idx = next(i for i, (t, _) in enumerate(fields) if t == _MSG_TYPE)
|
|
67
|
+
fields.insert(idx + 1, (_TRANSACT_TIME, transact_time))
|
|
68
|
+
|
|
69
|
+
return Message.from_fields(fields, data_dictionary)
|