Option C oc_diagnostics
Loading...
Searching...
No Matches
instrumentation_selftest_app.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# oc_diagnostics/instrumentation_selftest_app.py
4
5"""
6Instrumentation Self‑Test (Dash App)
7====================================
8
9Purpose
10-------
11A minimal, deterministic Dash application used exclusively for **manual**
12validation of the instrumentation system:
13
14- ID rewriting (instrument_ids)
15- Dynamic instrumentation of callback outputs
16- UI event logger integration
17- Dispatcher patching (middleware)
18- Logging behavior under different debug-level modes
19
20This module is NOT used in production and is intentionally verbose.
21
22.. warning::
23 **This is a manual integration harness, not an automated test.**
24 Running this module launches a Dash development server that blocks
25 indefinitely until interrupted (Ctrl-C). It produces no assertions,
26 no exit code, and no machine-readable result. It cannot be integrated
27 into CI/CD pipelines. Use ``instrumentation_selftest_cli.py`` for
28 headless, non-blocking instrumentation checks.
29
30Lifecycle
31---------
321. Create Dash app
332. Build static layout (UNINSTRUMENTED — IDs must remain strings)
343. Register UI event logger
354. Register callbacks
365. Enable instrumentation middleware
376. Run the development server (blocks until Ctrl-C)
38
39Static layout must NOT be instrumented because Dash callback decorators
40match string IDs. Dynamic content *is* instrumented explicitly using:
41
42 return apply_instrumentation(dynamic_component_tree)
43
44Debug-Level Modes
45-----------------
46MODE A — FORCE VERBOSE (default)
47 DEBUG_LEVELS["INSTRUMENTATION"] = 9
48 DEBUG_LEVELS["UI"] = 9
49
50MODE B — RESPECT GLOBAL LEVELS
51 Use whatever levels are defined in unified_debug.py
52
53Select mode via:
54 python -m oc_diagnostics.instrumentation_selftest_app --use-global
55
56Responsibilities
57----------------
58- Build a static Dash layout with string IDs (NOT instrumented at build
59 time, so callback decorators can match by string ID).
60- Register the UI event logger and all callbacks.
61- Enable instrumentation middleware via ``enable_instrumentation_middleware()``
62 after all callbacks are registered.
63- Optionally force maximum verbosity (default) or use global levels.
64- Launch the Dash development server (blocking call).
65
66Diagnostics
67-----------
68Domain: INSTRUMENTATION
69Levels:
70 L2 — lifecycle
71 L3 — semantic details
72 L4 — deep tracing
73
74Contracts
75---------
76- Must never be used in CI/CD or automated testing.
77- Must not instrument the static layout at build time.
78- Dynamic callback outputs must be instrumented via ``apply_instrumentation()``.
79- Requires Dash (``pip install oc_diagnostics[dash]``).
80"""
81
82import argparse
83from dash import Dash, html, dcc, Input, Output
84
86 enable_instrumentation_middleware,
87 apply_instrumentation,
88)
89from oc_diagnostics.unified_debug import _dbg, DEBUG_LEVELS
90from oc_diagnostics.ui_event_logger import ui_event_logger
91
92
93# ============================================================
94# Create the test app
95# ============================================================
96
97def create_test_app(force_verbose: bool):
98 """
99 Build the test app following the same lifecycle as the main app.
100
101 Parameters
102 ----------
103 force_verbose : bool
104 If True, override global debug levels and force maximum verbosity.
105 """
106
107 # ------------------------------------------------------------
108 # 0. Debug-level override (optional)
109 # ------------------------------------------------------------
110 if force_verbose:
111 DEBUG_LEVELS["INSTRUMENTATION"] = 9
112 DEBUG_LEVELS["UI"] = 9
113 _dbg("TEST", "debug_levels", "FORCING MAX VERBOSITY", override=True)
114 else:
115 _dbg("TEST", "debug_levels", "Using GLOBAL debug levels", override=True)
116
117 # ------------------------------------------------------------
118 # 1. Create Dash app
119 # ------------------------------------------------------------
120 app = Dash(__name__)
121
122 # ------------------------------------------------------------
123 # 2. Build static layout (UNINSTRUMENTED)
124 # ------------------------------------------------------------
125 app.layout = html.Div(
126 [
127 html.Button("Click me", id="btn"),
128 html.Div(id="out"),
129 dcc.Store(id="debug-settings-data-store"),
130 ]
131 )
132
133 # ------------------------------------------------------------
134 # 3. Register UI event logger
135 # ------------------------------------------------------------
136 ui_event_logger(app)
137
138 # ------------------------------------------------------------
139 # 4. Register callbacks
140 # ------------------------------------------------------------
141 @app.callback(
142 Output("out", "children"),
143 Input("btn", "n_clicks"),
144 )
145 def update_output(n):
146 if n is None:
147 return "Click the button"
148 # Dynamic instrumentation
149 return apply_instrumentation(html.Div(f"Clicked {n} times"))
150
151 # ------------------------------------------------------------
152 # 5. Enable instrumentation middleware
153 # ------------------------------------------------------------
154 enable_instrumentation_middleware(app)
155
156 return app
157
158
159# ============================================================
160# CLI entry point
161# ============================================================
162
163def main():
164 """
165 CLI entry point for the Dash instrumentation test app.
166 """
167 parser = argparse.ArgumentParser(description="Instrumentation Self-Test App")
168 parser.add_argument(
169 "--use-global",
170 action="store_true",
171 help="Use global debug levels instead of forcing verbosity",
172 )
173 parser.add_argument(
174 "--debug",
175 action="store_true",
176 help=(
177 "Enable Dash debug mode (Werkzeug reloader + debugger). "
178 "Off by default to avoid spawning a second process and exposing "
179 "the Werkzeug debug console. See OI-18 in AI_REVIEW_SYNTHESIS V2.md."
180 ),
181 )
182 args = parser.parse_args()
183
184 app = create_test_app(force_verbose=not args.use_global)
185 app.run_server(debug=args.debug)
186
187
188if __name__ == "__main__":
189 main()
NoReturn apply_instrumentation(*Any args, **Any kwargs)
Definition __init__.py:72