oscura 0.0.1__py3-none-any.whl → 0.1.0__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.0.dist-info/METADATA +300 -0
  460. oscura-0.1.0.dist-info/RECORD +463 -0
  461. oscura-0.1.0.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.0.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.0.dist-info}/WHEEL +0 -0
oscura/plugins/cli.py ADDED
@@ -0,0 +1,497 @@
1
+ """Plugin management CLI.
2
+
3
+ This module provides command-line interface for plugin management including
4
+ list, info, enable/disable, install, and validate operations.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import hashlib
10
+ import logging
11
+ import shutil
12
+ import subprocess
13
+ import sys
14
+ import tempfile
15
+ from pathlib import Path
16
+ from urllib.parse import urlparse
17
+
18
+ from oscura.plugins.discovery import discover_plugins, get_plugin_paths
19
+ from oscura.plugins.lifecycle import get_lifecycle_manager
20
+ from oscura.plugins.registry import get_plugin_registry
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class PluginInstaller:
26
+ """Plugin installer with integrity validation.
27
+
28
+ Supports installing plugins from:
29
+ - Repository URLs (git)
30
+ - Archive files (.tar.gz, .zip)
31
+ - Local directories
32
+
33
+ References:
34
+ PLUG-007: Plugin CLI - install from repository URL, integrity validation
35
+ """
36
+
37
+ def __init__(self, install_dir: Path | None = None) -> None:
38
+ """Initialize installer.
39
+
40
+ Args:
41
+ install_dir: Directory to install plugins (defaults to user plugins)
42
+ """
43
+ if install_dir is None:
44
+ paths = get_plugin_paths()
45
+ # Use first user-writable path
46
+ install_dir = paths[0] if paths else Path.home() / ".oscura" / "plugins"
47
+
48
+ self.install_dir = install_dir
49
+ self.install_dir.mkdir(parents=True, exist_ok=True)
50
+
51
+ def install_from_url(
52
+ self,
53
+ url: str,
54
+ *,
55
+ checksum: str | None = None,
56
+ checksum_algo: str = "sha256",
57
+ ) -> Path:
58
+ """Install plugin from URL.
59
+
60
+ Args:
61
+ url: Plugin repository URL or archive URL
62
+ checksum: Expected checksum for integrity validation
63
+ checksum_algo: Hash algorithm (sha256, sha512, md5)
64
+
65
+ Returns:
66
+ Path to installed plugin
67
+
68
+ Raises:
69
+ ValueError: If checksum verification fails or unsupported URL type.
70
+
71
+ References:
72
+ PLUG-007: Plugin CLI - install from repository URL, integrity validation
73
+
74
+ Example:
75
+ >>> installer = PluginInstaller()
76
+ >>> path = installer.install_from_url(
77
+ ... "https://github.com/user/plugin.git",
78
+ ... checksum="abc123...",
79
+ ... )
80
+ """
81
+ logger.info(f"Installing plugin from URL: {url}")
82
+
83
+ parsed = urlparse(url)
84
+
85
+ # Determine source type
86
+ if parsed.path.endswith(".git") or "github.com" in parsed.netloc:
87
+ return self._install_from_git(url, checksum, checksum_algo)
88
+ elif parsed.path.endswith((".tar.gz", ".zip")):
89
+ return self._install_from_archive(url, checksum, checksum_algo)
90
+ else:
91
+ raise ValueError(f"Unsupported URL type: {url}")
92
+
93
+ def _install_from_git(
94
+ self,
95
+ url: str,
96
+ checksum: str | None,
97
+ checksum_algo: str,
98
+ ) -> Path:
99
+ """Install plugin from git repository.
100
+
101
+ Args:
102
+ url: Git repository URL
103
+ checksum: Expected checksum
104
+ checksum_algo: Hash algorithm
105
+
106
+ Returns:
107
+ Path to installed plugin
108
+
109
+ Raises:
110
+ RuntimeError: If git clone fails.
111
+ ValueError: If checksum verification fails.
112
+
113
+ References:
114
+ PLUG-007: Plugin CLI - install from repository URL
115
+ """
116
+ # Extract plugin name from URL
117
+ plugin_name = Path(urlparse(url).path).stem
118
+ if plugin_name.endswith(".git"):
119
+ plugin_name = plugin_name[:-4]
120
+
121
+ target_dir = self.install_dir / plugin_name
122
+
123
+ # Clone repository to temp directory first
124
+ with tempfile.TemporaryDirectory() as temp_dir:
125
+ temp_path = Path(temp_dir) / plugin_name
126
+
127
+ try:
128
+ subprocess.run(
129
+ ["git", "clone", url, str(temp_path)],
130
+ check=True,
131
+ capture_output=True,
132
+ text=True,
133
+ )
134
+ except subprocess.CalledProcessError as e:
135
+ raise RuntimeError(f"Git clone failed: {e.stderr}") from e
136
+
137
+ # Validate checksum if provided
138
+ if checksum:
139
+ actual = self._compute_directory_checksum(temp_path, checksum_algo)
140
+ if actual != checksum:
141
+ raise ValueError(f"Checksum mismatch: expected {checksum}, got {actual}")
142
+
143
+ # Move to final location
144
+ if target_dir.exists():
145
+ logger.warning(f"Removing existing plugin at {target_dir}")
146
+ shutil.rmtree(target_dir)
147
+
148
+ shutil.copytree(temp_path, target_dir)
149
+
150
+ logger.info(f"Installed plugin '{plugin_name}' to {target_dir}")
151
+ return target_dir
152
+
153
+ def _install_from_archive(
154
+ self,
155
+ url: str,
156
+ checksum: str | None,
157
+ checksum_algo: str,
158
+ ) -> Path:
159
+ """Install plugin from archive file.
160
+
161
+ Args:
162
+ url: Archive URL
163
+ checksum: Expected checksum
164
+ checksum_algo: Hash algorithm
165
+
166
+ Returns:
167
+ Path to installed plugin
168
+
169
+ Raises:
170
+ RuntimeError: If download fails.
171
+ ValueError: If checksum verification fails or archive format is invalid.
172
+
173
+ References:
174
+ PLUG-007: Plugin CLI - integrity validation
175
+ """
176
+ import urllib.request
177
+
178
+ # Download archive
179
+ with tempfile.TemporaryDirectory() as temp_dir:
180
+ archive_path = Path(temp_dir) / "plugin.archive"
181
+
182
+ try:
183
+ urllib.request.urlretrieve(url, archive_path)
184
+ except Exception as e:
185
+ raise RuntimeError(f"Download failed: {e}") from e
186
+
187
+ # Validate checksum
188
+ if checksum:
189
+ actual = self._compute_file_checksum(archive_path, checksum_algo)
190
+ if actual != checksum:
191
+ raise ValueError(f"Checksum mismatch: expected {checksum}, got {actual}")
192
+
193
+ # Extract archive
194
+ extract_dir = Path(temp_dir) / "extracted"
195
+ shutil.unpack_archive(archive_path, extract_dir)
196
+
197
+ # Find plugin directory (should be single top-level dir)
198
+ plugin_dirs = [d for d in extract_dir.iterdir() if d.is_dir()]
199
+ if len(plugin_dirs) != 1:
200
+ raise ValueError(
201
+ f"Archive should contain single plugin directory, found {len(plugin_dirs)}"
202
+ )
203
+
204
+ plugin_dir = plugin_dirs[0]
205
+ plugin_name = plugin_dir.name
206
+
207
+ target_dir = self.install_dir / plugin_name
208
+
209
+ # Move to final location
210
+ if target_dir.exists():
211
+ logger.warning(f"Removing existing plugin at {target_dir}")
212
+ shutil.rmtree(target_dir)
213
+
214
+ shutil.copytree(plugin_dir, target_dir)
215
+
216
+ logger.info(f"Installed plugin '{plugin_name}' to {target_dir}")
217
+ return target_dir
218
+
219
+ def _compute_file_checksum(self, path: Path, algo: str) -> str:
220
+ """Compute checksum of a file.
221
+
222
+ Args:
223
+ path: File path
224
+ algo: Hash algorithm
225
+
226
+ Returns:
227
+ Hexadecimal checksum
228
+
229
+ References:
230
+ PLUG-007: Plugin CLI - integrity validation (checksum verification)
231
+ """
232
+ hasher = hashlib.new(algo)
233
+
234
+ with open(path, "rb") as f:
235
+ while chunk := f.read(8192):
236
+ hasher.update(chunk)
237
+
238
+ return hasher.hexdigest()
239
+
240
+ def _compute_directory_checksum(self, path: Path, algo: str) -> str:
241
+ """Compute checksum of a directory (all files).
242
+
243
+ Args:
244
+ path: Directory path
245
+ algo: Hash algorithm
246
+
247
+ Returns:
248
+ Hexadecimal checksum
249
+
250
+ References:
251
+ PLUG-007: Plugin CLI - integrity validation (checksum verification)
252
+ """
253
+ hasher = hashlib.new(algo)
254
+
255
+ # Sort files for consistent ordering
256
+ files = sorted(path.rglob("*"))
257
+
258
+ for file_path in files:
259
+ if file_path.is_file():
260
+ # Include relative path in hash
261
+ rel_path = file_path.relative_to(path)
262
+ hasher.update(str(rel_path).encode())
263
+
264
+ # Include file content
265
+ with open(file_path, "rb") as f:
266
+ while chunk := f.read(8192):
267
+ hasher.update(chunk)
268
+
269
+ return hasher.hexdigest()
270
+
271
+ def validate_integrity(
272
+ self,
273
+ plugin_path: Path,
274
+ expected_checksum: str,
275
+ algo: str = "sha256",
276
+ ) -> bool:
277
+ """Validate plugin integrity.
278
+
279
+ Args:
280
+ plugin_path: Path to plugin
281
+ expected_checksum: Expected checksum
282
+ algo: Hash algorithm
283
+
284
+ Returns:
285
+ True if checksum matches
286
+
287
+ References:
288
+ PLUG-007: Plugin CLI - integrity validation (checksum verification)
289
+ """
290
+ if plugin_path.is_file():
291
+ actual = self._compute_file_checksum(plugin_path, algo)
292
+ else:
293
+ actual = self._compute_directory_checksum(plugin_path, algo)
294
+
295
+ return actual == expected_checksum
296
+
297
+
298
+ def cli_list_plugins() -> None:
299
+ """List all available plugins (CLI command).
300
+
301
+ References:
302
+ PLUG-007: Plugin CLI
303
+ """
304
+ plugins = discover_plugins(compatible_only=False)
305
+
306
+ if not plugins:
307
+ print("No plugins found")
308
+ return
309
+
310
+ print(f"Found {len(plugins)} plugins:\n")
311
+
312
+ for plugin in plugins:
313
+ status = "enabled" if plugin.metadata.enabled else "disabled"
314
+ compat = "compatible" if plugin.compatible else "incompatible"
315
+
316
+ print(f" {plugin.metadata.name} v{plugin.metadata.version} [{status}]")
317
+ if plugin.path:
318
+ print(f" Path: {plugin.path}")
319
+ print(f" API: {plugin.metadata.api_version} ({compat})")
320
+
321
+ if plugin.metadata.provides:
322
+ provides: list[str] = []
323
+ for key, values in plugin.metadata.provides.items():
324
+ provides.extend(f"{key}:{v}" for v in values)
325
+ print(f" Provides: {', '.join(provides)}")
326
+
327
+ if plugin.load_error:
328
+ print(f" Error: {plugin.load_error}")
329
+
330
+ print()
331
+
332
+
333
+ def cli_plugin_info(name: str) -> None:
334
+ """Show detailed plugin information (CLI command).
335
+
336
+ Args:
337
+ name: Plugin name
338
+
339
+ References:
340
+ PLUG-007: Plugin CLI
341
+ """
342
+ registry = get_plugin_registry()
343
+ metadata = registry.get_metadata(name)
344
+
345
+ if metadata is None:
346
+ print(f"Plugin '{name}' not found")
347
+ sys.exit(1)
348
+
349
+ print(f"Name: {metadata.name}")
350
+ print(f"Version: {metadata.version}")
351
+ print(f"API Version: {metadata.api_version}")
352
+
353
+ if metadata.author:
354
+ print(f"Author: {metadata.author}")
355
+
356
+ if metadata.description:
357
+ print(f"Description: {metadata.description}")
358
+
359
+ if metadata.homepage:
360
+ print(f"Homepage: {metadata.homepage}")
361
+
362
+ if metadata.license:
363
+ print(f"License: {metadata.license}")
364
+
365
+ if metadata.path:
366
+ print(f"Path: {metadata.path}")
367
+
368
+ print(f"Status: {'enabled' if metadata.enabled else 'disabled'}")
369
+
370
+ if metadata.dependencies:
371
+ print("\nDependencies:")
372
+ for dep, version in metadata.dependencies.items():
373
+ print(f" - {dep} {version}")
374
+
375
+ if metadata.provides:
376
+ print("\nProvides:")
377
+ for key, values in metadata.provides.items():
378
+ for value in values:
379
+ print(f" - {key}: {value}")
380
+
381
+
382
+ def cli_enable_plugin(name: str) -> None:
383
+ """Enable a plugin (CLI command).
384
+
385
+ Args:
386
+ name: Plugin name
387
+
388
+ References:
389
+ PLUG-007: Plugin CLI
390
+ """
391
+ manager = get_lifecycle_manager()
392
+
393
+ try:
394
+ manager.enable_plugin(name)
395
+ print(f"Plugin '{name}' enabled")
396
+ except Exception as e:
397
+ print(f"Failed to enable plugin: {e}")
398
+ sys.exit(1)
399
+
400
+
401
+ def cli_disable_plugin(name: str) -> None:
402
+ """Disable a plugin (CLI command).
403
+
404
+ Args:
405
+ name: Plugin name
406
+
407
+ References:
408
+ PLUG-007: Plugin CLI
409
+ """
410
+ manager = get_lifecycle_manager()
411
+
412
+ try:
413
+ manager.disable_plugin(name)
414
+ print(f"Plugin '{name}' disabled")
415
+ except Exception as e:
416
+ print(f"Failed to disable plugin: {e}")
417
+ sys.exit(1)
418
+
419
+
420
+ def cli_validate_plugin(name: str) -> None:
421
+ """Validate a plugin (CLI command).
422
+
423
+ Args:
424
+ name: Plugin name
425
+
426
+ References:
427
+ PLUG-007: Plugin CLI - integrity validation
428
+ """
429
+ plugins = discover_plugins(compatible_only=False)
430
+ plugin = next((p for p in plugins if p.metadata.name == name), None)
431
+
432
+ if plugin is None:
433
+ print(f"Plugin '{name}' not found")
434
+ sys.exit(1)
435
+
436
+ print(f"Validating {name}...")
437
+
438
+ # Check metadata
439
+ if not plugin.metadata.name:
440
+ print(" ✗ Missing name")
441
+ sys.exit(1)
442
+ print(" ✓ Metadata valid")
443
+
444
+ # Check dependencies
445
+ if plugin.metadata.dependencies:
446
+ print(f" ✓ Dependencies declared: {len(plugin.metadata.dependencies)}")
447
+ else:
448
+ print(" ✓ No dependencies")
449
+
450
+ # Check API compatibility
451
+ if plugin.compatible:
452
+ print(" ✓ API version compatible")
453
+ else:
454
+ print(f" ✗ API version incompatible: {plugin.metadata.api_version}")
455
+ sys.exit(1)
456
+
457
+ # Check for errors
458
+ if plugin.load_error:
459
+ print(f" ✗ Load error: {plugin.load_error}")
460
+ sys.exit(1)
461
+
462
+ print("\nPlugin is valid")
463
+
464
+
465
+ def cli_install_plugin(
466
+ url: str,
467
+ *,
468
+ checksum: str | None = None,
469
+ ) -> None:
470
+ """Install a plugin from URL (CLI command).
471
+
472
+ Args:
473
+ url: Plugin repository or archive URL
474
+ checksum: Expected checksum for validation
475
+
476
+ References:
477
+ PLUG-007: Plugin CLI - install from repository URL, integrity validation
478
+ """
479
+ installer = PluginInstaller()
480
+
481
+ try:
482
+ path = installer.install_from_url(url, checksum=checksum)
483
+ print(f"Successfully installed plugin to {path}")
484
+ except Exception as e:
485
+ print(f"Installation failed: {e}")
486
+ sys.exit(1)
487
+
488
+
489
+ __all__ = [
490
+ "PluginInstaller",
491
+ "cli_disable_plugin",
492
+ "cli_enable_plugin",
493
+ "cli_install_plugin",
494
+ "cli_list_plugins",
495
+ "cli_plugin_info",
496
+ "cli_validate_plugin",
497
+ ]