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,411 @@
1
+ """Plugin discovery and scanning.
2
+
3
+ This module provides plugin discovery from filesystem directories
4
+ and Python entry points.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.plugins.discovery import discover_plugins
9
+ >>> plugins = discover_plugins()
10
+ >>> for plugin in plugins:
11
+ ... print(f"Found: {plugin.name} v{plugin.version}")
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import importlib
17
+ import importlib.metadata
18
+ import importlib.util
19
+ import os
20
+ import sys
21
+ from dataclasses import dataclass
22
+ from pathlib import Path
23
+ from typing import TYPE_CHECKING
24
+
25
+ from oscura.plugins.base import PluginBase, PluginMetadata
26
+
27
+ if TYPE_CHECKING:
28
+ from collections.abc import Iterator
29
+
30
+ # Try to import yaml for plugin.yaml parsing
31
+ try:
32
+ import yaml
33
+
34
+ YAML_AVAILABLE = True
35
+ except ImportError:
36
+ YAML_AVAILABLE = False
37
+
38
+
39
+ # Oscura API version for compatibility checking
40
+ OSCURA_API_VERSION = "1.0.0"
41
+
42
+
43
+ @dataclass
44
+ class DiscoveredPlugin:
45
+ """Information about a discovered plugin.
46
+
47
+ Attributes:
48
+ metadata: Plugin metadata.
49
+ path: Path to plugin directory or module.
50
+ entry_point: Entry point name (if from entry points).
51
+ compatible: Whether plugin is compatible with current API.
52
+ load_error: Error message if plugin failed to load.
53
+ """
54
+
55
+ metadata: PluginMetadata
56
+ path: Path | None = None
57
+ entry_point: str | None = None
58
+ compatible: bool = True
59
+ load_error: str | None = None
60
+
61
+
62
+ def get_plugin_paths() -> list[Path]:
63
+ """Get list of plugin search directories.
64
+
65
+ Returns paths in priority order:
66
+ 1. Project plugins: ./plugins/
67
+ 2. User plugins: ~/.oscura/plugins/
68
+ 3. System plugins: /usr/lib/oscura/plugins/
69
+
70
+ Returns:
71
+ List of plugin directory paths.
72
+ """
73
+ paths: list[Path] = []
74
+
75
+ # Project plugins (current directory)
76
+ project_plugins = Path.cwd() / "plugins"
77
+ if project_plugins.exists():
78
+ paths.append(project_plugins)
79
+
80
+ # User plugins
81
+ user_plugins = Path.home() / ".oscura" / "plugins"
82
+ paths.append(user_plugins) # Include even if doesn't exist
83
+
84
+ # XDG config home
85
+ xdg_config = os.environ.get("XDG_CONFIG_HOME")
86
+ if xdg_config:
87
+ xdg_plugins = Path(xdg_config) / "oscura" / "plugins"
88
+ paths.append(xdg_plugins)
89
+
90
+ # System plugins (Linux)
91
+ system_plugins = Path("/usr/lib/oscura/plugins")
92
+ if system_plugins.exists():
93
+ paths.append(system_plugins)
94
+
95
+ # Local lib (Linux)
96
+ local_plugins = Path("/usr/local/lib/oscura/plugins")
97
+ if local_plugins.exists():
98
+ paths.append(local_plugins)
99
+
100
+ return paths
101
+
102
+
103
+ def discover_plugins(
104
+ *,
105
+ compatible_only: bool = False,
106
+ include_disabled: bool = False,
107
+ ) -> list[DiscoveredPlugin]:
108
+ """Discover all available plugins.
109
+
110
+ Scans plugin directories and Python entry points for plugins.
111
+
112
+ Args:
113
+ compatible_only: If True, only return compatible plugins.
114
+ include_disabled: If True, include disabled plugins.
115
+
116
+ Returns:
117
+ List of discovered plugins.
118
+
119
+ Example:
120
+ >>> plugins = discover_plugins()
121
+ >>> print(f"Found {len(plugins)} plugins")
122
+ """
123
+ plugins: list[DiscoveredPlugin] = []
124
+ seen_names: set[str] = set()
125
+
126
+ # Scan plugin directories
127
+ for plugin_dir in get_plugin_paths():
128
+ if plugin_dir.exists() and plugin_dir.is_dir():
129
+ for plugin in scan_directory(plugin_dir):
130
+ if plugin.metadata.name not in seen_names:
131
+ plugins.append(plugin)
132
+ seen_names.add(plugin.metadata.name)
133
+
134
+ # Scan entry points
135
+ for plugin in scan_entry_points():
136
+ if plugin.metadata.name not in seen_names:
137
+ plugins.append(plugin)
138
+ seen_names.add(plugin.metadata.name)
139
+
140
+ # Filter by compatibility
141
+ if compatible_only:
142
+ plugins = [p for p in plugins if p.compatible]
143
+
144
+ # Filter disabled
145
+ if not include_disabled:
146
+ plugins = [p for p in plugins if p.metadata.enabled]
147
+
148
+ return plugins
149
+
150
+
151
+ def scan_directory(directory: Path) -> Iterator[DiscoveredPlugin]:
152
+ """Scan a directory for plugins.
153
+
154
+ Each subdirectory with a plugin.yaml or Python package
155
+ is considered a potential plugin.
156
+
157
+ Args:
158
+ directory: Directory to scan.
159
+
160
+ Yields:
161
+ DiscoveredPlugin for each found plugin.
162
+ """
163
+ if not directory.exists():
164
+ return
165
+
166
+ for item in directory.iterdir():
167
+ if item.is_dir():
168
+ # Check for plugin.yaml
169
+ plugin_yaml = item / "plugin.yaml"
170
+ plugin_yml = item / "plugin.yml"
171
+
172
+ if plugin_yaml.exists():
173
+ plugin = _load_plugin_from_yaml(plugin_yaml)
174
+ if plugin:
175
+ yield plugin
176
+ elif plugin_yml.exists():
177
+ plugin = _load_plugin_from_yaml(plugin_yml)
178
+ if plugin:
179
+ yield plugin
180
+
181
+ # Check for Python package with __init__.py
182
+ init_py = item / "__init__.py"
183
+ if init_py.exists():
184
+ plugin = _load_plugin_from_module(item)
185
+ if plugin:
186
+ yield plugin
187
+
188
+
189
+ def scan_entry_points() -> Iterator[DiscoveredPlugin]:
190
+ """Scan Python entry points for plugins.
191
+
192
+ Looks for entry points in the "oscura.plugins" group.
193
+
194
+ Yields:
195
+ DiscoveredPlugin for each found entry point.
196
+ """
197
+ try:
198
+ # Python 3.10+ has entry_points with group filtering
199
+ if hasattr(importlib.metadata, "entry_points"):
200
+ eps = importlib.metadata.entry_points()
201
+
202
+ # Handle different API versions
203
+ if hasattr(eps, "select"):
204
+ # Python 3.10+
205
+ plugins_eps = eps.select(group="oscura.plugins")
206
+ elif hasattr(eps, "get"):
207
+ # Python 3.9
208
+ plugins_eps = eps.get("oscura.plugins", [])
209
+ else:
210
+ # Python 3.8 style (dict)
211
+ plugins_eps = eps.get("oscura.plugins", []) # type: ignore[attr-defined]
212
+
213
+ for ep in plugins_eps:
214
+ try:
215
+ plugin_class = ep.load()
216
+
217
+ if isinstance(plugin_class, type) and issubclass(plugin_class, PluginBase):
218
+ instance = plugin_class()
219
+ metadata = instance.metadata
220
+
221
+ compatible = metadata.is_compatible_with(OSCURA_API_VERSION)
222
+
223
+ yield DiscoveredPlugin(
224
+ metadata=metadata,
225
+ entry_point=ep.name,
226
+ compatible=compatible,
227
+ )
228
+ except Exception as e:
229
+ # Create placeholder for failed load
230
+ yield DiscoveredPlugin(
231
+ metadata=PluginMetadata(
232
+ name=ep.name,
233
+ version="0.0.0",
234
+ description="Failed to load",
235
+ ),
236
+ entry_point=ep.name,
237
+ compatible=False,
238
+ load_error=str(e),
239
+ )
240
+
241
+ except Exception:
242
+ # Entry points not available or error
243
+ pass
244
+
245
+
246
+ def _load_plugin_from_yaml(yaml_path: Path) -> DiscoveredPlugin | None:
247
+ """Load plugin metadata from YAML file.
248
+
249
+ Args:
250
+ yaml_path: Path to plugin.yaml file.
251
+
252
+ Returns:
253
+ DiscoveredPlugin or None if load fails.
254
+ """
255
+ if not YAML_AVAILABLE:
256
+ return None
257
+
258
+ try:
259
+ with open(yaml_path, encoding="utf-8") as f:
260
+ data = yaml.safe_load(f)
261
+
262
+ if not isinstance(data, dict):
263
+ return None
264
+
265
+ # Extract metadata
266
+ metadata = PluginMetadata(
267
+ name=data.get("name", yaml_path.parent.name),
268
+ version=data.get("version", "0.0.0"),
269
+ api_version=data.get("api_version", "1.0.0"),
270
+ author=data.get("author", ""),
271
+ description=data.get("description", ""),
272
+ homepage=data.get("homepage", ""),
273
+ license=data.get("license", ""),
274
+ path=yaml_path.parent,
275
+ enabled=data.get("enabled", True),
276
+ )
277
+
278
+ # Parse dependencies
279
+ if "dependencies" in data:
280
+ deps = data["dependencies"]
281
+ if isinstance(deps, list):
282
+ for dep in deps:
283
+ if isinstance(dep, dict):
284
+ if "plugin" in dep:
285
+ metadata.dependencies[dep["plugin"]] = dep.get("version", "*")
286
+ elif "package" in dep:
287
+ metadata.dependencies[dep["package"]] = dep.get("version", "*")
288
+
289
+ # Parse provides
290
+ if "provides" in data:
291
+ provides = data["provides"]
292
+ if isinstance(provides, list):
293
+ for item in provides:
294
+ if isinstance(item, dict):
295
+ for key, value in item.items():
296
+ if key not in metadata.provides:
297
+ metadata.provides[key] = []
298
+ metadata.provides[key].append(value)
299
+
300
+ compatible = metadata.is_compatible_with(OSCURA_API_VERSION)
301
+
302
+ return DiscoveredPlugin(
303
+ metadata=metadata,
304
+ path=yaml_path.parent,
305
+ compatible=compatible,
306
+ )
307
+
308
+ except Exception as e:
309
+ return DiscoveredPlugin(
310
+ metadata=PluginMetadata(
311
+ name=yaml_path.parent.name,
312
+ version="0.0.0",
313
+ path=yaml_path.parent,
314
+ ),
315
+ path=yaml_path.parent,
316
+ compatible=False,
317
+ load_error=str(e),
318
+ )
319
+
320
+
321
+ def _load_plugin_from_module(module_path: Path) -> DiscoveredPlugin | None:
322
+ """Load plugin from Python module.
323
+
324
+ Args:
325
+ module_path: Path to Python package directory.
326
+
327
+ Returns:
328
+ DiscoveredPlugin or None if load fails.
329
+ """
330
+ try:
331
+ # Add parent to path temporarily
332
+ parent = str(module_path.parent)
333
+ if parent not in sys.path:
334
+ sys.path.insert(0, parent)
335
+ added_path = True
336
+ else:
337
+ added_path = False
338
+
339
+ try:
340
+ # Import the module
341
+ module_name = module_path.name
342
+ spec = importlib.util.spec_from_file_location(module_name, module_path / "__init__.py")
343
+
344
+ if spec is None or spec.loader is None:
345
+ return None
346
+
347
+ module = importlib.util.module_from_spec(spec)
348
+ spec.loader.exec_module(module)
349
+
350
+ # Look for Plugin class
351
+ plugin_class = None
352
+
353
+ # Check for explicit Plugin class
354
+ if hasattr(module, "Plugin"):
355
+ plugin_class = module.Plugin
356
+ elif hasattr(module, "plugin"):
357
+ plugin_class = module.plugin
358
+
359
+ # Check for any PluginBase subclass
360
+ if plugin_class is None:
361
+ for attr_name in dir(module):
362
+ attr = getattr(module, attr_name)
363
+ if (
364
+ isinstance(attr, type)
365
+ and issubclass(attr, PluginBase)
366
+ and attr is not PluginBase
367
+ ):
368
+ plugin_class = attr
369
+ break
370
+
371
+ if plugin_class is None:
372
+ return None
373
+
374
+ # Create instance and get metadata
375
+ instance = plugin_class()
376
+ metadata = instance.metadata
377
+ metadata.path = module_path
378
+
379
+ compatible = metadata.is_compatible_with(OSCURA_API_VERSION)
380
+
381
+ return DiscoveredPlugin(
382
+ metadata=metadata,
383
+ path=module_path,
384
+ compatible=compatible,
385
+ )
386
+
387
+ finally:
388
+ if added_path:
389
+ sys.path.remove(parent)
390
+
391
+ except Exception as e:
392
+ return DiscoveredPlugin(
393
+ metadata=PluginMetadata(
394
+ name=module_path.name,
395
+ version="0.0.0",
396
+ path=module_path,
397
+ ),
398
+ path=module_path,
399
+ compatible=False,
400
+ load_error=str(e),
401
+ )
402
+
403
+
404
+ __all__ = [
405
+ "OSCURA_API_VERSION",
406
+ "DiscoveredPlugin",
407
+ "discover_plugins",
408
+ "get_plugin_paths",
409
+ "scan_directory",
410
+ "scan_entry_points",
411
+ ]