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,458 @@
1
+ """Schema migration system for Oscura configuration files.
2
+
3
+ This module provides schema migration functionality to automatically upgrade
4
+ configuration files between schema versions while preserving user data.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.config.migration import migrate_config, register_migration
9
+ >>> # Register a migration function
10
+ >>> def migrate_1_0_to_1_1(config: dict) -> dict:
11
+ ... config['new_field'] = 'default_value'
12
+ ... return config
13
+ >>> register_migration("1.0.0", "1.1.0", migrate_1_0_to_1_1)
14
+ >>> # Migrate config to latest version
15
+ >>> old_config = {"version": "1.0.0", "name": "test"}
16
+ >>> new_config = migrate_config(old_config)
17
+ >>> print(new_config["version"])
18
+ 1.1.0
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import copy
24
+ from collections.abc import Callable
25
+ from dataclasses import dataclass
26
+ from typing import Any
27
+
28
+ from oscura.core.exceptions import ConfigurationError
29
+
30
+ # Type alias for migration functions
31
+ MigrationFunction = Callable[[dict[str, Any]], dict[str, Any]]
32
+
33
+
34
+ @dataclass
35
+ class Migration:
36
+ """Schema migration definition.
37
+
38
+ Attributes:
39
+ from_version: Source schema version (semver).
40
+ to_version: Target schema version (semver).
41
+ migrate_fn: Function to perform migration.
42
+ description: Human-readable description of changes.
43
+ """
44
+
45
+ from_version: str
46
+ to_version: str
47
+ migrate_fn: MigrationFunction
48
+ description: str = ""
49
+
50
+ def __post_init__(self) -> None:
51
+ """Validate migration after initialization."""
52
+ if not self.from_version:
53
+ raise ValueError("from_version cannot be empty")
54
+ if not self.to_version:
55
+ raise ValueError("to_version cannot be empty")
56
+ if not callable(self.migrate_fn):
57
+ raise ValueError("migrate_fn must be callable")
58
+
59
+
60
+ class SchemaMigration:
61
+ """Schema migration manager with version tracking.
62
+
63
+ Manages registration of migration functions and execution of migration
64
+ paths from older schema versions to newer ones.
65
+
66
+ Supports forward migrations only (not backward) to maintain data integrity.
67
+ Preserves unknown keys during migration to avoid data loss.
68
+
69
+ Example:
70
+ >>> migration = SchemaMigration()
71
+ >>> migration.register_migration("1.0.0", "1.1.0", upgrade_fn)
72
+ >>> config = {"version": "1.0.0", "data": "value"}
73
+ >>> migrated = migration.migrate_config(config, "1.1.0")
74
+ >>> print(migrated["version"])
75
+ 1.1.0
76
+ """
77
+
78
+ def __init__(self) -> None:
79
+ """Initialize empty migration registry."""
80
+ # Map of (from_version, to_version) -> Migration
81
+ self._migrations: dict[tuple[str, str], Migration] = {}
82
+ # Map of from_version -> list of to_versions for path finding
83
+ self._version_graph: dict[str, list[str]] = {}
84
+
85
+ def register_migration(
86
+ self,
87
+ from_version: str,
88
+ to_version: str,
89
+ migrate_fn: MigrationFunction,
90
+ *,
91
+ description: str = "",
92
+ ) -> None:
93
+ """Register a migration function.
94
+
95
+ Args:
96
+ from_version: Source schema version (semver).
97
+ to_version: Target schema version (semver).
98
+ migrate_fn: Function that takes config dict and returns migrated dict.
99
+ description: Human-readable description of migration.
100
+
101
+ Raises:
102
+ ValueError: If migration already registered for this version pair.
103
+
104
+ Example:
105
+ >>> migration = SchemaMigration()
106
+ >>> def upgrade(cfg): return {**cfg, "new_field": "default"}
107
+ >>> migration.register_migration("1.0.0", "1.1.0", upgrade)
108
+ """
109
+ key = (from_version, to_version)
110
+
111
+ if key in self._migrations:
112
+ raise ValueError(f"Migration from {from_version} to {to_version} already registered")
113
+
114
+ mig = Migration(
115
+ from_version=from_version,
116
+ to_version=to_version,
117
+ migrate_fn=migrate_fn,
118
+ description=description,
119
+ )
120
+
121
+ self._migrations[key] = mig
122
+
123
+ # Update graph for path finding
124
+ if from_version not in self._version_graph:
125
+ self._version_graph[from_version] = []
126
+ self._version_graph[from_version].append(to_version)
127
+
128
+ def migrate_config(
129
+ self,
130
+ config: dict[str, Any],
131
+ target_version: str | None = None,
132
+ ) -> dict[str, Any]:
133
+ """Migrate config to target version.
134
+
135
+ Automatically finds migration path and applies migrations in sequence.
136
+ Preserves unknown keys during migration.
137
+
138
+ Args:
139
+ config: Configuration dictionary to migrate.
140
+ target_version: Target schema version. If None, migrate to latest.
141
+
142
+ Returns:
143
+ Migrated configuration dictionary.
144
+
145
+ Raises:
146
+ ConfigurationError: If migration path not found or migration fails.
147
+
148
+ Example:
149
+ >>> migration = SchemaMigration()
150
+ >>> config = {"version": "1.0.0", "name": "test"}
151
+ >>> migrated = migration.migrate_config(config, "2.0.0")
152
+ """
153
+ # Make a deep copy to avoid mutating input
154
+ result = copy.deepcopy(config)
155
+
156
+ # Get current version
157
+ current_version = self.get_config_version(result)
158
+
159
+ # If no version field, add default
160
+ if current_version is None:
161
+ result["version"] = "1.0.0"
162
+ current_version = "1.0.0"
163
+
164
+ # If no target specified, use latest available
165
+ if target_version is None:
166
+ target_version = self._get_latest_version(current_version)
167
+ if target_version is None:
168
+ # No migrations available, return as-is
169
+ return result
170
+
171
+ # Already at target version
172
+ if current_version == target_version:
173
+ return result
174
+
175
+ # Find migration path
176
+ path = self._find_migration_path(current_version, target_version)
177
+
178
+ if path is None:
179
+ available = self.list_migrations()
180
+ raise ConfigurationError(
181
+ f"No migration path from {current_version} to {target_version}",
182
+ details=f"Available migrations: {available}",
183
+ fix_hint=f"Register migrations to connect {current_version} to {target_version}",
184
+ )
185
+
186
+ # Apply migrations in sequence
187
+ for from_ver, to_ver in path:
188
+ migration = self._migrations[(from_ver, to_ver)]
189
+
190
+ try:
191
+ result = migration.migrate_fn(result)
192
+ # Update version field
193
+ result["version"] = to_ver
194
+ except Exception as e:
195
+ raise ConfigurationError(
196
+ f"Migration from {from_ver} to {to_ver} failed",
197
+ details=str(e),
198
+ fix_hint="Check migration function implementation",
199
+ ) from e
200
+
201
+ return result
202
+
203
+ def get_config_version(self, config: dict[str, Any]) -> str | None:
204
+ """Extract version from config.
205
+
206
+ Args:
207
+ config: Configuration dictionary.
208
+
209
+ Returns:
210
+ Version string or None if not present.
211
+
212
+ Example:
213
+ >>> migration = SchemaMigration()
214
+ >>> config = {"version": "1.2.3", "data": "value"}
215
+ >>> migration.get_config_version(config)
216
+ '1.2.3'
217
+ """
218
+ return config.get("version")
219
+
220
+ def list_migrations(self) -> list[tuple[str, str]]:
221
+ """List available migrations.
222
+
223
+ Returns:
224
+ List of (from_version, to_version) tuples.
225
+
226
+ Example:
227
+ >>> migration = SchemaMigration()
228
+ >>> migration.register_migration("1.0.0", "1.1.0", lambda c: c)
229
+ >>> migration.list_migrations()
230
+ [('1.0.0', '1.1.0')]
231
+ """
232
+ return sorted(self._migrations.keys())
233
+
234
+ def has_migration(self, from_version: str, to_version: str) -> bool:
235
+ """Check if migration exists.
236
+
237
+ Args:
238
+ from_version: Source version.
239
+ to_version: Target version.
240
+
241
+ Returns:
242
+ True if migration exists.
243
+ """
244
+ return (from_version, to_version) in self._migrations
245
+
246
+ def _find_migration_path(
247
+ self,
248
+ from_version: str,
249
+ to_version: str,
250
+ ) -> list[tuple[str, str]] | None:
251
+ """Find shortest migration path using BFS.
252
+
253
+ Args:
254
+ from_version: Source version.
255
+ to_version: Target version.
256
+
257
+ Returns:
258
+ List of (from, to) version pairs representing migration path,
259
+ or None if no path exists.
260
+ """
261
+ if from_version == to_version:
262
+ return []
263
+
264
+ # BFS to find shortest path
265
+ queue: list[tuple[str, list[tuple[str, str]]]] = [(from_version, [])]
266
+ visited = {from_version}
267
+
268
+ while queue:
269
+ current, path = queue.pop(0)
270
+
271
+ # Get all possible next versions from current
272
+ if current in self._version_graph:
273
+ for next_version in self._version_graph[current]:
274
+ if next_version in visited:
275
+ continue
276
+
277
+ new_path = [*path, (current, next_version)]
278
+
279
+ if next_version == to_version:
280
+ return new_path
281
+
282
+ visited.add(next_version)
283
+ queue.append((next_version, new_path))
284
+
285
+ return None
286
+
287
+ def _get_latest_version(self, from_version: str) -> str | None:
288
+ """Get latest version reachable from given version.
289
+
290
+ Args:
291
+ from_version: Starting version.
292
+
293
+ Returns:
294
+ Latest version string or None if no migrations available.
295
+ """
296
+ if from_version not in self._version_graph:
297
+ return None
298
+
299
+ # Find all reachable versions
300
+ reachable = set()
301
+ queue = [from_version]
302
+ visited = {from_version}
303
+
304
+ while queue:
305
+ current = queue.pop(0)
306
+
307
+ if current in self._version_graph:
308
+ for next_version in self._version_graph[current]:
309
+ if next_version not in visited:
310
+ visited.add(next_version)
311
+ reachable.add(next_version)
312
+ queue.append(next_version)
313
+
314
+ if not reachable:
315
+ return None
316
+
317
+ # Sort versions and return latest (simple lexicographic sort)
318
+ # For proper semver comparison, could use packaging.version
319
+ return sorted(reachable, key=_parse_version)[-1]
320
+
321
+
322
+ # Global migration registry
323
+ _global_migration: SchemaMigration | None = None
324
+
325
+
326
+ def get_migration_registry() -> SchemaMigration:
327
+ """Get the global migration registry.
328
+
329
+ Initializes with built-in migrations on first call.
330
+
331
+ Returns:
332
+ Global SchemaMigration instance.
333
+ """
334
+ global _global_migration
335
+
336
+ if _global_migration is None:
337
+ _global_migration = SchemaMigration()
338
+ _register_builtin_migrations(_global_migration)
339
+
340
+ return _global_migration
341
+
342
+
343
+ def register_migration(
344
+ from_version: str,
345
+ to_version: str,
346
+ migrate_fn: MigrationFunction,
347
+ *,
348
+ description: str = "",
349
+ ) -> None:
350
+ """Register a migration with the global registry.
351
+
352
+ Args:
353
+ from_version: Source schema version.
354
+ to_version: Target schema version.
355
+ migrate_fn: Migration function.
356
+ description: Human-readable description.
357
+ """
358
+ get_migration_registry().register_migration(
359
+ from_version, to_version, migrate_fn, description=description
360
+ )
361
+
362
+
363
+ def migrate_config(
364
+ config: dict[str, Any],
365
+ target_version: str | None = None,
366
+ ) -> dict[str, Any]:
367
+ """Migrate configuration to target version using global registry.
368
+
369
+ Args:
370
+ config: Configuration to migrate.
371
+ target_version: Target version or None for latest.
372
+
373
+ Returns:
374
+ Migrated configuration.
375
+ """
376
+ return get_migration_registry().migrate_config(config, target_version)
377
+
378
+
379
+ def get_config_version(config: dict[str, Any]) -> str | None:
380
+ """Get version from configuration.
381
+
382
+ Args:
383
+ config: Configuration dictionary.
384
+
385
+ Returns:
386
+ Version string or None.
387
+ """
388
+ return get_migration_registry().get_config_version(config)
389
+
390
+
391
+ def list_migrations() -> list[tuple[str, str]]:
392
+ """List all registered migrations.
393
+
394
+ Returns:
395
+ List of (from_version, to_version) tuples.
396
+ """
397
+ return get_migration_registry().list_migrations()
398
+
399
+
400
+ def _parse_version(version: str) -> tuple[int, ...]:
401
+ """Parse semver version string into tuple for comparison.
402
+
403
+ Args:
404
+ version: Version string (e.g., "1.2.3").
405
+
406
+ Returns:
407
+ Tuple of integers (e.g., (1, 2, 3)).
408
+ """
409
+ try:
410
+ return tuple(int(part) for part in version.split("."))
411
+ except (ValueError, AttributeError):
412
+ # Return (0, 0, 0) for invalid versions
413
+ return (0, 0, 0)
414
+
415
+
416
+ def _register_builtin_migrations(migration: SchemaMigration) -> None:
417
+ """Register built-in migrations for core schemas.
418
+
419
+ Args:
420
+ migration: Migration registry to populate.
421
+ """
422
+
423
+ # Example migration for protocol schema (1.0.0 -> 1.1.0)
424
+ # This is a placeholder - real migrations would be added as needed
425
+ def _migrate_protocol_1_0_to_1_1(config: dict[str, Any]) -> dict[str, Any]:
426
+ """Migrate protocol config from 1.0.0 to 1.1.0.
427
+
428
+ Example migration that preserves all existing fields.
429
+
430
+ Args:
431
+ config: Configuration dictionary to migrate.
432
+
433
+ Returns:
434
+ Migrated configuration dictionary.
435
+ """
436
+ # Keep all existing fields (preserves unknown keys)
437
+ # Add new optional fields with defaults if needed
438
+ return config
439
+
440
+ # Register when actual schema changes are needed
441
+ # migration.register_migration(
442
+ # "1.0.0",
443
+ # "1.1.0",
444
+ # _migrate_protocol_1_0_to_1_1,
445
+ # description="Protocol schema update for new features",
446
+ # )
447
+
448
+
449
+ __all__ = [
450
+ "Migration",
451
+ "MigrationFunction",
452
+ "SchemaMigration",
453
+ "get_config_version",
454
+ "get_migration_registry",
455
+ "list_migrations",
456
+ "migrate_config",
457
+ "register_migration",
458
+ ]