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.
Files changed (108) hide show
  1. {fixtureqa-0.9.2/fixtureqa.egg-info → fixtureqa-0.9.4}/PKG-INFO +2 -2
  2. fixtureqa-0.9.4/fixture/core/fix_builder.py +69 -0
  3. fixtureqa-0.9.4/fixture/static/assets/index-BhnT--gk.js +102 -0
  4. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/static/index.html +1 -1
  5. {fixtureqa-0.9.2 → fixtureqa-0.9.4/fixtureqa.egg-info}/PKG-INFO +2 -2
  6. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixtureqa.egg-info/SOURCES.txt +1 -1
  7. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixtureqa.egg-info/requires.txt +1 -1
  8. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/pyproject.toml +2 -2
  9. fixtureqa-0.9.2/fixture/core/fix_builder.py +0 -132
  10. fixtureqa-0.9.2/fixture/static/assets/index-WJ1w221m.js +0 -102
  11. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/LICENSE +0 -0
  12. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/README.md +0 -0
  13. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/__init__.py +0 -0
  14. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/__main__.py +0 -0
  15. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/__init__.py +0 -0
  16. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/app.py +0 -0
  17. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/connection_manager.py +0 -0
  18. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/deps.py +0 -0
  19. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/__init__.py +0 -0
  20. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/admin.py +0 -0
  21. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/auth.py +0 -0
  22. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/branding.py +0 -0
  23. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/custom_tags.py +0 -0
  24. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/fix_spec.py +0 -0
  25. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/messages.py +0 -0
  26. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/perf.py +0 -0
  27. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/scenarios.py +0 -0
  28. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/sessions.py +0 -0
  29. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/setup.py +0 -0
  30. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/spec_overlay.py +0 -0
  31. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/specs.py +0 -0
  32. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/templates.py +0 -0
  33. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/routers/ws.py +0 -0
  34. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/api/schemas.py +0 -0
  35. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/config/__init__.py +0 -0
  36. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/__init__.py +0 -0
  37. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/atomic_io.py +0 -0
  38. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/auth.py +0 -0
  39. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/config_store.py +0 -0
  40. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/custom_tag_store.py +0 -0
  41. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/db_migrations.py +0 -0
  42. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/events.py +0 -0
  43. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/fix_application.py +0 -0
  44. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/fix_parser.py +0 -0
  45. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/fix_spec_parser.py +0 -0
  46. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/fix_tags.py +0 -0
  47. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/fix_time.py +0 -0
  48. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/housekeeping.py +0 -0
  49. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/inbound.py +0 -0
  50. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/json_store.py +0 -0
  51. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/latency_csv_writer.py +0 -0
  52. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/message_log.py +0 -0
  53. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/message_store.py +0 -0
  54. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/models.py +0 -0
  55. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/perf_charts.py +0 -0
  56. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/perf_engine.py +0 -0
  57. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/perf_models.py +0 -0
  58. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/perf_payload.py +0 -0
  59. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/perf_stats.py +0 -0
  60. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/perf_store.py +0 -0
  61. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/perf_writer.py +0 -0
  62. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/scenario_runner.py +0 -0
  63. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/scenario_store.py +0 -0
  64. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/session.py +0 -0
  65. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/session_manager.py +0 -0
  66. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/spec_overlay_store.py +0 -0
  67. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/spec_store.py +0 -0
  68. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/template_store.py +0 -0
  69. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/user_store.py +0 -0
  70. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/value_gen.py +0 -0
  71. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/core/venue_responses.py +0 -0
  72. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/server.py +0 -0
  73. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/static/assets/ag-grid-_QKprVdm.js +0 -0
  74. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/static/assets/index-B-hpSZWZ.css +0 -0
  75. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/static/assets/index-CyNOPa0n.js +0 -0
  76. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/static/assets/react-vendor-2eF0YfZT.js +0 -0
  77. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/static/favicon.svg +0 -0
  78. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixture/ui/__init__.py +0 -0
  79. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixtureqa.egg-info/dependency_links.txt +0 -0
  80. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixtureqa.egg-info/entry_points.txt +0 -0
  81. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/fixtureqa.egg-info/top_level.txt +0 -0
  82. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/setup.cfg +0 -0
  83. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_atomic_io.py +0 -0
  84. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_auth.py +0 -0
  85. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_config_store.py +0 -0
  86. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_connection_manager.py +0 -0
  87. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_db_migrations.py +0 -0
  88. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_fix_builder.py +0 -0
  89. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_health.py +0 -0
  90. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_inbound.py +0 -0
  91. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_inbound_validation.py +0 -0
  92. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_message_store.py +0 -0
  93. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_non_stop_session.py +0 -0
  94. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_perf_api.py +0 -0
  95. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_perf_engine.py +0 -0
  96. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_perf_models.py +0 -0
  97. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_perf_payload.py +0 -0
  98. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_perf_rehydrate.py +0 -0
  99. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_scenarios.py +0 -0
  100. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_session_auto_response.py +0 -0
  101. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_session_lifecycle.py +0 -0
  102. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_session_manager_concurrency.py +0 -0
  103. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_sessions.py +0 -0
  104. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_specs.py +0 -0
  105. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_templates.py +0 -0
  106. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_timezone_data.py +0 -0
  107. {fixtureqa-0.9.2 → fixtureqa-0.9.4}/tests/test_value_gen.py +0 -0
  108. {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.2
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.1
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)