oscura 0.0.1__py3-none-any.whl → 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oscura/__init__.py +813 -8
- oscura/__main__.py +392 -0
- oscura/analyzers/__init__.py +37 -0
- oscura/analyzers/digital/__init__.py +177 -0
- oscura/analyzers/digital/bus.py +691 -0
- oscura/analyzers/digital/clock.py +805 -0
- oscura/analyzers/digital/correlation.py +720 -0
- oscura/analyzers/digital/edges.py +632 -0
- oscura/analyzers/digital/extraction.py +413 -0
- oscura/analyzers/digital/quality.py +878 -0
- oscura/analyzers/digital/signal_quality.py +877 -0
- oscura/analyzers/digital/thresholds.py +708 -0
- oscura/analyzers/digital/timing.py +1104 -0
- oscura/analyzers/eye/__init__.py +46 -0
- oscura/analyzers/eye/diagram.py +434 -0
- oscura/analyzers/eye/metrics.py +555 -0
- oscura/analyzers/jitter/__init__.py +83 -0
- oscura/analyzers/jitter/ber.py +333 -0
- oscura/analyzers/jitter/decomposition.py +759 -0
- oscura/analyzers/jitter/measurements.py +413 -0
- oscura/analyzers/jitter/spectrum.py +220 -0
- oscura/analyzers/measurements.py +40 -0
- oscura/analyzers/packet/__init__.py +171 -0
- oscura/analyzers/packet/daq.py +1077 -0
- oscura/analyzers/packet/metrics.py +437 -0
- oscura/analyzers/packet/parser.py +327 -0
- oscura/analyzers/packet/payload.py +2156 -0
- oscura/analyzers/packet/payload_analysis.py +1312 -0
- oscura/analyzers/packet/payload_extraction.py +236 -0
- oscura/analyzers/packet/payload_patterns.py +670 -0
- oscura/analyzers/packet/stream.py +359 -0
- oscura/analyzers/patterns/__init__.py +266 -0
- oscura/analyzers/patterns/clustering.py +1036 -0
- oscura/analyzers/patterns/discovery.py +539 -0
- oscura/analyzers/patterns/learning.py +797 -0
- oscura/analyzers/patterns/matching.py +1091 -0
- oscura/analyzers/patterns/periodic.py +650 -0
- oscura/analyzers/patterns/sequences.py +767 -0
- oscura/analyzers/power/__init__.py +116 -0
- oscura/analyzers/power/ac_power.py +391 -0
- oscura/analyzers/power/basic.py +383 -0
- oscura/analyzers/power/conduction.py +314 -0
- oscura/analyzers/power/efficiency.py +297 -0
- oscura/analyzers/power/ripple.py +356 -0
- oscura/analyzers/power/soa.py +372 -0
- oscura/analyzers/power/switching.py +479 -0
- oscura/analyzers/protocol/__init__.py +150 -0
- oscura/analyzers/protocols/__init__.py +150 -0
- oscura/analyzers/protocols/base.py +500 -0
- oscura/analyzers/protocols/can.py +620 -0
- oscura/analyzers/protocols/can_fd.py +448 -0
- oscura/analyzers/protocols/flexray.py +405 -0
- oscura/analyzers/protocols/hdlc.py +399 -0
- oscura/analyzers/protocols/i2c.py +368 -0
- oscura/analyzers/protocols/i2s.py +296 -0
- oscura/analyzers/protocols/jtag.py +393 -0
- oscura/analyzers/protocols/lin.py +445 -0
- oscura/analyzers/protocols/manchester.py +333 -0
- oscura/analyzers/protocols/onewire.py +501 -0
- oscura/analyzers/protocols/spi.py +334 -0
- oscura/analyzers/protocols/swd.py +325 -0
- oscura/analyzers/protocols/uart.py +393 -0
- oscura/analyzers/protocols/usb.py +495 -0
- oscura/analyzers/signal_integrity/__init__.py +63 -0
- oscura/analyzers/signal_integrity/embedding.py +294 -0
- oscura/analyzers/signal_integrity/equalization.py +370 -0
- oscura/analyzers/signal_integrity/sparams.py +484 -0
- oscura/analyzers/spectral/__init__.py +53 -0
- oscura/analyzers/spectral/chunked.py +273 -0
- oscura/analyzers/spectral/chunked_fft.py +571 -0
- oscura/analyzers/spectral/chunked_wavelet.py +391 -0
- oscura/analyzers/spectral/fft.py +92 -0
- oscura/analyzers/statistical/__init__.py +250 -0
- oscura/analyzers/statistical/checksum.py +923 -0
- oscura/analyzers/statistical/chunked_corr.py +228 -0
- oscura/analyzers/statistical/classification.py +778 -0
- oscura/analyzers/statistical/entropy.py +1113 -0
- oscura/analyzers/statistical/ngrams.py +614 -0
- oscura/analyzers/statistics/__init__.py +119 -0
- oscura/analyzers/statistics/advanced.py +885 -0
- oscura/analyzers/statistics/basic.py +263 -0
- oscura/analyzers/statistics/correlation.py +630 -0
- oscura/analyzers/statistics/distribution.py +298 -0
- oscura/analyzers/statistics/outliers.py +463 -0
- oscura/analyzers/statistics/streaming.py +93 -0
- oscura/analyzers/statistics/trend.py +520 -0
- oscura/analyzers/validation.py +598 -0
- oscura/analyzers/waveform/__init__.py +36 -0
- oscura/analyzers/waveform/measurements.py +943 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
- oscura/analyzers/waveform/spectral.py +1689 -0
- oscura/analyzers/waveform/wavelets.py +298 -0
- oscura/api/__init__.py +62 -0
- oscura/api/dsl.py +538 -0
- oscura/api/fluent.py +571 -0
- oscura/api/operators.py +498 -0
- oscura/api/optimization.py +392 -0
- oscura/api/profiling.py +396 -0
- oscura/automotive/__init__.py +73 -0
- oscura/automotive/can/__init__.py +52 -0
- oscura/automotive/can/analysis.py +356 -0
- oscura/automotive/can/checksum.py +250 -0
- oscura/automotive/can/correlation.py +212 -0
- oscura/automotive/can/discovery.py +355 -0
- oscura/automotive/can/message_wrapper.py +375 -0
- oscura/automotive/can/models.py +385 -0
- oscura/automotive/can/patterns.py +381 -0
- oscura/automotive/can/session.py +452 -0
- oscura/automotive/can/state_machine.py +300 -0
- oscura/automotive/can/stimulus_response.py +461 -0
- oscura/automotive/dbc/__init__.py +15 -0
- oscura/automotive/dbc/generator.py +156 -0
- oscura/automotive/dbc/parser.py +146 -0
- oscura/automotive/dtc/__init__.py +30 -0
- oscura/automotive/dtc/database.py +3036 -0
- oscura/automotive/j1939/__init__.py +14 -0
- oscura/automotive/j1939/decoder.py +745 -0
- oscura/automotive/loaders/__init__.py +35 -0
- oscura/automotive/loaders/asc.py +98 -0
- oscura/automotive/loaders/blf.py +77 -0
- oscura/automotive/loaders/csv_can.py +136 -0
- oscura/automotive/loaders/dispatcher.py +136 -0
- oscura/automotive/loaders/mdf.py +331 -0
- oscura/automotive/loaders/pcap.py +132 -0
- oscura/automotive/obd/__init__.py +14 -0
- oscura/automotive/obd/decoder.py +707 -0
- oscura/automotive/uds/__init__.py +48 -0
- oscura/automotive/uds/decoder.py +265 -0
- oscura/automotive/uds/models.py +64 -0
- oscura/automotive/visualization.py +369 -0
- oscura/batch/__init__.py +55 -0
- oscura/batch/advanced.py +627 -0
- oscura/batch/aggregate.py +300 -0
- oscura/batch/analyze.py +139 -0
- oscura/batch/logging.py +487 -0
- oscura/batch/metrics.py +556 -0
- oscura/builders/__init__.py +41 -0
- oscura/builders/signal_builder.py +1131 -0
- oscura/cli/__init__.py +14 -0
- oscura/cli/batch.py +339 -0
- oscura/cli/characterize.py +273 -0
- oscura/cli/compare.py +775 -0
- oscura/cli/decode.py +551 -0
- oscura/cli/main.py +247 -0
- oscura/cli/shell.py +350 -0
- oscura/comparison/__init__.py +66 -0
- oscura/comparison/compare.py +397 -0
- oscura/comparison/golden.py +487 -0
- oscura/comparison/limits.py +391 -0
- oscura/comparison/mask.py +434 -0
- oscura/comparison/trace_diff.py +30 -0
- oscura/comparison/visualization.py +481 -0
- oscura/compliance/__init__.py +70 -0
- oscura/compliance/advanced.py +756 -0
- oscura/compliance/masks.py +363 -0
- oscura/compliance/reporting.py +483 -0
- oscura/compliance/testing.py +298 -0
- oscura/component/__init__.py +38 -0
- oscura/component/impedance.py +365 -0
- oscura/component/reactive.py +598 -0
- oscura/component/transmission_line.py +312 -0
- oscura/config/__init__.py +191 -0
- oscura/config/defaults.py +254 -0
- oscura/config/loader.py +348 -0
- oscura/config/memory.py +271 -0
- oscura/config/migration.py +458 -0
- oscura/config/pipeline.py +1077 -0
- oscura/config/preferences.py +530 -0
- oscura/config/protocol.py +875 -0
- oscura/config/schema.py +713 -0
- oscura/config/settings.py +420 -0
- oscura/config/thresholds.py +599 -0
- oscura/convenience.py +457 -0
- oscura/core/__init__.py +299 -0
- oscura/core/audit.py +457 -0
- oscura/core/backend_selector.py +405 -0
- oscura/core/cache.py +590 -0
- oscura/core/cancellation.py +439 -0
- oscura/core/confidence.py +225 -0
- oscura/core/config.py +506 -0
- oscura/core/correlation.py +216 -0
- oscura/core/cross_domain.py +422 -0
- oscura/core/debug.py +301 -0
- oscura/core/edge_cases.py +541 -0
- oscura/core/exceptions.py +535 -0
- oscura/core/gpu_backend.py +523 -0
- oscura/core/lazy.py +832 -0
- oscura/core/log_query.py +540 -0
- oscura/core/logging.py +931 -0
- oscura/core/logging_advanced.py +952 -0
- oscura/core/memoize.py +171 -0
- oscura/core/memory_check.py +274 -0
- oscura/core/memory_guard.py +290 -0
- oscura/core/memory_limits.py +336 -0
- oscura/core/memory_monitor.py +453 -0
- oscura/core/memory_progress.py +465 -0
- oscura/core/memory_warnings.py +315 -0
- oscura/core/numba_backend.py +362 -0
- oscura/core/performance.py +352 -0
- oscura/core/progress.py +524 -0
- oscura/core/provenance.py +358 -0
- oscura/core/results.py +331 -0
- oscura/core/types.py +504 -0
- oscura/core/uncertainty.py +383 -0
- oscura/discovery/__init__.py +52 -0
- oscura/discovery/anomaly_detector.py +672 -0
- oscura/discovery/auto_decoder.py +415 -0
- oscura/discovery/comparison.py +497 -0
- oscura/discovery/quality_validator.py +528 -0
- oscura/discovery/signal_detector.py +769 -0
- oscura/dsl/__init__.py +73 -0
- oscura/dsl/commands.py +246 -0
- oscura/dsl/interpreter.py +455 -0
- oscura/dsl/parser.py +689 -0
- oscura/dsl/repl.py +172 -0
- oscura/exceptions.py +59 -0
- oscura/exploratory/__init__.py +111 -0
- oscura/exploratory/error_recovery.py +642 -0
- oscura/exploratory/fuzzy.py +513 -0
- oscura/exploratory/fuzzy_advanced.py +786 -0
- oscura/exploratory/legacy.py +831 -0
- oscura/exploratory/parse.py +358 -0
- oscura/exploratory/recovery.py +275 -0
- oscura/exploratory/sync.py +382 -0
- oscura/exploratory/unknown.py +707 -0
- oscura/export/__init__.py +25 -0
- oscura/export/wireshark/README.md +265 -0
- oscura/export/wireshark/__init__.py +47 -0
- oscura/export/wireshark/generator.py +312 -0
- oscura/export/wireshark/lua_builder.py +159 -0
- oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
- oscura/export/wireshark/type_mapping.py +165 -0
- oscura/export/wireshark/validator.py +105 -0
- oscura/exporters/__init__.py +94 -0
- oscura/exporters/csv.py +303 -0
- oscura/exporters/exporters.py +44 -0
- oscura/exporters/hdf5.py +219 -0
- oscura/exporters/html_export.py +701 -0
- oscura/exporters/json_export.py +291 -0
- oscura/exporters/markdown_export.py +367 -0
- oscura/exporters/matlab_export.py +354 -0
- oscura/exporters/npz_export.py +219 -0
- oscura/exporters/spice_export.py +210 -0
- oscura/extensibility/__init__.py +131 -0
- oscura/extensibility/docs.py +752 -0
- oscura/extensibility/extensions.py +1125 -0
- oscura/extensibility/logging.py +259 -0
- oscura/extensibility/measurements.py +485 -0
- oscura/extensibility/plugins.py +414 -0
- oscura/extensibility/registry.py +346 -0
- oscura/extensibility/templates.py +913 -0
- oscura/extensibility/validation.py +651 -0
- oscura/filtering/__init__.py +89 -0
- oscura/filtering/base.py +563 -0
- oscura/filtering/convenience.py +564 -0
- oscura/filtering/design.py +725 -0
- oscura/filtering/filters.py +32 -0
- oscura/filtering/introspection.py +605 -0
- oscura/guidance/__init__.py +24 -0
- oscura/guidance/recommender.py +429 -0
- oscura/guidance/wizard.py +518 -0
- oscura/inference/__init__.py +251 -0
- oscura/inference/active_learning/README.md +153 -0
- oscura/inference/active_learning/__init__.py +38 -0
- oscura/inference/active_learning/lstar.py +257 -0
- oscura/inference/active_learning/observation_table.py +230 -0
- oscura/inference/active_learning/oracle.py +78 -0
- oscura/inference/active_learning/teachers/__init__.py +15 -0
- oscura/inference/active_learning/teachers/simulator.py +192 -0
- oscura/inference/adaptive_tuning.py +453 -0
- oscura/inference/alignment.py +653 -0
- oscura/inference/bayesian.py +943 -0
- oscura/inference/binary.py +1016 -0
- oscura/inference/crc_reverse.py +711 -0
- oscura/inference/logic.py +288 -0
- oscura/inference/message_format.py +1305 -0
- oscura/inference/protocol.py +417 -0
- oscura/inference/protocol_dsl.py +1084 -0
- oscura/inference/protocol_library.py +1230 -0
- oscura/inference/sequences.py +809 -0
- oscura/inference/signal_intelligence.py +1509 -0
- oscura/inference/spectral.py +215 -0
- oscura/inference/state_machine.py +634 -0
- oscura/inference/stream.py +918 -0
- oscura/integrations/__init__.py +59 -0
- oscura/integrations/llm.py +1827 -0
- oscura/jupyter/__init__.py +32 -0
- oscura/jupyter/display.py +268 -0
- oscura/jupyter/magic.py +334 -0
- oscura/loaders/__init__.py +526 -0
- oscura/loaders/binary.py +69 -0
- oscura/loaders/configurable.py +1255 -0
- oscura/loaders/csv.py +26 -0
- oscura/loaders/csv_loader.py +473 -0
- oscura/loaders/hdf5.py +9 -0
- oscura/loaders/hdf5_loader.py +510 -0
- oscura/loaders/lazy.py +370 -0
- oscura/loaders/mmap_loader.py +583 -0
- oscura/loaders/numpy_loader.py +436 -0
- oscura/loaders/pcap.py +432 -0
- oscura/loaders/preprocessing.py +368 -0
- oscura/loaders/rigol.py +287 -0
- oscura/loaders/sigrok.py +321 -0
- oscura/loaders/tdms.py +367 -0
- oscura/loaders/tektronix.py +711 -0
- oscura/loaders/validation.py +584 -0
- oscura/loaders/vcd.py +464 -0
- oscura/loaders/wav.py +233 -0
- oscura/math/__init__.py +45 -0
- oscura/math/arithmetic.py +824 -0
- oscura/math/interpolation.py +413 -0
- oscura/onboarding/__init__.py +39 -0
- oscura/onboarding/help.py +498 -0
- oscura/onboarding/tutorials.py +405 -0
- oscura/onboarding/wizard.py +466 -0
- oscura/optimization/__init__.py +19 -0
- oscura/optimization/parallel.py +440 -0
- oscura/optimization/search.py +532 -0
- oscura/pipeline/__init__.py +43 -0
- oscura/pipeline/base.py +338 -0
- oscura/pipeline/composition.py +242 -0
- oscura/pipeline/parallel.py +448 -0
- oscura/pipeline/pipeline.py +375 -0
- oscura/pipeline/reverse_engineering.py +1119 -0
- oscura/plugins/__init__.py +122 -0
- oscura/plugins/base.py +272 -0
- oscura/plugins/cli.py +497 -0
- oscura/plugins/discovery.py +411 -0
- oscura/plugins/isolation.py +418 -0
- oscura/plugins/lifecycle.py +959 -0
- oscura/plugins/manager.py +493 -0
- oscura/plugins/registry.py +421 -0
- oscura/plugins/versioning.py +372 -0
- oscura/py.typed +0 -0
- oscura/quality/__init__.py +65 -0
- oscura/quality/ensemble.py +740 -0
- oscura/quality/explainer.py +338 -0
- oscura/quality/scoring.py +616 -0
- oscura/quality/warnings.py +456 -0
- oscura/reporting/__init__.py +248 -0
- oscura/reporting/advanced.py +1234 -0
- oscura/reporting/analyze.py +448 -0
- oscura/reporting/argument_preparer.py +596 -0
- oscura/reporting/auto_report.py +507 -0
- oscura/reporting/batch.py +615 -0
- oscura/reporting/chart_selection.py +223 -0
- oscura/reporting/comparison.py +330 -0
- oscura/reporting/config.py +615 -0
- oscura/reporting/content/__init__.py +39 -0
- oscura/reporting/content/executive.py +127 -0
- oscura/reporting/content/filtering.py +191 -0
- oscura/reporting/content/minimal.py +257 -0
- oscura/reporting/content/verbosity.py +162 -0
- oscura/reporting/core.py +508 -0
- oscura/reporting/core_formats/__init__.py +17 -0
- oscura/reporting/core_formats/multi_format.py +210 -0
- oscura/reporting/engine.py +836 -0
- oscura/reporting/export.py +366 -0
- oscura/reporting/formatting/__init__.py +129 -0
- oscura/reporting/formatting/emphasis.py +81 -0
- oscura/reporting/formatting/numbers.py +403 -0
- oscura/reporting/formatting/standards.py +55 -0
- oscura/reporting/formatting.py +466 -0
- oscura/reporting/html.py +578 -0
- oscura/reporting/index.py +590 -0
- oscura/reporting/multichannel.py +296 -0
- oscura/reporting/output.py +379 -0
- oscura/reporting/pdf.py +373 -0
- oscura/reporting/plots.py +731 -0
- oscura/reporting/pptx_export.py +360 -0
- oscura/reporting/renderers/__init__.py +11 -0
- oscura/reporting/renderers/pdf.py +94 -0
- oscura/reporting/sections.py +471 -0
- oscura/reporting/standards.py +680 -0
- oscura/reporting/summary_generator.py +368 -0
- oscura/reporting/tables.py +397 -0
- oscura/reporting/template_system.py +724 -0
- oscura/reporting/templates/__init__.py +15 -0
- oscura/reporting/templates/definition.py +205 -0
- oscura/reporting/templates/index.html +649 -0
- oscura/reporting/templates/index.md +173 -0
- oscura/schemas/__init__.py +158 -0
- oscura/schemas/bus_configuration.json +322 -0
- oscura/schemas/device_mapping.json +182 -0
- oscura/schemas/packet_format.json +418 -0
- oscura/schemas/protocol_definition.json +363 -0
- oscura/search/__init__.py +16 -0
- oscura/search/anomaly.py +292 -0
- oscura/search/context.py +149 -0
- oscura/search/pattern.py +160 -0
- oscura/session/__init__.py +34 -0
- oscura/session/annotations.py +289 -0
- oscura/session/history.py +313 -0
- oscura/session/session.py +445 -0
- oscura/streaming/__init__.py +43 -0
- oscura/streaming/chunked.py +611 -0
- oscura/streaming/progressive.py +393 -0
- oscura/streaming/realtime.py +622 -0
- oscura/testing/__init__.py +54 -0
- oscura/testing/synthetic.py +808 -0
- oscura/triggering/__init__.py +68 -0
- oscura/triggering/base.py +229 -0
- oscura/triggering/edge.py +353 -0
- oscura/triggering/pattern.py +344 -0
- oscura/triggering/pulse.py +581 -0
- oscura/triggering/window.py +453 -0
- oscura/ui/__init__.py +48 -0
- oscura/ui/formatters.py +526 -0
- oscura/ui/progressive_display.py +340 -0
- oscura/utils/__init__.py +99 -0
- oscura/utils/autodetect.py +338 -0
- oscura/utils/buffer.py +389 -0
- oscura/utils/lazy.py +407 -0
- oscura/utils/lazy_imports.py +147 -0
- oscura/utils/memory.py +836 -0
- oscura/utils/memory_advanced.py +1326 -0
- oscura/utils/memory_extensions.py +465 -0
- oscura/utils/progressive.py +352 -0
- oscura/utils/windowing.py +362 -0
- oscura/visualization/__init__.py +321 -0
- oscura/visualization/accessibility.py +526 -0
- oscura/visualization/annotations.py +374 -0
- oscura/visualization/axis_scaling.py +305 -0
- oscura/visualization/colors.py +453 -0
- oscura/visualization/digital.py +337 -0
- oscura/visualization/eye.py +420 -0
- oscura/visualization/histogram.py +281 -0
- oscura/visualization/interactive.py +858 -0
- oscura/visualization/jitter.py +702 -0
- oscura/visualization/keyboard.py +394 -0
- oscura/visualization/layout.py +365 -0
- oscura/visualization/optimization.py +1028 -0
- oscura/visualization/palettes.py +446 -0
- oscura/visualization/plot.py +92 -0
- oscura/visualization/power.py +290 -0
- oscura/visualization/power_extended.py +626 -0
- oscura/visualization/presets.py +467 -0
- oscura/visualization/protocols.py +932 -0
- oscura/visualization/render.py +207 -0
- oscura/visualization/rendering.py +444 -0
- oscura/visualization/reverse_engineering.py +791 -0
- oscura/visualization/signal_integrity.py +808 -0
- oscura/visualization/specialized.py +553 -0
- oscura/visualization/spectral.py +811 -0
- oscura/visualization/styles.py +381 -0
- oscura/visualization/thumbnails.py +311 -0
- oscura/visualization/time_axis.py +351 -0
- oscura/visualization/waveform.py +367 -0
- oscura/workflow/__init__.py +13 -0
- oscura/workflow/dag.py +377 -0
- oscura/workflows/__init__.py +58 -0
- oscura/workflows/compliance.py +280 -0
- oscura/workflows/digital.py +272 -0
- oscura/workflows/multi_trace.py +502 -0
- oscura/workflows/power.py +178 -0
- oscura/workflows/protocol.py +492 -0
- oscura/workflows/reverse_engineering.py +639 -0
- oscura/workflows/signal_integrity.py +227 -0
- oscura-0.1.0.dist-info/METADATA +300 -0
- oscura-0.1.0.dist-info/RECORD +463 -0
- oscura-0.1.0.dist-info/entry_points.txt +2 -0
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/licenses/LICENSE +1 -1
- oscura-0.0.1.dist-info/METADATA +0 -63
- oscura-0.0.1.dist-info/RECORD +0 -5
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/WHEEL +0 -0
oscura/core/memoize.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""Memory-safe memoization decorators for TraceKit analyzer functions.
|
|
2
|
+
|
|
3
|
+
This module provides lightweight memoization decorators optimized for analyzer
|
|
4
|
+
functions that process numpy arrays. Unlike the full TraceKitCache, these
|
|
5
|
+
decorators provide simple in-memory caching with bounded size.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
>>> from oscura.core.memoize import memoize_analysis
|
|
10
|
+
>>> @memoize_analysis(maxsize=32)
|
|
11
|
+
... def expensive_fft(signal, nperseg):
|
|
12
|
+
... return scipy.fft.fft(signal, n=nperseg)
|
|
13
|
+
>>> result = expensive_fft(signal_array, 1024) # Computed
|
|
14
|
+
>>> result = expensive_fft(signal_array, 1024) # Cached
|
|
15
|
+
|
|
16
|
+
References:
|
|
17
|
+
functools.lru_cache for standard Python memoization
|
|
18
|
+
hashlib for stable array hashing
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import hashlib
|
|
24
|
+
from functools import wraps
|
|
25
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
26
|
+
|
|
27
|
+
import numpy as np
|
|
28
|
+
from numpy.typing import NDArray
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from collections.abc import Callable
|
|
32
|
+
|
|
33
|
+
T = TypeVar("T")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def array_hash(arr: NDArray[Any], sample_size: int = 10000) -> str:
|
|
37
|
+
"""Create stable hash for numpy array.
|
|
38
|
+
|
|
39
|
+
Uses first `sample_size` bytes of array data to create a hash key.
|
|
40
|
+
This is faster than hashing the entire array while maintaining
|
|
41
|
+
good cache hit rates for typical analysis workflows.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
arr: Numpy array to hash.
|
|
45
|
+
sample_size: Number of bytes to sample for hashing (default: 10KB).
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
16-character hex hash string.
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
>>> arr = np.arange(1000000, dtype=np.float32)
|
|
52
|
+
>>> hash1 = array_hash(arr)
|
|
53
|
+
>>> hash2 = array_hash(arr)
|
|
54
|
+
>>> assert hash1 == hash2
|
|
55
|
+
"""
|
|
56
|
+
# Use shape, dtype, and sample of data for hash
|
|
57
|
+
hash_obj = hashlib.sha256()
|
|
58
|
+
|
|
59
|
+
# Include shape and dtype
|
|
60
|
+
hash_obj.update(str(arr.shape).encode())
|
|
61
|
+
hash_obj.update(str(arr.dtype).encode())
|
|
62
|
+
|
|
63
|
+
# Sample first N bytes of data
|
|
64
|
+
data_bytes = arr.tobytes()[:sample_size]
|
|
65
|
+
hash_obj.update(data_bytes)
|
|
66
|
+
|
|
67
|
+
return hash_obj.hexdigest()[:16]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def memoize_analysis(maxsize: int = 32) -> Callable[[Callable[..., T]], Callable[..., T]]:
|
|
71
|
+
"""Decorator for memoizing analysis functions with numpy arrays.
|
|
72
|
+
|
|
73
|
+
Automatically hashes numpy array arguments for cache keys.
|
|
74
|
+
Memory-safe with bounded cache size using LRU eviction.
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
maxsize: Maximum number of cached results (default: 32).
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Decorator function.
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
>>> @memoize_analysis(maxsize=16)
|
|
85
|
+
... def detect_edges(signal, threshold):
|
|
86
|
+
... # Expensive edge detection...
|
|
87
|
+
... return edges
|
|
88
|
+
>>> # First call computes
|
|
89
|
+
>>> edges1 = detect_edges(signal_array, 0.5)
|
|
90
|
+
>>> # Second call uses cache
|
|
91
|
+
>>> edges2 = detect_edges(signal_array, 0.5)
|
|
92
|
+
>>> assert edges1 is edges2
|
|
93
|
+
|
|
94
|
+
Note:
|
|
95
|
+
Cache is stored per-function. Use TraceKitCache from core.cache
|
|
96
|
+
for persistent cross-function caching.
|
|
97
|
+
|
|
98
|
+
References:
|
|
99
|
+
PERF-001: Performance optimization requirements
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def decorator(func: Callable[..., T]) -> Callable[..., T]:
|
|
103
|
+
cache: dict[str, T] = {}
|
|
104
|
+
cache_order: list[str] = [] # Track insertion order for LRU
|
|
105
|
+
|
|
106
|
+
@wraps(func)
|
|
107
|
+
def wrapper(*args: Any, **kwargs: Any) -> T:
|
|
108
|
+
# Build cache key from args
|
|
109
|
+
key_parts: list[str] = []
|
|
110
|
+
|
|
111
|
+
for arg in args:
|
|
112
|
+
if isinstance(arg, np.ndarray):
|
|
113
|
+
key_parts.append(f"arr_{len(arg)}_{array_hash(arg)}")
|
|
114
|
+
else:
|
|
115
|
+
key_parts.append(str(arg))
|
|
116
|
+
|
|
117
|
+
for k, v in sorted(kwargs.items()):
|
|
118
|
+
if isinstance(v, np.ndarray):
|
|
119
|
+
key_parts.append(f"{k}=arr_{len(v)}_{array_hash(v)}")
|
|
120
|
+
else:
|
|
121
|
+
key_parts.append(f"{k}={v}")
|
|
122
|
+
|
|
123
|
+
cache_key = ":".join(key_parts)
|
|
124
|
+
|
|
125
|
+
# Check cache
|
|
126
|
+
if cache_key in cache:
|
|
127
|
+
# Move to end (most recently used)
|
|
128
|
+
cache_order.remove(cache_key)
|
|
129
|
+
cache_order.append(cache_key)
|
|
130
|
+
return cache[cache_key]
|
|
131
|
+
|
|
132
|
+
# Compute result
|
|
133
|
+
result = func(*args, **kwargs)
|
|
134
|
+
|
|
135
|
+
# Evict oldest if at capacity
|
|
136
|
+
if len(cache) >= maxsize:
|
|
137
|
+
oldest = cache_order.pop(0)
|
|
138
|
+
del cache[oldest]
|
|
139
|
+
|
|
140
|
+
# Store result
|
|
141
|
+
cache[cache_key] = result
|
|
142
|
+
cache_order.append(cache_key)
|
|
143
|
+
|
|
144
|
+
return result
|
|
145
|
+
|
|
146
|
+
def cache_clear() -> None:
|
|
147
|
+
"""Clear all cached results."""
|
|
148
|
+
cache.clear()
|
|
149
|
+
cache_order.clear()
|
|
150
|
+
|
|
151
|
+
def cache_info() -> dict[str, Any]:
|
|
152
|
+
"""Get cache statistics.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Dictionary with cache size and maxsize.
|
|
156
|
+
"""
|
|
157
|
+
return {"size": len(cache), "maxsize": maxsize}
|
|
158
|
+
|
|
159
|
+
# Attach utility methods
|
|
160
|
+
wrapper.cache_clear = cache_clear # type: ignore[attr-defined]
|
|
161
|
+
wrapper.cache_info = cache_info # type: ignore[attr-defined]
|
|
162
|
+
|
|
163
|
+
return wrapper
|
|
164
|
+
|
|
165
|
+
return decorator
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
__all__ = [
|
|
169
|
+
"array_hash",
|
|
170
|
+
"memoize_analysis",
|
|
171
|
+
]
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""Pre-flight memory checking for TraceKit operations.
|
|
2
|
+
|
|
3
|
+
This module provides automatic memory verification before executing
|
|
4
|
+
memory-intensive operations to prevent OOM crashes.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> from oscura.core.memory_check import check_operation_memory, require_memory
|
|
9
|
+
>>> check = check_operation_memory('spectrogram', samples=1e9, nperseg=4096)
|
|
10
|
+
>>> if not check.sufficient:
|
|
11
|
+
... print(check.recommendation)
|
|
12
|
+
|
|
13
|
+
References:
|
|
14
|
+
See oscura.utils.memory for memory estimation functions.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from typing import TYPE_CHECKING, Any
|
|
20
|
+
|
|
21
|
+
from oscura.utils.memory import (
|
|
22
|
+
MemoryCheck,
|
|
23
|
+
MemoryCheckError,
|
|
24
|
+
check_memory_available,
|
|
25
|
+
require_memory,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from collections.abc import Callable
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Operations that automatically perform memory checks
|
|
33
|
+
_AUTO_CHECK_OPERATIONS = {
|
|
34
|
+
"fft",
|
|
35
|
+
"psd",
|
|
36
|
+
"spectrogram",
|
|
37
|
+
"eye_diagram",
|
|
38
|
+
"correlate",
|
|
39
|
+
"filter",
|
|
40
|
+
"stft",
|
|
41
|
+
"cwt",
|
|
42
|
+
"dwt",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Global flag to bypass memory checks (use with caution)
|
|
46
|
+
_force_memory = False
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def set_force_memory(enabled: bool) -> None:
|
|
50
|
+
"""Enable or disable forced memory bypass.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
enabled: If True, bypass memory checks (dangerous).
|
|
55
|
+
|
|
56
|
+
Warning:
|
|
57
|
+
Bypassing memory checks can lead to system crashes.
|
|
58
|
+
Only use when you are certain the operation will succeed.
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
>>> set_force_memory(True) # Bypass all memory checks
|
|
62
|
+
>>> # ... perform operation ...
|
|
63
|
+
>>> set_force_memory(False) # Re-enable checks
|
|
64
|
+
"""
|
|
65
|
+
global _force_memory
|
|
66
|
+
_force_memory = enabled
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def is_force_memory() -> bool:
|
|
70
|
+
"""Check if memory checks are bypassed.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
True if memory checks are disabled.
|
|
74
|
+
"""
|
|
75
|
+
return _force_memory
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def check_operation_memory(
|
|
79
|
+
operation: str,
|
|
80
|
+
samples: int | float | None = None,
|
|
81
|
+
**kwargs: Any,
|
|
82
|
+
) -> MemoryCheck:
|
|
83
|
+
"""Check if sufficient memory is available for an operation.
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
This is a convenience wrapper around utils.memory.check_memory_available
|
|
87
|
+
with automatic bypass support.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
operation: Operation name (fft, psd, spectrogram, etc.).
|
|
91
|
+
samples: Number of samples to process.
|
|
92
|
+
**kwargs: Additional operation-specific parameters.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
MemoryCheck with sufficiency status and recommendations.
|
|
96
|
+
|
|
97
|
+
Example:
|
|
98
|
+
>>> check = check_operation_memory('fft', samples=1e9, nfft=8192)
|
|
99
|
+
>>> if not check.sufficient:
|
|
100
|
+
... print(f"Insufficient memory: {check.recommendation}")
|
|
101
|
+
"""
|
|
102
|
+
# Bypass check if forced
|
|
103
|
+
if _force_memory:
|
|
104
|
+
return MemoryCheck(
|
|
105
|
+
sufficient=True,
|
|
106
|
+
available=0,
|
|
107
|
+
required=0,
|
|
108
|
+
recommendation="Memory check bypassed (--force-memory enabled)",
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return check_memory_available(operation, samples, **kwargs)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def auto_check_memory(
|
|
115
|
+
operation: str,
|
|
116
|
+
samples: int | float | None = None,
|
|
117
|
+
**kwargs: Any,
|
|
118
|
+
) -> None:
|
|
119
|
+
"""Automatically check memory and raise error if insufficient.
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
This function is called automatically by operations that support
|
|
123
|
+
memory checking (fft, psd, spectrogram, etc.).
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
operation: Operation name.
|
|
127
|
+
samples: Number of samples.
|
|
128
|
+
**kwargs: Additional parameters.
|
|
129
|
+
|
|
130
|
+
Example:
|
|
131
|
+
>>> try:
|
|
132
|
+
... auto_check_memory('spectrogram', samples=1e9, nperseg=4096)
|
|
133
|
+
... except MemoryCheckError as e:
|
|
134
|
+
... print(f"Memory check failed: {e}")
|
|
135
|
+
... print(f"Suggestion: {e.recommendation}")
|
|
136
|
+
|
|
137
|
+
Note:
|
|
138
|
+
May raise MemoryCheckError if insufficient memory and not forced.
|
|
139
|
+
"""
|
|
140
|
+
# Skip check if operation doesn't require it
|
|
141
|
+
if operation not in _AUTO_CHECK_OPERATIONS:
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
# Bypass if forced
|
|
145
|
+
if _force_memory:
|
|
146
|
+
return
|
|
147
|
+
|
|
148
|
+
# Perform check
|
|
149
|
+
require_memory(operation, samples, **kwargs)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def with_memory_check(func: Callable) -> Callable: # type: ignore[type-arg]
|
|
153
|
+
"""Decorator to add automatic memory checking to a function.
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
The decorated function must accept 'samples' as a keyword argument
|
|
157
|
+
and should have an 'operation' attribute or name that matches
|
|
158
|
+
a supported operation type.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
func: Function to decorate.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Decorated function with memory checking.
|
|
165
|
+
|
|
166
|
+
Example:
|
|
167
|
+
>>> @with_memory_check
|
|
168
|
+
... def my_fft(signal, samples=None, **kwargs):
|
|
169
|
+
... # ... FFT implementation ...
|
|
170
|
+
... pass
|
|
171
|
+
>>> my_fft.operation = 'fft' # Specify operation type
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
175
|
+
# Extract operation name
|
|
176
|
+
operation = getattr(func, "operation", func.__name__)
|
|
177
|
+
|
|
178
|
+
# Extract samples if available
|
|
179
|
+
samples = kwargs.get("samples")
|
|
180
|
+
if samples is None and len(args) > 0:
|
|
181
|
+
# Try to infer from first argument
|
|
182
|
+
try:
|
|
183
|
+
import numpy as np
|
|
184
|
+
|
|
185
|
+
if isinstance(args[0], np.ndarray):
|
|
186
|
+
samples = len(args[0])
|
|
187
|
+
except (ImportError, TypeError):
|
|
188
|
+
pass
|
|
189
|
+
|
|
190
|
+
# Perform check
|
|
191
|
+
if operation in _AUTO_CHECK_OPERATIONS and not _force_memory:
|
|
192
|
+
auto_check_memory(operation, samples, **kwargs)
|
|
193
|
+
|
|
194
|
+
# Call original function
|
|
195
|
+
return func(*args, **kwargs)
|
|
196
|
+
|
|
197
|
+
# Preserve function metadata
|
|
198
|
+
wrapper.__name__ = func.__name__
|
|
199
|
+
wrapper.__doc__ = func.__doc__
|
|
200
|
+
wrapper.__module__ = func.__module__
|
|
201
|
+
|
|
202
|
+
return wrapper
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def register_auto_check_operation(operation: str) -> None:
|
|
206
|
+
"""Register an operation for automatic memory checking.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
operation: Operation name to register.
|
|
210
|
+
|
|
211
|
+
Example:
|
|
212
|
+
>>> register_auto_check_operation('custom_transform')
|
|
213
|
+
>>> # Now custom_transform will automatically check memory
|
|
214
|
+
"""
|
|
215
|
+
_AUTO_CHECK_OPERATIONS.add(operation)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def unregister_auto_check_operation(operation: str) -> None:
|
|
219
|
+
"""Unregister an operation from automatic memory checking.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
operation: Operation name to unregister.
|
|
223
|
+
|
|
224
|
+
Example:
|
|
225
|
+
>>> unregister_auto_check_operation('custom_transform')
|
|
226
|
+
>>> # Now custom_transform will not automatically check memory
|
|
227
|
+
"""
|
|
228
|
+
_AUTO_CHECK_OPERATIONS.discard(operation)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _reset_auto_check_operations() -> None:
|
|
232
|
+
"""Reset AUTO_CHECK_OPERATIONS to default state.
|
|
233
|
+
|
|
234
|
+
WARNING: This is for testing only. It resets the global state to defaults.
|
|
235
|
+
"""
|
|
236
|
+
global _AUTO_CHECK_OPERATIONS
|
|
237
|
+
_AUTO_CHECK_OPERATIONS = {
|
|
238
|
+
"fft",
|
|
239
|
+
"psd",
|
|
240
|
+
"spectrogram",
|
|
241
|
+
"eye_diagram",
|
|
242
|
+
"correlate",
|
|
243
|
+
"filter",
|
|
244
|
+
"stft",
|
|
245
|
+
"cwt",
|
|
246
|
+
"dwt",
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def get_auto_check_operations() -> set[str]:
|
|
251
|
+
"""Get set of operations that automatically check memory.
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
Set of operation names.
|
|
255
|
+
|
|
256
|
+
Example:
|
|
257
|
+
>>> ops = get_auto_check_operations()
|
|
258
|
+
>>> print(f"Auto-checked operations: {', '.join(sorted(ops))}")
|
|
259
|
+
"""
|
|
260
|
+
return _AUTO_CHECK_OPERATIONS.copy()
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
__all__ = [
|
|
264
|
+
"MemoryCheck",
|
|
265
|
+
"MemoryCheckError",
|
|
266
|
+
"auto_check_memory",
|
|
267
|
+
"check_operation_memory",
|
|
268
|
+
"get_auto_check_operations",
|
|
269
|
+
"is_force_memory",
|
|
270
|
+
"register_auto_check_operation",
|
|
271
|
+
"set_force_memory",
|
|
272
|
+
"unregister_auto_check_operation",
|
|
273
|
+
"with_memory_check",
|
|
274
|
+
]
|