fixtureqa 0.4.23__tar.gz → 0.4.24__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.4.23/fixtureqa.egg-info → fixtureqa-0.4.24}/PKG-INFO +1 -1
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/routers/perf.py +64 -14
- {fixtureqa-0.4.23 → fixtureqa-0.4.24/fixtureqa.egg-info}/PKG-INFO +1 -1
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/pyproject.toml +1 -1
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_perf_api.py +46 -5
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/LICENSE +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/README.md +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/__init__.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/__main__.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/__init__.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/app.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/connection_manager.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/deps.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/routers/__init__.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/routers/admin.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/routers/auth.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/routers/branding.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/routers/custom_tags.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/routers/fix_spec.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/routers/messages.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/routers/scenarios.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/routers/sessions.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/routers/setup.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/routers/spec_overlay.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/routers/templates.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/routers/ws.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/api/schemas.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/config/__init__.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/__init__.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/atomic_io.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/auth.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/config_store.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/custom_tag_store.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/db_migrations.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/events.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/fix_application.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/fix_builder.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/fix_parser.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/fix_spec_parser.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/fix_tags.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/fix_time.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/housekeeping.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/inbound.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/json_store.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/latency_csv_writer.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/message_log.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/message_store.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/models.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/perf_charts.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/perf_engine.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/perf_models.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/perf_payload.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/perf_stats.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/perf_store.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/perf_writer.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/scenario_runner.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/scenario_store.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/session.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/session_manager.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/spec_overlay_store.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/template_store.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/user_store.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/core/venue_responses.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/fix_specs/FIX42.xml +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/fix_specs/FIX44.xml +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/server.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/static/assets/ag-grid-_QKprVdm.js +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/static/assets/index-B3ccKxdv.js +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/static/assets/index-CyNOPa0n.js +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/static/assets/index-meqdijDf.css +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/static/assets/react-vendor-2eF0YfZT.js +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/static/favicon.svg +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/static/index.html +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixture/ui/__init__.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixtureqa.egg-info/SOURCES.txt +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixtureqa.egg-info/dependency_links.txt +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixtureqa.egg-info/entry_points.txt +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixtureqa.egg-info/requires.txt +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/fixtureqa.egg-info/top_level.txt +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/setup.cfg +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_atomic_io.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_auth.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_config_store.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_connection_manager.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_db_migrations.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_fix_builder.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_health.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_inbound.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_inbound_validation.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_message_store.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_perf_engine.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_perf_models.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_perf_payload.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_perf_rehydrate.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_scenarios.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_session_auto_response.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_session_lifecycle.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_session_manager_concurrency.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_sessions.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_templates.py +0 -0
- {fixtureqa-0.4.23 → fixtureqa-0.4.24}/tests/test_ws.py +0 -0
|
@@ -44,6 +44,32 @@ Admin = Annotated[User, Depends(require_platform_admin)]
|
|
|
44
44
|
|
|
45
45
|
_TERMINAL = ("completed", "stopped", "error")
|
|
46
46
|
|
|
47
|
+
# Per-message persistence — durable_seqnums (fsync per message) and log_messages
|
|
48
|
+
# (a messages.db write per message) — happens inline on the event loop's
|
|
49
|
+
# send/receive path. Measured sustainable rate is ~36 msg/s on a local disk and
|
|
50
|
+
# materially worse on a Docker/WSL2 named volume; past that the loop stalls on
|
|
51
|
+
# fsync and the whole server appears frozen. We hold a conservative ceiling so a
|
|
52
|
+
# logged run at a low rate still works, while a high-rate logged run is refused
|
|
53
|
+
# up front (before the run starts) rather than wedging the server.
|
|
54
|
+
DURABLE_SAFE_MSG_RATE = 30 # messages/second a logged/durable session can sustain
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _est_msgs_per_order(body: RunConfig) -> int:
|
|
58
|
+
"""Rough peak FIX message count generated per order, for the durability rate
|
|
59
|
+
guard: 35=D out + 35=8 ack + fills, plus a request+reply pair for each of the
|
|
60
|
+
amend / cancel flavours when enabled. Deliberately an upper-ish bound."""
|
|
61
|
+
n = 2 + body.payload.fill.fills_per_order # order + ack + fills
|
|
62
|
+
if body.amend.enabled:
|
|
63
|
+
n += 2 # 35=G + 35=8 replace
|
|
64
|
+
if body.cancel.enabled:
|
|
65
|
+
n += 2 # 35=F + 35=8/9 reply
|
|
66
|
+
return max(1, n)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _order_rate_per_s(body: RunConfig) -> float:
|
|
70
|
+
r = body.rate
|
|
71
|
+
return r.orders_per_window * 1000.0 / r.order_window_ms
|
|
72
|
+
|
|
47
73
|
|
|
48
74
|
def _require_started(sm: SessionManager, sid: str, label: str):
|
|
49
75
|
try:
|
|
@@ -68,10 +94,45 @@ async def create_run(body: RunConfig, sm: SM, reg: REG, ts: TS, admin: Admin):
|
|
|
68
94
|
# (a sync def route runs in a threadpool with no running loop).
|
|
69
95
|
need_client = body.mode in ("loopback", "manager", "client")
|
|
70
96
|
need_venue = body.mode in ("loopback", "manager", "venue")
|
|
97
|
+
participating: list[tuple[str, object]] = [] # (label, config) for fixture legs
|
|
71
98
|
if need_client:
|
|
72
|
-
_require_started(sm, body.client_session_id, "client")
|
|
99
|
+
participating.append(("client", _require_started(sm, body.client_session_id, "client").config))
|
|
73
100
|
if need_venue:
|
|
74
|
-
_require_started(sm, body.venue_session_id, "venue")
|
|
101
|
+
participating.append(("venue", _require_started(sm, body.venue_session_id, "venue").config))
|
|
102
|
+
|
|
103
|
+
# Durability rate guard — BEFORE reg.create() (which starts the run). A
|
|
104
|
+
# session with durable_seqnums or log_messages persists every message inline
|
|
105
|
+
# on the event loop; above a low rate this stalls the loop and freezes the
|
|
106
|
+
# whole server. Allow a logged run at a safe low rate (useful to inspect the
|
|
107
|
+
# messages); refuse above it with the safe order rate spelled out.
|
|
108
|
+
logged = [(label, cfg) for label, cfg in participating
|
|
109
|
+
if cfg.durable_seqnums or cfg.log_messages]
|
|
110
|
+
durable_warnings: list[str] = []
|
|
111
|
+
if logged:
|
|
112
|
+
mpo = _est_msgs_per_order(body)
|
|
113
|
+
est_msg_rate = _order_rate_per_s(body) * mpo
|
|
114
|
+
safe_order_rate = DURABLE_SAFE_MSG_RATE / mpo
|
|
115
|
+
|
|
116
|
+
def _flags(cfg) -> str:
|
|
117
|
+
f = [n for n, on in (("durable_seqnums", cfg.durable_seqnums),
|
|
118
|
+
("log_messages", cfg.log_messages)) if on]
|
|
119
|
+
return "+".join(f)
|
|
120
|
+
who = ", ".join(f"{label} ({_flags(cfg)})" for label, cfg in logged)
|
|
121
|
+
|
|
122
|
+
if est_msg_rate > DURABLE_SAFE_MSG_RATE:
|
|
123
|
+
raise HTTPException(status_code=409, detail=(
|
|
124
|
+
f"This run would generate ~{est_msg_rate:.0f} msg/s "
|
|
125
|
+
f"(~{mpo} msgs/order × {_order_rate_per_s(body):.0f} orders/s), but "
|
|
126
|
+
f"{who} persist every message inline on the event loop "
|
|
127
|
+
f"(fsync / messages.db write). Above ~{DURABLE_SAFE_MSG_RATE} msg/s "
|
|
128
|
+
f"this stalls the server. Either lower the order rate to "
|
|
129
|
+
f"≤ {safe_order_rate:.0f}/s to keep message logging, or convert the "
|
|
130
|
+
f"session(s) to a Performance session (durable_seqnums + log_messages "
|
|
131
|
+
f"off) for high-rate runs."))
|
|
132
|
+
durable_warnings.append(
|
|
133
|
+
f"message persistence is on for {who}; safe only up to "
|
|
134
|
+
f"~{safe_order_rate:.0f} orders/s (~{DURABLE_SAFE_MSG_RATE} msg/s) — at "
|
|
135
|
+
f"higher rates the event loop stalls on per-message fsync/DB writes")
|
|
75
136
|
|
|
76
137
|
# CompIDs from the driving (client, else venue) session — for template tokens.
|
|
77
138
|
drive_sid = body.client_session_id if need_client else body.venue_session_id
|
|
@@ -82,18 +143,7 @@ async def create_run(body: RunConfig, sm: SM, reg: REG, ts: TS, admin: Admin):
|
|
|
82
143
|
except PerfConfigError as e:
|
|
83
144
|
raise HTTPException(status_code=422, detail=str(e))
|
|
84
145
|
|
|
85
|
-
|
|
86
|
-
checked = []
|
|
87
|
-
if need_client:
|
|
88
|
-
checked.append((body.client_session_id, "client"))
|
|
89
|
-
if need_venue:
|
|
90
|
-
checked.append((body.venue_session_id, "venue"))
|
|
91
|
-
for sid, label in checked:
|
|
92
|
-
cfg = sm.get_state(sid).config
|
|
93
|
-
if cfg.durable_seqnums:
|
|
94
|
-
run.warnings.append(f"{label} session has durable_seqnums=True — fsync per message caps throughput")
|
|
95
|
-
if cfg.log_messages:
|
|
96
|
-
run.warnings.append(f"{label} session has log_messages=True — persistence churn adds loop latency")
|
|
146
|
+
run.warnings.extend(durable_warnings)
|
|
97
147
|
return {"run_id": run.run_id}
|
|
98
148
|
|
|
99
149
|
|
|
@@ -15,12 +15,13 @@ def _free_port() -> int:
|
|
|
15
15
|
return s.getsockname()[1]
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def _perf_session(port, *, acceptor: bool, sender, target, role
|
|
18
|
+
def _perf_session(port, *, acceptor: bool, sender, target, role,
|
|
19
|
+
durable=False, log=False):
|
|
19
20
|
body = {
|
|
20
21
|
"sender_comp_id": sender, "target_comp_id": target,
|
|
21
22
|
"connection_type": "acceptor" if acceptor else "initiator",
|
|
22
23
|
"port": port, "use_file_log": False,
|
|
23
|
-
"durable_seqnums":
|
|
24
|
+
"durable_seqnums": durable, "log_messages": log, "session_role": role,
|
|
24
25
|
}
|
|
25
26
|
if not acceptor:
|
|
26
27
|
body["host"] = "127.0.0.1"
|
|
@@ -38,12 +39,14 @@ def _wait_status(client, sid, target, timeout=10.0):
|
|
|
38
39
|
raise TimeoutError(f"{sid} did not reach {target} (last {st})")
|
|
39
40
|
|
|
40
41
|
|
|
41
|
-
def _loopback_pair(client):
|
|
42
|
+
def _loopback_pair(client, *, durable=False, log=False):
|
|
42
43
|
port = _free_port()
|
|
43
44
|
acc = client.post("/api/sessions/", json=_perf_session(
|
|
44
|
-
port, acceptor=True, sender="VENUE", target="CLIENT", role="venue"
|
|
45
|
+
port, acceptor=True, sender="VENUE", target="CLIENT", role="venue",
|
|
46
|
+
durable=durable, log=log)).json()["session_id"]
|
|
45
47
|
ini = client.post("/api/sessions/", json=_perf_session(
|
|
46
|
-
port, acceptor=False, sender="CLIENT", target="VENUE", role="client"
|
|
48
|
+
port, acceptor=False, sender="CLIENT", target="VENUE", role="client",
|
|
49
|
+
durable=durable, log=log)).json()["session_id"]
|
|
47
50
|
client.post(f"/api/sessions/{acc}/start")
|
|
48
51
|
client.post(f"/api/sessions/{ini}/start")
|
|
49
52
|
_wait_status(client, ini, "LOGGED_ON")
|
|
@@ -101,6 +104,44 @@ def test_perf_run_lifecycle_and_ws(authed):
|
|
|
101
104
|
assert exp.status_code == 200 and exp.text.startswith("id,run_id,")
|
|
102
105
|
|
|
103
106
|
|
|
107
|
+
def test_perf_run_refused_on_durable_session_at_high_rate(authed):
|
|
108
|
+
# A durable/logged session persists every message inline on the event loop;
|
|
109
|
+
# a high-rate run would stall the server, so it must be refused up front
|
|
110
|
+
# (before the run starts) — not silently freeze.
|
|
111
|
+
client, _ = authed
|
|
112
|
+
ini, acc = _loopback_pair(client, durable=True, log=True)
|
|
113
|
+
# _run_body default: 20 orders/100ms = 200 orders/s → far over the safe rate.
|
|
114
|
+
r = client.post("/api/perf/runs", json=_run_body(ini, acc, duration=2))
|
|
115
|
+
assert r.status_code == 409, r.text
|
|
116
|
+
detail = r.json()["detail"]
|
|
117
|
+
assert "Performance session" in detail and "msg/s" in detail
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_perf_run_allows_durable_session_at_low_rate(authed):
|
|
121
|
+
# Logging a perf run at a low rate is allowed (useful to inspect messages) —
|
|
122
|
+
# it just carries a warning about the safe ceiling.
|
|
123
|
+
client, _ = authed
|
|
124
|
+
ini, acc = _loopback_pair(client, durable=True, log=True)
|
|
125
|
+
body = _run_body(ini, acc, duration=2)
|
|
126
|
+
body["rate"] = {"orders_per_window": 1, "order_window_ms": 1000,
|
|
127
|
+
"fills_per_window": 1, "fill_window_ms": 1000} # 1 order/s
|
|
128
|
+
r = client.post("/api/perf/runs", json=body)
|
|
129
|
+
assert r.status_code == 201, r.text
|
|
130
|
+
run_id = r.json()["run_id"]
|
|
131
|
+
st = client.get(f"/api/perf/runs/{run_id}").json()
|
|
132
|
+
assert any("message persistence is on" in w for w in st["warnings"]), st["warnings"]
|
|
133
|
+
client.delete(f"/api/perf/runs/{run_id}") # stop the slow run
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_perf_run_no_rate_limit_on_perf_sessions(authed):
|
|
137
|
+
# Performance sessions (durability off) carry no rate guard — high rate ok.
|
|
138
|
+
client, _ = authed
|
|
139
|
+
ini, acc = _loopback_pair(client) # durable=False, log=False
|
|
140
|
+
r = client.post("/api/perf/runs", json=_run_body(ini, acc, duration=2))
|
|
141
|
+
assert r.status_code == 201, r.text
|
|
142
|
+
client.delete(f"/api/perf/runs/{r.json()['run_id']}")
|
|
143
|
+
|
|
144
|
+
|
|
104
145
|
def test_perf_configs_crud(authed):
|
|
105
146
|
client, _ = authed
|
|
106
147
|
r = client.post("/api/perf/configs", json=_run_body("c", "v"))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|