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,290 @@
1
+ """Memory-safe guards for Oscura analysis.
2
+
3
+ This module provides memory guards and resource limiting utilities to prevent
4
+ out-of-memory conditions during analysis operations.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.core.memory_guard import MemoryGuard, check_memory_available
9
+ >>> if check_memory_available(500): # Need at least 500MB
10
+ ... with MemoryGuard(max_mb=1000, name="fft") as guard:
11
+ ... result = compute_fft(data)
12
+ ... if not guard.check():
13
+ ... raise MemoryError("Exceeded memory limit")
14
+
15
+ References:
16
+ See oscura.core.memory_monitor for runtime monitoring.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import logging
22
+ import os
23
+ import sys
24
+ from typing import Any
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ def get_memory_usage_mb() -> float:
30
+ """Get current process memory usage in MB.
31
+
32
+ Returns:
33
+ Current resident set size (RSS) in megabytes.
34
+
35
+ Example:
36
+ >>> mem_mb = get_memory_usage_mb()
37
+ >>> print(f"Current process using {mem_mb:.1f} MB")
38
+ """
39
+ try:
40
+ import psutil
41
+
42
+ process = psutil.Process(os.getpid())
43
+ return float(process.memory_info().rss / (1024 * 1024))
44
+ except ImportError:
45
+ # Fallback for systems without psutil
46
+ logger.debug("psutil not available, memory usage tracking disabled")
47
+ return 0.0
48
+
49
+
50
+ def check_memory_available(required_mb: float = 100) -> bool:
51
+ """Check if sufficient memory is available.
52
+
53
+ Args:
54
+ required_mb: Required available memory in megabytes.
55
+
56
+ Returns:
57
+ True if sufficient memory is available.
58
+
59
+ Example:
60
+ >>> if not check_memory_available(1000):
61
+ ... print("Warning: Less than 1GB available")
62
+ ... # Reduce batch size or chunk operations
63
+ """
64
+ try:
65
+ import psutil
66
+
67
+ available = float(psutil.virtual_memory().available / (1024 * 1024))
68
+ return bool(available > required_mb)
69
+ except ImportError:
70
+ # Assume OK if we can't check
71
+ return True
72
+
73
+
74
+ class MemoryGuard:
75
+ """Context manager for memory-safe operations.
76
+
77
+
78
+ Monitors memory usage within a context and raises warnings/errors
79
+ if limits are exceeded.
80
+
81
+ Attributes:
82
+ max_mb: Maximum memory limit in megabytes.
83
+ name: Operation name for logging.
84
+ start_mem: Starting memory usage (MB).
85
+
86
+ Example:
87
+ >>> with MemoryGuard(max_mb=2000, name="spectrogram") as guard:
88
+ ... # Perform memory-intensive operation
89
+ ... for chunk in data_chunks:
90
+ ... process_chunk(chunk)
91
+ ... if not guard.check():
92
+ ... break # Stop before exceeding limit
93
+ >>> stats = guard.get_stats()
94
+ >>> print(f"Peak: {stats['peak_mb']:.1f} MB, Delta: {stats['delta_mb']:.1f} MB")
95
+ """
96
+
97
+ def __init__(self, max_mb: float = 1000, name: str = "operation"):
98
+ """Initialize memory guard.
99
+
100
+ Args:
101
+ max_mb: Maximum memory increase allowed in megabytes.
102
+ name: Operation name for logging and error messages.
103
+ """
104
+ self.max_mb = max_mb
105
+ self.name = name
106
+ self.start_mem = 0.0
107
+ self._peak_mem = 0.0
108
+
109
+ def __enter__(self) -> MemoryGuard:
110
+ """Enter context and record starting memory."""
111
+ self.start_mem = get_memory_usage_mb()
112
+ self._peak_mem = self.start_mem
113
+ return self
114
+
115
+ def __exit__(
116
+ self,
117
+ exc_type: type[BaseException] | None,
118
+ exc_val: BaseException | None,
119
+ exc_tb: Any,
120
+ ) -> None:
121
+ """Exit context and report memory usage."""
122
+ # Note: exc_val and exc_tb intentionally unused but required for Python 3.11+ compatibility
123
+ end_mem = get_memory_usage_mb()
124
+ delta = end_mem - self.start_mem
125
+
126
+ if delta > self.max_mb:
127
+ logger.warning(
128
+ f"{self.name} used {delta:.1f} MB (limit: {self.max_mb:.1f} MB). "
129
+ f"Consider reducing batch size or enabling chunked processing."
130
+ )
131
+
132
+ # Update peak
133
+ self._peak_mem = max(self._peak_mem, end_mem)
134
+
135
+ def check(self) -> bool:
136
+ """Check if within memory limit.
137
+
138
+ Returns:
139
+ True if within limit, False if limit exceeded.
140
+
141
+ Example:
142
+ >>> with MemoryGuard(max_mb=500) as guard:
143
+ ... for i in range(1000):
144
+ ... # Do work
145
+ ... if i % 100 == 0 and not guard.check():
146
+ ... raise MemoryError("Memory limit exceeded")
147
+ """
148
+ current = get_memory_usage_mb()
149
+ self._peak_mem = max(self._peak_mem, current)
150
+ delta = current - self.start_mem
151
+
152
+ if delta > self.max_mb:
153
+ logger.warning(
154
+ f"{self.name}: Memory usage {delta:.1f} MB exceeds limit {self.max_mb:.1f} MB"
155
+ )
156
+ return False
157
+
158
+ return True
159
+
160
+ def get_stats(self) -> dict[str, float]:
161
+ """Get memory statistics for this guard.
162
+
163
+ Returns:
164
+ Dictionary with keys:
165
+ - start_mb: Starting memory
166
+ - current_mb: Current memory
167
+ - peak_mb: Peak memory
168
+ - delta_mb: Memory increase since start
169
+ - limit_mb: Configured limit
170
+
171
+ Example:
172
+ >>> with MemoryGuard(max_mb=1000, name="test") as guard:
173
+ ... # ... work ...
174
+ ... pass
175
+ >>> stats = guard.get_stats()
176
+ >>> print(f"Used {stats['delta_mb']:.1f} / {stats['limit_mb']:.1f} MB")
177
+ """
178
+ current = get_memory_usage_mb()
179
+ return {
180
+ "start_mb": self.start_mem,
181
+ "current_mb": current,
182
+ "peak_mb": self._peak_mem,
183
+ "delta_mb": current - self.start_mem,
184
+ "limit_mb": self.max_mb,
185
+ }
186
+
187
+
188
+ def safe_array_size(shape: tuple[int, ...], dtype_bytes: int = 8) -> int:
189
+ """Calculate array size in bytes, checking for overflow.
190
+
191
+
192
+ Args:
193
+ shape: Array shape tuple.
194
+ dtype_bytes: Bytes per element (default: 8 for float64).
195
+
196
+ Returns:
197
+ Total array size in bytes.
198
+
199
+ Raises:
200
+ OverflowError: If array size would overflow.
201
+
202
+ Example:
203
+ >>> size = safe_array_size((1000, 1000, 8), dtype_bytes=8)
204
+ >>> print(f"Array would use {size / 1e6:.1f} MB")
205
+ >>> # Check if safe to allocate
206
+ >>> if can_allocate(size):
207
+ ... arr = np.zeros((1000, 1000, 8))
208
+ """
209
+ try:
210
+ import numpy as np
211
+
212
+ total_elements = np.prod(shape)
213
+
214
+ # Check for overflow in element count
215
+ if total_elements > sys.maxsize // dtype_bytes:
216
+ raise OverflowError(f"Array size too large: {shape}")
217
+
218
+ size = int(total_elements) * dtype_bytes
219
+ return size
220
+
221
+ except (OverflowError, ValueError) as e:
222
+ raise OverflowError(f"Array dimensions {shape} would cause overflow") from e
223
+
224
+
225
+ def can_allocate(size_bytes: int) -> bool:
226
+ """Check if allocation is safe given available memory.
227
+
228
+
229
+ Args:
230
+ size_bytes: Requested allocation size in bytes.
231
+
232
+ Returns:
233
+ True if allocation is safe (with 2x safety margin).
234
+
235
+ Example:
236
+ >>> import numpy as np
237
+ >>> shape = (10000, 10000)
238
+ >>> size = safe_array_size(shape, dtype_bytes=8)
239
+ >>> if can_allocate(size):
240
+ ... arr = np.zeros(shape)
241
+ ... else:
242
+ ... print("Not enough memory, use chunked processing")
243
+ """
244
+ size_mb = size_bytes / (1024 * 1024)
245
+
246
+ # Check with 2x safety margin
247
+ return check_memory_available(size_mb * 2)
248
+
249
+
250
+ def get_safe_chunk_size(
251
+ total_samples: int,
252
+ dtype_bytes: int = 8,
253
+ max_chunk_mb: float = 100,
254
+ ) -> int:
255
+ """Calculate safe chunk size for processing large datasets.
256
+
257
+
258
+ Args:
259
+ total_samples: Total number of samples to process.
260
+ dtype_bytes: Bytes per sample (default: 8 for float64).
261
+ max_chunk_mb: Maximum chunk size in megabytes.
262
+
263
+ Returns:
264
+ Chunk size in samples that fits within memory limit.
265
+
266
+ Example:
267
+ >>> total = 1_000_000_000 # 1 billion samples
268
+ >>> chunk_size = get_safe_chunk_size(total, max_chunk_mb=100)
269
+ >>> print(f"Process in chunks of {chunk_size:,} samples")
270
+ >>> for i in range(0, total, chunk_size):
271
+ ... chunk = data[i:i+chunk_size]
272
+ ... process(chunk)
273
+ """
274
+ max_bytes = max_chunk_mb * 1024 * 1024
275
+ max_samples = max_bytes // dtype_bytes
276
+
277
+ # Ensure at least 1000 samples per chunk, but not more than total
278
+ chunk_size = max(1000, min(max_samples, total_samples))
279
+
280
+ return int(chunk_size)
281
+
282
+
283
+ __all__ = [
284
+ "MemoryGuard",
285
+ "can_allocate",
286
+ "check_memory_available",
287
+ "get_memory_usage_mb",
288
+ "get_safe_chunk_size",
289
+ "safe_array_size",
290
+ ]
@@ -0,0 +1,336 @@
1
+ """Per-operation memory limits for Oscura.
2
+
3
+ This module provides fine-grained memory control for individual operations
4
+ with automatic parameter adjustment to fit memory constraints.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.core.memory_limits import apply_memory_limit
9
+ >>> params = apply_memory_limit('spectrogram', samples=1e9, max_memory='512MB')
10
+ >>> print(f"Adjusted nperseg: {params['nperseg']}")
11
+
12
+ References:
13
+ See oscura.config.memory for global memory configuration.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import warnings
19
+ from typing import Any
20
+
21
+ from oscura.config.memory import get_memory_config
22
+ from oscura.utils.memory import estimate_memory
23
+
24
+
25
+ def parse_memory_limit(limit: int | str | None) -> int | None:
26
+ """Parse memory limit from various formats.
27
+
28
+ Args:
29
+ limit: Memory limit as bytes (int), string ("4GB", "512MB"), or None.
30
+
31
+ Returns:
32
+ Memory limit in bytes, or None for no limit.
33
+
34
+ Raises:
35
+ ValueError: If format is invalid.
36
+
37
+ Example:
38
+ >>> parse_memory_limit("4GB")
39
+ 4000000000
40
+ >>> parse_memory_limit(512 * 1024**2)
41
+ 536870912
42
+ >>> parse_memory_limit(None) is None
43
+ True
44
+ """
45
+ if limit is None:
46
+ return None
47
+
48
+ if isinstance(limit, str):
49
+ limit_upper = limit.upper().strip()
50
+ try:
51
+ if limit_upper.endswith("GB"):
52
+ return int(float(limit_upper[:-2]) * 1e9)
53
+ elif limit_upper.endswith("MB"):
54
+ return int(float(limit_upper[:-2]) * 1e6)
55
+ elif limit_upper.endswith("KB"):
56
+ return int(float(limit_upper[:-2]) * 1e3)
57
+ elif limit_upper.endswith("GIB"):
58
+ return int(float(limit_upper[:-3]) * 1024**3)
59
+ elif limit_upper.endswith("MIB"):
60
+ return int(float(limit_upper[:-3]) * 1024**2)
61
+ elif limit_upper.endswith("KIB"):
62
+ return int(float(limit_upper[:-3]) * 1024)
63
+ else:
64
+ return int(float(limit_upper))
65
+ except ValueError as e:
66
+ raise ValueError(f"Invalid memory limit format: {limit}") from e
67
+
68
+ return int(limit)
69
+
70
+
71
+ def apply_memory_limit(
72
+ operation: str,
73
+ samples: int | float,
74
+ *,
75
+ max_memory: int | str | None = None,
76
+ **params: Any,
77
+ ) -> dict[str, Any]:
78
+ """Apply memory limit and adjust parameters to fit.
79
+
80
+
81
+ Args:
82
+ operation: Operation name (fft, psd, spectrogram, etc.).
83
+ samples: Number of samples to process.
84
+ max_memory: Maximum memory limit (overrides global config if provided).
85
+ **params: Operation parameters to adjust.
86
+
87
+ Returns:
88
+ Adjusted parameters dictionary that fits within memory limit.
89
+
90
+ Example:
91
+ >>> params = apply_memory_limit('spectrogram', samples=1e9, max_memory='512MB', nperseg=8192)
92
+ >>> print(f"Adjusted to nperseg={params['nperseg']} to fit 512MB")
93
+
94
+ Note:
95
+ If parameters cannot be adjusted to fit memory, a warning is issued
96
+ and the original parameters are returned.
97
+ """
98
+ # Parse memory limit
99
+ limit_bytes = parse_memory_limit(max_memory)
100
+ if limit_bytes is None:
101
+ # Use global config
102
+ config = get_memory_config()
103
+ limit_bytes = config.max_memory
104
+ if limit_bytes is None:
105
+ # No limit, return params unchanged
106
+ return params
107
+
108
+ samples = int(samples)
109
+
110
+ # Estimate with current parameters
111
+ current_estimate = estimate_memory(operation, samples, **params)
112
+
113
+ if current_estimate.total <= limit_bytes:
114
+ # Already within limit
115
+ return params
116
+
117
+ # Need to adjust parameters
118
+ adjusted_params = params.copy()
119
+
120
+ if operation in ("fft", "psd"):
121
+ # Reduce nfft if specified
122
+ if "nfft" in adjusted_params:
123
+ # Try reducing nfft
124
+ original_nfft = adjusted_params["nfft"]
125
+ # Binary search for suitable nfft
126
+ nfft = _find_max_nfft(operation, samples, limit_bytes, **adjusted_params)
127
+ if nfft < original_nfft:
128
+ adjusted_params["nfft"] = nfft
129
+ warnings.warn(
130
+ f"Reduced nfft from {original_nfft} to {nfft} to fit {limit_bytes / 1e6:.1f} MB limit",
131
+ UserWarning,
132
+ stacklevel=2,
133
+ )
134
+
135
+ elif operation == "spectrogram":
136
+ # Adjust nperseg and/or nfft
137
+ original_nperseg = adjusted_params.get("nperseg", 256)
138
+ original_nfft = adjusted_params.get("nfft", original_nperseg)
139
+
140
+ # Try reducing nperseg first
141
+ nperseg = _find_max_nperseg(samples, limit_bytes, noverlap=adjusted_params.get("noverlap"))
142
+ if nperseg < original_nperseg:
143
+ adjusted_params["nperseg"] = nperseg
144
+ # Adjust noverlap proportionally
145
+ if "noverlap" in adjusted_params:
146
+ overlap_ratio = adjusted_params["noverlap"] / original_nperseg
147
+ adjusted_params["noverlap"] = int(nperseg * overlap_ratio)
148
+ warnings.warn(
149
+ f"Reduced nperseg from {original_nperseg} to {nperseg} to fit {limit_bytes / 1e6:.1f} MB limit",
150
+ UserWarning,
151
+ stacklevel=2,
152
+ )
153
+
154
+ # Also reduce nfft if needed
155
+ if "nfft" in adjusted_params and adjusted_params["nfft"] > nperseg:
156
+ adjusted_params["nfft"] = nperseg
157
+
158
+ elif operation == "eye_diagram":
159
+ # Reduce samples_per_ui or num_uis
160
+ if "num_uis" in adjusted_params:
161
+ original_num_uis = adjusted_params["num_uis"]
162
+ # Calculate max num_uis that fits
163
+ samples_per_ui = adjusted_params.get("samples_per_ui", 100)
164
+ max_num_uis = _find_max_num_uis(limit_bytes, samples_per_ui)
165
+ if max_num_uis < original_num_uis:
166
+ adjusted_params["num_uis"] = max_num_uis
167
+ warnings.warn(
168
+ f"Reduced num_uis from {original_num_uis} to {max_num_uis} to fit {limit_bytes / 1e6:.1f} MB limit",
169
+ UserWarning,
170
+ stacklevel=2,
171
+ )
172
+
173
+ # Verify final estimate
174
+ final_estimate = estimate_memory(operation, samples, **adjusted_params)
175
+ if final_estimate.total > limit_bytes:
176
+ warnings.warn(
177
+ f"Could not adjust parameters to fit {limit_bytes / 1e6:.1f} MB limit. "
178
+ f"Operation requires {final_estimate.total / 1e6:.1f} MB. "
179
+ "Consider using chunked processing or increasing memory limit.",
180
+ UserWarning,
181
+ stacklevel=2,
182
+ )
183
+
184
+ return adjusted_params
185
+
186
+
187
+ def _find_max_nfft(operation: str, samples: int, limit_bytes: int, **params: Any) -> int:
188
+ """Binary search for maximum nfft that fits memory limit.
189
+
190
+ Args:
191
+ operation: Operation name.
192
+ samples: Number of samples.
193
+ limit_bytes: Memory limit in bytes.
194
+ **params: Additional parameters.
195
+
196
+ Returns:
197
+ Maximum nfft that fits within limit.
198
+ """
199
+ min_nfft = 64
200
+ max_nfft = params.get("nfft", 8192)
201
+
202
+ # Binary search
203
+ while min_nfft < max_nfft:
204
+ mid_nfft = (min_nfft + max_nfft + 1) // 2
205
+ test_params = {**params, "nfft": mid_nfft}
206
+ estimate = estimate_memory(operation, samples, **test_params)
207
+
208
+ if estimate.total <= limit_bytes:
209
+ min_nfft = mid_nfft
210
+ else:
211
+ max_nfft = mid_nfft - 1
212
+
213
+ return min_nfft
214
+
215
+
216
+ def _find_max_nperseg(samples: int, limit_bytes: int, noverlap: int | None = None) -> int:
217
+ """Binary search for maximum nperseg that fits memory limit.
218
+
219
+ Args:
220
+ samples: Number of samples.
221
+ limit_bytes: Memory limit in bytes.
222
+ noverlap: Overlap samples (if specified).
223
+
224
+ Returns:
225
+ Maximum nperseg that fits within limit.
226
+ """
227
+ min_nperseg = 64
228
+ max_nperseg = min(8192, samples // 4)
229
+
230
+ # Binary search
231
+ while min_nperseg < max_nperseg:
232
+ mid_nperseg = (min_nperseg + max_nperseg + 1) // 2
233
+
234
+ # Calculate memory for this nperseg
235
+ hop = mid_nperseg - (noverlap or mid_nperseg // 2)
236
+ num_segments = max(1, (samples - (noverlap or mid_nperseg // 2)) // hop)
237
+
238
+ # Estimate memory
239
+ bytes_per_sample = 8 # float64
240
+ data_mem = samples * bytes_per_sample
241
+ intermediate_mem = mid_nperseg * bytes_per_sample * 2 + mid_nperseg * bytes_per_sample * 2
242
+ output_mem = (mid_nperseg // 2 + 1) * num_segments * bytes_per_sample * 2
243
+
244
+ total_mem = data_mem + intermediate_mem + output_mem
245
+
246
+ if total_mem <= limit_bytes:
247
+ min_nperseg = mid_nperseg
248
+ else:
249
+ max_nperseg = mid_nperseg - 1
250
+
251
+ return min_nperseg
252
+
253
+
254
+ def _find_max_num_uis(limit_bytes: int, samples_per_ui: int) -> int:
255
+ """Find maximum num_uis that fits memory limit for eye diagrams.
256
+
257
+ Args:
258
+ limit_bytes: Memory limit in bytes.
259
+ samples_per_ui: Samples per unit interval.
260
+
261
+ Returns:
262
+ Maximum num_uis that fits.
263
+ """
264
+ bytes_per_sample = 8 # float64
265
+ # Eye diagram memory: samples_per_ui * num_uis * bytes_per_sample
266
+ max_num_uis = limit_bytes // (samples_per_ui * bytes_per_sample * 2)
267
+ return max(100, int(max_num_uis)) # At least 100 UIs
268
+
269
+
270
+ def get_operation_memory_limit(
271
+ operation: str,
272
+ max_memory: int | str | None = None,
273
+ ) -> int:
274
+ """Get effective memory limit for an operation.
275
+
276
+ Args:
277
+ operation: Operation name.
278
+ max_memory: Override limit (or None for global config).
279
+
280
+ Returns:
281
+ Memory limit in bytes.
282
+
283
+ Example:
284
+ >>> limit = get_operation_memory_limit('spectrogram', max_memory='512MB')
285
+ >>> print(f"Limit: {limit / 1e6:.1f} MB")
286
+ """
287
+ # Parse override
288
+ limit_bytes = parse_memory_limit(max_memory)
289
+ if limit_bytes is not None:
290
+ return limit_bytes
291
+
292
+ # Use global config
293
+ config = get_memory_config()
294
+ if config.max_memory is not None:
295
+ return config.max_memory
296
+
297
+ # Default: 80% of available
298
+ from oscura.utils.memory import get_available_memory
299
+
300
+ return int(get_available_memory() * 0.8)
301
+
302
+
303
+ def check_operation_fits(
304
+ operation: str,
305
+ samples: int | float,
306
+ *,
307
+ max_memory: int | str | None = None,
308
+ **params: Any,
309
+ ) -> bool:
310
+ """Check if operation with given parameters fits within memory limit.
311
+
312
+ Args:
313
+ operation: Operation name.
314
+ samples: Number of samples.
315
+ max_memory: Memory limit (or None for global config).
316
+ **params: Operation parameters.
317
+
318
+ Returns:
319
+ True if operation fits within limit.
320
+
321
+ Example:
322
+ >>> fits = check_operation_fits('fft', samples=1e9, max_memory='4GB', nfft=8192)
323
+ >>> if not fits:
324
+ ... print("FFT too large for 4GB limit")
325
+ """
326
+ limit_bytes = get_operation_memory_limit(operation, max_memory)
327
+ estimate = estimate_memory(operation, samples, **params)
328
+ return estimate.total <= limit_bytes
329
+
330
+
331
+ __all__ = [
332
+ "apply_memory_limit",
333
+ "check_operation_fits",
334
+ "get_operation_memory_limit",
335
+ "parse_memory_limit",
336
+ ]