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,414 @@
1
+ """Plugin architecture for third-party extensions.
2
+
3
+ This module implements entry point discovery for third-party plugins,
4
+ allowing custom decoders, measurements, and file formats to be loaded
5
+ dynamically.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import importlib.metadata
11
+ import logging
12
+ from dataclasses import dataclass
13
+ from typing import Any
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class PluginError(Exception):
19
+ """Exception raised when plugin loading fails.
20
+
21
+ This exception is used to isolate plugin failures so they don't
22
+ crash the main application.
23
+
24
+ Example:
25
+ >>> try:
26
+ ... plugin = osc.load_plugin('oscura.decoders', 'flexray')
27
+ ... except osc.PluginError as e:
28
+ ... print(f"Plugin failed: {e}")
29
+
30
+ References:
31
+ API-007: Plugin Architecture
32
+ """
33
+
34
+
35
+ @dataclass
36
+ class PluginMetadata:
37
+ """Metadata about a loaded plugin.
38
+
39
+ Attributes:
40
+ name: Plugin name.
41
+ entry_point: Entry point group.
42
+ version: Plugin version (if available).
43
+ module: Module name.
44
+ callable: The loaded plugin object.
45
+ dependencies: Plugin dependencies (if available).
46
+
47
+ Example:
48
+ >>> plugin = load_plugin('oscura.decoders', 'can')
49
+ >>> print(f"Loaded {plugin.name} v{plugin.version}")
50
+
51
+ References:
52
+ API-007: Plugin Architecture
53
+ """
54
+
55
+ name: str
56
+ entry_point: str
57
+ version: str | None = None
58
+ module: str | None = None
59
+ callable: Any | None = None
60
+ dependencies: list[str] | None = None
61
+
62
+ def __repr__(self) -> str:
63
+ """String representation of plugin metadata.
64
+
65
+ Returns:
66
+ String representation showing plugin name, version, and module.
67
+ """
68
+ parts = [f"name='{self.name}'"]
69
+ if self.version:
70
+ parts.append(f"version='{self.version}'")
71
+ if self.module:
72
+ parts.append(f"module='{self.module}'")
73
+ return f"PluginMetadata({', '.join(parts)})"
74
+
75
+
76
+ class PluginManager:
77
+ """Manager for discovering and loading third-party plugins.
78
+
79
+ Discovers plugins via setuptools entry points and provides lazy loading
80
+ with error isolation. Supports multiple entry point groups for different
81
+ plugin types.
82
+
83
+ Entry point groups:
84
+ - oscura.decoders: Protocol decoders
85
+ - oscura.measurements: Custom measurements
86
+ - oscura.loaders: File format loaders
87
+ - oscura.exporters: Export format handlers
88
+
89
+ Example:
90
+ >>> import oscura as osc
91
+ >>> # Plugins auto-discovered from installed packages
92
+ >>> # Use plugin decoder
93
+ >>> can_frames = osc.decode(trace, protocol='can', baudrate=500000)
94
+ >>> # List available plugins
95
+ >>> plugins = osc.list_plugins()
96
+ >>> print(f"Installed plugins: {plugins}")
97
+
98
+ Advanced Example:
99
+ >>> # Manually load plugin with error handling
100
+ >>> try:
101
+ ... plugin = osc.load_plugin('oscura.decoders', 'flexray')
102
+ ... print(f"Plugin loaded: {plugin.name} v{plugin.version}")
103
+ ... except osc.PluginError as e:
104
+ ... print(f"Plugin failed to load: {e}")
105
+
106
+ Plugin Package Example:
107
+ In your package's pyproject.toml:
108
+ ```toml
109
+ [project.entry-points."oscura.decoders"]
110
+ flexray = "my_package.flexray:FlexRayDecoder"
111
+ ```
112
+
113
+ References:
114
+ API-007: Plugin Architecture
115
+ importlib.metadata entry points
116
+ https://packaging.python.org/en/latest/guides/creating-and-discovering-plugins/
117
+ """
118
+
119
+ # Standard entry point groups
120
+ ENTRY_POINT_GROUPS = [ # noqa: RUF012
121
+ "oscura.decoders",
122
+ "oscura.measurements",
123
+ "oscura.loaders",
124
+ "oscura.exporters",
125
+ ]
126
+
127
+ def __init__(self) -> None:
128
+ """Initialize plugin manager."""
129
+ self._loaded_plugins: dict[tuple[str, str], PluginMetadata] = {}
130
+ self._failed_plugins: dict[tuple[str, str], Exception] = {}
131
+
132
+ def discover_plugins(self, group: str | None = None) -> dict[str, list[str]]:
133
+ """Discover available plugins via entry points.
134
+
135
+ Args:
136
+ group: Specific entry point group to search. If None, searches
137
+ all standard groups.
138
+
139
+ Returns:
140
+ Dictionary mapping group names to lists of plugin names.
141
+
142
+ Example:
143
+ >>> manager = PluginManager()
144
+ >>> plugins = manager.discover_plugins()
145
+ >>> print(plugins)
146
+ {'oscura.decoders': ['uart', 'spi', 'can'], ...}
147
+
148
+ References:
149
+ importlib.metadata.entry_points
150
+ """
151
+ discovered: dict[str, list[str]] = {}
152
+ groups = [group] if group else self.ENTRY_POINT_GROUPS
153
+
154
+ for group_name in groups:
155
+ try:
156
+ # Get entry points for this group
157
+ # Python 3.10+ API
158
+ eps = importlib.metadata.entry_points(group=group_name)
159
+ discovered[group_name] = [ep.name for ep in eps]
160
+ except (AttributeError, TypeError):
161
+ # Fallback for Python 3.9 and earlier
162
+ try:
163
+ eps = importlib.metadata.entry_points().get(group_name, []) # type: ignore[attr-defined]
164
+ discovered[group_name] = [ep.name for ep in eps]
165
+ except Exception as e:
166
+ logger.warning(f"Failed to discover plugins for group '{group_name}': {e}")
167
+ discovered[group_name] = []
168
+
169
+ return discovered
170
+
171
+ def load_plugin(
172
+ self,
173
+ group: str,
174
+ name: str,
175
+ reload: bool = False,
176
+ ) -> PluginMetadata:
177
+ """Load a plugin by group and name.
178
+
179
+ Loads the plugin lazily on first use. Subsequent calls return cached
180
+ instance unless reload=True.
181
+
182
+ Args:
183
+ group: Entry point group.
184
+ name: Plugin name.
185
+ reload: Force reload even if already loaded. Default False.
186
+
187
+ Returns:
188
+ PluginMetadata with loaded plugin information.
189
+
190
+ Raises:
191
+ PluginError: If plugin fails to load.
192
+
193
+ Example:
194
+ >>> manager = PluginManager()
195
+ >>> plugin = manager.load_plugin('oscura.decoders', 'can')
196
+ >>> decoder = plugin.callable
197
+
198
+ References:
199
+ API-007: Plugin Architecture
200
+ """
201
+ plugin_key = (group, name)
202
+
203
+ # Check if already loaded
204
+ if not reload and plugin_key in self._loaded_plugins:
205
+ return self._loaded_plugins[plugin_key]
206
+
207
+ # Check if previously failed
208
+ if not reload and plugin_key in self._failed_plugins:
209
+ raise PluginError(
210
+ f"Plugin '{name}' in group '{group}' previously failed to load: "
211
+ f"{self._failed_plugins[plugin_key]}"
212
+ )
213
+
214
+ try:
215
+ # Find entry point
216
+ entry_point = self._find_entry_point(group, name)
217
+
218
+ if entry_point is None:
219
+ raise PluginError(f"Plugin '{name}' not found in group '{group}'")
220
+
221
+ # Load the plugin
222
+ logger.info(f"Loading plugin '{name}' from group '{group}'")
223
+ plugin_obj = entry_point.load()
224
+
225
+ # Get version if available
226
+ version = None
227
+ if hasattr(entry_point, "dist") and entry_point.dist:
228
+ version = entry_point.dist.version
229
+
230
+ # Create metadata
231
+ metadata = PluginMetadata(
232
+ name=name,
233
+ entry_point=group,
234
+ version=version,
235
+ module=entry_point.value,
236
+ callable=plugin_obj,
237
+ )
238
+
239
+ # Cache loaded plugin
240
+ self._loaded_plugins[plugin_key] = metadata
241
+
242
+ logger.info(f"Successfully loaded plugin '{name}' v{version}")
243
+ return metadata
244
+
245
+ except Exception as e:
246
+ # Cache failure
247
+ self._failed_plugins[plugin_key] = e
248
+ logger.error(f"Failed to load plugin '{name}': {e}")
249
+ raise PluginError(f"Failed to load plugin '{name}' from group '{group}': {e}") from e
250
+
251
+ def get_plugin(self, group: str, name: str) -> Any:
252
+ """Get loaded plugin callable.
253
+
254
+ Convenience method that loads plugin if needed and returns the
255
+ callable object.
256
+
257
+ Args:
258
+ group: Entry point group.
259
+ name: Plugin name.
260
+
261
+ Returns:
262
+ The loaded plugin object.
263
+
264
+ Example:
265
+ >>> manager = PluginManager()
266
+ >>> decoder = manager.get_plugin('oscura.decoders', 'can')
267
+ >>> frames = decoder.decode(trace)
268
+ """
269
+ metadata = self.load_plugin(group, name)
270
+ return metadata.callable
271
+
272
+ def is_loaded(self, group: str, name: str) -> bool:
273
+ """Check if plugin is already loaded.
274
+
275
+ Args:
276
+ group: Entry point group.
277
+ name: Plugin name.
278
+
279
+ Returns:
280
+ True if plugin is loaded.
281
+
282
+ Example:
283
+ >>> if manager.is_loaded('oscura.decoders', 'can'):
284
+ ... print("CAN decoder already loaded")
285
+ """
286
+ return (group, name) in self._loaded_plugins
287
+
288
+ def list_loaded_plugins(self) -> list[PluginMetadata]:
289
+ """List all loaded plugins.
290
+
291
+ Returns:
292
+ List of PluginMetadata for loaded plugins.
293
+
294
+ Example:
295
+ >>> loaded = manager.list_loaded_plugins()
296
+ >>> for plugin in loaded:
297
+ ... print(f"{plugin.name} v{plugin.version}")
298
+ """
299
+ return list(self._loaded_plugins.values())
300
+
301
+ def unload_plugin(self, group: str, name: str) -> None:
302
+ """Unload a plugin from cache.
303
+
304
+ Args:
305
+ group: Entry point group.
306
+ name: Plugin name.
307
+
308
+ Example:
309
+ >>> manager.unload_plugin('oscura.decoders', 'can')
310
+ """
311
+ plugin_key = (group, name)
312
+ if plugin_key in self._loaded_plugins:
313
+ del self._loaded_plugins[plugin_key]
314
+ if plugin_key in self._failed_plugins:
315
+ del self._failed_plugins[plugin_key]
316
+
317
+ def _find_entry_point(self, group: str, name: str) -> Any | None:
318
+ """Find entry point by group and name.
319
+
320
+ Args:
321
+ group: Entry point group.
322
+ name: Entry point name.
323
+
324
+ Returns:
325
+ Entry point object or None if not found.
326
+ """
327
+ try:
328
+ # Python 3.10+ API
329
+ eps = importlib.metadata.entry_points(group=group)
330
+ for ep in eps:
331
+ if ep.name == name:
332
+ return ep
333
+ except (AttributeError, TypeError):
334
+ # Fallback for Python 3.9 and earlier
335
+ try:
336
+ eps = importlib.metadata.entry_points().get(group, []) # type: ignore[attr-defined]
337
+ for ep in eps:
338
+ if ep.name == name:
339
+ return ep
340
+ except Exception:
341
+ pass
342
+
343
+ return None
344
+
345
+
346
+ # Global plugin manager instance
347
+ _manager = PluginManager()
348
+
349
+
350
+ def load_plugin(group: str, name: str) -> PluginMetadata:
351
+ """Load a plugin from the global plugin manager.
352
+
353
+ Convenience function for loading plugins without accessing the manager
354
+ directly.
355
+
356
+ Args:
357
+ group: Entry point group.
358
+ name: Plugin name.
359
+
360
+ Returns:
361
+ PluginMetadata with loaded plugin.
362
+
363
+ Example:
364
+ >>> import oscura as osc
365
+ >>> plugin = osc.load_plugin('oscura.decoders', 'flexray')
366
+ >>> print(f"Loaded {plugin.name} v{plugin.version}")
367
+
368
+ References:
369
+ API-007: Plugin Architecture
370
+ """
371
+ return _manager.load_plugin(group, name)
372
+
373
+
374
+ def list_plugins(group: str | None = None) -> dict[str, list[str]]:
375
+ """List available plugins.
376
+
377
+ Args:
378
+ group: Specific group to list. If None, lists all groups.
379
+
380
+ Returns:
381
+ Dictionary mapping group names to plugin names.
382
+
383
+ Example:
384
+ >>> import oscura as osc
385
+ >>> plugins = osc.list_plugins()
386
+ >>> print(f"Available decoders: {plugins['oscura.decoders']}")
387
+
388
+ References:
389
+ API-007: Plugin Architecture
390
+ """
391
+ return _manager.discover_plugins(group)
392
+
393
+
394
+ def get_plugin_manager() -> PluginManager:
395
+ """Get the global plugin manager instance.
396
+
397
+ Returns:
398
+ Global PluginManager instance.
399
+
400
+ Example:
401
+ >>> manager = osc.get_plugin_manager()
402
+ >>> loaded = manager.list_loaded_plugins()
403
+ """
404
+ return _manager
405
+
406
+
407
+ __all__ = [
408
+ "PluginError",
409
+ "PluginManager",
410
+ "PluginMetadata",
411
+ "get_plugin_manager",
412
+ "list_plugins",
413
+ "load_plugin",
414
+ ]