oscura 0.5.1__py3-none-any.whl → 0.7.0__py3-none-any.whl

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 (497) hide show
  1. oscura/__init__.py +169 -167
  2. oscura/analyzers/__init__.py +3 -0
  3. oscura/analyzers/classification.py +659 -0
  4. oscura/analyzers/digital/edges.py +325 -65
  5. oscura/analyzers/digital/quality.py +293 -166
  6. oscura/analyzers/digital/timing.py +260 -115
  7. oscura/analyzers/digital/timing_numba.py +334 -0
  8. oscura/analyzers/entropy.py +605 -0
  9. oscura/analyzers/eye/diagram.py +176 -109
  10. oscura/analyzers/eye/metrics.py +5 -5
  11. oscura/analyzers/jitter/__init__.py +6 -4
  12. oscura/analyzers/jitter/ber.py +52 -52
  13. oscura/analyzers/jitter/classification.py +156 -0
  14. oscura/analyzers/jitter/decomposition.py +163 -113
  15. oscura/analyzers/jitter/spectrum.py +80 -64
  16. oscura/analyzers/ml/__init__.py +39 -0
  17. oscura/analyzers/ml/features.py +600 -0
  18. oscura/analyzers/ml/signal_classifier.py +604 -0
  19. oscura/analyzers/packet/daq.py +246 -158
  20. oscura/analyzers/packet/parser.py +12 -1
  21. oscura/analyzers/packet/payload.py +50 -2110
  22. oscura/analyzers/packet/payload_analysis.py +361 -181
  23. oscura/analyzers/packet/payload_patterns.py +133 -70
  24. oscura/analyzers/packet/stream.py +84 -23
  25. oscura/analyzers/patterns/__init__.py +26 -5
  26. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  27. oscura/analyzers/patterns/clustering.py +169 -108
  28. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  29. oscura/analyzers/patterns/discovery.py +1 -1
  30. oscura/analyzers/patterns/matching.py +581 -197
  31. oscura/analyzers/patterns/pattern_mining.py +778 -0
  32. oscura/analyzers/patterns/periodic.py +121 -38
  33. oscura/analyzers/patterns/sequences.py +175 -78
  34. oscura/analyzers/power/conduction.py +1 -1
  35. oscura/analyzers/power/soa.py +6 -6
  36. oscura/analyzers/power/switching.py +250 -110
  37. oscura/analyzers/protocol/__init__.py +17 -1
  38. oscura/analyzers/protocols/base.py +6 -6
  39. oscura/analyzers/protocols/ble/__init__.py +38 -0
  40. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  41. oscura/analyzers/protocols/ble/uuids.py +288 -0
  42. oscura/analyzers/protocols/can.py +257 -127
  43. oscura/analyzers/protocols/can_fd.py +107 -80
  44. oscura/analyzers/protocols/flexray.py +139 -80
  45. oscura/analyzers/protocols/hdlc.py +93 -58
  46. oscura/analyzers/protocols/i2c.py +247 -106
  47. oscura/analyzers/protocols/i2s.py +138 -86
  48. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  49. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  50. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  51. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  52. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  53. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  54. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  55. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  56. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  57. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  58. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  59. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  60. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  61. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  62. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  63. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  64. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  65. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  66. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  67. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  68. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  69. oscura/analyzers/protocols/jtag.py +180 -98
  70. oscura/analyzers/protocols/lin.py +219 -114
  71. oscura/analyzers/protocols/manchester.py +4 -4
  72. oscura/analyzers/protocols/onewire.py +253 -149
  73. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  74. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  75. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  76. oscura/analyzers/protocols/spi.py +192 -95
  77. oscura/analyzers/protocols/swd.py +321 -167
  78. oscura/analyzers/protocols/uart.py +267 -125
  79. oscura/analyzers/protocols/usb.py +235 -131
  80. oscura/analyzers/side_channel/power.py +17 -12
  81. oscura/analyzers/signal/__init__.py +15 -0
  82. oscura/analyzers/signal/timing_analysis.py +1086 -0
  83. oscura/analyzers/signal_integrity/__init__.py +4 -1
  84. oscura/analyzers/signal_integrity/sparams.py +2 -19
  85. oscura/analyzers/spectral/chunked.py +129 -60
  86. oscura/analyzers/spectral/chunked_fft.py +300 -94
  87. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  88. oscura/analyzers/statistical/checksum.py +376 -217
  89. oscura/analyzers/statistical/classification.py +229 -107
  90. oscura/analyzers/statistical/entropy.py +78 -53
  91. oscura/analyzers/statistics/correlation.py +407 -211
  92. oscura/analyzers/statistics/outliers.py +2 -2
  93. oscura/analyzers/statistics/streaming.py +30 -5
  94. oscura/analyzers/validation.py +216 -101
  95. oscura/analyzers/waveform/measurements.py +9 -0
  96. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  97. oscura/analyzers/waveform/spectral.py +500 -228
  98. oscura/api/__init__.py +31 -5
  99. oscura/api/dsl/__init__.py +582 -0
  100. oscura/{dsl → api/dsl}/commands.py +43 -76
  101. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  102. oscura/{dsl → api/dsl}/parser.py +107 -77
  103. oscura/{dsl → api/dsl}/repl.py +2 -2
  104. oscura/api/dsl.py +1 -1
  105. oscura/{integrations → api/integrations}/__init__.py +1 -1
  106. oscura/{integrations → api/integrations}/llm.py +201 -102
  107. oscura/api/operators.py +3 -3
  108. oscura/api/optimization.py +144 -30
  109. oscura/api/rest_server.py +921 -0
  110. oscura/api/server/__init__.py +17 -0
  111. oscura/api/server/dashboard.py +850 -0
  112. oscura/api/server/static/README.md +34 -0
  113. oscura/api/server/templates/base.html +181 -0
  114. oscura/api/server/templates/export.html +120 -0
  115. oscura/api/server/templates/home.html +284 -0
  116. oscura/api/server/templates/protocols.html +58 -0
  117. oscura/api/server/templates/reports.html +43 -0
  118. oscura/api/server/templates/session_detail.html +89 -0
  119. oscura/api/server/templates/sessions.html +83 -0
  120. oscura/api/server/templates/waveforms.html +73 -0
  121. oscura/automotive/__init__.py +8 -1
  122. oscura/automotive/can/__init__.py +10 -0
  123. oscura/automotive/can/checksum.py +3 -1
  124. oscura/automotive/can/dbc_generator.py +590 -0
  125. oscura/automotive/can/message_wrapper.py +121 -74
  126. oscura/automotive/can/patterns.py +98 -21
  127. oscura/automotive/can/session.py +292 -56
  128. oscura/automotive/can/state_machine.py +6 -3
  129. oscura/automotive/can/stimulus_response.py +97 -75
  130. oscura/automotive/dbc/__init__.py +10 -2
  131. oscura/automotive/dbc/generator.py +84 -56
  132. oscura/automotive/dbc/parser.py +6 -6
  133. oscura/automotive/dtc/data.json +17 -102
  134. oscura/automotive/dtc/database.py +2 -2
  135. oscura/automotive/flexray/__init__.py +31 -0
  136. oscura/automotive/flexray/analyzer.py +504 -0
  137. oscura/automotive/flexray/crc.py +185 -0
  138. oscura/automotive/flexray/fibex.py +449 -0
  139. oscura/automotive/j1939/__init__.py +45 -8
  140. oscura/automotive/j1939/analyzer.py +605 -0
  141. oscura/automotive/j1939/spns.py +326 -0
  142. oscura/automotive/j1939/transport.py +306 -0
  143. oscura/automotive/lin/__init__.py +47 -0
  144. oscura/automotive/lin/analyzer.py +612 -0
  145. oscura/automotive/loaders/blf.py +13 -2
  146. oscura/automotive/loaders/csv_can.py +143 -72
  147. oscura/automotive/loaders/dispatcher.py +50 -2
  148. oscura/automotive/loaders/mdf.py +86 -45
  149. oscura/automotive/loaders/pcap.py +111 -61
  150. oscura/automotive/uds/__init__.py +4 -0
  151. oscura/automotive/uds/analyzer.py +725 -0
  152. oscura/automotive/uds/decoder.py +140 -58
  153. oscura/automotive/uds/models.py +7 -1
  154. oscura/automotive/visualization.py +1 -1
  155. oscura/cli/analyze.py +348 -0
  156. oscura/cli/batch.py +142 -122
  157. oscura/cli/benchmark.py +275 -0
  158. oscura/cli/characterize.py +137 -82
  159. oscura/cli/compare.py +224 -131
  160. oscura/cli/completion.py +250 -0
  161. oscura/cli/config_cmd.py +361 -0
  162. oscura/cli/decode.py +164 -87
  163. oscura/cli/export.py +286 -0
  164. oscura/cli/main.py +115 -31
  165. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  166. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  167. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  168. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  169. oscura/cli/progress.py +147 -0
  170. oscura/cli/shell.py +157 -135
  171. oscura/cli/validate_cmd.py +204 -0
  172. oscura/cli/visualize.py +158 -0
  173. oscura/convenience.py +125 -79
  174. oscura/core/__init__.py +4 -2
  175. oscura/core/backend_selector.py +3 -3
  176. oscura/core/cache.py +126 -15
  177. oscura/core/cancellation.py +1 -1
  178. oscura/{config → core/config}/__init__.py +20 -11
  179. oscura/{config → core/config}/defaults.py +1 -1
  180. oscura/{config → core/config}/loader.py +7 -5
  181. oscura/{config → core/config}/memory.py +5 -5
  182. oscura/{config → core/config}/migration.py +1 -1
  183. oscura/{config → core/config}/pipeline.py +99 -23
  184. oscura/{config → core/config}/preferences.py +1 -1
  185. oscura/{config → core/config}/protocol.py +3 -3
  186. oscura/{config → core/config}/schema.py +426 -272
  187. oscura/{config → core/config}/settings.py +1 -1
  188. oscura/{config → core/config}/thresholds.py +195 -153
  189. oscura/core/correlation.py +5 -6
  190. oscura/core/cross_domain.py +0 -2
  191. oscura/core/debug.py +9 -5
  192. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  193. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  194. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  195. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  196. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  197. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  198. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  199. oscura/core/gpu_backend.py +11 -7
  200. oscura/core/log_query.py +101 -11
  201. oscura/core/logging.py +126 -54
  202. oscura/core/logging_advanced.py +5 -5
  203. oscura/core/memory_limits.py +108 -70
  204. oscura/core/memory_monitor.py +2 -2
  205. oscura/core/memory_progress.py +7 -7
  206. oscura/core/memory_warnings.py +1 -1
  207. oscura/core/numba_backend.py +13 -13
  208. oscura/{plugins → core/plugins}/__init__.py +9 -9
  209. oscura/{plugins → core/plugins}/base.py +7 -7
  210. oscura/{plugins → core/plugins}/cli.py +3 -3
  211. oscura/{plugins → core/plugins}/discovery.py +186 -106
  212. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  213. oscura/{plugins → core/plugins}/manager.py +7 -7
  214. oscura/{plugins → core/plugins}/registry.py +3 -3
  215. oscura/{plugins → core/plugins}/versioning.py +1 -1
  216. oscura/core/progress.py +16 -1
  217. oscura/core/provenance.py +8 -2
  218. oscura/{schemas → core/schemas}/__init__.py +2 -2
  219. oscura/{schemas → core/schemas}/device_mapping.json +2 -8
  220. oscura/{schemas → core/schemas}/packet_format.json +4 -24
  221. oscura/{schemas → core/schemas}/protocol_definition.json +2 -12
  222. oscura/core/types.py +4 -0
  223. oscura/core/uncertainty.py +3 -3
  224. oscura/correlation/__init__.py +52 -0
  225. oscura/correlation/multi_protocol.py +811 -0
  226. oscura/discovery/auto_decoder.py +117 -35
  227. oscura/discovery/comparison.py +191 -86
  228. oscura/discovery/quality_validator.py +155 -68
  229. oscura/discovery/signal_detector.py +196 -79
  230. oscura/export/__init__.py +18 -8
  231. oscura/export/kaitai_struct.py +513 -0
  232. oscura/export/scapy_layer.py +801 -0
  233. oscura/export/wireshark/generator.py +1 -1
  234. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  235. oscura/export/wireshark_dissector.py +746 -0
  236. oscura/guidance/wizard.py +207 -111
  237. oscura/hardware/__init__.py +19 -0
  238. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  239. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  240. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  241. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  242. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  243. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  244. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  245. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  246. oscura/hardware/firmware/__init__.py +29 -0
  247. oscura/hardware/firmware/pattern_recognition.py +874 -0
  248. oscura/hardware/hal_detector.py +736 -0
  249. oscura/hardware/security/__init__.py +37 -0
  250. oscura/hardware/security/side_channel_detector.py +1126 -0
  251. oscura/inference/__init__.py +4 -0
  252. oscura/inference/active_learning/observation_table.py +4 -1
  253. oscura/inference/alignment.py +216 -123
  254. oscura/inference/bayesian.py +113 -33
  255. oscura/inference/crc_reverse.py +101 -55
  256. oscura/inference/logic.py +6 -2
  257. oscura/inference/message_format.py +342 -183
  258. oscura/inference/protocol.py +95 -44
  259. oscura/inference/protocol_dsl.py +180 -82
  260. oscura/inference/signal_intelligence.py +1439 -706
  261. oscura/inference/spectral.py +99 -57
  262. oscura/inference/state_machine.py +810 -158
  263. oscura/inference/stream.py +270 -110
  264. oscura/iot/__init__.py +34 -0
  265. oscura/iot/coap/__init__.py +32 -0
  266. oscura/iot/coap/analyzer.py +668 -0
  267. oscura/iot/coap/options.py +212 -0
  268. oscura/iot/lorawan/__init__.py +21 -0
  269. oscura/iot/lorawan/crypto.py +206 -0
  270. oscura/iot/lorawan/decoder.py +801 -0
  271. oscura/iot/lorawan/mac_commands.py +341 -0
  272. oscura/iot/mqtt/__init__.py +27 -0
  273. oscura/iot/mqtt/analyzer.py +999 -0
  274. oscura/iot/mqtt/properties.py +315 -0
  275. oscura/iot/zigbee/__init__.py +31 -0
  276. oscura/iot/zigbee/analyzer.py +615 -0
  277. oscura/iot/zigbee/security.py +153 -0
  278. oscura/iot/zigbee/zcl.py +349 -0
  279. oscura/jupyter/display.py +125 -45
  280. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  281. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  282. oscura/jupyter/exploratory/fuzzy.py +746 -0
  283. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  284. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  285. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  286. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  287. oscura/jupyter/exploratory/sync.py +612 -0
  288. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  289. oscura/jupyter/magic.py +4 -4
  290. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  291. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  292. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  293. oscura/loaders/__init__.py +183 -67
  294. oscura/loaders/binary.py +88 -1
  295. oscura/loaders/chipwhisperer.py +153 -137
  296. oscura/loaders/configurable.py +208 -86
  297. oscura/loaders/csv_loader.py +458 -215
  298. oscura/loaders/hdf5_loader.py +278 -119
  299. oscura/loaders/lazy.py +87 -54
  300. oscura/loaders/mmap_loader.py +1 -1
  301. oscura/loaders/numpy_loader.py +253 -116
  302. oscura/loaders/pcap.py +226 -151
  303. oscura/loaders/rigol.py +110 -49
  304. oscura/loaders/sigrok.py +201 -78
  305. oscura/loaders/tdms.py +81 -58
  306. oscura/loaders/tektronix.py +291 -174
  307. oscura/loaders/touchstone.py +182 -87
  308. oscura/loaders/tss.py +456 -0
  309. oscura/loaders/vcd.py +215 -117
  310. oscura/loaders/wav.py +155 -68
  311. oscura/reporting/__init__.py +9 -0
  312. oscura/reporting/analyze.py +352 -146
  313. oscura/reporting/argument_preparer.py +69 -14
  314. oscura/reporting/auto_report.py +97 -61
  315. oscura/reporting/batch.py +131 -58
  316. oscura/reporting/chart_selection.py +57 -45
  317. oscura/reporting/comparison.py +63 -17
  318. oscura/reporting/content/executive.py +76 -24
  319. oscura/reporting/core_formats/multi_format.py +11 -8
  320. oscura/reporting/engine.py +312 -158
  321. oscura/reporting/enhanced_reports.py +949 -0
  322. oscura/reporting/export.py +86 -43
  323. oscura/reporting/formatting/numbers.py +69 -42
  324. oscura/reporting/html.py +139 -58
  325. oscura/reporting/index.py +137 -65
  326. oscura/reporting/output.py +158 -67
  327. oscura/reporting/pdf.py +67 -102
  328. oscura/reporting/plots.py +191 -112
  329. oscura/reporting/sections.py +88 -47
  330. oscura/reporting/standards.py +104 -61
  331. oscura/reporting/summary_generator.py +75 -55
  332. oscura/reporting/tables.py +138 -54
  333. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  334. oscura/sessions/__init__.py +14 -23
  335. oscura/sessions/base.py +3 -3
  336. oscura/sessions/blackbox.py +106 -10
  337. oscura/sessions/generic.py +2 -2
  338. oscura/sessions/legacy.py +783 -0
  339. oscura/side_channel/__init__.py +63 -0
  340. oscura/side_channel/dpa.py +1025 -0
  341. oscura/utils/__init__.py +15 -1
  342. oscura/utils/bitwise.py +118 -0
  343. oscura/{builders → utils/builders}/__init__.py +1 -1
  344. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  345. oscura/{comparison → utils/comparison}/compare.py +202 -101
  346. oscura/{comparison → utils/comparison}/golden.py +83 -63
  347. oscura/{comparison → utils/comparison}/limits.py +313 -89
  348. oscura/{comparison → utils/comparison}/mask.py +151 -45
  349. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  350. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  351. oscura/{component → utils/component}/__init__.py +3 -3
  352. oscura/{component → utils/component}/impedance.py +122 -58
  353. oscura/{component → utils/component}/reactive.py +165 -168
  354. oscura/{component → utils/component}/transmission_line.py +3 -3
  355. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  356. oscura/{filtering → utils/filtering}/base.py +1 -1
  357. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  358. oscura/{filtering → utils/filtering}/design.py +169 -93
  359. oscura/{filtering → utils/filtering}/filters.py +2 -2
  360. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  361. oscura/utils/geometry.py +31 -0
  362. oscura/utils/imports.py +184 -0
  363. oscura/utils/lazy.py +1 -1
  364. oscura/{math → utils/math}/__init__.py +2 -2
  365. oscura/{math → utils/math}/arithmetic.py +114 -48
  366. oscura/{math → utils/math}/interpolation.py +139 -106
  367. oscura/utils/memory.py +129 -66
  368. oscura/utils/memory_advanced.py +92 -9
  369. oscura/utils/memory_extensions.py +10 -8
  370. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  371. oscura/{optimization → utils/optimization}/search.py +2 -2
  372. oscura/utils/performance/__init__.py +58 -0
  373. oscura/utils/performance/caching.py +889 -0
  374. oscura/utils/performance/lsh_clustering.py +333 -0
  375. oscura/utils/performance/memory_optimizer.py +699 -0
  376. oscura/utils/performance/optimizations.py +675 -0
  377. oscura/utils/performance/parallel.py +654 -0
  378. oscura/utils/performance/profiling.py +661 -0
  379. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  380. oscura/{pipeline → utils/pipeline}/composition.py +1 -1
  381. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  382. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  383. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  384. oscura/{search → utils/search}/__init__.py +3 -3
  385. oscura/{search → utils/search}/anomaly.py +188 -58
  386. oscura/utils/search/context.py +294 -0
  387. oscura/{search → utils/search}/pattern.py +138 -10
  388. oscura/utils/serial.py +51 -0
  389. oscura/utils/storage/__init__.py +61 -0
  390. oscura/utils/storage/database.py +1166 -0
  391. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  392. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  393. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  394. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  395. oscura/{triggering → utils/triggering}/base.py +6 -6
  396. oscura/{triggering → utils/triggering}/edge.py +2 -2
  397. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  398. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  399. oscura/{triggering → utils/triggering}/window.py +2 -2
  400. oscura/utils/validation.py +32 -0
  401. oscura/validation/__init__.py +121 -0
  402. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  403. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  404. oscura/{compliance → validation/compliance}/masks.py +1 -1
  405. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  406. oscura/{compliance → validation/compliance}/testing.py +114 -52
  407. oscura/validation/compliance_tests.py +915 -0
  408. oscura/validation/fuzzer.py +990 -0
  409. oscura/validation/grammar_tests.py +596 -0
  410. oscura/validation/grammar_validator.py +904 -0
  411. oscura/validation/hil_testing.py +977 -0
  412. oscura/{quality → validation/quality}/__init__.py +4 -4
  413. oscura/{quality → validation/quality}/ensemble.py +251 -171
  414. oscura/{quality → validation/quality}/explainer.py +3 -3
  415. oscura/{quality → validation/quality}/scoring.py +1 -1
  416. oscura/{quality → validation/quality}/warnings.py +4 -4
  417. oscura/validation/regression_suite.py +808 -0
  418. oscura/validation/replay.py +788 -0
  419. oscura/{testing → validation/testing}/__init__.py +2 -2
  420. oscura/{testing → validation/testing}/synthetic.py +5 -5
  421. oscura/visualization/__init__.py +9 -0
  422. oscura/visualization/accessibility.py +1 -1
  423. oscura/visualization/annotations.py +64 -67
  424. oscura/visualization/colors.py +7 -7
  425. oscura/visualization/digital.py +180 -81
  426. oscura/visualization/eye.py +236 -85
  427. oscura/visualization/interactive.py +320 -143
  428. oscura/visualization/jitter.py +587 -247
  429. oscura/visualization/layout.py +169 -134
  430. oscura/visualization/optimization.py +103 -52
  431. oscura/visualization/palettes.py +1 -1
  432. oscura/visualization/power.py +427 -211
  433. oscura/visualization/power_extended.py +626 -297
  434. oscura/visualization/presets.py +2 -0
  435. oscura/visualization/protocols.py +495 -181
  436. oscura/visualization/render.py +79 -63
  437. oscura/visualization/reverse_engineering.py +171 -124
  438. oscura/visualization/signal_integrity.py +460 -279
  439. oscura/visualization/specialized.py +190 -100
  440. oscura/visualization/spectral.py +670 -255
  441. oscura/visualization/thumbnails.py +166 -137
  442. oscura/visualization/waveform.py +150 -63
  443. oscura/workflows/__init__.py +3 -0
  444. oscura/{batch → workflows/batch}/__init__.py +5 -5
  445. oscura/{batch → workflows/batch}/advanced.py +150 -75
  446. oscura/workflows/batch/aggregate.py +531 -0
  447. oscura/workflows/batch/analyze.py +236 -0
  448. oscura/{batch → workflows/batch}/logging.py +2 -2
  449. oscura/{batch → workflows/batch}/metrics.py +1 -1
  450. oscura/workflows/complete_re.py +1144 -0
  451. oscura/workflows/compliance.py +44 -54
  452. oscura/workflows/digital.py +197 -51
  453. oscura/workflows/legacy/__init__.py +12 -0
  454. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  455. oscura/workflows/multi_trace.py +9 -9
  456. oscura/workflows/power.py +42 -62
  457. oscura/workflows/protocol.py +82 -49
  458. oscura/workflows/reverse_engineering.py +351 -150
  459. oscura/workflows/signal_integrity.py +157 -82
  460. oscura-0.7.0.dist-info/METADATA +661 -0
  461. oscura-0.7.0.dist-info/RECORD +591 -0
  462. oscura/batch/aggregate.py +0 -300
  463. oscura/batch/analyze.py +0 -139
  464. oscura/dsl/__init__.py +0 -73
  465. oscura/exceptions.py +0 -59
  466. oscura/exploratory/fuzzy.py +0 -513
  467. oscura/exploratory/sync.py +0 -384
  468. oscura/exporters/__init__.py +0 -94
  469. oscura/exporters/csv.py +0 -303
  470. oscura/exporters/exporters.py +0 -44
  471. oscura/exporters/hdf5.py +0 -217
  472. oscura/exporters/html_export.py +0 -701
  473. oscura/exporters/json_export.py +0 -291
  474. oscura/exporters/markdown_export.py +0 -367
  475. oscura/exporters/matlab_export.py +0 -354
  476. oscura/exporters/npz_export.py +0 -219
  477. oscura/exporters/spice_export.py +0 -210
  478. oscura/search/context.py +0 -149
  479. oscura/session/__init__.py +0 -34
  480. oscura/session/annotations.py +0 -289
  481. oscura/session/history.py +0 -313
  482. oscura/session/session.py +0 -520
  483. oscura/workflow/__init__.py +0 -13
  484. oscura-0.5.1.dist-info/METADATA +0 -583
  485. oscura-0.5.1.dist-info/RECORD +0 -481
  486. /oscura/core/{config.py → config/legacy.py} +0 -0
  487. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  488. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  489. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  490. /oscura/{schemas → core/schemas}/bus_configuration.json +0 -0
  491. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  492. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  493. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  494. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  495. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/WHEEL +0 -0
  496. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/entry_points.txt +0 -0
  497. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -6,7 +6,7 @@ configuration options.
6
6
 
7
7
 
8
8
  Example:
9
- >>> from oscura.config.settings import get_settings, Settings
9
+ >>> from oscura.core.config.settings import get_settings, Settings
10
10
  >>> settings = get_settings()
11
11
  >>> settings.enable_feature("advanced_analysis")
12
12
  >>> if settings.is_feature_enabled("advanced_analysis"):
@@ -15,7 +15,7 @@ from pathlib import Path
15
15
 
16
16
  import yaml
17
17
 
18
- from oscura.config.schema import validate_against_schema
18
+ from oscura.core.config.schema import validate_against_schema
19
19
  from oscura.core.exceptions import ConfigurationError
20
20
 
21
21
  logger = logging.getLogger(__name__)
@@ -206,129 +206,25 @@ class ThresholdRegistry:
206
206
  # Double-check locking pattern
207
207
  if cls._instance is None:
208
208
  cls._instance = super().__new__(cls)
209
- cls._instance._families = {} # type: ignore[attr-defined]
210
- cls._instance._profiles = {} # type: ignore[attr-defined]
211
- cls._instance._session_overrides = {} # type: ignore[attr-defined]
212
- cls._instance._state_lock = threading.Lock() # type: ignore[attr-defined]
209
+ cls._instance._families = {}
210
+ cls._instance._profiles = {}
211
+ cls._instance._session_overrides = {}
212
+ cls._instance._state_lock = threading.Lock()
213
213
  cls._instance._register_builtins()
214
214
  return cls._instance
215
215
 
216
216
  def _register_builtins(self) -> None:
217
217
  """Register built-in logic family definitions."""
218
- builtins = [
219
- # TTL
220
- LogicFamily(
221
- name="TTL",
222
- VIH=2.0,
223
- VIL=0.8,
224
- VOH=2.4,
225
- VOL=0.4,
226
- VCC=5.0,
227
- description="Standard TTL (74xx series)",
228
- ),
229
- # CMOS variants
230
- LogicFamily(
231
- name="CMOS_5V",
232
- VIH=3.5,
233
- VIL=1.5,
234
- VOH=4.9,
235
- VOL=0.1,
236
- VCC=5.0,
237
- description="CMOS 5V (74HCxx series)",
238
- ),
239
- LogicFamily(
240
- name="LVTTL_3V3",
241
- VIH=2.0,
242
- VIL=0.8,
243
- VOH=2.4,
244
- VOL=0.4,
245
- VCC=3.3,
246
- description="Low Voltage TTL 3.3V",
247
- ),
248
- LogicFamily(
249
- name="LVCMOS_3V3",
250
- VIH=2.0,
251
- VIL=0.7,
252
- VOH=2.4,
253
- VOL=0.4,
254
- VCC=3.3,
255
- description="Low Voltage CMOS 3.3V",
256
- ),
257
- LogicFamily(
258
- name="LVCMOS_2V5",
259
- VIH=1.7,
260
- VIL=0.7,
261
- VOH=2.0,
262
- VOL=0.4,
263
- VCC=2.5,
264
- description="Low Voltage CMOS 2.5V",
265
- ),
266
- LogicFamily(
267
- name="LVCMOS_1V8",
268
- VIH=1.17,
269
- VIL=0.63,
270
- VOH=1.35,
271
- VOL=0.45,
272
- VCC=1.8,
273
- description="Low Voltage CMOS 1.8V",
274
- ),
275
- LogicFamily(
276
- name="LVCMOS_1V5",
277
- VIH=0.975,
278
- VIL=0.525,
279
- VOH=1.125,
280
- VOL=0.375,
281
- VCC=1.5,
282
- description="Low Voltage CMOS 1.5V",
283
- ),
284
- LogicFamily(
285
- name="LVCMOS_1V2",
286
- VIH=0.84, # 0.7 * 1.2
287
- VIL=0.36, # 0.3 * 1.2
288
- VOH=1.1,
289
- VOL=0.1,
290
- VCC=1.2,
291
- description="Low Voltage CMOS 1.2V",
292
- ),
293
- # ECL
294
- LogicFamily(
295
- name="ECL",
296
- VIH=-0.9,
297
- VIL=-1.7,
298
- VOH=-0.9,
299
- VOL=-1.75,
300
- VCC=-5.2,
301
- description="Emitter-Coupled Logic (ECL 10K)",
302
- ),
303
- ]
218
+ builtins = _get_builtin_logic_families()
304
219
 
305
220
  for family in builtins:
306
- self._families[family.name] = family # type: ignore[attr-defined]
221
+ self._families[family.name] = family
307
222
 
308
223
  # Built-in profiles
309
- builtin_profiles = [
310
- ThresholdProfile(
311
- name="strict",
312
- base_family="TTL",
313
- tolerance=0,
314
- description="Exact specification values",
315
- ),
316
- ThresholdProfile(
317
- name="relaxed",
318
- base_family="TTL",
319
- tolerance=20,
320
- description="20% tolerance for real-world signals",
321
- ),
322
- ThresholdProfile(
323
- name="auto",
324
- base_family="TTL",
325
- tolerance=10,
326
- description="Auto-adjusted based on signal confidence",
327
- ),
328
- ]
224
+ builtin_profiles = _get_builtin_profiles()
329
225
 
330
226
  for profile in builtin_profiles:
331
- self._profiles[profile.name] = profile # type: ignore[attr-defined]
227
+ self._profiles[profile.name] = profile
332
228
 
333
229
  def get_family(self, name: str) -> LogicFamily:
334
230
  """Get logic family by name.
@@ -342,32 +238,56 @@ class ThresholdRegistry:
342
238
  Raises:
343
239
  KeyError: If family not found
344
240
  """
345
- with self._state_lock: # type: ignore[attr-defined]
346
- # Try exact match first
347
- if name in self._families: # type: ignore[attr-defined]
348
- family = self._families[name] # type: ignore[attr-defined]
349
- # Try case-insensitive match
350
- elif name.upper() in self._families: # type: ignore[attr-defined]
351
- family = self._families[name.upper()] # type: ignore[attr-defined]
352
- else:
353
- available = list(self._families.keys()) # type: ignore[attr-defined]
354
- raise KeyError(f"Logic family '{name}' not found. Available: {available}")
355
-
356
- # Apply session overrides
357
- if self._session_overrides: # type: ignore[attr-defined]
358
- return LogicFamily(
359
- name=family.name,
360
- VIH=self._session_overrides.get("VIH", family.VIH), # type: ignore[attr-defined]
361
- VIL=self._session_overrides.get("VIL", family.VIL), # type: ignore[attr-defined]
362
- VOH=self._session_overrides.get("VOH", family.VOH), # type: ignore[attr-defined]
363
- VOL=self._session_overrides.get("VOL", family.VOL), # type: ignore[attr-defined]
364
- VCC=self._session_overrides.get("VCC", family.VCC), # type: ignore[attr-defined]
365
- description=family.description,
366
- temperature_range=family.temperature_range,
367
- source="override",
368
- )
241
+ with self._state_lock:
242
+ family = self._lookup_family(name)
243
+ return self._apply_overrides_if_needed(family)
244
+
245
+ def _lookup_family(self, name: str) -> LogicFamily:
246
+ """Look up family by name (exact or case-insensitive).
247
+
248
+ Args:
249
+ name: Family name to look up.
250
+
251
+ Returns:
252
+ Logic family definition.
253
+
254
+ Raises:
255
+ KeyError: If family not found.
256
+ """
257
+ # Try exact match first
258
+ if name in self._families:
259
+ return self._families[name]
260
+
261
+ # Try case-insensitive match
262
+ if name.upper() in self._families:
263
+ return self._families[name.upper()]
264
+
265
+ available = list(self._families.keys())
266
+ raise KeyError(f"Logic family '{name}' not found. Available: {available}")
267
+
268
+ def _apply_overrides_if_needed(self, family: LogicFamily) -> LogicFamily:
269
+ """Apply session overrides to family if configured.
270
+
271
+ Args:
272
+ family: Base family.
369
273
 
370
- return family # type: ignore[no-any-return]
274
+ Returns:
275
+ Family with overrides applied, or original if no overrides.
276
+ """
277
+ if not self._session_overrides:
278
+ return family
279
+
280
+ return LogicFamily(
281
+ name=family.name,
282
+ VIH=self._session_overrides.get("VIH", family.VIH),
283
+ VIL=self._session_overrides.get("VIL", family.VIL),
284
+ VOH=self._session_overrides.get("VOH", family.VOH),
285
+ VOL=self._session_overrides.get("VOL", family.VOL),
286
+ VCC=self._session_overrides.get("VCC", family.VCC),
287
+ description=family.description,
288
+ temperature_range=family.temperature_range,
289
+ source="override",
290
+ )
371
291
 
372
292
  def list_families(self) -> list[str]:
373
293
  """List all available logic families.
@@ -375,8 +295,8 @@ class ThresholdRegistry:
375
295
  Returns:
376
296
  List of family names
377
297
  """
378
- with self._state_lock: # type: ignore[attr-defined]
379
- return sorted(self._families.keys()) # type: ignore[attr-defined]
298
+ with self._state_lock:
299
+ return sorted(self._families.keys())
380
300
 
381
301
  def register_family(self, family: LogicFamily, *, namespace: str = "user") -> None:
382
302
  """Register custom logic family.
@@ -390,7 +310,7 @@ class ThresholdRegistry:
390
310
  >>> registry.register_family(custom)
391
311
  >>> # Available as "user.my_custom"
392
312
  """
393
- with self._state_lock: # type: ignore[attr-defined]
313
+ with self._state_lock:
394
314
  # Namespace custom families
395
315
  if namespace and not family.name.startswith(f"{namespace}."):
396
316
  name = f"{namespace}.{family.name}"
@@ -410,7 +330,7 @@ class ThresholdRegistry:
410
330
  source=family.source,
411
331
  )
412
332
 
413
- self._families[name] = family # type: ignore[attr-defined]
333
+ self._families[name] = family
414
334
  logger.info(f"Registered custom logic family: {name}")
415
335
 
416
336
  def set_threshold_override(self, **kwargs: float) -> None:
@@ -428,20 +348,20 @@ class ThresholdRegistry:
428
348
  >>> registry.set_threshold_override(VIH=2.5, VIL=0.7)
429
349
  """
430
350
  valid_keys = {"VIH", "VIL", "VOH", "VOL", "VCC"}
431
- with self._state_lock: # type: ignore[attr-defined]
351
+ with self._state_lock:
432
352
  for key, value in kwargs.items():
433
353
  if key not in valid_keys:
434
354
  raise ValueError(f"Invalid threshold key: {key}. Valid: {valid_keys}")
435
355
  if not 0.0 <= value <= 10.0:
436
356
  raise ValueError(f"Threshold {key}={value}V out of range (0-10V)")
437
- self._session_overrides[key] = value # type: ignore[attr-defined]
357
+ self._session_overrides[key] = value
438
358
 
439
359
  logger.info(f"Set threshold overrides: {kwargs}")
440
360
 
441
361
  def reset_overrides(self) -> None:
442
362
  """Reset session threshold overrides."""
443
- with self._state_lock: # type: ignore[attr-defined]
444
- self._session_overrides.clear() # type: ignore[attr-defined]
363
+ with self._state_lock:
364
+ self._session_overrides.clear()
445
365
  logger.info("Reset threshold overrides")
446
366
 
447
367
  def get_profile(self, name: str) -> ThresholdProfile:
@@ -456,12 +376,12 @@ class ThresholdRegistry:
456
376
  Raises:
457
377
  KeyError: If profile not found.
458
378
  """
459
- with self._state_lock: # type: ignore[attr-defined]
460
- if name not in self._profiles: # type: ignore[attr-defined]
379
+ with self._state_lock:
380
+ if name not in self._profiles:
461
381
  raise KeyError(
462
382
  f"Profile '{name}' not found. Available: {list(self._profiles.keys())}"
463
- ) # type: ignore[attr-defined]
464
- return self._profiles[name] # type: ignore[no-any-return, attr-defined]
383
+ )
384
+ return self._profiles[name]
465
385
 
466
386
  def apply_profile(self, name: str) -> LogicFamily:
467
387
  """Apply a threshold profile.
@@ -490,14 +410,14 @@ class ThresholdRegistry:
490
410
  >>> registry.set_threshold_override(VIH=2.5)
491
411
  >>> registry.save_profile("my_profile")
492
412
  """
493
- with self._state_lock: # type: ignore[attr-defined]
413
+ with self._state_lock:
494
414
  profile = ThresholdProfile(
495
415
  name=name,
496
416
  base_family=base_family or "TTL",
497
- overrides=dict(self._session_overrides), # type: ignore[attr-defined]
417
+ overrides=dict(self._session_overrides),
498
418
  description=f"User profile {name}",
499
419
  )
500
- self._profiles[name] = profile # type: ignore[attr-defined]
420
+ self._profiles[name] = profile
501
421
 
502
422
  if path:
503
423
  path = Path(path)
@@ -513,6 +433,128 @@ class ThresholdRegistry:
513
433
  logger.info(f"Saved profile to {path}")
514
434
 
515
435
 
436
+ def _get_builtin_logic_families() -> list[LogicFamily]:
437
+ """Get list of built-in logic family definitions.
438
+
439
+ Returns:
440
+ List of standard logic families.
441
+ """
442
+ return [
443
+ # TTL
444
+ LogicFamily(
445
+ name="TTL",
446
+ VIH=2.0,
447
+ VIL=0.8,
448
+ VOH=2.4,
449
+ VOL=0.4,
450
+ VCC=5.0,
451
+ description="Standard TTL (74xx series)",
452
+ ),
453
+ # CMOS variants
454
+ LogicFamily(
455
+ name="CMOS_5V",
456
+ VIH=3.5,
457
+ VIL=1.5,
458
+ VOH=4.9,
459
+ VOL=0.1,
460
+ VCC=5.0,
461
+ description="CMOS 5V (74HCxx series)",
462
+ ),
463
+ LogicFamily(
464
+ name="LVTTL_3V3",
465
+ VIH=2.0,
466
+ VIL=0.8,
467
+ VOH=2.4,
468
+ VOL=0.4,
469
+ VCC=3.3,
470
+ description="Low Voltage TTL 3.3V",
471
+ ),
472
+ LogicFamily(
473
+ name="LVCMOS_3V3",
474
+ VIH=2.0,
475
+ VIL=0.7,
476
+ VOH=2.4,
477
+ VOL=0.4,
478
+ VCC=3.3,
479
+ description="Low Voltage CMOS 3.3V",
480
+ ),
481
+ LogicFamily(
482
+ name="LVCMOS_2V5",
483
+ VIH=1.7,
484
+ VIL=0.7,
485
+ VOH=2.0,
486
+ VOL=0.4,
487
+ VCC=2.5,
488
+ description="Low Voltage CMOS 2.5V",
489
+ ),
490
+ LogicFamily(
491
+ name="LVCMOS_1V8",
492
+ VIH=1.17,
493
+ VIL=0.63,
494
+ VOH=1.35,
495
+ VOL=0.45,
496
+ VCC=1.8,
497
+ description="Low Voltage CMOS 1.8V",
498
+ ),
499
+ LogicFamily(
500
+ name="LVCMOS_1V5",
501
+ VIH=0.975,
502
+ VIL=0.525,
503
+ VOH=1.125,
504
+ VOL=0.375,
505
+ VCC=1.5,
506
+ description="Low Voltage CMOS 1.5V",
507
+ ),
508
+ LogicFamily(
509
+ name="LVCMOS_1V2",
510
+ VIH=0.84, # 0.7 * 1.2
511
+ VIL=0.36, # 0.3 * 1.2
512
+ VOH=1.1,
513
+ VOL=0.1,
514
+ VCC=1.2,
515
+ description="Low Voltage CMOS 1.2V",
516
+ ),
517
+ # ECL
518
+ LogicFamily(
519
+ name="ECL",
520
+ VIH=-0.9,
521
+ VIL=-1.7,
522
+ VOH=-0.9,
523
+ VOL=-1.75,
524
+ VCC=-5.2,
525
+ description="Emitter-Coupled Logic (ECL 10K)",
526
+ ),
527
+ ]
528
+
529
+
530
+ def _get_builtin_profiles() -> list[ThresholdProfile]:
531
+ """Get list of built-in threshold profiles.
532
+
533
+ Returns:
534
+ List of standard threshold profiles.
535
+ """
536
+ return [
537
+ ThresholdProfile(
538
+ name="strict",
539
+ base_family="TTL",
540
+ tolerance=0,
541
+ description="Exact specification values",
542
+ ),
543
+ ThresholdProfile(
544
+ name="relaxed",
545
+ base_family="TTL",
546
+ tolerance=20,
547
+ description="20% tolerance for real-world signals",
548
+ ),
549
+ ThresholdProfile(
550
+ name="auto",
551
+ base_family="TTL",
552
+ tolerance=10,
553
+ description="Auto-adjusted based on signal confidence",
554
+ ),
555
+ ]
556
+
557
+
516
558
  def load_logic_family(path: str | Path) -> LogicFamily:
517
559
  """Load logic family from YAML/JSON file.
518
560
 
@@ -20,8 +20,8 @@ import asyncio
20
20
  import contextvars
21
21
  import functools
22
22
  import uuid
23
- from collections.abc import Callable
24
- from typing import Any, ParamSpec, TypeVar
23
+ from collections.abc import Callable, Coroutine
24
+ from typing import Any, ParamSpec, TypeVar, cast
25
25
 
26
26
  # Context variable for correlation ID (thread-safe and async-safe)
27
27
  _correlation_id: contextvars.ContextVar[str | None] = contextvars.ContextVar(
@@ -180,9 +180,8 @@ def with_correlation_id(
180
180
  async def async_wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
181
181
  with CorrelationContext(corr_id):
182
182
  # func returns a Coroutine, we need to await it
183
- coro = func(*args, **kwargs)
184
- # Type assertion for mypy - we know this is a coroutine
185
- return await coro # type: ignore[misc, no-any-return]
183
+ coro = cast("Coroutine[Any, Any, R]", func(*args, **kwargs))
184
+ return await coro
186
185
 
187
186
  return async_wrapper # type: ignore[return-value]
188
187
  else:
@@ -192,7 +191,7 @@ def with_correlation_id(
192
191
  with CorrelationContext(corr_id):
193
192
  return func(*args, **kwargs)
194
193
 
195
- return sync_wrapper # type: ignore[return-value]
194
+ return sync_wrapper
196
195
 
197
196
  return decorator
198
197
 
@@ -78,8 +78,6 @@ class CorrelationResult:
78
78
  Returns:
79
79
  Coherence score based on agreement/conflict ratio.
80
80
  """
81
- if not self.insights:
82
- return 0.5
83
81
  total = self.agreements_detected + self.conflicts_detected
84
82
  if total == 0:
85
83
  return 0.5
oscura/core/debug.py CHANGED
@@ -5,11 +5,11 @@ verbosity levels for troubleshooting and diagnostics.
5
5
 
6
6
 
7
7
  Example:
8
- >>> from oscura.core.debug import enable_debug, is_debug_enabled, debug_context
8
+ >>> from oscura.core.debug import enable_debug, is_debug_enabled, DebugContext
9
9
  >>> enable_debug(level='verbose')
10
10
  >>> if is_debug_enabled():
11
11
  ... logger.debug("Extra diagnostic information")
12
- >>> with debug_context(level='trace'):
12
+ >>> with DebugContext(level='trace'):
13
13
  ... # Temporarily increase verbosity
14
14
  ... analyze_complex_signal()
15
15
 
@@ -157,7 +157,7 @@ def get_debug_level() -> DebugLevel:
157
157
  return DebugLevel(_debug_level.get())
158
158
 
159
159
 
160
- class debug_context:
160
+ class DebugContext:
161
161
  """Context manager for temporary debug level changes.
162
162
 
163
163
  Temporarily sets a debug level for the duration of a code block,
@@ -168,7 +168,7 @@ class debug_context:
168
168
 
169
169
  Example:
170
170
  >>> # Normal debug level
171
- >>> with debug_context(level='trace'):
171
+ >>> with DebugContext(level='trace'):
172
172
  ... # Temporarily enable trace-level debugging
173
173
  ... analyze_signal(complex_data)
174
174
  >>> # Back to normal debug level
@@ -190,7 +190,7 @@ class debug_context:
190
190
  self.token: contextvars.Token | None = None # type: ignore[type-arg]
191
191
  self.previous_log_level: str | None = None
192
192
 
193
- def __enter__(self) -> debug_context:
193
+ def __enter__(self) -> DebugContext:
194
194
  """Enter the debug context and set new level."""
195
195
  # Save current debug level
196
196
  self.token = _debug_level.set(self.level)
@@ -226,6 +226,10 @@ class debug_context:
226
226
  set_log_level(self.previous_log_level)
227
227
 
228
228
 
229
+ # Backward compatibility alias (deprecated, use DebugContext)
230
+ debug_context = DebugContext
231
+
232
+
229
233
  def should_log_debug(min_level: DebugLevel = DebugLevel.NORMAL) -> bool:
230
234
  """Check if debug logging should occur at specified level.
231
235