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,465 @@
1
+ """Extended memory management utilities for Oscura.
2
+
3
+ Additional memory management features including context managers,
4
+ LRU caching, and HDF5 lazy loading support.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.utils.memory_extensions import ResourceManager, LRUCache
9
+ >>> with ResourceManager(large_array) as data:
10
+ ... result = process(data)
11
+ >>> # Data automatically cleaned up
12
+
13
+ References:
14
+ Python resource management patterns
15
+ functools.lru_cache documentation
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import gc
21
+ import hashlib
22
+ import os
23
+ import time
24
+ from collections import OrderedDict
25
+ from typing import TYPE_CHECKING, Any, TypeVar
26
+
27
+ import numpy as np
28
+
29
+ if TYPE_CHECKING:
30
+ from collections.abc import Callable
31
+
32
+ from numpy.typing import NDArray
33
+
34
+ T = TypeVar("T")
35
+
36
+
37
+ # =============================================================================
38
+ # Context Managers for Resource Cleanup (MEM-019)
39
+ # =============================================================================
40
+
41
+
42
+ class ResourceManager:
43
+ """Context manager for large data resources with automatic cleanup.
44
+
45
+ Ensures prompt memory release when done with large datasets.
46
+
47
+ Args:
48
+ resource: Resource to manage (array, file handle, etc.).
49
+ cleanup_func: Optional cleanup function to call on exit.
50
+
51
+ Example:
52
+ >>> import numpy as np
53
+ >>> with ResourceManager(np.zeros(1000000)) as data:
54
+ ... result = process(data)
55
+ >>> # Data is automatically released
56
+
57
+ References:
58
+ MEM-019: Explicit Resource Cleanup
59
+ """
60
+
61
+ def __init__(
62
+ self,
63
+ resource: Any,
64
+ cleanup_func: Callable[[Any], None] | None = None,
65
+ ) -> None:
66
+ self._resource = resource
67
+ self._cleanup_func = cleanup_func
68
+
69
+ def __enter__(self) -> Any:
70
+ """Enter context, return resource."""
71
+ return self._resource
72
+
73
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
74
+ """Exit context, cleanup resource."""
75
+ # Note: exc_val and exc_tb intentionally unused but required for Python 3.11+ compatibility
76
+ if self._cleanup_func is not None:
77
+ self._cleanup_func(self._resource)
78
+
79
+ # Delete reference
80
+ self._resource = None
81
+
82
+ # Force garbage collection
83
+ gc.collect()
84
+
85
+
86
+ class ArrayManager(ResourceManager):
87
+ """Context manager specifically for numpy arrays.
88
+
89
+ Example:
90
+ >>> import numpy as np
91
+ >>> with ArrayManager(np.zeros((10000, 10000))) as arr:
92
+ ... result = np.sum(arr)
93
+ >>> # Array memory is released
94
+ """
95
+
96
+ def __init__(self, array: NDArray[Any]) -> None:
97
+ super().__init__(array, cleanup_func=lambda x: None) # noqa: ARG005
98
+
99
+
100
+ # =============================================================================
101
+ # LRU Cache for Intermediate Results (MEM-021, MEM-029)
102
+ # =============================================================================
103
+
104
+
105
+ class LRUCache[T]:
106
+ """Least-Recently-Used cache with memory-based eviction.
107
+
108
+ Caches intermediate results with automatic eviction when
109
+ memory limit is exceeded.
110
+
111
+ Args:
112
+ max_memory_bytes: Maximum cache size in bytes.
113
+ max_entries: Maximum number of cache entries (default unlimited).
114
+
115
+ Example:
116
+ >>> cache = LRUCache(max_memory_bytes=1_000_000_000) # 1 GB
117
+ >>> cache.put("key1", large_array, size_bytes=800_000_000)
118
+ >>> result = cache.get("key1")
119
+ >>> cache.clear()
120
+
121
+ References:
122
+ MEM-021: Intermediate Result Eviction
123
+ MEM-029: LRU Cache for Intermediate Results
124
+ """
125
+
126
+ def __init__(
127
+ self,
128
+ max_memory_bytes: int,
129
+ max_entries: int | None = None,
130
+ ) -> None:
131
+ self._max_memory = max_memory_bytes
132
+ self._max_entries = max_entries
133
+ self._cache: OrderedDict[str, tuple[T, int, float]] = OrderedDict()
134
+ self._current_memory: int = 0
135
+ self._hits: int = 0
136
+ self._misses: int = 0
137
+
138
+ def get(self, key: str) -> T | None:
139
+ """Get cached value by key.
140
+
141
+ Args:
142
+ key: Cache key.
143
+
144
+ Returns:
145
+ Cached value or None if not found.
146
+ """
147
+ if key in self._cache:
148
+ # Move to end (most recently used)
149
+ value, size, _ = self._cache.pop(key)
150
+ self._cache[key] = (value, size, time.time())
151
+ self._hits += 1
152
+ return value
153
+ else:
154
+ self._misses += 1
155
+ return None
156
+
157
+ def put(self, key: str, value: T, size_bytes: int | None = None) -> None:
158
+ """Put value in cache.
159
+
160
+ Args:
161
+ key: Cache key.
162
+ value: Value to cache.
163
+ size_bytes: Size in bytes. If None, estimated from value.
164
+ """
165
+ # Estimate size if not provided
166
+ if size_bytes is None:
167
+ size_bytes = self._estimate_size(value)
168
+
169
+ # Check if single item exceeds max memory
170
+ if size_bytes > self._max_memory:
171
+ # Don't cache items larger than max memory
172
+ return
173
+
174
+ # Evict until we have space
175
+ while (
176
+ self._current_memory + size_bytes > self._max_memory
177
+ or (self._max_entries and len(self._cache) >= self._max_entries)
178
+ ) and len(self._cache) > 0:
179
+ self._evict_oldest()
180
+
181
+ # Remove if key already exists
182
+ if key in self._cache:
183
+ _, old_size, _ = self._cache.pop(key)
184
+ self._current_memory -= old_size
185
+
186
+ # Add new entry
187
+ self._cache[key] = (value, size_bytes, time.time())
188
+ self._current_memory += size_bytes
189
+
190
+ def _evict_oldest(self) -> None:
191
+ """Evict least recently used entry."""
192
+ if len(self._cache) > 0:
193
+ _, (_, size, _) = self._cache.popitem(last=False)
194
+ self._current_memory -= size
195
+
196
+ def clear(self) -> None:
197
+ """Clear entire cache."""
198
+ self._cache.clear()
199
+ self._current_memory = 0
200
+
201
+ def _estimate_size(self, value: T) -> int:
202
+ """Estimate size of cached value."""
203
+ if isinstance(value, np.ndarray):
204
+ return int(value.nbytes)
205
+ elif isinstance(value, list | tuple):
206
+ # Rough estimate for sequences
207
+ return sum(self._estimate_size(item) for item in value)
208
+ elif isinstance(value, dict):
209
+ return sum(self._estimate_size(k) + self._estimate_size(v) for k, v in value.items())
210
+ else:
211
+ # Fallback: assume 1KB for unknown types
212
+ return 1024
213
+
214
+ def stats(self) -> dict[str, int | float]:
215
+ """Get cache statistics.
216
+
217
+ Returns:
218
+ Dictionary with cache stats.
219
+ """
220
+ total_requests = self._hits + self._misses
221
+ hit_rate = self._hits / total_requests if total_requests > 0 else 0.0
222
+
223
+ return {
224
+ "entries": len(self._cache),
225
+ "memory_bytes": self._current_memory,
226
+ "memory_mb": self._current_memory / (1024 * 1024),
227
+ "hits": self._hits,
228
+ "misses": self._misses,
229
+ "hit_rate": hit_rate,
230
+ }
231
+
232
+ def __len__(self) -> int:
233
+ """Number of cached entries."""
234
+ return len(self._cache)
235
+
236
+
237
+ # Global result cache
238
+ _result_cache: LRUCache[Any] | None = None
239
+
240
+
241
+ def get_result_cache() -> LRUCache[Any]:
242
+ """Get global result cache.
243
+
244
+ Returns:
245
+ Global LRU cache instance.
246
+
247
+ Example:
248
+ >>> cache = get_result_cache()
249
+ >>> cache.put("fft_result", fft_data, size_bytes=8000000)
250
+ >>> result = cache.get("fft_result")
251
+ """
252
+ global _result_cache
253
+ if _result_cache is None:
254
+ # Default: 2 GB cache
255
+ max_cache_size = int(os.environ.get("TK_CACHE_SIZE", 2 * 1024 * 1024 * 1024)) # noqa: PLW1508
256
+ _result_cache = LRUCache(max_memory_bytes=max_cache_size)
257
+ return _result_cache
258
+
259
+
260
+ def clear_cache() -> None:
261
+ """Clear the global result cache.
262
+
263
+ Example:
264
+ >>> clear_cache()
265
+ >>> # All cached results released
266
+ """
267
+ cache = get_result_cache()
268
+ cache.clear()
269
+
270
+
271
+ def show_cache_stats() -> dict[str, int | float]:
272
+ """Show statistics for the global cache.
273
+
274
+ Returns:
275
+ Dictionary with cache statistics.
276
+
277
+ Example:
278
+ >>> stats = show_cache_stats()
279
+ >>> print(f"Hit rate: {stats['hit_rate']*100:.1f}%")
280
+ >>> print(f"Memory used: {stats['memory_mb']:.1f} MB")
281
+ """
282
+ cache = get_result_cache()
283
+ return cache.stats()
284
+
285
+
286
+ def cache_key(*args: Any, **kwargs: Any) -> str:
287
+ """Generate cache key from arguments.
288
+
289
+ Args:
290
+ *args: Positional arguments.
291
+ **kwargs: Keyword arguments.
292
+
293
+ Returns:
294
+ Hash-based cache key.
295
+
296
+ Example:
297
+ >>> key = cache_key("fft", samples=1000, nfft=2048)
298
+ >>> # Use key for caching
299
+ """
300
+ # Create stable string representation
301
+ parts = [str(arg) for arg in args]
302
+ parts.extend(f"{k}={v}" for k, v in sorted(kwargs.items()))
303
+ key_str = "|".join(parts)
304
+
305
+ # Hash for consistent key
306
+ return hashlib.md5(key_str.encode()).hexdigest()
307
+
308
+
309
+ # =============================================================================
310
+ # HDF5 Lazy Loading (MEM-017)
311
+ # =============================================================================
312
+
313
+
314
+ def load_hdf5_lazy(
315
+ file_path: str,
316
+ dataset_path: str = "/data",
317
+ ) -> Any:
318
+ """Load HDF5 dataset as lazy h5py.Dataset (not fully in memory).
319
+
320
+ Enables partial loading via slicing without loading entire dataset.
321
+
322
+ Args:
323
+ file_path: Path to HDF5 file.
324
+ dataset_path: Path to dataset within file (default "/data").
325
+
326
+ Returns:
327
+ h5py.Dataset object (lazy, not loaded until accessed).
328
+
329
+ Raises:
330
+ ImportError: If h5py is not available.
331
+ FileNotFoundError: If file does not exist.
332
+ KeyError: If dataset not found in file.
333
+
334
+ Example:
335
+ >>> # Load dataset lazily
336
+ >>> dataset = load_hdf5_lazy("large_file.h5", "/signals/ch1")
337
+ >>> # Only load specific range (not entire file)
338
+ >>> chunk = dataset[1000:2000]
339
+ >>> print(f"Chunk shape: {chunk.shape}")
340
+
341
+ References:
342
+ MEM-017: HDF5 Chunked Dataset Access
343
+ """
344
+ try:
345
+ import h5py
346
+ except ImportError:
347
+ raise ImportError( # noqa: B904
348
+ "h5py required for lazy HDF5 loading. Install with: pip install h5py"
349
+ )
350
+
351
+ from pathlib import Path
352
+
353
+ path = Path(file_path)
354
+ if not path.exists():
355
+ raise FileNotFoundError(f"File not found: {file_path}")
356
+
357
+ # Open file in read mode
358
+ # Note: File handle should be kept open for lazy access
359
+ # User is responsible for closing the file handle
360
+ f = h5py.File(file_path, "r")
361
+
362
+ if dataset_path not in f:
363
+ available = list(f.keys())
364
+ f.close()
365
+ raise KeyError(
366
+ f"Dataset '{dataset_path}' not found in HDF5 file. "
367
+ f"Available datasets: {', '.join(available)}"
368
+ )
369
+
370
+ dataset = f[dataset_path]
371
+
372
+ return dataset
373
+
374
+
375
+ class LazyHDF5Array:
376
+ """Wrapper for lazy HDF5 dataset access with context management.
377
+
378
+ Provides automatic file handle cleanup and numpy-like slicing.
379
+
380
+ Args:
381
+ file_path: Path to HDF5 file.
382
+ dataset_path: Path to dataset within file.
383
+
384
+ Example:
385
+ >>> with LazyHDF5Array("data.h5", "/signals/ch1") as arr:
386
+ ... # Only loads specific slice
387
+ ... chunk = arr[1000:2000]
388
+ ... print(f"Shape: {arr.shape}, dtype: {arr.dtype}")
389
+ >>> # File automatically closed
390
+
391
+ References:
392
+ MEM-017: HDF5 Chunked Dataset Access
393
+ MEM-019: Explicit Resource Cleanup
394
+ """
395
+
396
+ def __init__(self, file_path: str, dataset_path: str = "/data"):
397
+ self._file_path = file_path
398
+ self._dataset_path = dataset_path
399
+ self._file = None
400
+ self._dataset = None
401
+
402
+ def __enter__(self) -> LazyHDF5Array:
403
+ """Open HDF5 file and dataset."""
404
+ try:
405
+ import h5py
406
+ except ImportError:
407
+ raise ImportError("h5py required. Install with: pip install h5py") # noqa: B904
408
+
409
+ self._file = h5py.File(self._file_path, "r")
410
+ self._dataset = self._file[self._dataset_path] # type: ignore[index]
411
+ return self
412
+
413
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
414
+ """Close HDF5 file."""
415
+ # Note: exc_val and exc_tb intentionally unused but required for Python 3.11+ compatibility
416
+ if self._file is not None:
417
+ self._file.close() # type: ignore[unreachable]
418
+ self._file = None
419
+ self._dataset = None
420
+
421
+ def __getitem__(self, key: Any) -> NDArray[Any]:
422
+ """Get item/slice from dataset (triggers partial load)."""
423
+ if self._dataset is None:
424
+ raise RuntimeError("LazyHDF5Array must be used as context manager")
425
+ return np.asarray(self._dataset[key]) # type: ignore[unreachable]
426
+
427
+ @property
428
+ def shape(self) -> tuple[int, ...]:
429
+ """Dataset shape."""
430
+ if self._dataset is None:
431
+ raise RuntimeError("LazyHDF5Array must be used as context manager")
432
+ return self._dataset.shape # type: ignore[unreachable]
433
+
434
+ @property
435
+ def dtype(self) -> np.dtype[Any]:
436
+ """Dataset dtype."""
437
+ if self._dataset is None:
438
+ raise RuntimeError("LazyHDF5Array must be used as context manager")
439
+ return self._dataset.dtype # type: ignore[unreachable]
440
+
441
+ @property
442
+ def size(self) -> int:
443
+ """Total number of elements."""
444
+ if self._dataset is None:
445
+ raise RuntimeError("LazyHDF5Array must be used as context manager")
446
+ return self._dataset.size # type: ignore[unreachable]
447
+
448
+ def __len__(self) -> int:
449
+ """Length of first dimension."""
450
+ if self._dataset is None:
451
+ raise RuntimeError("LazyHDF5Array must be used as context manager")
452
+ return len(self._dataset) # type: ignore[unreachable]
453
+
454
+
455
+ __all__ = [
456
+ "ArrayManager",
457
+ "LRUCache",
458
+ "LazyHDF5Array",
459
+ "ResourceManager",
460
+ "cache_key",
461
+ "clear_cache",
462
+ "get_result_cache",
463
+ "load_hdf5_lazy",
464
+ "show_cache_stats",
465
+ ]