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,540 @@
1
+ """Log query and export functionality.
2
+
3
+ This module provides searchable log querying with filtering and export
4
+ capabilities for analysis and reporting.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.core.log_query import LogQuery
9
+ >>> query = LogQuery()
10
+ >>> # Query last hour of ERROR logs
11
+ >>> from datetime import datetime, UTC, timedelta
12
+ >>> results = query.query_logs(
13
+ ... start_time=datetime.now(UTC) - timedelta(hours=1),
14
+ ... level="ERROR"
15
+ ... )
16
+ >>> # Export to JSON
17
+ >>> query.export_logs(results, "errors.json", format="json")
18
+
19
+ References:
20
+ LOG-010: Searchable Log Query and Export
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import csv
26
+ import json
27
+ import re
28
+ from dataclasses import asdict, dataclass
29
+ from pathlib import Path
30
+ from typing import TYPE_CHECKING, Any, Literal
31
+
32
+ from oscura.core.logging import format_timestamp
33
+
34
+ if TYPE_CHECKING:
35
+ from datetime import datetime
36
+
37
+
38
+ @dataclass
39
+ class LogRecord:
40
+ """Structured log record for querying.
41
+
42
+ Attributes:
43
+ timestamp: ISO 8601 timestamp of the log entry.
44
+ level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL).
45
+ module: Logger name/module.
46
+ message: Log message.
47
+ correlation_id: Optional correlation ID for tracing.
48
+ metadata: Additional metadata fields.
49
+
50
+ References:
51
+ LOG-010: Searchable Log Query
52
+ """
53
+
54
+ timestamp: str
55
+ level: str
56
+ module: str
57
+ message: str
58
+ correlation_id: str | None = None
59
+ metadata: dict[str, Any] | None = None
60
+
61
+ def to_dict(self) -> dict[str, Any]:
62
+ """Convert log record to dictionary.
63
+
64
+ Returns:
65
+ Dictionary representation of the log record.
66
+ """
67
+ result = asdict(self)
68
+ if result["metadata"] is None:
69
+ result["metadata"] = {}
70
+ return result
71
+
72
+ @classmethod
73
+ def from_dict(cls, data: dict[str, Any]) -> LogRecord:
74
+ """Create log record from dictionary.
75
+
76
+ Args:
77
+ data: Dictionary containing log record data.
78
+
79
+ Returns:
80
+ LogRecord instance.
81
+ """
82
+ return cls(
83
+ timestamp=data["timestamp"],
84
+ level=data["level"],
85
+ module=data["module"],
86
+ message=data["message"],
87
+ correlation_id=data.get("correlation_id"),
88
+ metadata=data.get("metadata"),
89
+ )
90
+
91
+
92
+ class LogQuery:
93
+ """Query and filter log records from various sources.
94
+
95
+ Provides structured querying of logs with filtering by timestamp,
96
+ level, module, correlation ID, and message patterns. Supports
97
+ pagination and multiple export formats.
98
+
99
+ Example:
100
+ >>> query = LogQuery()
101
+ >>> # Load logs from file
102
+ >>> query.load_from_file("oscura.log")
103
+ >>> # Query with filters
104
+ >>> results = query.query_logs(
105
+ ... level="ERROR",
106
+ ... module_pattern="oscura.loaders.*"
107
+ ... )
108
+ >>> # Export filtered results
109
+ >>> query.export_logs(results, "filtered.csv", format="csv")
110
+
111
+ References:
112
+ LOG-010: Searchable Log Query and Export
113
+ """
114
+
115
+ def __init__(self): # type: ignore[no-untyped-def]
116
+ """Initialize log query engine."""
117
+ self._records: list[LogRecord] = []
118
+
119
+ def load_from_file(self, path: str, format: Literal["json", "text"] = "text") -> int:
120
+ """Load log records from file.
121
+
122
+ Args:
123
+ path: Path to log file.
124
+ format: File format (json for JSON lines, text for plain text).
125
+
126
+ Returns:
127
+ Number of records loaded.
128
+
129
+ Raises:
130
+ FileNotFoundError: If log file does not exist.
131
+ ValueError: If format is not supported.
132
+
133
+ Example:
134
+ >>> query = LogQuery()
135
+ >>> count = query.load_from_file("logs.json", format="json")
136
+ >>> print(f"Loaded {count} records")
137
+
138
+ References:
139
+ LOG-010: Searchable Log Query
140
+ """
141
+ path_obj = Path(path)
142
+ if not path_obj.exists():
143
+ raise FileNotFoundError(f"Log file not found: {path}")
144
+
145
+ if format == "json":
146
+ return self._load_json_lines(path_obj)
147
+ elif format == "text":
148
+ return self._load_text(path_obj)
149
+ else:
150
+ raise ValueError(f"Unsupported format: {format}")
151
+
152
+ def add_record(self, record: LogRecord) -> None:
153
+ """Add a log record to the query index.
154
+
155
+ Args:
156
+ record: LogRecord to add.
157
+
158
+ Example:
159
+ >>> query = LogQuery()
160
+ >>> record = LogRecord(
161
+ ... timestamp="2025-12-21T10:00:00.000000Z",
162
+ ... level="INFO",
163
+ ... module="oscura.test",
164
+ ... message="Test message"
165
+ ... )
166
+ >>> query.add_record(record)
167
+ """
168
+ self._records.append(record)
169
+
170
+ def query_logs(
171
+ self,
172
+ *,
173
+ start_time: datetime | None = None,
174
+ end_time: datetime | None = None,
175
+ level: str | None = None,
176
+ module: str | None = None,
177
+ module_pattern: str | None = None,
178
+ correlation_id: str | None = None,
179
+ message_pattern: str | None = None,
180
+ limit: int | None = None,
181
+ offset: int = 0,
182
+ ) -> list[LogRecord]:
183
+ """Query log records with filtering.
184
+
185
+ Args:
186
+ start_time: Return only logs after this time (UTC).
187
+ end_time: Return only logs before this time (UTC).
188
+ level: Filter by exact log level.
189
+ module: Filter by exact module name.
190
+ module_pattern: Filter by module name pattern (glob-style, e.g., "oscura.loaders.*").
191
+ correlation_id: Filter by correlation ID.
192
+ message_pattern: Filter by message regex pattern.
193
+ limit: Maximum number of results to return.
194
+ offset: Number of results to skip (for pagination).
195
+
196
+ Returns:
197
+ List of matching LogRecord objects.
198
+
199
+ Example:
200
+ >>> from datetime import datetime, UTC, timedelta
201
+ >>> query = LogQuery()
202
+ >>> # Last hour of errors
203
+ >>> results = query.query_logs(
204
+ ... start_time=datetime.now(UTC) - timedelta(hours=1),
205
+ ... level="ERROR"
206
+ ... )
207
+ >>> # Specific module with pattern
208
+ >>> results = query.query_logs(
209
+ ... module_pattern="oscura.analyzers.*",
210
+ ... message_pattern="FFT.*failed"
211
+ ... )
212
+ >>> # Paginated results
213
+ >>> page_1 = query.query_logs(limit=100, offset=0)
214
+ >>> page_2 = query.query_logs(limit=100, offset=100)
215
+
216
+ References:
217
+ LOG-010: Searchable Log Query and Export
218
+ """
219
+ results = self._records.copy()
220
+
221
+ # Filter by timestamp range
222
+ if start_time is not None:
223
+ start_str = format_timestamp(start_time, format="iso8601")
224
+ results = [r for r in results if r.timestamp >= start_str]
225
+
226
+ if end_time is not None:
227
+ end_str = format_timestamp(end_time, format="iso8601")
228
+ results = [r for r in results if r.timestamp <= end_str]
229
+
230
+ # Filter by log level
231
+ if level is not None:
232
+ results = [r for r in results if r.level == level.upper()]
233
+
234
+ # Filter by module
235
+ if module is not None:
236
+ results = [r for r in results if r.module == module]
237
+
238
+ # Filter by module pattern
239
+ if module_pattern is not None:
240
+ # Convert glob pattern to regex
241
+ pattern = module_pattern.replace(".", r"\.").replace("*", ".*")
242
+ regex = re.compile(f"^{pattern}$")
243
+ results = [r for r in results if regex.match(r.module)]
244
+
245
+ # Filter by correlation ID
246
+ if correlation_id is not None:
247
+ results = [r for r in results if r.correlation_id == correlation_id]
248
+
249
+ # Filter by message pattern
250
+ if message_pattern is not None:
251
+ regex = re.compile(message_pattern)
252
+ results = [r for r in results if regex.search(r.message)]
253
+
254
+ # Apply pagination
255
+ if offset > 0:
256
+ results = results[offset:]
257
+ if limit is not None:
258
+ results = results[:limit]
259
+
260
+ return results
261
+
262
+ def export_logs(
263
+ self,
264
+ records: list[LogRecord],
265
+ path: str,
266
+ format: Literal["json", "csv", "text"] = "json",
267
+ ) -> None:
268
+ """Export log records to file.
269
+
270
+ Args:
271
+ records: List of LogRecord objects to export.
272
+ path: Output file path.
273
+ format: Export format (json, csv, or text).
274
+
275
+ Raises:
276
+ ValueError: If format is not supported.
277
+
278
+ Example:
279
+ >>> query = LogQuery()
280
+ >>> results = query.query_logs(level="ERROR")
281
+ >>> query.export_logs(results, "errors.json", format="json")
282
+ >>> query.export_logs(results, "errors.csv", format="csv")
283
+ >>> query.export_logs(results, "errors.txt", format="text")
284
+
285
+ References:
286
+ LOG-010: Searchable Log Query and Export
287
+ """
288
+ path_obj = Path(path)
289
+ path_obj.parent.mkdir(parents=True, exist_ok=True)
290
+
291
+ if format == "json":
292
+ self._export_json(records, path_obj)
293
+ elif format == "csv":
294
+ self._export_csv(records, path_obj)
295
+ elif format == "text":
296
+ self._export_text(records, path_obj)
297
+ else:
298
+ raise ValueError(f"Unsupported export format: {format}")
299
+
300
+ def get_statistics(self) -> dict[str, Any]:
301
+ """Get statistics about loaded log records.
302
+
303
+ Returns:
304
+ Dictionary with statistics:
305
+ - total: Total number of records
306
+ - by_level: Count by log level
307
+ - by_module: Count by module
308
+ - time_range: Earliest and latest timestamps
309
+
310
+ Example:
311
+ >>> query = LogQuery()
312
+ >>> query.load_from_file("logs.json")
313
+ >>> stats = query.get_statistics()
314
+ >>> print(f"Total logs: {stats['total']}")
315
+ >>> print(f"Errors: {stats['by_level'].get('ERROR', 0)}")
316
+
317
+ References:
318
+ LOG-010: Log Query
319
+ """
320
+ if not self._records:
321
+ return {
322
+ "total": 0,
323
+ "by_level": {},
324
+ "by_module": {},
325
+ "time_range": None,
326
+ }
327
+
328
+ from collections import Counter
329
+
330
+ level_counts = Counter(r.level for r in self._records)
331
+ module_counts = Counter(r.module for r in self._records)
332
+
333
+ timestamps = sorted(r.timestamp for r in self._records)
334
+ time_range = {
335
+ "earliest": timestamps[0],
336
+ "latest": timestamps[-1],
337
+ }
338
+
339
+ return {
340
+ "total": len(self._records),
341
+ "by_level": dict(level_counts),
342
+ "by_module": dict(module_counts.most_common(20)),
343
+ "time_range": time_range,
344
+ }
345
+
346
+ def clear(self) -> None:
347
+ """Clear all loaded log records.
348
+
349
+ Example:
350
+ >>> query = LogQuery()
351
+ >>> query.clear()
352
+ """
353
+ self._records.clear()
354
+
355
+ def _load_json_lines(self, path: Path) -> int:
356
+ """Load JSON lines format logs.
357
+
358
+ Args:
359
+ path: Path to JSON lines file.
360
+
361
+ Returns:
362
+ Number of records loaded.
363
+ """
364
+ count = 0
365
+ with open(path, encoding="utf-8") as f:
366
+ for line in f:
367
+ line = line.strip()
368
+ if not line:
369
+ continue
370
+ try:
371
+ data = json.loads(line)
372
+ record = LogRecord(
373
+ timestamp=data.get("timestamp", ""),
374
+ level=data.get("level", "INFO"),
375
+ module=data.get("module", "unknown"),
376
+ message=data.get("message", ""),
377
+ correlation_id=data.get("correlation_id"),
378
+ metadata={
379
+ k: v
380
+ for k, v in data.items()
381
+ if k
382
+ not in ("timestamp", "level", "module", "message", "correlation_id")
383
+ },
384
+ )
385
+ self._records.append(record)
386
+ count += 1
387
+ except (json.JSONDecodeError, KeyError):
388
+ # Skip malformed lines
389
+ continue
390
+ return count
391
+
392
+ def _load_text(self, path: Path) -> int:
393
+ """Load plain text format logs.
394
+
395
+ Attempts to parse common log formats.
396
+
397
+ Args:
398
+ path: Path to text log file.
399
+
400
+ Returns:
401
+ Number of records loaded.
402
+ """
403
+ count = 0
404
+ with open(path, encoding="utf-8") as f:
405
+ for line in f:
406
+ line = line.strip()
407
+ if not line:
408
+ continue
409
+
410
+ # Try to parse common format: TIMESTAMP [LEVEL] MODULE: MESSAGE
411
+ match = re.match(
412
+ r"^(\S+)\s+\[(\w+)\]\s+(\S+):\s+(.*)$",
413
+ line,
414
+ )
415
+ if match:
416
+ timestamp, level, module, message = match.groups()
417
+ record = LogRecord(
418
+ timestamp=timestamp,
419
+ level=level,
420
+ module=module,
421
+ message=message,
422
+ )
423
+ self._records.append(record)
424
+ count += 1
425
+
426
+ return count
427
+
428
+ def _export_json(self, records: list[LogRecord], path: Path) -> None:
429
+ """Export records as JSON.
430
+
431
+ Args:
432
+ records: Records to export.
433
+ path: Output path.
434
+ """
435
+ with open(path, "w", encoding="utf-8") as f:
436
+ json.dump(
437
+ [r.to_dict() for r in records],
438
+ f,
439
+ indent=2,
440
+ default=str,
441
+ )
442
+
443
+ def _export_csv(self, records: list[LogRecord], path: Path) -> None:
444
+ """Export records as CSV.
445
+
446
+ Args:
447
+ records: Records to export.
448
+ path: Output path.
449
+ """
450
+ if not records:
451
+ return
452
+
453
+ with open(path, "w", newline="", encoding="utf-8") as f:
454
+ fieldnames = ["timestamp", "level", "module", "message", "correlation_id"]
455
+ writer = csv.DictWriter(f, fieldnames=fieldnames)
456
+ writer.writeheader()
457
+
458
+ for record in records:
459
+ writer.writerow(
460
+ {
461
+ "timestamp": record.timestamp,
462
+ "level": record.level,
463
+ "module": record.module,
464
+ "message": record.message,
465
+ "correlation_id": record.correlation_id or "",
466
+ }
467
+ )
468
+
469
+ def _export_text(self, records: list[LogRecord], path: Path) -> None:
470
+ """Export records as plain text.
471
+
472
+ Args:
473
+ records: Records to export.
474
+ path: Output path.
475
+ """
476
+ with open(path, "w", encoding="utf-8") as f:
477
+ for record in records:
478
+ line = f"{record.timestamp} [{record.level}] {record.module}: {record.message}"
479
+ if record.correlation_id:
480
+ line += f" [corr_id={record.correlation_id}]"
481
+ f.write(line + "\n")
482
+
483
+
484
+ def query_logs(
485
+ log_file: str,
486
+ *,
487
+ start_time: datetime | None = None,
488
+ end_time: datetime | None = None,
489
+ level: str | None = None,
490
+ module: str | None = None,
491
+ correlation_id: str | None = None,
492
+ message_pattern: str | None = None,
493
+ limit: int | None = None,
494
+ ) -> list[LogRecord]:
495
+ """Convenience function to query logs from a file.
496
+
497
+ Args:
498
+ log_file: Path to log file.
499
+ start_time: Filter by start time (UTC).
500
+ end_time: Filter by end time (UTC).
501
+ level: Filter by log level.
502
+ module: Filter by module name.
503
+ correlation_id: Filter by correlation ID.
504
+ message_pattern: Filter by message regex pattern.
505
+ limit: Maximum number of results.
506
+
507
+ Returns:
508
+ List of matching LogRecord objects.
509
+
510
+ Example:
511
+ >>> from datetime import datetime, UTC, timedelta
512
+ >>> from oscura.core.log_query import query_logs
513
+ >>> # Query last hour of errors
514
+ >>> results = query_logs(
515
+ ... "oscura.log",
516
+ ... start_time=datetime.now(UTC) - timedelta(hours=1),
517
+ ... level="ERROR"
518
+ ... )
519
+
520
+ References:
521
+ LOG-010: Searchable Log Query and Export
522
+ """
523
+ query = LogQuery() # type: ignore[no-untyped-call]
524
+ query.load_from_file(log_file, format="json" if log_file.endswith(".json") else "text")
525
+ return query.query_logs(
526
+ start_time=start_time,
527
+ end_time=end_time,
528
+ level=level,
529
+ module=module,
530
+ correlation_id=correlation_id,
531
+ message_pattern=message_pattern,
532
+ limit=limit,
533
+ )
534
+
535
+
536
+ __all__ = [
537
+ "LogQuery",
538
+ "LogRecord",
539
+ "query_logs",
540
+ ]