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,651 @@
1
+ """Extension validation system for Oscura plugins and custom decoders.
2
+
3
+ This module provides comprehensive validation of extensions including metadata
4
+ validation, interface compliance checking, dependency verification, and
5
+ security checks.
6
+
7
+
8
+ Example:
9
+ >>> from oscura.extensibility.validation import validate_extension
10
+ >>> from pathlib import Path
11
+ >>>
12
+ >>> # Validate a plugin directory
13
+ >>> result = validate_extension(Path("my_plugin/"))
14
+ >>> if result.is_valid:
15
+ ... print("Plugin is valid!")
16
+ >>> else:
17
+ ... for error in result.errors:
18
+ ... print(f"Error: {error}")
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import ast
24
+ import inspect
25
+ import logging
26
+ from dataclasses import dataclass, field
27
+ from typing import TYPE_CHECKING, Any
28
+
29
+ if TYPE_CHECKING:
30
+ from collections.abc import Callable
31
+ from pathlib import Path
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+
36
+ @dataclass
37
+ class ValidationIssue:
38
+ """A validation issue found during extension validation.
39
+
40
+ Attributes:
41
+ severity: Severity level ("error", "warning", "info")
42
+ message: Human-readable error message
43
+ location: Optional location information (file, line number)
44
+ fix_hint: Optional suggestion for fixing the issue
45
+ """
46
+
47
+ severity: str
48
+ message: str
49
+ location: str = ""
50
+ fix_hint: str = ""
51
+
52
+
53
+ @dataclass
54
+ class ValidationResult:
55
+ """Result of extension validation.
56
+
57
+ Attributes:
58
+ is_valid: Whether extension passed all validation checks
59
+ errors: List of error-level issues
60
+ warnings: List of warning-level issues
61
+ info: List of informational messages
62
+ metadata: Extracted extension metadata
63
+ """
64
+
65
+ is_valid: bool = True
66
+ errors: list[ValidationIssue] = field(default_factory=list)
67
+ warnings: list[ValidationIssue] = field(default_factory=list)
68
+ info: list[ValidationIssue] = field(default_factory=list)
69
+ metadata: dict[str, Any] = field(default_factory=dict)
70
+
71
+ def add_error(self, message: str, location: str = "", fix_hint: str = "") -> None:
72
+ """Add an error issue.
73
+
74
+ Args:
75
+ message: Error message
76
+ location: Optional location
77
+ fix_hint: Optional fix suggestion
78
+ """
79
+ self.errors.append(
80
+ ValidationIssue(
81
+ severity="error",
82
+ message=message,
83
+ location=location,
84
+ fix_hint=fix_hint,
85
+ )
86
+ )
87
+ self.is_valid = False
88
+
89
+ def add_warning(self, message: str, location: str = "", fix_hint: str = "") -> None:
90
+ """Add a warning issue.
91
+
92
+ Args:
93
+ message: Warning message
94
+ location: Optional location
95
+ fix_hint: Optional fix suggestion
96
+ """
97
+ self.warnings.append(
98
+ ValidationIssue(
99
+ severity="warning",
100
+ message=message,
101
+ location=location,
102
+ fix_hint=fix_hint,
103
+ )
104
+ )
105
+
106
+ def add_info(self, message: str, location: str = "") -> None:
107
+ """Add an informational message.
108
+
109
+ Args:
110
+ message: Info message
111
+ location: Optional location
112
+ """
113
+ self.info.append(
114
+ ValidationIssue(
115
+ severity="info",
116
+ message=message,
117
+ location=location,
118
+ )
119
+ )
120
+
121
+ @property
122
+ def all_issues(self) -> list[ValidationIssue]:
123
+ """Get all issues sorted by severity.
124
+
125
+ Returns:
126
+ List of all issues (errors, warnings, info)
127
+ """
128
+ return self.errors + self.warnings + self.info
129
+
130
+
131
+ def validate_extension(
132
+ extension_path: Path,
133
+ *,
134
+ check_dependencies: bool = True,
135
+ check_security: bool = True,
136
+ strict: bool = False,
137
+ ) -> ValidationResult:
138
+ """Validate an extension (plugin, decoder, etc.) at the given path.
139
+
140
+ Performs comprehensive validation including:
141
+ - Metadata validation (pyproject.toml or plugin.yaml)
142
+ - Interface compliance checking
143
+ - Entry point validation
144
+ - Dependency verification
145
+ - Security checks (if enabled)
146
+ - Code quality checks (if strict)
147
+
148
+ Args:
149
+ extension_path: Path to extension directory
150
+ check_dependencies: Verify dependencies are satisfied
151
+ check_security: Perform security checks
152
+ strict: Enable strict validation (warnings become errors)
153
+
154
+ Returns:
155
+ ValidationResult with validation outcome
156
+
157
+ Example:
158
+ >>> from pathlib import Path
159
+ >>> result = validate_extension(Path("plugins/my_decoder/"))
160
+ >>> if not result.is_valid:
161
+ ... for error in result.errors:
162
+ ... print(f"Error: {error.message}")
163
+ ... if error.fix_hint:
164
+ ... print(f" Fix: {error.fix_hint}")
165
+
166
+ References:
167
+ EXT-005: Extension Validation
168
+ """
169
+ result = ValidationResult()
170
+
171
+ if not extension_path.exists():
172
+ result.add_error(
173
+ f"Extension path does not exist: {extension_path}",
174
+ fix_hint="Check the path is correct",
175
+ )
176
+ return result
177
+
178
+ if not extension_path.is_dir():
179
+ result.add_error(
180
+ f"Extension path is not a directory: {extension_path}",
181
+ fix_hint="Provide path to extension directory",
182
+ )
183
+ return result
184
+
185
+ result.add_info(f"Validating extension at: {extension_path}")
186
+
187
+ # Validate metadata
188
+ _validate_metadata(extension_path, result)
189
+
190
+ # Validate structure
191
+ _validate_structure(extension_path, result)
192
+
193
+ # Validate entry points
194
+ _validate_entry_points(extension_path, result)
195
+
196
+ # Validate implementation
197
+ _validate_implementation(extension_path, result)
198
+
199
+ # Check dependencies if requested
200
+ if check_dependencies:
201
+ _check_dependencies(extension_path, result)
202
+
203
+ # Security checks if requested
204
+ if check_security:
205
+ _check_security(extension_path, result)
206
+
207
+ # Convert warnings to errors in strict mode
208
+ if strict and result.warnings:
209
+ for warning in result.warnings:
210
+ result.add_error(
211
+ f"Strict mode: {warning.message}",
212
+ location=warning.location,
213
+ fix_hint=warning.fix_hint,
214
+ )
215
+ result.warnings = []
216
+
217
+ return result
218
+
219
+
220
+ def validate_decoder_interface(
221
+ decoder_class: type,
222
+ ) -> ValidationResult:
223
+ """Validate that a decoder class implements the required interface.
224
+
225
+ Checks for:
226
+ - Required methods (decode, get_metadata)
227
+ - Optional methods (configure, reset, validate_config)
228
+ - Method signatures
229
+ - Return types
230
+
231
+ Args:
232
+ decoder_class: Decoder class to validate
233
+
234
+ Returns:
235
+ ValidationResult with validation outcome
236
+
237
+ Example:
238
+ >>> class MyDecoder:
239
+ ... def decode(self, trace):
240
+ ... return []
241
+ ... def get_metadata(self):
242
+ ... return {"name": "my_decoder"}
243
+ >>> result = validate_decoder_interface(MyDecoder)
244
+ >>> assert result.is_valid
245
+
246
+ References:
247
+ EXT-006: Custom Decoder Registration
248
+ """
249
+ result = ValidationResult()
250
+
251
+ # Required methods
252
+ required_methods = {
253
+ "decode": {
254
+ "params": ["self", "trace"],
255
+ "returns": "list",
256
+ },
257
+ "get_metadata": {
258
+ "params": ["self"],
259
+ "returns": "dict",
260
+ },
261
+ }
262
+
263
+ # Optional methods
264
+ optional_methods = {
265
+ "configure": {"params": ["self"], "returns": None},
266
+ "reset": {"params": ["self"], "returns": None},
267
+ "validate_config": {"params": ["self", "config"], "returns": "bool"},
268
+ }
269
+
270
+ # Check required methods
271
+ for method_name in required_methods:
272
+ if not hasattr(decoder_class, method_name):
273
+ result.add_error(
274
+ f"Missing required method: {method_name}",
275
+ location=f"{decoder_class.__name__}",
276
+ fix_hint=f"Add method: def {method_name}(self, ...): ...",
277
+ )
278
+ continue
279
+
280
+ method = getattr(decoder_class, method_name)
281
+ if not callable(method):
282
+ result.add_error(
283
+ f"Method {method_name} is not callable",
284
+ location=f"{decoder_class.__name__}.{method_name}",
285
+ )
286
+
287
+ # Check optional methods if present
288
+ for method_name in optional_methods:
289
+ if hasattr(decoder_class, method_name):
290
+ method = getattr(decoder_class, method_name)
291
+ if not callable(method):
292
+ result.add_warning(
293
+ f"Optional method {method_name} exists but is not callable",
294
+ location=f"{decoder_class.__name__}.{method_name}",
295
+ )
296
+
297
+ # Check documentation requirements (EXT-006)
298
+ if not decoder_class.__doc__ or not decoder_class.__doc__.strip():
299
+ result.add_error(
300
+ "Decoder class must have a docstring documenting its purpose and usage",
301
+ location=f"{decoder_class.__name__}",
302
+ fix_hint='Add docstring: """Decoder for XYZ protocol."""',
303
+ )
304
+
305
+ # Extract metadata
306
+ result.metadata = {
307
+ "class_name": decoder_class.__name__,
308
+ "module": decoder_class.__module__,
309
+ "required_methods": list(required_methods.keys()),
310
+ "optional_methods": list(optional_methods.keys()),
311
+ "has_docstring": decoder_class.__doc__ is not None,
312
+ }
313
+
314
+ if result.is_valid:
315
+ result.add_info(f"Decoder interface validation passed for {decoder_class.__name__}")
316
+
317
+ return result
318
+
319
+
320
+ def validate_hook_function(
321
+ func: Callable[[Any], Any],
322
+ ) -> ValidationResult:
323
+ """Validate that a function is suitable for use as a hook.
324
+
325
+ Checks:
326
+ - Function signature accepts HookContext
327
+ - Function returns HookContext
328
+ - Function has docstring
329
+ - Function handles exceptions
330
+
331
+ Args:
332
+ func: Hook function to validate
333
+
334
+ Returns:
335
+ ValidationResult with validation outcome
336
+
337
+ Example:
338
+ >>> def my_hook(context):
339
+ ... '''Validate context.'''
340
+ ... return context
341
+ >>> result = validate_hook_function(my_hook)
342
+ >>> assert result.is_valid
343
+
344
+ References:
345
+ EXT-005: Hook System
346
+ """
347
+ result = ValidationResult()
348
+
349
+ if not callable(func):
350
+ result.add_error( # type: ignore[unreachable]
351
+ "Hook must be callable",
352
+ fix_hint="Provide a function or callable object",
353
+ )
354
+ return result
355
+
356
+ # Check signature
357
+ sig = inspect.signature(func)
358
+ params = list(sig.parameters.keys())
359
+
360
+ if len(params) < 1:
361
+ result.add_error(
362
+ "Hook function must accept at least one parameter (context)",
363
+ location=func.__name__,
364
+ fix_hint="Add parameter: def hook(context): ...",
365
+ )
366
+
367
+ # Check for docstring
368
+ if not func.__doc__:
369
+ result.add_warning(
370
+ "Hook function should have a docstring",
371
+ location=func.__name__,
372
+ fix_hint='Add docstring: """Hook description."""',
373
+ )
374
+
375
+ result.metadata = {
376
+ "name": func.__name__,
377
+ "params": params,
378
+ "has_docstring": func.__doc__ is not None,
379
+ }
380
+
381
+ if result.is_valid:
382
+ result.add_info(f"Hook function validation passed for {func.__name__}")
383
+
384
+ return result
385
+
386
+
387
+ def _validate_metadata(extension_path: Path, result: ValidationResult) -> None:
388
+ """Validate extension metadata (pyproject.toml or plugin.yaml).
389
+
390
+ Args:
391
+ extension_path: Path to extension directory
392
+ result: ValidationResult to append issues to
393
+ """
394
+ pyproject = extension_path / "pyproject.toml"
395
+ plugin_yaml = extension_path / "plugin.yaml"
396
+
397
+ if not pyproject.exists() and not plugin_yaml.exists():
398
+ result.add_error(
399
+ "No metadata file found (pyproject.toml or plugin.yaml)",
400
+ location=str(extension_path),
401
+ fix_hint="Create pyproject.toml with [project] section",
402
+ )
403
+ return
404
+
405
+ if pyproject.exists():
406
+ try:
407
+ import tomllib
408
+
409
+ with open(pyproject, "rb") as f:
410
+ data = tomllib.load(f)
411
+
412
+ # Check required project fields
413
+ if "project" not in data:
414
+ result.add_error(
415
+ "pyproject.toml missing [project] section",
416
+ location=str(pyproject),
417
+ )
418
+ else:
419
+ project = data["project"]
420
+ required = ["name", "version", "description"]
421
+ for field in required:
422
+ if field not in project:
423
+ result.add_error(
424
+ f"pyproject.toml missing required field: {field}",
425
+ location="[project]",
426
+ fix_hint=f'Add: {field} = "..."',
427
+ )
428
+
429
+ result.metadata.update(
430
+ {
431
+ "name": project.get("name", ""),
432
+ "version": project.get("version", ""),
433
+ "description": project.get("description", ""),
434
+ }
435
+ )
436
+
437
+ except Exception as e:
438
+ result.add_error(
439
+ f"Failed to parse pyproject.toml: {e}",
440
+ location=str(pyproject),
441
+ )
442
+
443
+
444
+ def _validate_structure(extension_path: Path, result: ValidationResult) -> None:
445
+ """Validate extension directory structure.
446
+
447
+ Args:
448
+ extension_path: Path to extension directory
449
+ result: ValidationResult to append issues to
450
+ """
451
+ # Check for __init__.py
452
+ init_py = extension_path / "__init__.py"
453
+ if not init_py.exists():
454
+ result.add_warning(
455
+ "No __init__.py found",
456
+ location=str(extension_path),
457
+ fix_hint="Add __init__.py to make it a Python package",
458
+ )
459
+
460
+ # Check for tests directory
461
+ tests_dir = extension_path / "tests"
462
+ if not tests_dir.exists():
463
+ result.add_warning(
464
+ "No tests/ directory found",
465
+ location=str(extension_path),
466
+ fix_hint="Add tests/ directory with unit tests",
467
+ )
468
+ else:
469
+ # Check for test files
470
+ test_files = list(tests_dir.glob("test_*.py"))
471
+ if not test_files:
472
+ result.add_warning(
473
+ "No test files found in tests/",
474
+ location=str(tests_dir),
475
+ fix_hint="Add test_*.py files",
476
+ )
477
+
478
+ # Check for README
479
+ readme_files = list(extension_path.glob("README.*"))
480
+ if not readme_files:
481
+ result.add_warning(
482
+ "No README file found",
483
+ location=str(extension_path),
484
+ fix_hint="Add README.md with usage documentation",
485
+ )
486
+
487
+
488
+ def _validate_entry_points(extension_path: Path, result: ValidationResult) -> None:
489
+ """Validate entry points configuration.
490
+
491
+ Args:
492
+ extension_path: Path to extension directory
493
+ result: ValidationResult to append issues to
494
+ """
495
+ pyproject = extension_path / "pyproject.toml"
496
+ if not pyproject.exists():
497
+ return
498
+
499
+ try:
500
+ import tomllib
501
+
502
+ with open(pyproject, "rb") as f:
503
+ data = tomllib.load(f)
504
+
505
+ # Check for entry points
506
+ if "project" not in data or "entry-points" not in data["project"]:
507
+ result.add_info(
508
+ "No entry points defined (plugin may be used as library)",
509
+ location=str(pyproject),
510
+ )
511
+ return
512
+
513
+ entry_points = data["project"]["entry-points"]
514
+ oscura_groups = [k for k in entry_points if k.startswith("oscura.")]
515
+
516
+ if not oscura_groups:
517
+ result.add_warning(
518
+ "No Oscura entry points found",
519
+ location="[project.entry-points]",
520
+ fix_hint="Add entry point like: oscura.decoders = ...",
521
+ )
522
+ else:
523
+ result.metadata["entry_points"] = oscura_groups
524
+ result.add_info(f"Found entry point groups: {', '.join(oscura_groups)}")
525
+
526
+ except Exception as e:
527
+ result.add_warning(f"Failed to validate entry points: {e}")
528
+
529
+
530
+ def _validate_implementation(extension_path: Path, result: ValidationResult) -> None:
531
+ """Validate extension implementation files.
532
+
533
+ Args:
534
+ extension_path: Path to extension directory
535
+ result: ValidationResult to append issues to
536
+ """
537
+ # Find Python files
538
+ py_files = list(extension_path.glob("*.py"))
539
+ py_files = [f for f in py_files if f.name != "__init__.py"]
540
+
541
+ if not py_files:
542
+ result.add_warning(
543
+ "No implementation files found",
544
+ location=str(extension_path),
545
+ fix_hint="Add Python module with implementation",
546
+ )
547
+ return
548
+
549
+ # Basic syntax check
550
+ for py_file in py_files:
551
+ try:
552
+ with open(py_file, encoding="utf-8") as f:
553
+ source = f.read()
554
+ ast.parse(source)
555
+ result.add_info(f"Syntax check passed: {py_file.name}")
556
+ except SyntaxError as e:
557
+ result.add_error(
558
+ f"Syntax error in {py_file.name}: {e}",
559
+ location=f"{py_file.name}:{e.lineno}",
560
+ fix_hint="Fix syntax error",
561
+ )
562
+
563
+
564
+ def _check_dependencies(extension_path: Path, result: ValidationResult) -> None:
565
+ """Check extension dependencies are satisfied.
566
+
567
+ Args:
568
+ extension_path: Path to extension directory
569
+ result: ValidationResult to append issues to
570
+ """
571
+ pyproject = extension_path / "pyproject.toml"
572
+ if not pyproject.exists():
573
+ return
574
+
575
+ try:
576
+ import tomllib
577
+
578
+ with open(pyproject, "rb") as f:
579
+ data = tomllib.load(f)
580
+
581
+ if "project" not in data or "dependencies" not in data["project"]:
582
+ result.add_info("No dependencies declared")
583
+ return
584
+
585
+ dependencies = data["project"]["dependencies"]
586
+ result.metadata["dependencies"] = dependencies
587
+
588
+ # Check if oscura is in dependencies
589
+ oscura_deps = [d for d in dependencies if "oscura" in d.lower()]
590
+ if not oscura_deps:
591
+ result.add_warning(
592
+ "Oscura not listed in dependencies",
593
+ location="[project.dependencies]",
594
+ fix_hint='Add: "oscura>=0.1.0"',
595
+ )
596
+
597
+ except Exception as e:
598
+ result.add_warning(f"Failed to check dependencies: {e}")
599
+
600
+
601
+ def _check_security(extension_path: Path, result: ValidationResult) -> None:
602
+ """Perform basic security checks on extension.
603
+
604
+ Args:
605
+ extension_path: Path to extension directory
606
+ result: ValidationResult to append issues to
607
+ """
608
+ # Check for common security issues
609
+ py_files = list(extension_path.rglob("*.py"))
610
+
611
+ dangerous_imports = ["pickle", "eval", "exec", "compile", "__import__"]
612
+ dangerous_calls = ["eval(", "exec(", "compile(", "__import__("]
613
+
614
+ for py_file in py_files:
615
+ try:
616
+ with open(py_file, encoding="utf-8") as f:
617
+ source = f.read()
618
+
619
+ # Check for dangerous imports
620
+ tree = ast.parse(source)
621
+ for node in ast.walk(tree):
622
+ if isinstance(node, ast.Import):
623
+ for alias in node.names:
624
+ if alias.name in dangerous_imports:
625
+ result.add_warning(
626
+ f"Potentially unsafe import: {alias.name}",
627
+ location=f"{py_file.name}:{node.lineno}",
628
+ fix_hint="Consider safer alternatives",
629
+ )
630
+
631
+ # Check for dangerous function calls
632
+ for call in dangerous_calls:
633
+ if call in source:
634
+ result.add_warning(
635
+ f"Potentially unsafe call: {call}",
636
+ location=py_file.name,
637
+ fix_hint="Avoid eval/exec for security",
638
+ )
639
+
640
+ except Exception:
641
+ # Ignore parse errors, already caught in implementation validation
642
+ pass
643
+
644
+
645
+ __all__ = [
646
+ "ValidationIssue",
647
+ "ValidationResult",
648
+ "validate_decoder_interface",
649
+ "validate_extension",
650
+ "validate_hook_function",
651
+ ]