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,913 @@
1
+ """Plugin template generation for creating new Oscura plugins.
2
+
3
+ This module provides tools for generating plugin skeletons with all necessary
4
+ boilerplate code, tests, and documentation.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import textwrap
10
+ from dataclasses import dataclass
11
+ from typing import TYPE_CHECKING, Literal
12
+
13
+ if TYPE_CHECKING:
14
+ from pathlib import Path
15
+
16
+ # Plugin type definitions
17
+ PluginType = Literal["analyzer", "loader", "exporter", "decoder"]
18
+
19
+
20
+ @dataclass
21
+ class PluginTemplate:
22
+ """Configuration for plugin template generation.
23
+
24
+ Attributes:
25
+ name: Plugin name (e.g., 'my_custom_decoder').
26
+ plugin_type: Type of plugin ('analyzer', 'loader', 'exporter', 'decoder').
27
+ output_dir: Directory where plugin will be generated.
28
+ author: Plugin author name.
29
+ description: Brief description of plugin functionality.
30
+ version: Initial plugin version (default: '0.1.0').
31
+
32
+ Example:
33
+ >>> template = PluginTemplate(
34
+ ... name='flexray_decoder',
35
+ ... plugin_type='decoder',
36
+ ... output_dir=Path('plugins/flexray'),
37
+ ... author='John Doe',
38
+ ... description='FlexRay protocol decoder'
39
+ ... )
40
+
41
+ References:
42
+ PLUG-008: Plugin Template Generator
43
+ """
44
+
45
+ name: str
46
+ plugin_type: PluginType
47
+ output_dir: Path
48
+ author: str = "Plugin Author"
49
+ description: str = "Custom Oscura plugin"
50
+ version: str = "0.1.0"
51
+
52
+
53
+ def generate_plugin_template(
54
+ name: str,
55
+ plugin_type: PluginType,
56
+ output_dir: Path,
57
+ *,
58
+ author: str = "Plugin Author",
59
+ description: str | None = None,
60
+ version: str = "0.1.0",
61
+ ) -> Path:
62
+ """Generate a plugin skeleton with all necessary boilerplate.
63
+
64
+ Creates a complete plugin package structure including:
65
+ - __init__.py with plugin metadata
66
+ - Main module with stub implementation
67
+ - tests/ directory with test stubs
68
+ - README.md with usage instructions
69
+ - pyproject.toml for packaging
70
+
71
+ Args:
72
+ name: Plugin name (will be converted to snake_case).
73
+ plugin_type: Type of plugin to generate.
74
+ output_dir: Directory where plugin will be created.
75
+ author: Plugin author name.
76
+ description: Plugin description (auto-generated if None).
77
+ version: Initial plugin version.
78
+
79
+ Returns:
80
+ Path to the generated plugin directory.
81
+
82
+ Raises:
83
+ ValueError: If plugin_type is invalid.
84
+
85
+ Example:
86
+ >>> from pathlib import Path
87
+ >>> plugin_dir = generate_plugin_template(
88
+ ... name='flexray_decoder',
89
+ ... plugin_type='decoder',
90
+ ... output_dir=Path('plugins/flexray'),
91
+ ... author='John Doe',
92
+ ... description='FlexRay protocol decoder'
93
+ ... )
94
+ >>> print(f"Plugin generated at {plugin_dir}")
95
+
96
+ Plugin Structure:
97
+ ```
98
+ plugins/flexray/
99
+ ├── __init__.py # Plugin metadata and entry point
100
+ ├── flexray_decoder.py # Main implementation
101
+ ├── tests/
102
+ │ ├── __init__.py
103
+ │ └── test_flexray_decoder.py
104
+ ├── README.md # Usage documentation
105
+ └── pyproject.toml # Packaging configuration
106
+ ```
107
+
108
+ References:
109
+ PLUG-008: Plugin Template Generator
110
+ """
111
+ # Validate plugin type
112
+ valid_types: set[PluginType] = {"analyzer", "loader", "exporter", "decoder"}
113
+ if plugin_type not in valid_types:
114
+ raise ValueError(
115
+ f"Invalid plugin_type '{plugin_type}'. Must be one of: {', '.join(valid_types)}"
116
+ )
117
+
118
+ # Generate default description if not provided
119
+ if description is None:
120
+ description = f"Custom {plugin_type} plugin for Oscura"
121
+
122
+ # Create template configuration
123
+ template = PluginTemplate(
124
+ name=name,
125
+ plugin_type=plugin_type,
126
+ output_dir=output_dir,
127
+ author=author,
128
+ description=description,
129
+ version=version,
130
+ )
131
+
132
+ # Generate plugin directory structure
133
+ _generate_plugin_structure(template)
134
+
135
+ return output_dir
136
+
137
+
138
+ def _generate_plugin_structure(template: PluginTemplate) -> None:
139
+ """Generate complete plugin directory structure.
140
+
141
+ Args:
142
+ template: Plugin template configuration.
143
+
144
+ Raises:
145
+ FileExistsError: If plugin directory already exists.
146
+ """
147
+ output_dir = template.output_dir
148
+
149
+ # Check if directory exists
150
+ if output_dir.exists():
151
+ raise FileExistsError(
152
+ f"Plugin directory already exists: {output_dir}\n"
153
+ f"Remove it or choose a different output_dir."
154
+ )
155
+
156
+ # Create directory structure
157
+ output_dir.mkdir(parents=True, exist_ok=False)
158
+ tests_dir = output_dir / "tests"
159
+ tests_dir.mkdir(exist_ok=False)
160
+
161
+ # Generate files
162
+ _write_init_py(template)
163
+ _write_main_module(template)
164
+ _write_test_init(template)
165
+ _write_test_module(template)
166
+ _write_readme(template)
167
+ _write_pyproject_toml(template)
168
+
169
+
170
+ def _write_init_py(template: PluginTemplate) -> None:
171
+ """Write plugin __init__.py with metadata.
172
+
173
+ Args:
174
+ template: Plugin template configuration.
175
+ """
176
+ content = textwrap.dedent(f'''\
177
+ """{template.description}
178
+
179
+ This plugin integrates with Oscura via entry points.
180
+
181
+ Plugin Metadata:
182
+ Name: {template.name}
183
+ Type: {template.plugin_type}
184
+ Version: {template.version}
185
+ Author: {template.author}
186
+
187
+ Installation:
188
+ pip install -e .
189
+
190
+ Usage:
191
+ import oscura as osc
192
+ # Plugin auto-discovered via entry points
193
+ # See README.md for usage examples
194
+
195
+ References:
196
+ PLUG-008: Plugin Template Generator
197
+ """
198
+ from .{template.name} import {_get_class_name(template)}
199
+
200
+ __version__ = "{template.version}"
201
+
202
+ __all__ = [
203
+ "{_get_class_name(template)}",
204
+ ]
205
+ ''')
206
+
207
+ (template.output_dir / "__init__.py").write_text(content)
208
+
209
+
210
+ def _write_main_module(template: PluginTemplate) -> None:
211
+ """Write main plugin module with stub implementation.
212
+
213
+ Args:
214
+ template: Plugin template configuration.
215
+ """
216
+ class_name = _get_class_name(template)
217
+
218
+ if template.plugin_type == "decoder":
219
+ content = _generate_decoder_stub(template, class_name)
220
+ elif template.plugin_type == "analyzer":
221
+ content = _generate_analyzer_stub(template, class_name)
222
+ elif template.plugin_type == "loader":
223
+ content = _generate_loader_stub(template, class_name)
224
+ elif template.plugin_type == "exporter":
225
+ content = _generate_exporter_stub(template, class_name)
226
+ else:
227
+ # Fallback generic stub
228
+ content = _generate_generic_stub(template, class_name) # type: ignore[unreachable]
229
+
230
+ (template.output_dir / f"{template.name}.py").write_text(content)
231
+
232
+
233
+ def _write_test_init(template: PluginTemplate) -> None:
234
+ """Write tests/__init__.py.
235
+
236
+ Args:
237
+ template: Plugin template configuration.
238
+ """
239
+ content = '"""Test suite for plugin."""\n'
240
+ (template.output_dir / "tests" / "__init__.py").write_text(content)
241
+
242
+
243
+ def _write_test_module(template: PluginTemplate) -> None:
244
+ """Write test module with example tests.
245
+
246
+ Args:
247
+ template: Plugin template configuration.
248
+ """
249
+ class_name = _get_class_name(template)
250
+
251
+ content = textwrap.dedent(f'''\
252
+ """Tests for {template.name} plugin.
253
+
254
+ This module contains unit tests for the plugin implementation.
255
+ """
256
+ import numpy as np
257
+ import pytest
258
+
259
+ from {template.name} import {class_name}
260
+
261
+
262
+ def test_{template.name}_initialization():
263
+ """Test plugin can be instantiated."""
264
+ plugin = {class_name}()
265
+ assert plugin is not None
266
+
267
+
268
+ def test_{template.name}_basic_functionality():
269
+ """Test basic plugin functionality."""
270
+ plugin = {class_name}()
271
+
272
+ # USER: Implement test for your plugin's main functionality
273
+ # Example:
274
+ # result = plugin.process(test_data)
275
+ # assert result is not None
276
+
277
+ # Placeholder assertion - replace with actual tests
278
+ assert True
279
+
280
+
281
+ def test_{template.name}_error_handling():
282
+ """Test plugin handles errors gracefully."""
283
+ plugin = {class_name}()
284
+
285
+ # USER: Implement error condition tests
286
+ # Example:
287
+ # with pytest.raises(ValueError):
288
+ # plugin.process(invalid_data)
289
+
290
+ # Placeholder assertion - replace with actual tests
291
+ assert True
292
+
293
+
294
+ @pytest.mark.parametrize("param", [1, 2, 3])
295
+ def test_{template.name}_parametrized(param):
296
+ """Example parametrized test."""
297
+ plugin = {class_name}()
298
+
299
+ # USER: Implement parametrized test logic
300
+ assert param > 0
301
+ ''')
302
+
303
+ (template.output_dir / "tests" / f"test_{template.name}.py").write_text(content)
304
+
305
+
306
+ def _write_readme(template: PluginTemplate) -> None:
307
+ """Write README.md with usage instructions.
308
+
309
+ Args:
310
+ template: Plugin template configuration.
311
+ """
312
+ class_name = _get_class_name(template)
313
+ entry_point_group = _get_entry_point_group(template.plugin_type)
314
+
315
+ content = textwrap.dedent(f"""\
316
+ # {class_name}
317
+
318
+ {template.description}
319
+
320
+ ## Installation
321
+
322
+ Install in development mode:
323
+
324
+ ```bash
325
+ cd {template.output_dir.name}
326
+ pip install -e .
327
+ ```
328
+
329
+ ## Usage
330
+
331
+ The plugin integrates automatically with Oscura via entry points:
332
+
333
+ ```python
334
+ import oscura as osc
335
+
336
+ # Plugin is automatically discovered
337
+ # USER: Add usage examples specific to your plugin
338
+ ```
339
+
340
+ ### Direct Usage
341
+
342
+ ```python
343
+ from {template.name} import {class_name}
344
+
345
+ # Create instance
346
+ plugin = {class_name}()
347
+
348
+ # USER: Add direct usage examples
349
+ ```
350
+
351
+ ## CLI Integration
352
+
353
+ After installation, the plugin is available in Oscura CLI:
354
+
355
+ ```bash
356
+ # List installed plugins
357
+ oscura plugin list
358
+
359
+ # Show plugin info
360
+ oscura plugin info {template.name}
361
+ ```
362
+
363
+ ## Development
364
+
365
+ ### Running Tests
366
+
367
+ ```bash
368
+ pytest tests/
369
+ ```
370
+
371
+ ### Code Quality
372
+
373
+ ```bash
374
+ # Linting
375
+ ruff check {template.name}.py
376
+
377
+ # Formatting
378
+ ruff format {template.name}.py
379
+
380
+ # Type checking
381
+ mypy {template.name}.py
382
+ ```
383
+
384
+ ## Plugin Type: {template.plugin_type}
385
+
386
+ This is a **{template.plugin_type}** plugin for Oscura.
387
+
388
+ ### Entry Point
389
+
390
+ Registered in `{entry_point_group}` entry point group.
391
+
392
+ ## Requirements
393
+
394
+ - Oscura >= 0.1.0
395
+ - Python >= 3.12
396
+
397
+ ## License
398
+
399
+ MIT
400
+
401
+ ## Author
402
+
403
+ {template.author}
404
+
405
+ ## Version
406
+
407
+ {template.version}
408
+ """)
409
+
410
+ (template.output_dir / "README.md").write_text(content)
411
+
412
+
413
+ def _write_pyproject_toml(template: PluginTemplate) -> None:
414
+ """Write pyproject.toml for plugin packaging.
415
+
416
+ Args:
417
+ template: Plugin template configuration.
418
+ """
419
+ entry_point_group = _get_entry_point_group(template.plugin_type)
420
+ class_name = _get_class_name(template)
421
+
422
+ content = textwrap.dedent(f'''\
423
+ [project]
424
+ name = "{template.name}"
425
+ version = "{template.version}"
426
+ description = "{template.description}"
427
+ readme = "README.md"
428
+ license = {{ text = "MIT" }}
429
+ requires-python = ">=3.12"
430
+ authors = [
431
+ {{ name = "{template.author}" }}
432
+ ]
433
+ keywords = ["oscura", "plugin", "{template.plugin_type}"]
434
+ classifiers = [
435
+ "Development Status :: 3 - Alpha",
436
+ "Intended Audience :: Developers",
437
+ "License :: OSI Approved :: MIT License",
438
+ "Programming Language :: Python :: 3",
439
+ "Programming Language :: Python :: 3.12",
440
+ "Programming Language :: Python :: 3.13",
441
+ ]
442
+
443
+ dependencies = [
444
+ "oscura>=0.1.0",
445
+ "numpy>=1.26.0",
446
+ ]
447
+
448
+ [project.optional-dependencies]
449
+ dev = [
450
+ "pytest>=8.3.0",
451
+ "pytest-cov>=6.0.0",
452
+ "ruff>=0.8.0",
453
+ "mypy>=1.13.0",
454
+ ]
455
+
456
+ # Oscura plugin entry point
457
+ [project.entry-points."{entry_point_group}"]
458
+ {template.name} = "{template.name}:{class_name}"
459
+
460
+ [build-system]
461
+ requires = ["hatchling"]
462
+ build-backend = "hatchling.build"
463
+
464
+ [tool.pytest.ini_options]
465
+ testpaths = ["tests"]
466
+ python_files = ["test_*.py"]
467
+ python_classes = ["Test*"]
468
+ python_functions = ["test_*"]
469
+
470
+ [tool.ruff]
471
+ line-length = 88
472
+ target-version = "py312"
473
+
474
+ [tool.ruff.lint]
475
+ select = ["E", "F", "W", "I", "N", "UP", "YTT", "B", "A", "C4", "T10", "RUF"]
476
+
477
+ [tool.mypy]
478
+ python_version = "3.12"
479
+ warn_return_any = true
480
+ warn_unused_configs = true
481
+ disallow_untyped_defs = true
482
+ ''')
483
+
484
+ (template.output_dir / "pyproject.toml").write_text(content)
485
+
486
+
487
+ def _get_class_name(template: PluginTemplate) -> str:
488
+ """Generate class name from plugin name.
489
+
490
+ Args:
491
+ template: Plugin template configuration.
492
+
493
+ Returns:
494
+ PascalCase class name.
495
+
496
+ Example:
497
+ >>> template = PluginTemplate('my_decoder', 'decoder', Path('.'))
498
+ >>> _get_class_name(template)
499
+ 'MyDecoder'
500
+ """
501
+ # Convert snake_case to PascalCase
502
+ parts = template.name.split("_")
503
+ return "".join(word.capitalize() for word in parts)
504
+
505
+
506
+ def _get_entry_point_group(plugin_type: PluginType) -> str:
507
+ """Get entry point group for plugin type.
508
+
509
+ Args:
510
+ plugin_type: Type of plugin.
511
+
512
+ Returns:
513
+ Entry point group name.
514
+ """
515
+ return f"oscura.{plugin_type}s"
516
+
517
+
518
+ def _generate_decoder_stub(template: PluginTemplate, class_name: str) -> str:
519
+ """Generate decoder plugin stub.
520
+
521
+ Args:
522
+ template: Plugin template configuration.
523
+ class_name: Class name for the decoder.
524
+
525
+ Returns:
526
+ Python source code for decoder stub.
527
+ """
528
+ return textwrap.dedent(f'''\
529
+ """{template.description}
530
+
531
+ This decoder implements protocol decoding for Oscura.
532
+
533
+ References:
534
+ PLUG-008: Plugin Template Generator
535
+ """
536
+ from __future__ import annotations
537
+
538
+ import numpy as np
539
+ from numpy.typing import NDArray
540
+
541
+
542
+ class {class_name}:
543
+ """Protocol decoder implementation.
544
+
545
+ Attributes:
546
+ sample_rate: Sample rate of input signal in Hz.
547
+
548
+ Example:
549
+ >>> decoder = {class_name}(sample_rate=1_000_000)
550
+ >>> frames = decoder.decode(digital_signal)
551
+
552
+ References:
553
+ PLUG-008: Plugin Template Generator
554
+ """
555
+ def __init__(self, *, sample_rate: float = 1_000_000.0) -> None:
556
+ """Initialize decoder.
557
+
558
+ Args:
559
+ sample_rate: Sample rate in Hz.
560
+ """
561
+ self.sample_rate = sample_rate
562
+
563
+ def decode(
564
+ self,
565
+ signal: NDArray[np.uint8],
566
+ ) -> list[dict[str, object]]:
567
+ """Decode protocol frames from digital signal.
568
+
569
+ Args:
570
+ signal: Digital signal (0/1 values).
571
+
572
+ Returns:
573
+ List of decoded frames, each a dictionary with frame data.
574
+
575
+ Raises:
576
+ ValueError: If signal is empty or invalid.
577
+
578
+ Example:
579
+ >>> signal = np.array([0, 1, 1, 0, 1], dtype=np.uint8)
580
+ >>> frames = decoder.decode(signal)
581
+ """
582
+ if len(signal) == 0:
583
+ raise ValueError("Signal cannot be empty")
584
+
585
+ # USER: Implement protocol decoding logic here
586
+ # This stub returns an empty list - replace with actual decoding
587
+ frames: list[dict[str, object]] = []
588
+
589
+ return frames
590
+
591
+ def configure(self, **params: object) -> None:
592
+ """Configure decoder parameters.
593
+
594
+ Args:
595
+ **params: Decoder-specific parameters.
596
+
597
+ Example:
598
+ >>> decoder.configure(baudrate=115200, parity='none')
599
+ """
600
+ # USER: Implement configuration logic here
601
+ # Store parameters as instance attributes
602
+ for key, value in params.items():
603
+ setattr(self, key, value)
604
+ ''')
605
+
606
+
607
+ def _generate_analyzer_stub(template: PluginTemplate, class_name: str) -> str:
608
+ """Generate analyzer plugin stub.
609
+
610
+ Args:
611
+ template: Plugin template configuration.
612
+ class_name: Class name for the analyzer.
613
+
614
+ Returns:
615
+ Python source code for analyzer stub.
616
+ """
617
+ return textwrap.dedent(f'''\
618
+ """{template.description}
619
+
620
+ This analyzer implements custom signal analysis for Oscura.
621
+
622
+ References:
623
+ PLUG-008: Plugin Template Generator
624
+ """
625
+ from __future__ import annotations
626
+
627
+ import numpy as np
628
+ from numpy.typing import NDArray
629
+
630
+
631
+ class {class_name}:
632
+ """Signal analyzer implementation.
633
+
634
+ Example:
635
+ >>> analyzer = {class_name}()
636
+ >>> result = analyzer.analyze(signal)
637
+
638
+ References:
639
+ PLUG-008: Plugin Template Generator
640
+ """
641
+ def __init__(self) -> None:
642
+ """Initialize analyzer."""
643
+ pass
644
+
645
+ def analyze(
646
+ self,
647
+ signal: NDArray[np.float64],
648
+ *,
649
+ sample_rate: float = 1_000_000.0,
650
+ ) -> dict[str, object]:
651
+ """Analyze signal and extract features.
652
+
653
+ Args:
654
+ signal: Input signal array.
655
+ sample_rate: Sample rate in Hz.
656
+
657
+ Returns:
658
+ Dictionary containing analysis results.
659
+
660
+ Raises:
661
+ ValueError: If signal is empty or invalid.
662
+
663
+ Example:
664
+ >>> signal = np.sin(2 * np.pi * 1000 * np.linspace(0, 1, 1000))
665
+ >>> result = analyzer.analyze(signal, sample_rate=1000)
666
+ """
667
+ if len(signal) == 0:
668
+ raise ValueError("Signal cannot be empty")
669
+
670
+ # USER: Implement analysis logic here
671
+ # This stub returns placeholder results - replace with actual analysis
672
+ result: dict[str, object] = {{
673
+ "status": "not_implemented",
674
+ "sample_count": len(signal),
675
+ "sample_rate": sample_rate,
676
+ }}
677
+
678
+ return result
679
+ ''')
680
+
681
+
682
+ def _generate_loader_stub(template: PluginTemplate, class_name: str) -> str:
683
+ """Generate loader plugin stub.
684
+
685
+ Args:
686
+ template: Plugin template configuration.
687
+ class_name: Class name for the loader.
688
+
689
+ Returns:
690
+ Python source code for loader stub.
691
+ """
692
+ return textwrap.dedent(f'''\
693
+ """{template.description}
694
+
695
+ This loader implements file format loading for Oscura.
696
+
697
+ References:
698
+ PLUG-008: Plugin Template Generator
699
+ """
700
+ from __future__ import annotations
701
+
702
+ import numpy as np
703
+ from numpy.typing import NDArray
704
+ from pathlib import Path
705
+
706
+
707
+ class {class_name}:
708
+ """File format loader implementation.
709
+
710
+ Example:
711
+ >>> loader = {class_name}()
712
+ >>> data = loader.load(Path("capture.dat"))
713
+
714
+ References:
715
+ PLUG-008: Plugin Template Generator
716
+ """
717
+ def __init__(self) -> None:
718
+ """Initialize loader."""
719
+ pass
720
+
721
+ def load(self, file_path: Path) -> dict[str, NDArray[np.float64]]:
722
+ """Load data from file.
723
+
724
+ Args:
725
+ file_path: Path to file to load.
726
+
727
+ Returns:
728
+ Dictionary mapping channel names to signal arrays.
729
+
730
+ Raises:
731
+ FileNotFoundError: If file does not exist.
732
+ ValueError: If file format is invalid.
733
+
734
+ Example:
735
+ >>> data = loader.load(Path("capture.dat"))
736
+ >>> print(f"Loaded {{len(data)}} channels")
737
+ """
738
+ if not file_path.exists():
739
+ raise FileNotFoundError(f"File not found: {{file_path}}")
740
+
741
+ # USER: Implement file loading logic here
742
+ # This stub returns empty data - replace with actual loading
743
+ data: dict[str, NDArray[np.float64]] = {{}}
744
+
745
+ return data
746
+
747
+ @staticmethod
748
+ def can_load(file_path: Path) -> bool:
749
+ """Check if this loader can handle the file.
750
+
751
+ Args:
752
+ file_path: Path to file.
753
+
754
+ Returns:
755
+ True if loader can handle this file format.
756
+
757
+ Example:
758
+ >>> if loader.can_load(Path("capture.dat")):
759
+ ... data = loader.load(Path("capture.dat"))
760
+ """
761
+ # USER: Implement format detection here
762
+ # Check file extension, magic bytes, etc.
763
+ return file_path.suffix == ".dat"
764
+ ''')
765
+
766
+
767
+ def _generate_exporter_stub(template: PluginTemplate, class_name: str) -> str:
768
+ """Generate exporter plugin stub.
769
+
770
+ Args:
771
+ template: Plugin template configuration.
772
+ class_name: Class name for the exporter.
773
+
774
+ Returns:
775
+ Python source code for exporter stub.
776
+ """
777
+ return textwrap.dedent(f'''\
778
+ """{template.description}
779
+
780
+ This exporter implements custom export format for Oscura.
781
+
782
+ References:
783
+ PLUG-008: Plugin Template Generator
784
+ """
785
+ from __future__ import annotations
786
+
787
+ import numpy as np
788
+ from numpy.typing import NDArray
789
+ from pathlib import Path
790
+
791
+
792
+ class {class_name}:
793
+ """Export format implementation.
794
+
795
+ Example:
796
+ >>> exporter = {class_name}()
797
+ >>> exporter.export(data, Path("output.dat"))
798
+
799
+ References:
800
+ PLUG-008: Plugin Template Generator
801
+ """
802
+ def __init__(self) -> None:
803
+ """Initialize exporter."""
804
+ pass
805
+
806
+ def export(
807
+ self,
808
+ data: dict[str, NDArray[np.float64]],
809
+ output_path: Path,
810
+ ) -> None:
811
+ """Export data to file.
812
+
813
+ Args:
814
+ data: Dictionary mapping channel names to signal arrays.
815
+ output_path: Path where file will be written.
816
+
817
+ Raises:
818
+ ValueError: If data is invalid.
819
+ OSError: If file cannot be written.
820
+
821
+ Example:
822
+ >>> data = {{"ch1": np.sin(np.linspace(0, 10, 100))}}
823
+ >>> exporter.export(data, Path("output.dat"))
824
+ """
825
+ if not data:
826
+ raise ValueError("Data dictionary cannot be empty")
827
+
828
+ # USER: Implement export logic here
829
+ # Write data to output_path in your custom format
830
+
831
+ # Placeholder implementation - replace with actual export
832
+ with output_path.open("w") as f:
833
+ f.write("# USER: Implement export format\\n")
834
+ for name, values in data.items():
835
+ f.write(f"# Channel: {{name}}, samples: {{len(values)}}\\n")
836
+
837
+ @staticmethod
838
+ def supports_format(format_name: str) -> bool:
839
+ """Check if this exporter supports the format.
840
+
841
+ Args:
842
+ format_name: Name of export format.
843
+
844
+ Returns:
845
+ True if format is supported.
846
+
847
+ Example:
848
+ >>> if exporter.supports_format("custom"):
849
+ ... exporter.export(data, path)
850
+ """
851
+ # USER: Implement format support detection here
852
+ return format_name == "custom"
853
+ ''')
854
+
855
+
856
+ def _generate_generic_stub(template: PluginTemplate, class_name: str) -> str:
857
+ """Generate generic plugin stub.
858
+
859
+ Args:
860
+ template: Plugin template configuration.
861
+ class_name: Class name for the plugin.
862
+
863
+ Returns:
864
+ Python source code for generic stub.
865
+ """
866
+ return textwrap.dedent(f'''\
867
+ """{template.description}
868
+
869
+ This is a generic plugin implementation for Oscura.
870
+
871
+ References:
872
+ PLUG-008: Plugin Template Generator
873
+ """
874
+ from __future__ import annotations
875
+
876
+
877
+ class {class_name}:
878
+ """Generic plugin implementation.
879
+
880
+ Example:
881
+ >>> plugin = {class_name}()
882
+ >>> result = plugin.process()
883
+
884
+ References:
885
+ PLUG-008: Plugin Template Generator
886
+ """
887
+ def __init__(self) -> None:
888
+ """Initialize plugin."""
889
+ pass
890
+
891
+ def process(self) -> dict[str, object]:
892
+ """Process data or perform plugin function.
893
+
894
+ Returns:
895
+ Dictionary containing results.
896
+
897
+ Example:
898
+ >>> result = plugin.process()
899
+ """
900
+ # USER: Implement plugin logic here
901
+ result: dict[str, object] = {{
902
+ "status": "not_implemented",
903
+ }}
904
+
905
+ return result
906
+ ''')
907
+
908
+
909
+ __all__ = [
910
+ "PluginTemplate",
911
+ "PluginType",
912
+ "generate_plugin_template",
913
+ ]