oscura 0.0.1__py3-none-any.whl → 0.1.1__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 (465) hide show
  1. oscura/__init__.py +813 -8
  2. oscura/__main__.py +392 -0
  3. oscura/analyzers/__init__.py +37 -0
  4. oscura/analyzers/digital/__init__.py +177 -0
  5. oscura/analyzers/digital/bus.py +691 -0
  6. oscura/analyzers/digital/clock.py +805 -0
  7. oscura/analyzers/digital/correlation.py +720 -0
  8. oscura/analyzers/digital/edges.py +632 -0
  9. oscura/analyzers/digital/extraction.py +413 -0
  10. oscura/analyzers/digital/quality.py +878 -0
  11. oscura/analyzers/digital/signal_quality.py +877 -0
  12. oscura/analyzers/digital/thresholds.py +708 -0
  13. oscura/analyzers/digital/timing.py +1104 -0
  14. oscura/analyzers/eye/__init__.py +46 -0
  15. oscura/analyzers/eye/diagram.py +434 -0
  16. oscura/analyzers/eye/metrics.py +555 -0
  17. oscura/analyzers/jitter/__init__.py +83 -0
  18. oscura/analyzers/jitter/ber.py +333 -0
  19. oscura/analyzers/jitter/decomposition.py +759 -0
  20. oscura/analyzers/jitter/measurements.py +413 -0
  21. oscura/analyzers/jitter/spectrum.py +220 -0
  22. oscura/analyzers/measurements.py +40 -0
  23. oscura/analyzers/packet/__init__.py +171 -0
  24. oscura/analyzers/packet/daq.py +1077 -0
  25. oscura/analyzers/packet/metrics.py +437 -0
  26. oscura/analyzers/packet/parser.py +327 -0
  27. oscura/analyzers/packet/payload.py +2156 -0
  28. oscura/analyzers/packet/payload_analysis.py +1312 -0
  29. oscura/analyzers/packet/payload_extraction.py +236 -0
  30. oscura/analyzers/packet/payload_patterns.py +670 -0
  31. oscura/analyzers/packet/stream.py +359 -0
  32. oscura/analyzers/patterns/__init__.py +266 -0
  33. oscura/analyzers/patterns/clustering.py +1036 -0
  34. oscura/analyzers/patterns/discovery.py +539 -0
  35. oscura/analyzers/patterns/learning.py +797 -0
  36. oscura/analyzers/patterns/matching.py +1091 -0
  37. oscura/analyzers/patterns/periodic.py +650 -0
  38. oscura/analyzers/patterns/sequences.py +767 -0
  39. oscura/analyzers/power/__init__.py +116 -0
  40. oscura/analyzers/power/ac_power.py +391 -0
  41. oscura/analyzers/power/basic.py +383 -0
  42. oscura/analyzers/power/conduction.py +314 -0
  43. oscura/analyzers/power/efficiency.py +297 -0
  44. oscura/analyzers/power/ripple.py +356 -0
  45. oscura/analyzers/power/soa.py +372 -0
  46. oscura/analyzers/power/switching.py +479 -0
  47. oscura/analyzers/protocol/__init__.py +150 -0
  48. oscura/analyzers/protocols/__init__.py +150 -0
  49. oscura/analyzers/protocols/base.py +500 -0
  50. oscura/analyzers/protocols/can.py +620 -0
  51. oscura/analyzers/protocols/can_fd.py +448 -0
  52. oscura/analyzers/protocols/flexray.py +405 -0
  53. oscura/analyzers/protocols/hdlc.py +399 -0
  54. oscura/analyzers/protocols/i2c.py +368 -0
  55. oscura/analyzers/protocols/i2s.py +296 -0
  56. oscura/analyzers/protocols/jtag.py +393 -0
  57. oscura/analyzers/protocols/lin.py +445 -0
  58. oscura/analyzers/protocols/manchester.py +333 -0
  59. oscura/analyzers/protocols/onewire.py +501 -0
  60. oscura/analyzers/protocols/spi.py +334 -0
  61. oscura/analyzers/protocols/swd.py +325 -0
  62. oscura/analyzers/protocols/uart.py +393 -0
  63. oscura/analyzers/protocols/usb.py +495 -0
  64. oscura/analyzers/signal_integrity/__init__.py +63 -0
  65. oscura/analyzers/signal_integrity/embedding.py +294 -0
  66. oscura/analyzers/signal_integrity/equalization.py +370 -0
  67. oscura/analyzers/signal_integrity/sparams.py +484 -0
  68. oscura/analyzers/spectral/__init__.py +53 -0
  69. oscura/analyzers/spectral/chunked.py +273 -0
  70. oscura/analyzers/spectral/chunked_fft.py +571 -0
  71. oscura/analyzers/spectral/chunked_wavelet.py +391 -0
  72. oscura/analyzers/spectral/fft.py +92 -0
  73. oscura/analyzers/statistical/__init__.py +250 -0
  74. oscura/analyzers/statistical/checksum.py +923 -0
  75. oscura/analyzers/statistical/chunked_corr.py +228 -0
  76. oscura/analyzers/statistical/classification.py +778 -0
  77. oscura/analyzers/statistical/entropy.py +1113 -0
  78. oscura/analyzers/statistical/ngrams.py +614 -0
  79. oscura/analyzers/statistics/__init__.py +119 -0
  80. oscura/analyzers/statistics/advanced.py +885 -0
  81. oscura/analyzers/statistics/basic.py +263 -0
  82. oscura/analyzers/statistics/correlation.py +630 -0
  83. oscura/analyzers/statistics/distribution.py +298 -0
  84. oscura/analyzers/statistics/outliers.py +463 -0
  85. oscura/analyzers/statistics/streaming.py +93 -0
  86. oscura/analyzers/statistics/trend.py +520 -0
  87. oscura/analyzers/validation.py +598 -0
  88. oscura/analyzers/waveform/__init__.py +36 -0
  89. oscura/analyzers/waveform/measurements.py +943 -0
  90. oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
  91. oscura/analyzers/waveform/spectral.py +1689 -0
  92. oscura/analyzers/waveform/wavelets.py +298 -0
  93. oscura/api/__init__.py +62 -0
  94. oscura/api/dsl.py +538 -0
  95. oscura/api/fluent.py +571 -0
  96. oscura/api/operators.py +498 -0
  97. oscura/api/optimization.py +392 -0
  98. oscura/api/profiling.py +396 -0
  99. oscura/automotive/__init__.py +73 -0
  100. oscura/automotive/can/__init__.py +52 -0
  101. oscura/automotive/can/analysis.py +356 -0
  102. oscura/automotive/can/checksum.py +250 -0
  103. oscura/automotive/can/correlation.py +212 -0
  104. oscura/automotive/can/discovery.py +355 -0
  105. oscura/automotive/can/message_wrapper.py +375 -0
  106. oscura/automotive/can/models.py +385 -0
  107. oscura/automotive/can/patterns.py +381 -0
  108. oscura/automotive/can/session.py +452 -0
  109. oscura/automotive/can/state_machine.py +300 -0
  110. oscura/automotive/can/stimulus_response.py +461 -0
  111. oscura/automotive/dbc/__init__.py +15 -0
  112. oscura/automotive/dbc/generator.py +156 -0
  113. oscura/automotive/dbc/parser.py +146 -0
  114. oscura/automotive/dtc/__init__.py +30 -0
  115. oscura/automotive/dtc/database.py +3036 -0
  116. oscura/automotive/j1939/__init__.py +14 -0
  117. oscura/automotive/j1939/decoder.py +745 -0
  118. oscura/automotive/loaders/__init__.py +35 -0
  119. oscura/automotive/loaders/asc.py +98 -0
  120. oscura/automotive/loaders/blf.py +77 -0
  121. oscura/automotive/loaders/csv_can.py +136 -0
  122. oscura/automotive/loaders/dispatcher.py +136 -0
  123. oscura/automotive/loaders/mdf.py +331 -0
  124. oscura/automotive/loaders/pcap.py +132 -0
  125. oscura/automotive/obd/__init__.py +14 -0
  126. oscura/automotive/obd/decoder.py +707 -0
  127. oscura/automotive/uds/__init__.py +48 -0
  128. oscura/automotive/uds/decoder.py +265 -0
  129. oscura/automotive/uds/models.py +64 -0
  130. oscura/automotive/visualization.py +369 -0
  131. oscura/batch/__init__.py +55 -0
  132. oscura/batch/advanced.py +627 -0
  133. oscura/batch/aggregate.py +300 -0
  134. oscura/batch/analyze.py +139 -0
  135. oscura/batch/logging.py +487 -0
  136. oscura/batch/metrics.py +556 -0
  137. oscura/builders/__init__.py +41 -0
  138. oscura/builders/signal_builder.py +1131 -0
  139. oscura/cli/__init__.py +14 -0
  140. oscura/cli/batch.py +339 -0
  141. oscura/cli/characterize.py +273 -0
  142. oscura/cli/compare.py +775 -0
  143. oscura/cli/decode.py +551 -0
  144. oscura/cli/main.py +247 -0
  145. oscura/cli/shell.py +350 -0
  146. oscura/comparison/__init__.py +66 -0
  147. oscura/comparison/compare.py +397 -0
  148. oscura/comparison/golden.py +487 -0
  149. oscura/comparison/limits.py +391 -0
  150. oscura/comparison/mask.py +434 -0
  151. oscura/comparison/trace_diff.py +30 -0
  152. oscura/comparison/visualization.py +481 -0
  153. oscura/compliance/__init__.py +70 -0
  154. oscura/compliance/advanced.py +756 -0
  155. oscura/compliance/masks.py +363 -0
  156. oscura/compliance/reporting.py +483 -0
  157. oscura/compliance/testing.py +298 -0
  158. oscura/component/__init__.py +38 -0
  159. oscura/component/impedance.py +365 -0
  160. oscura/component/reactive.py +598 -0
  161. oscura/component/transmission_line.py +312 -0
  162. oscura/config/__init__.py +191 -0
  163. oscura/config/defaults.py +254 -0
  164. oscura/config/loader.py +348 -0
  165. oscura/config/memory.py +271 -0
  166. oscura/config/migration.py +458 -0
  167. oscura/config/pipeline.py +1077 -0
  168. oscura/config/preferences.py +530 -0
  169. oscura/config/protocol.py +875 -0
  170. oscura/config/schema.py +713 -0
  171. oscura/config/settings.py +420 -0
  172. oscura/config/thresholds.py +599 -0
  173. oscura/convenience.py +457 -0
  174. oscura/core/__init__.py +299 -0
  175. oscura/core/audit.py +457 -0
  176. oscura/core/backend_selector.py +405 -0
  177. oscura/core/cache.py +590 -0
  178. oscura/core/cancellation.py +439 -0
  179. oscura/core/confidence.py +225 -0
  180. oscura/core/config.py +506 -0
  181. oscura/core/correlation.py +216 -0
  182. oscura/core/cross_domain.py +422 -0
  183. oscura/core/debug.py +301 -0
  184. oscura/core/edge_cases.py +541 -0
  185. oscura/core/exceptions.py +535 -0
  186. oscura/core/gpu_backend.py +523 -0
  187. oscura/core/lazy.py +832 -0
  188. oscura/core/log_query.py +540 -0
  189. oscura/core/logging.py +931 -0
  190. oscura/core/logging_advanced.py +952 -0
  191. oscura/core/memoize.py +171 -0
  192. oscura/core/memory_check.py +274 -0
  193. oscura/core/memory_guard.py +290 -0
  194. oscura/core/memory_limits.py +336 -0
  195. oscura/core/memory_monitor.py +453 -0
  196. oscura/core/memory_progress.py +465 -0
  197. oscura/core/memory_warnings.py +315 -0
  198. oscura/core/numba_backend.py +362 -0
  199. oscura/core/performance.py +352 -0
  200. oscura/core/progress.py +524 -0
  201. oscura/core/provenance.py +358 -0
  202. oscura/core/results.py +331 -0
  203. oscura/core/types.py +504 -0
  204. oscura/core/uncertainty.py +383 -0
  205. oscura/discovery/__init__.py +52 -0
  206. oscura/discovery/anomaly_detector.py +672 -0
  207. oscura/discovery/auto_decoder.py +415 -0
  208. oscura/discovery/comparison.py +497 -0
  209. oscura/discovery/quality_validator.py +528 -0
  210. oscura/discovery/signal_detector.py +769 -0
  211. oscura/dsl/__init__.py +73 -0
  212. oscura/dsl/commands.py +246 -0
  213. oscura/dsl/interpreter.py +455 -0
  214. oscura/dsl/parser.py +689 -0
  215. oscura/dsl/repl.py +172 -0
  216. oscura/exceptions.py +59 -0
  217. oscura/exploratory/__init__.py +111 -0
  218. oscura/exploratory/error_recovery.py +642 -0
  219. oscura/exploratory/fuzzy.py +513 -0
  220. oscura/exploratory/fuzzy_advanced.py +786 -0
  221. oscura/exploratory/legacy.py +831 -0
  222. oscura/exploratory/parse.py +358 -0
  223. oscura/exploratory/recovery.py +275 -0
  224. oscura/exploratory/sync.py +382 -0
  225. oscura/exploratory/unknown.py +707 -0
  226. oscura/export/__init__.py +25 -0
  227. oscura/export/wireshark/README.md +265 -0
  228. oscura/export/wireshark/__init__.py +47 -0
  229. oscura/export/wireshark/generator.py +312 -0
  230. oscura/export/wireshark/lua_builder.py +159 -0
  231. oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
  232. oscura/export/wireshark/type_mapping.py +165 -0
  233. oscura/export/wireshark/validator.py +105 -0
  234. oscura/exporters/__init__.py +94 -0
  235. oscura/exporters/csv.py +303 -0
  236. oscura/exporters/exporters.py +44 -0
  237. oscura/exporters/hdf5.py +219 -0
  238. oscura/exporters/html_export.py +701 -0
  239. oscura/exporters/json_export.py +291 -0
  240. oscura/exporters/markdown_export.py +367 -0
  241. oscura/exporters/matlab_export.py +354 -0
  242. oscura/exporters/npz_export.py +219 -0
  243. oscura/exporters/spice_export.py +210 -0
  244. oscura/extensibility/__init__.py +131 -0
  245. oscura/extensibility/docs.py +752 -0
  246. oscura/extensibility/extensions.py +1125 -0
  247. oscura/extensibility/logging.py +259 -0
  248. oscura/extensibility/measurements.py +485 -0
  249. oscura/extensibility/plugins.py +414 -0
  250. oscura/extensibility/registry.py +346 -0
  251. oscura/extensibility/templates.py +913 -0
  252. oscura/extensibility/validation.py +651 -0
  253. oscura/filtering/__init__.py +89 -0
  254. oscura/filtering/base.py +563 -0
  255. oscura/filtering/convenience.py +564 -0
  256. oscura/filtering/design.py +725 -0
  257. oscura/filtering/filters.py +32 -0
  258. oscura/filtering/introspection.py +605 -0
  259. oscura/guidance/__init__.py +24 -0
  260. oscura/guidance/recommender.py +429 -0
  261. oscura/guidance/wizard.py +518 -0
  262. oscura/inference/__init__.py +251 -0
  263. oscura/inference/active_learning/README.md +153 -0
  264. oscura/inference/active_learning/__init__.py +38 -0
  265. oscura/inference/active_learning/lstar.py +257 -0
  266. oscura/inference/active_learning/observation_table.py +230 -0
  267. oscura/inference/active_learning/oracle.py +78 -0
  268. oscura/inference/active_learning/teachers/__init__.py +15 -0
  269. oscura/inference/active_learning/teachers/simulator.py +192 -0
  270. oscura/inference/adaptive_tuning.py +453 -0
  271. oscura/inference/alignment.py +653 -0
  272. oscura/inference/bayesian.py +943 -0
  273. oscura/inference/binary.py +1016 -0
  274. oscura/inference/crc_reverse.py +711 -0
  275. oscura/inference/logic.py +288 -0
  276. oscura/inference/message_format.py +1305 -0
  277. oscura/inference/protocol.py +417 -0
  278. oscura/inference/protocol_dsl.py +1084 -0
  279. oscura/inference/protocol_library.py +1230 -0
  280. oscura/inference/sequences.py +809 -0
  281. oscura/inference/signal_intelligence.py +1509 -0
  282. oscura/inference/spectral.py +215 -0
  283. oscura/inference/state_machine.py +634 -0
  284. oscura/inference/stream.py +918 -0
  285. oscura/integrations/__init__.py +59 -0
  286. oscura/integrations/llm.py +1827 -0
  287. oscura/jupyter/__init__.py +32 -0
  288. oscura/jupyter/display.py +268 -0
  289. oscura/jupyter/magic.py +334 -0
  290. oscura/loaders/__init__.py +526 -0
  291. oscura/loaders/binary.py +69 -0
  292. oscura/loaders/configurable.py +1255 -0
  293. oscura/loaders/csv.py +26 -0
  294. oscura/loaders/csv_loader.py +473 -0
  295. oscura/loaders/hdf5.py +9 -0
  296. oscura/loaders/hdf5_loader.py +510 -0
  297. oscura/loaders/lazy.py +370 -0
  298. oscura/loaders/mmap_loader.py +583 -0
  299. oscura/loaders/numpy_loader.py +436 -0
  300. oscura/loaders/pcap.py +432 -0
  301. oscura/loaders/preprocessing.py +368 -0
  302. oscura/loaders/rigol.py +287 -0
  303. oscura/loaders/sigrok.py +321 -0
  304. oscura/loaders/tdms.py +367 -0
  305. oscura/loaders/tektronix.py +711 -0
  306. oscura/loaders/validation.py +584 -0
  307. oscura/loaders/vcd.py +464 -0
  308. oscura/loaders/wav.py +233 -0
  309. oscura/math/__init__.py +45 -0
  310. oscura/math/arithmetic.py +824 -0
  311. oscura/math/interpolation.py +413 -0
  312. oscura/onboarding/__init__.py +39 -0
  313. oscura/onboarding/help.py +498 -0
  314. oscura/onboarding/tutorials.py +405 -0
  315. oscura/onboarding/wizard.py +466 -0
  316. oscura/optimization/__init__.py +19 -0
  317. oscura/optimization/parallel.py +440 -0
  318. oscura/optimization/search.py +532 -0
  319. oscura/pipeline/__init__.py +43 -0
  320. oscura/pipeline/base.py +338 -0
  321. oscura/pipeline/composition.py +242 -0
  322. oscura/pipeline/parallel.py +448 -0
  323. oscura/pipeline/pipeline.py +375 -0
  324. oscura/pipeline/reverse_engineering.py +1119 -0
  325. oscura/plugins/__init__.py +122 -0
  326. oscura/plugins/base.py +272 -0
  327. oscura/plugins/cli.py +497 -0
  328. oscura/plugins/discovery.py +411 -0
  329. oscura/plugins/isolation.py +418 -0
  330. oscura/plugins/lifecycle.py +959 -0
  331. oscura/plugins/manager.py +493 -0
  332. oscura/plugins/registry.py +421 -0
  333. oscura/plugins/versioning.py +372 -0
  334. oscura/py.typed +0 -0
  335. oscura/quality/__init__.py +65 -0
  336. oscura/quality/ensemble.py +740 -0
  337. oscura/quality/explainer.py +338 -0
  338. oscura/quality/scoring.py +616 -0
  339. oscura/quality/warnings.py +456 -0
  340. oscura/reporting/__init__.py +248 -0
  341. oscura/reporting/advanced.py +1234 -0
  342. oscura/reporting/analyze.py +448 -0
  343. oscura/reporting/argument_preparer.py +596 -0
  344. oscura/reporting/auto_report.py +507 -0
  345. oscura/reporting/batch.py +615 -0
  346. oscura/reporting/chart_selection.py +223 -0
  347. oscura/reporting/comparison.py +330 -0
  348. oscura/reporting/config.py +615 -0
  349. oscura/reporting/content/__init__.py +39 -0
  350. oscura/reporting/content/executive.py +127 -0
  351. oscura/reporting/content/filtering.py +191 -0
  352. oscura/reporting/content/minimal.py +257 -0
  353. oscura/reporting/content/verbosity.py +162 -0
  354. oscura/reporting/core.py +508 -0
  355. oscura/reporting/core_formats/__init__.py +17 -0
  356. oscura/reporting/core_formats/multi_format.py +210 -0
  357. oscura/reporting/engine.py +836 -0
  358. oscura/reporting/export.py +366 -0
  359. oscura/reporting/formatting/__init__.py +129 -0
  360. oscura/reporting/formatting/emphasis.py +81 -0
  361. oscura/reporting/formatting/numbers.py +403 -0
  362. oscura/reporting/formatting/standards.py +55 -0
  363. oscura/reporting/formatting.py +466 -0
  364. oscura/reporting/html.py +578 -0
  365. oscura/reporting/index.py +590 -0
  366. oscura/reporting/multichannel.py +296 -0
  367. oscura/reporting/output.py +379 -0
  368. oscura/reporting/pdf.py +373 -0
  369. oscura/reporting/plots.py +731 -0
  370. oscura/reporting/pptx_export.py +360 -0
  371. oscura/reporting/renderers/__init__.py +11 -0
  372. oscura/reporting/renderers/pdf.py +94 -0
  373. oscura/reporting/sections.py +471 -0
  374. oscura/reporting/standards.py +680 -0
  375. oscura/reporting/summary_generator.py +368 -0
  376. oscura/reporting/tables.py +397 -0
  377. oscura/reporting/template_system.py +724 -0
  378. oscura/reporting/templates/__init__.py +15 -0
  379. oscura/reporting/templates/definition.py +205 -0
  380. oscura/reporting/templates/index.html +649 -0
  381. oscura/reporting/templates/index.md +173 -0
  382. oscura/schemas/__init__.py +158 -0
  383. oscura/schemas/bus_configuration.json +322 -0
  384. oscura/schemas/device_mapping.json +182 -0
  385. oscura/schemas/packet_format.json +418 -0
  386. oscura/schemas/protocol_definition.json +363 -0
  387. oscura/search/__init__.py +16 -0
  388. oscura/search/anomaly.py +292 -0
  389. oscura/search/context.py +149 -0
  390. oscura/search/pattern.py +160 -0
  391. oscura/session/__init__.py +34 -0
  392. oscura/session/annotations.py +289 -0
  393. oscura/session/history.py +313 -0
  394. oscura/session/session.py +445 -0
  395. oscura/streaming/__init__.py +43 -0
  396. oscura/streaming/chunked.py +611 -0
  397. oscura/streaming/progressive.py +393 -0
  398. oscura/streaming/realtime.py +622 -0
  399. oscura/testing/__init__.py +54 -0
  400. oscura/testing/synthetic.py +808 -0
  401. oscura/triggering/__init__.py +68 -0
  402. oscura/triggering/base.py +229 -0
  403. oscura/triggering/edge.py +353 -0
  404. oscura/triggering/pattern.py +344 -0
  405. oscura/triggering/pulse.py +581 -0
  406. oscura/triggering/window.py +453 -0
  407. oscura/ui/__init__.py +48 -0
  408. oscura/ui/formatters.py +526 -0
  409. oscura/ui/progressive_display.py +340 -0
  410. oscura/utils/__init__.py +99 -0
  411. oscura/utils/autodetect.py +338 -0
  412. oscura/utils/buffer.py +389 -0
  413. oscura/utils/lazy.py +407 -0
  414. oscura/utils/lazy_imports.py +147 -0
  415. oscura/utils/memory.py +836 -0
  416. oscura/utils/memory_advanced.py +1326 -0
  417. oscura/utils/memory_extensions.py +465 -0
  418. oscura/utils/progressive.py +352 -0
  419. oscura/utils/windowing.py +362 -0
  420. oscura/visualization/__init__.py +321 -0
  421. oscura/visualization/accessibility.py +526 -0
  422. oscura/visualization/annotations.py +374 -0
  423. oscura/visualization/axis_scaling.py +305 -0
  424. oscura/visualization/colors.py +453 -0
  425. oscura/visualization/digital.py +337 -0
  426. oscura/visualization/eye.py +420 -0
  427. oscura/visualization/histogram.py +281 -0
  428. oscura/visualization/interactive.py +858 -0
  429. oscura/visualization/jitter.py +702 -0
  430. oscura/visualization/keyboard.py +394 -0
  431. oscura/visualization/layout.py +365 -0
  432. oscura/visualization/optimization.py +1028 -0
  433. oscura/visualization/palettes.py +446 -0
  434. oscura/visualization/plot.py +92 -0
  435. oscura/visualization/power.py +290 -0
  436. oscura/visualization/power_extended.py +626 -0
  437. oscura/visualization/presets.py +467 -0
  438. oscura/visualization/protocols.py +932 -0
  439. oscura/visualization/render.py +207 -0
  440. oscura/visualization/rendering.py +444 -0
  441. oscura/visualization/reverse_engineering.py +791 -0
  442. oscura/visualization/signal_integrity.py +808 -0
  443. oscura/visualization/specialized.py +553 -0
  444. oscura/visualization/spectral.py +811 -0
  445. oscura/visualization/styles.py +381 -0
  446. oscura/visualization/thumbnails.py +311 -0
  447. oscura/visualization/time_axis.py +351 -0
  448. oscura/visualization/waveform.py +367 -0
  449. oscura/workflow/__init__.py +13 -0
  450. oscura/workflow/dag.py +377 -0
  451. oscura/workflows/__init__.py +58 -0
  452. oscura/workflows/compliance.py +280 -0
  453. oscura/workflows/digital.py +272 -0
  454. oscura/workflows/multi_trace.py +502 -0
  455. oscura/workflows/power.py +178 -0
  456. oscura/workflows/protocol.py +492 -0
  457. oscura/workflows/reverse_engineering.py +639 -0
  458. oscura/workflows/signal_integrity.py +227 -0
  459. oscura-0.1.1.dist-info/METADATA +300 -0
  460. oscura-0.1.1.dist-info/RECORD +463 -0
  461. oscura-0.1.1.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/licenses/LICENSE +1 -1
  463. oscura-0.0.1.dist-info/METADATA +0 -63
  464. oscura-0.0.1.dist-info/RECORD +0 -5
  465. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,487 @@
1
+ """Aggregate logging for batch processing operations.
2
+
3
+ This module provides consolidated logging for parallel batch workers
4
+ with job-level summaries and per-file tracking.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.batch.logging import BatchLogger
9
+ >>> logger = BatchLogger(batch_id="job-001")
10
+ >>> with logger.file_context("capture1.wfm") as file_log:
11
+ ... file_log.info("Processing file")
12
+ ... result = analyze(file)
13
+ >>> logger.summary()
14
+
15
+ References:
16
+ LOG-011,
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import logging
22
+ import threading
23
+ import time
24
+ import uuid
25
+ from collections import defaultdict
26
+ from contextlib import contextmanager
27
+ from dataclasses import dataclass, field
28
+ from datetime import UTC, datetime
29
+ from typing import TYPE_CHECKING, Any
30
+
31
+ if TYPE_CHECKING:
32
+ from collections.abc import Iterator
33
+
34
+ from oscura.core.logging import format_timestamp, get_logger
35
+
36
+
37
+ @dataclass
38
+ class FileLogEntry:
39
+ """Log entry for a single file in a batch job.
40
+
41
+ Attributes:
42
+ file_id: Unique identifier for this file.
43
+ filename: Path to the file.
44
+ start_time: Processing start time.
45
+ end_time: Processing end time.
46
+ status: Processing status (pending, processing, success, error).
47
+ error_message: Error message if status is 'error'.
48
+ log_messages: List of log messages for this file.
49
+
50
+ References:
51
+ LOG-011: Aggregate Logging for Batch Processing
52
+ """
53
+
54
+ file_id: str
55
+ filename: str
56
+ start_time: float | None = None
57
+ end_time: float | None = None
58
+ status: str = "pending"
59
+ error_message: str | None = None
60
+ log_messages: list[dict[str, Any]] = field(default_factory=list)
61
+
62
+ @property
63
+ def duration(self) -> float | None:
64
+ """Get processing duration in seconds."""
65
+ if self.start_time is not None and self.end_time is not None:
66
+ return self.end_time - self.start_time
67
+ return None
68
+
69
+ def to_dict(self) -> dict[str, Any]:
70
+ """Convert to dictionary representation."""
71
+ return {
72
+ "file_id": self.file_id,
73
+ "filename": self.filename,
74
+ "start_time": format_timestamp(datetime.fromtimestamp(self.start_time, tz=UTC))
75
+ if self.start_time
76
+ else None,
77
+ "end_time": format_timestamp(datetime.fromtimestamp(self.end_time, tz=UTC))
78
+ if self.end_time
79
+ else None,
80
+ "duration_seconds": self.duration,
81
+ "status": self.status,
82
+ "error_message": self.error_message,
83
+ "log_count": len(self.log_messages),
84
+ }
85
+
86
+
87
+ @dataclass
88
+ class BatchSummary:
89
+ """Summary of batch processing results.
90
+
91
+ Attributes:
92
+ batch_id: Unique identifier for the batch job.
93
+ total_files: Total number of files in the batch.
94
+ success_count: Number of successfully processed files.
95
+ error_count: Number of files that failed.
96
+ total_duration: Total processing time in seconds.
97
+ errors_by_type: Count of errors grouped by type.
98
+
99
+ References:
100
+ LOG-011: Aggregate Logging for Batch Processing
101
+ """
102
+
103
+ batch_id: str
104
+ total_files: int
105
+ success_count: int
106
+ error_count: int
107
+ total_duration: float
108
+ start_time: str
109
+ end_time: str
110
+ errors_by_type: dict[str, int] = field(default_factory=dict)
111
+ files_per_second: float = 0.0
112
+ average_duration_per_file: float = 0.0
113
+
114
+ def to_dict(self) -> dict[str, Any]:
115
+ """Convert to dictionary representation."""
116
+ return {
117
+ "batch_id": self.batch_id,
118
+ "total_files": self.total_files,
119
+ "success_count": self.success_count,
120
+ "error_count": self.error_count,
121
+ "success_rate": self.success_count / self.total_files if self.total_files > 0 else 0.0,
122
+ "total_duration_seconds": self.total_duration,
123
+ "start_time": self.start_time,
124
+ "end_time": self.end_time,
125
+ "files_per_second": self.files_per_second,
126
+ "average_duration_per_file": self.average_duration_per_file,
127
+ "errors_by_type": self.errors_by_type,
128
+ }
129
+
130
+
131
+ class FileLogger:
132
+ """Logger for individual file processing within a batch.
133
+
134
+ Provides logging methods that automatically tag logs with
135
+ batch_id and file_id for aggregation.
136
+
137
+ References:
138
+ LOG-011: Aggregate Logging for Batch Processing
139
+ """
140
+
141
+ def __init__(
142
+ self,
143
+ entry: FileLogEntry,
144
+ batch_id: str,
145
+ parent_logger: logging.Logger,
146
+ ):
147
+ """Initialize file logger.
148
+
149
+ Args:
150
+ entry: FileLogEntry for this file.
151
+ batch_id: Batch job identifier.
152
+ parent_logger: Parent logger for output.
153
+ """
154
+ self._entry = entry
155
+ self._batch_id = batch_id
156
+ self._logger = parent_logger
157
+
158
+ def _log(self, level: int, message: str, **kwargs: Any) -> None:
159
+ """Log a message with batch/file context."""
160
+ log_entry = {
161
+ "timestamp": format_timestamp(),
162
+ "level": logging.getLevelName(level),
163
+ "message": message,
164
+ "batch_id": self._batch_id,
165
+ "file_id": self._entry.file_id,
166
+ "filename": self._entry.filename,
167
+ **kwargs,
168
+ }
169
+ self._entry.log_messages.append(log_entry)
170
+ self._logger.log(
171
+ level,
172
+ message,
173
+ extra={
174
+ "batch_id": self._batch_id,
175
+ "file_id": self._entry.file_id,
176
+ **kwargs,
177
+ },
178
+ )
179
+
180
+ def debug(self, message: str, **kwargs: Any) -> None:
181
+ """Log debug message."""
182
+ self._log(logging.DEBUG, message, **kwargs)
183
+
184
+ def info(self, message: str, **kwargs: Any) -> None:
185
+ """Log info message."""
186
+ self._log(logging.INFO, message, **kwargs)
187
+
188
+ def warning(self, message: str, **kwargs: Any) -> None:
189
+ """Log warning message."""
190
+ self._log(logging.WARNING, message, **kwargs)
191
+
192
+ def error(self, message: str, **kwargs: Any) -> None:
193
+ """Log error message."""
194
+ self._log(logging.ERROR, message, **kwargs)
195
+
196
+
197
+ class BatchLogger:
198
+ """Aggregate logger for batch processing operations.
199
+
200
+ Consolidates logs from parallel batch workers with job-level
201
+ summaries and per-file tracking.
202
+
203
+ Example:
204
+ >>> logger = BatchLogger(batch_id="job-001")
205
+ >>> with logger.file_context("capture1.wfm") as file_log:
206
+ ... file_log.info("Loading file")
207
+ ... result = analyze(file)
208
+ ... file_log.info("Analysis complete", result=result)
209
+ >>> summary = logger.summary()
210
+
211
+ References:
212
+ LOG-011: Aggregate Logging for Batch Processing
213
+ LOG-013: Batch Job Correlation ID and Lineage
214
+ """
215
+
216
+ def __init__(
217
+ self,
218
+ batch_id: str | None = None,
219
+ logger_name: str = "oscura.batch",
220
+ ):
221
+ """Initialize batch logger.
222
+
223
+ Args:
224
+ batch_id: Unique batch job identifier. Auto-generated if None.
225
+ logger_name: Name for the underlying logger.
226
+ """
227
+ self.batch_id = batch_id or str(uuid.uuid4())
228
+ self._logger = get_logger(logger_name)
229
+ self._files: dict[str, FileLogEntry] = {}
230
+ self._lock = threading.Lock()
231
+ self._start_time: float | None = None
232
+ self._end_time: float | None = None
233
+ self._error_types: dict[str, int] = defaultdict(int)
234
+
235
+ def start(self) -> None:
236
+ """Mark batch job as started.
237
+
238
+ Records the start time for duration calculation.
239
+ """
240
+ self._start_time = time.time()
241
+ self._logger.info(
242
+ "Batch job started",
243
+ extra={"batch_id": self.batch_id},
244
+ )
245
+
246
+ def finish(self) -> None:
247
+ """Mark batch job as finished.
248
+
249
+ Records the end time and logs the completion summary.
250
+ """
251
+ self._end_time = time.time()
252
+ summary = self.summary()
253
+ self._logger.info(
254
+ "Batch job completed",
255
+ extra={
256
+ "batch_id": self.batch_id,
257
+ "total_files": summary.total_files,
258
+ "success_count": summary.success_count,
259
+ "error_count": summary.error_count,
260
+ "duration": summary.total_duration,
261
+ },
262
+ )
263
+
264
+ def register_file(self, filename: str) -> str:
265
+ """Register a file for processing.
266
+
267
+ Args:
268
+ filename: Path to the file.
269
+
270
+ Returns:
271
+ Unique file_id for this file.
272
+ """
273
+ file_id = str(uuid.uuid4())
274
+ with self._lock:
275
+ self._files[file_id] = FileLogEntry(
276
+ file_id=file_id,
277
+ filename=filename,
278
+ )
279
+ return file_id
280
+
281
+ @contextmanager
282
+ def file_context(self, filename: str) -> Iterator[FileLogger]:
283
+ """Context manager for file processing.
284
+
285
+ Automatically tracks start/end time and status.
286
+
287
+ Args:
288
+ filename: Path to the file being processed.
289
+
290
+ Yields:
291
+ FileLogger for logging within this file's context.
292
+
293
+ Raises:
294
+ Exception: Re-raises any exception from the processing context.
295
+
296
+ Example:
297
+ >>> with batch_logger.file_context("data.wfm") as log:
298
+ ... log.info("Processing started")
299
+ ... result = process_file("data.wfm")
300
+ """
301
+ file_id = self.register_file(filename)
302
+ entry = self._files[file_id]
303
+
304
+ entry.start_time = time.time()
305
+ entry.status = "processing"
306
+
307
+ file_logger = FileLogger(entry, self.batch_id, self._logger)
308
+
309
+ try:
310
+ yield file_logger
311
+ entry.status = "success"
312
+ except Exception as e:
313
+ entry.status = "error"
314
+ entry.error_message = str(e)
315
+ error_type = type(e).__name__
316
+ with self._lock:
317
+ self._error_types[error_type] += 1
318
+ file_logger.error("Processing failed: %s", e, exception_type=error_type) # type: ignore[call-arg]
319
+ raise
320
+ finally:
321
+ entry.end_time = time.time()
322
+
323
+ def mark_success(self, file_id: str) -> None:
324
+ """Mark a file as successfully processed.
325
+
326
+ Args:
327
+ file_id: File identifier from register_file.
328
+ """
329
+ with self._lock:
330
+ if file_id in self._files:
331
+ self._files[file_id].status = "success"
332
+ self._files[file_id].end_time = time.time()
333
+
334
+ def mark_error(self, file_id: str, error: str, error_type: str = "Unknown") -> None:
335
+ """Mark a file as failed.
336
+
337
+ Args:
338
+ file_id: File identifier from register_file.
339
+ error: Error message.
340
+ error_type: Type of error for aggregation.
341
+ """
342
+ with self._lock:
343
+ if file_id in self._files:
344
+ self._files[file_id].status = "error"
345
+ self._files[file_id].error_message = error
346
+ self._files[file_id].end_time = time.time()
347
+ self._error_types[error_type] += 1
348
+
349
+ def summary(self) -> BatchSummary:
350
+ """Generate batch processing summary.
351
+
352
+ Returns:
353
+ BatchSummary with aggregated statistics.
354
+
355
+ References:
356
+ LOG-011: Aggregate Logging for Batch Processing
357
+ """
358
+ with self._lock:
359
+ files = list(self._files.values())
360
+ errors_by_type = dict(self._error_types)
361
+
362
+ total_files = len(files)
363
+ success_count = sum(1 for f in files if f.status == "success")
364
+ error_count = sum(1 for f in files if f.status == "error")
365
+
366
+ # Calculate timing
367
+ start_time = self._start_time or (
368
+ min((f.start_time for f in files if f.start_time), default=0)
369
+ )
370
+ end_time = self._end_time or (max((f.end_time for f in files if f.end_time), default=0))
371
+ total_duration = end_time - start_time if start_time and end_time else 0.0
372
+
373
+ # Calculate per-file metrics
374
+ durations = [f.duration for f in files if f.duration is not None]
375
+ avg_duration = sum(durations) / len(durations) if durations else 0.0
376
+ files_per_second = total_files / total_duration if total_duration > 0 else 0.0
377
+
378
+ return BatchSummary(
379
+ batch_id=self.batch_id,
380
+ total_files=total_files,
381
+ success_count=success_count,
382
+ error_count=error_count,
383
+ total_duration=total_duration,
384
+ start_time=format_timestamp(datetime.fromtimestamp(start_time, tz=UTC))
385
+ if start_time
386
+ else "",
387
+ end_time=format_timestamp(datetime.fromtimestamp(end_time, tz=UTC)) if end_time else "",
388
+ errors_by_type=errors_by_type,
389
+ files_per_second=files_per_second,
390
+ average_duration_per_file=avg_duration,
391
+ )
392
+
393
+ def get_file_logs(self, file_id: str) -> list[dict[str, Any]]:
394
+ """Get all log messages for a specific file.
395
+
396
+ Args:
397
+ file_id: File identifier.
398
+
399
+ Returns:
400
+ List of log message dictionaries.
401
+ """
402
+ with self._lock:
403
+ if file_id in self._files:
404
+ return list(self._files[file_id].log_messages)
405
+ return []
406
+
407
+ def get_all_files(self) -> list[dict[str, Any]]:
408
+ """Get summary information for all files.
409
+
410
+ Returns:
411
+ List of file summary dictionaries.
412
+ """
413
+ with self._lock:
414
+ return [f.to_dict() for f in self._files.values()]
415
+
416
+ def get_errors(self) -> list[dict[str, Any]]:
417
+ """Get all files that encountered errors.
418
+
419
+ Returns:
420
+ List of error file dictionaries with error details.
421
+ """
422
+ with self._lock:
423
+ return [
424
+ {
425
+ **f.to_dict(),
426
+ "logs": f.log_messages,
427
+ }
428
+ for f in self._files.values()
429
+ if f.status == "error"
430
+ ]
431
+
432
+
433
+ def aggregate_batch_logs(
434
+ batch_loggers: list[BatchLogger],
435
+ ) -> dict[str, Any]:
436
+ """Aggregate logs from multiple batch loggers.
437
+
438
+ Combines summaries from multiple batch jobs into a single
439
+ aggregate report.
440
+
441
+ Args:
442
+ batch_loggers: List of BatchLogger instances.
443
+
444
+ Returns:
445
+ Aggregated summary dictionary.
446
+
447
+ References:
448
+ LOG-011: Aggregate Logging for Batch Processing
449
+ """
450
+ total_files = 0
451
+ total_success = 0
452
+ total_errors = 0
453
+ total_duration = 0.0
454
+ all_errors_by_type: dict[str, int] = defaultdict(int)
455
+ batch_summaries = []
456
+
457
+ for logger in batch_loggers:
458
+ summary = logger.summary()
459
+ batch_summaries.append(summary.to_dict())
460
+ total_files += summary.total_files
461
+ total_success += summary.success_count
462
+ total_errors += summary.error_count
463
+ total_duration += summary.total_duration
464
+ for error_type, count in summary.errors_by_type.items():
465
+ all_errors_by_type[error_type] += count
466
+
467
+ return {
468
+ "aggregate": {
469
+ "total_batches": len(batch_loggers),
470
+ "total_files": total_files,
471
+ "total_success": total_success,
472
+ "total_errors": total_errors,
473
+ "total_duration_seconds": total_duration,
474
+ "overall_success_rate": total_success / total_files if total_files > 0 else 0.0,
475
+ "errors_by_type": dict(all_errors_by_type),
476
+ },
477
+ "batches": batch_summaries,
478
+ }
479
+
480
+
481
+ __all__ = [
482
+ "BatchLogger",
483
+ "BatchSummary",
484
+ "FileLogEntry",
485
+ "FileLogger",
486
+ "aggregate_batch_logs",
487
+ ]