oscura 0.5.1__py3-none-any.whl → 0.7.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 +169 -167
- oscura/analyzers/__init__.py +3 -0
- oscura/analyzers/classification.py +659 -0
- oscura/analyzers/digital/edges.py +325 -65
- oscura/analyzers/digital/quality.py +293 -166
- oscura/analyzers/digital/timing.py +260 -115
- oscura/analyzers/digital/timing_numba.py +334 -0
- oscura/analyzers/entropy.py +605 -0
- oscura/analyzers/eye/diagram.py +176 -109
- oscura/analyzers/eye/metrics.py +5 -5
- oscura/analyzers/jitter/__init__.py +6 -4
- oscura/analyzers/jitter/ber.py +52 -52
- oscura/analyzers/jitter/classification.py +156 -0
- oscura/analyzers/jitter/decomposition.py +163 -113
- oscura/analyzers/jitter/spectrum.py +80 -64
- oscura/analyzers/ml/__init__.py +39 -0
- oscura/analyzers/ml/features.py +600 -0
- oscura/analyzers/ml/signal_classifier.py +604 -0
- oscura/analyzers/packet/daq.py +246 -158
- oscura/analyzers/packet/parser.py +12 -1
- oscura/analyzers/packet/payload.py +50 -2110
- oscura/analyzers/packet/payload_analysis.py +361 -181
- oscura/analyzers/packet/payload_patterns.py +133 -70
- oscura/analyzers/packet/stream.py +84 -23
- oscura/analyzers/patterns/__init__.py +26 -5
- oscura/analyzers/patterns/anomaly_detection.py +908 -0
- oscura/analyzers/patterns/clustering.py +169 -108
- oscura/analyzers/patterns/clustering_optimized.py +227 -0
- oscura/analyzers/patterns/discovery.py +1 -1
- oscura/analyzers/patterns/matching.py +581 -197
- oscura/analyzers/patterns/pattern_mining.py +778 -0
- oscura/analyzers/patterns/periodic.py +121 -38
- oscura/analyzers/patterns/sequences.py +175 -78
- oscura/analyzers/power/conduction.py +1 -1
- oscura/analyzers/power/soa.py +6 -6
- oscura/analyzers/power/switching.py +250 -110
- oscura/analyzers/protocol/__init__.py +17 -1
- oscura/analyzers/protocols/base.py +6 -6
- oscura/analyzers/protocols/ble/__init__.py +38 -0
- oscura/analyzers/protocols/ble/analyzer.py +809 -0
- oscura/analyzers/protocols/ble/uuids.py +288 -0
- oscura/analyzers/protocols/can.py +257 -127
- oscura/analyzers/protocols/can_fd.py +107 -80
- oscura/analyzers/protocols/flexray.py +139 -80
- oscura/analyzers/protocols/hdlc.py +93 -58
- oscura/analyzers/protocols/i2c.py +247 -106
- oscura/analyzers/protocols/i2s.py +138 -86
- oscura/analyzers/protocols/industrial/__init__.py +40 -0
- oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
- oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
- oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
- oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
- oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
- oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
- oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
- oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
- oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
- oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
- oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
- oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
- oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
- oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
- oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
- oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
- oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
- oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
- oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
- oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
- oscura/analyzers/protocols/jtag.py +180 -98
- oscura/analyzers/protocols/lin.py +219 -114
- oscura/analyzers/protocols/manchester.py +4 -4
- oscura/analyzers/protocols/onewire.py +253 -149
- oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
- oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
- oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
- oscura/analyzers/protocols/spi.py +192 -95
- oscura/analyzers/protocols/swd.py +321 -167
- oscura/analyzers/protocols/uart.py +267 -125
- oscura/analyzers/protocols/usb.py +235 -131
- oscura/analyzers/side_channel/power.py +17 -12
- oscura/analyzers/signal/__init__.py +15 -0
- oscura/analyzers/signal/timing_analysis.py +1086 -0
- oscura/analyzers/signal_integrity/__init__.py +4 -1
- oscura/analyzers/signal_integrity/sparams.py +2 -19
- oscura/analyzers/spectral/chunked.py +129 -60
- oscura/analyzers/spectral/chunked_fft.py +300 -94
- oscura/analyzers/spectral/chunked_wavelet.py +100 -80
- oscura/analyzers/statistical/checksum.py +376 -217
- oscura/analyzers/statistical/classification.py +229 -107
- oscura/analyzers/statistical/entropy.py +78 -53
- oscura/analyzers/statistics/correlation.py +407 -211
- oscura/analyzers/statistics/outliers.py +2 -2
- oscura/analyzers/statistics/streaming.py +30 -5
- oscura/analyzers/validation.py +216 -101
- oscura/analyzers/waveform/measurements.py +9 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
- oscura/analyzers/waveform/spectral.py +500 -228
- oscura/api/__init__.py +31 -5
- oscura/api/dsl/__init__.py +582 -0
- oscura/{dsl → api/dsl}/commands.py +43 -76
- oscura/{dsl → api/dsl}/interpreter.py +26 -51
- oscura/{dsl → api/dsl}/parser.py +107 -77
- oscura/{dsl → api/dsl}/repl.py +2 -2
- oscura/api/dsl.py +1 -1
- oscura/{integrations → api/integrations}/__init__.py +1 -1
- oscura/{integrations → api/integrations}/llm.py +201 -102
- oscura/api/operators.py +3 -3
- oscura/api/optimization.py +144 -30
- oscura/api/rest_server.py +921 -0
- oscura/api/server/__init__.py +17 -0
- oscura/api/server/dashboard.py +850 -0
- oscura/api/server/static/README.md +34 -0
- oscura/api/server/templates/base.html +181 -0
- oscura/api/server/templates/export.html +120 -0
- oscura/api/server/templates/home.html +284 -0
- oscura/api/server/templates/protocols.html +58 -0
- oscura/api/server/templates/reports.html +43 -0
- oscura/api/server/templates/session_detail.html +89 -0
- oscura/api/server/templates/sessions.html +83 -0
- oscura/api/server/templates/waveforms.html +73 -0
- oscura/automotive/__init__.py +8 -1
- oscura/automotive/can/__init__.py +10 -0
- oscura/automotive/can/checksum.py +3 -1
- oscura/automotive/can/dbc_generator.py +590 -0
- oscura/automotive/can/message_wrapper.py +121 -74
- oscura/automotive/can/patterns.py +98 -21
- oscura/automotive/can/session.py +292 -56
- oscura/automotive/can/state_machine.py +6 -3
- oscura/automotive/can/stimulus_response.py +97 -75
- oscura/automotive/dbc/__init__.py +10 -2
- oscura/automotive/dbc/generator.py +84 -56
- oscura/automotive/dbc/parser.py +6 -6
- oscura/automotive/dtc/data.json +17 -102
- oscura/automotive/dtc/database.py +2 -2
- oscura/automotive/flexray/__init__.py +31 -0
- oscura/automotive/flexray/analyzer.py +504 -0
- oscura/automotive/flexray/crc.py +185 -0
- oscura/automotive/flexray/fibex.py +449 -0
- oscura/automotive/j1939/__init__.py +45 -8
- oscura/automotive/j1939/analyzer.py +605 -0
- oscura/automotive/j1939/spns.py +326 -0
- oscura/automotive/j1939/transport.py +306 -0
- oscura/automotive/lin/__init__.py +47 -0
- oscura/automotive/lin/analyzer.py +612 -0
- oscura/automotive/loaders/blf.py +13 -2
- oscura/automotive/loaders/csv_can.py +143 -72
- oscura/automotive/loaders/dispatcher.py +50 -2
- oscura/automotive/loaders/mdf.py +86 -45
- oscura/automotive/loaders/pcap.py +111 -61
- oscura/automotive/uds/__init__.py +4 -0
- oscura/automotive/uds/analyzer.py +725 -0
- oscura/automotive/uds/decoder.py +140 -58
- oscura/automotive/uds/models.py +7 -1
- oscura/automotive/visualization.py +1 -1
- oscura/cli/analyze.py +348 -0
- oscura/cli/batch.py +142 -122
- oscura/cli/benchmark.py +275 -0
- oscura/cli/characterize.py +137 -82
- oscura/cli/compare.py +224 -131
- oscura/cli/completion.py +250 -0
- oscura/cli/config_cmd.py +361 -0
- oscura/cli/decode.py +164 -87
- oscura/cli/export.py +286 -0
- oscura/cli/main.py +115 -31
- oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
- oscura/{onboarding → cli/onboarding}/help.py +80 -58
- oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
- oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
- oscura/cli/progress.py +147 -0
- oscura/cli/shell.py +157 -135
- oscura/cli/validate_cmd.py +204 -0
- oscura/cli/visualize.py +158 -0
- oscura/convenience.py +125 -79
- oscura/core/__init__.py +4 -2
- oscura/core/backend_selector.py +3 -3
- oscura/core/cache.py +126 -15
- oscura/core/cancellation.py +1 -1
- oscura/{config → core/config}/__init__.py +20 -11
- oscura/{config → core/config}/defaults.py +1 -1
- oscura/{config → core/config}/loader.py +7 -5
- oscura/{config → core/config}/memory.py +5 -5
- oscura/{config → core/config}/migration.py +1 -1
- oscura/{config → core/config}/pipeline.py +99 -23
- oscura/{config → core/config}/preferences.py +1 -1
- oscura/{config → core/config}/protocol.py +3 -3
- oscura/{config → core/config}/schema.py +426 -272
- oscura/{config → core/config}/settings.py +1 -1
- oscura/{config → core/config}/thresholds.py +195 -153
- oscura/core/correlation.py +5 -6
- oscura/core/cross_domain.py +0 -2
- oscura/core/debug.py +9 -5
- oscura/{extensibility → core/extensibility}/docs.py +158 -70
- oscura/{extensibility → core/extensibility}/extensions.py +160 -76
- oscura/{extensibility → core/extensibility}/logging.py +1 -1
- oscura/{extensibility → core/extensibility}/measurements.py +1 -1
- oscura/{extensibility → core/extensibility}/plugins.py +1 -1
- oscura/{extensibility → core/extensibility}/templates.py +73 -3
- oscura/{extensibility → core/extensibility}/validation.py +1 -1
- oscura/core/gpu_backend.py +11 -7
- oscura/core/log_query.py +101 -11
- oscura/core/logging.py +126 -54
- oscura/core/logging_advanced.py +5 -5
- oscura/core/memory_limits.py +108 -70
- oscura/core/memory_monitor.py +2 -2
- oscura/core/memory_progress.py +7 -7
- oscura/core/memory_warnings.py +1 -1
- oscura/core/numba_backend.py +13 -13
- oscura/{plugins → core/plugins}/__init__.py +9 -9
- oscura/{plugins → core/plugins}/base.py +7 -7
- oscura/{plugins → core/plugins}/cli.py +3 -3
- oscura/{plugins → core/plugins}/discovery.py +186 -106
- oscura/{plugins → core/plugins}/lifecycle.py +1 -1
- oscura/{plugins → core/plugins}/manager.py +7 -7
- oscura/{plugins → core/plugins}/registry.py +3 -3
- oscura/{plugins → core/plugins}/versioning.py +1 -1
- oscura/core/progress.py +16 -1
- oscura/core/provenance.py +8 -2
- oscura/{schemas → core/schemas}/__init__.py +2 -2
- oscura/{schemas → core/schemas}/device_mapping.json +2 -8
- oscura/{schemas → core/schemas}/packet_format.json +4 -24
- oscura/{schemas → core/schemas}/protocol_definition.json +2 -12
- oscura/core/types.py +4 -0
- oscura/core/uncertainty.py +3 -3
- oscura/correlation/__init__.py +52 -0
- oscura/correlation/multi_protocol.py +811 -0
- oscura/discovery/auto_decoder.py +117 -35
- oscura/discovery/comparison.py +191 -86
- oscura/discovery/quality_validator.py +155 -68
- oscura/discovery/signal_detector.py +196 -79
- oscura/export/__init__.py +18 -8
- oscura/export/kaitai_struct.py +513 -0
- oscura/export/scapy_layer.py +801 -0
- oscura/export/wireshark/generator.py +1 -1
- oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
- oscura/export/wireshark_dissector.py +746 -0
- oscura/guidance/wizard.py +207 -111
- oscura/hardware/__init__.py +19 -0
- oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
- oscura/{acquisition → hardware/acquisition}/file.py +2 -2
- oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
- oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
- oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
- oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
- oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
- oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
- oscura/hardware/firmware/__init__.py +29 -0
- oscura/hardware/firmware/pattern_recognition.py +874 -0
- oscura/hardware/hal_detector.py +736 -0
- oscura/hardware/security/__init__.py +37 -0
- oscura/hardware/security/side_channel_detector.py +1126 -0
- oscura/inference/__init__.py +4 -0
- oscura/inference/active_learning/observation_table.py +4 -1
- oscura/inference/alignment.py +216 -123
- oscura/inference/bayesian.py +113 -33
- oscura/inference/crc_reverse.py +101 -55
- oscura/inference/logic.py +6 -2
- oscura/inference/message_format.py +342 -183
- oscura/inference/protocol.py +95 -44
- oscura/inference/protocol_dsl.py +180 -82
- oscura/inference/signal_intelligence.py +1439 -706
- oscura/inference/spectral.py +99 -57
- oscura/inference/state_machine.py +810 -158
- oscura/inference/stream.py +270 -110
- oscura/iot/__init__.py +34 -0
- oscura/iot/coap/__init__.py +32 -0
- oscura/iot/coap/analyzer.py +668 -0
- oscura/iot/coap/options.py +212 -0
- oscura/iot/lorawan/__init__.py +21 -0
- oscura/iot/lorawan/crypto.py +206 -0
- oscura/iot/lorawan/decoder.py +801 -0
- oscura/iot/lorawan/mac_commands.py +341 -0
- oscura/iot/mqtt/__init__.py +27 -0
- oscura/iot/mqtt/analyzer.py +999 -0
- oscura/iot/mqtt/properties.py +315 -0
- oscura/iot/zigbee/__init__.py +31 -0
- oscura/iot/zigbee/analyzer.py +615 -0
- oscura/iot/zigbee/security.py +153 -0
- oscura/iot/zigbee/zcl.py +349 -0
- oscura/jupyter/display.py +125 -45
- oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
- oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
- oscura/jupyter/exploratory/fuzzy.py +746 -0
- oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
- oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
- oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
- oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
- oscura/jupyter/exploratory/sync.py +612 -0
- oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
- oscura/jupyter/magic.py +4 -4
- oscura/{ui → jupyter/ui}/__init__.py +2 -2
- oscura/{ui → jupyter/ui}/formatters.py +3 -3
- oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
- oscura/loaders/__init__.py +183 -67
- oscura/loaders/binary.py +88 -1
- oscura/loaders/chipwhisperer.py +153 -137
- oscura/loaders/configurable.py +208 -86
- oscura/loaders/csv_loader.py +458 -215
- oscura/loaders/hdf5_loader.py +278 -119
- oscura/loaders/lazy.py +87 -54
- oscura/loaders/mmap_loader.py +1 -1
- oscura/loaders/numpy_loader.py +253 -116
- oscura/loaders/pcap.py +226 -151
- oscura/loaders/rigol.py +110 -49
- oscura/loaders/sigrok.py +201 -78
- oscura/loaders/tdms.py +81 -58
- oscura/loaders/tektronix.py +291 -174
- oscura/loaders/touchstone.py +182 -87
- oscura/loaders/tss.py +456 -0
- oscura/loaders/vcd.py +215 -117
- oscura/loaders/wav.py +155 -68
- oscura/reporting/__init__.py +9 -0
- oscura/reporting/analyze.py +352 -146
- oscura/reporting/argument_preparer.py +69 -14
- oscura/reporting/auto_report.py +97 -61
- oscura/reporting/batch.py +131 -58
- oscura/reporting/chart_selection.py +57 -45
- oscura/reporting/comparison.py +63 -17
- oscura/reporting/content/executive.py +76 -24
- oscura/reporting/core_formats/multi_format.py +11 -8
- oscura/reporting/engine.py +312 -158
- oscura/reporting/enhanced_reports.py +949 -0
- oscura/reporting/export.py +86 -43
- oscura/reporting/formatting/numbers.py +69 -42
- oscura/reporting/html.py +139 -58
- oscura/reporting/index.py +137 -65
- oscura/reporting/output.py +158 -67
- oscura/reporting/pdf.py +67 -102
- oscura/reporting/plots.py +191 -112
- oscura/reporting/sections.py +88 -47
- oscura/reporting/standards.py +104 -61
- oscura/reporting/summary_generator.py +75 -55
- oscura/reporting/tables.py +138 -54
- oscura/reporting/templates/enhanced/protocol_re.html +525 -0
- oscura/sessions/__init__.py +14 -23
- oscura/sessions/base.py +3 -3
- oscura/sessions/blackbox.py +106 -10
- oscura/sessions/generic.py +2 -2
- oscura/sessions/legacy.py +783 -0
- oscura/side_channel/__init__.py +63 -0
- oscura/side_channel/dpa.py +1025 -0
- oscura/utils/__init__.py +15 -1
- oscura/utils/bitwise.py +118 -0
- oscura/{builders → utils/builders}/__init__.py +1 -1
- oscura/{comparison → utils/comparison}/__init__.py +6 -6
- oscura/{comparison → utils/comparison}/compare.py +202 -101
- oscura/{comparison → utils/comparison}/golden.py +83 -63
- oscura/{comparison → utils/comparison}/limits.py +313 -89
- oscura/{comparison → utils/comparison}/mask.py +151 -45
- oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
- oscura/{comparison → utils/comparison}/visualization.py +147 -89
- oscura/{component → utils/component}/__init__.py +3 -3
- oscura/{component → utils/component}/impedance.py +122 -58
- oscura/{component → utils/component}/reactive.py +165 -168
- oscura/{component → utils/component}/transmission_line.py +3 -3
- oscura/{filtering → utils/filtering}/__init__.py +6 -6
- oscura/{filtering → utils/filtering}/base.py +1 -1
- oscura/{filtering → utils/filtering}/convenience.py +2 -2
- oscura/{filtering → utils/filtering}/design.py +169 -93
- oscura/{filtering → utils/filtering}/filters.py +2 -2
- oscura/{filtering → utils/filtering}/introspection.py +2 -2
- oscura/utils/geometry.py +31 -0
- oscura/utils/imports.py +184 -0
- oscura/utils/lazy.py +1 -1
- oscura/{math → utils/math}/__init__.py +2 -2
- oscura/{math → utils/math}/arithmetic.py +114 -48
- oscura/{math → utils/math}/interpolation.py +139 -106
- oscura/utils/memory.py +129 -66
- oscura/utils/memory_advanced.py +92 -9
- oscura/utils/memory_extensions.py +10 -8
- oscura/{optimization → utils/optimization}/__init__.py +1 -1
- oscura/{optimization → utils/optimization}/search.py +2 -2
- oscura/utils/performance/__init__.py +58 -0
- oscura/utils/performance/caching.py +889 -0
- oscura/utils/performance/lsh_clustering.py +333 -0
- oscura/utils/performance/memory_optimizer.py +699 -0
- oscura/utils/performance/optimizations.py +675 -0
- oscura/utils/performance/parallel.py +654 -0
- oscura/utils/performance/profiling.py +661 -0
- oscura/{pipeline → utils/pipeline}/base.py +1 -1
- oscura/{pipeline → utils/pipeline}/composition.py +1 -1
- oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
- oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
- oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
- oscura/{search → utils/search}/__init__.py +3 -3
- oscura/{search → utils/search}/anomaly.py +188 -58
- oscura/utils/search/context.py +294 -0
- oscura/{search → utils/search}/pattern.py +138 -10
- oscura/utils/serial.py +51 -0
- oscura/utils/storage/__init__.py +61 -0
- oscura/utils/storage/database.py +1166 -0
- oscura/{streaming → utils/streaming}/chunked.py +302 -143
- oscura/{streaming → utils/streaming}/progressive.py +1 -1
- oscura/{streaming → utils/streaming}/realtime.py +3 -2
- oscura/{triggering → utils/triggering}/__init__.py +6 -6
- oscura/{triggering → utils/triggering}/base.py +6 -6
- oscura/{triggering → utils/triggering}/edge.py +2 -2
- oscura/{triggering → utils/triggering}/pattern.py +2 -2
- oscura/{triggering → utils/triggering}/pulse.py +115 -74
- oscura/{triggering → utils/triggering}/window.py +2 -2
- oscura/utils/validation.py +32 -0
- oscura/validation/__init__.py +121 -0
- oscura/{compliance → validation/compliance}/__init__.py +5 -5
- oscura/{compliance → validation/compliance}/advanced.py +5 -5
- oscura/{compliance → validation/compliance}/masks.py +1 -1
- oscura/{compliance → validation/compliance}/reporting.py +127 -53
- oscura/{compliance → validation/compliance}/testing.py +114 -52
- oscura/validation/compliance_tests.py +915 -0
- oscura/validation/fuzzer.py +990 -0
- oscura/validation/grammar_tests.py +596 -0
- oscura/validation/grammar_validator.py +904 -0
- oscura/validation/hil_testing.py +977 -0
- oscura/{quality → validation/quality}/__init__.py +4 -4
- oscura/{quality → validation/quality}/ensemble.py +251 -171
- oscura/{quality → validation/quality}/explainer.py +3 -3
- oscura/{quality → validation/quality}/scoring.py +1 -1
- oscura/{quality → validation/quality}/warnings.py +4 -4
- oscura/validation/regression_suite.py +808 -0
- oscura/validation/replay.py +788 -0
- oscura/{testing → validation/testing}/__init__.py +2 -2
- oscura/{testing → validation/testing}/synthetic.py +5 -5
- oscura/visualization/__init__.py +9 -0
- oscura/visualization/accessibility.py +1 -1
- oscura/visualization/annotations.py +64 -67
- oscura/visualization/colors.py +7 -7
- oscura/visualization/digital.py +180 -81
- oscura/visualization/eye.py +236 -85
- oscura/visualization/interactive.py +320 -143
- oscura/visualization/jitter.py +587 -247
- oscura/visualization/layout.py +169 -134
- oscura/visualization/optimization.py +103 -52
- oscura/visualization/palettes.py +1 -1
- oscura/visualization/power.py +427 -211
- oscura/visualization/power_extended.py +626 -297
- oscura/visualization/presets.py +2 -0
- oscura/visualization/protocols.py +495 -181
- oscura/visualization/render.py +79 -63
- oscura/visualization/reverse_engineering.py +171 -124
- oscura/visualization/signal_integrity.py +460 -279
- oscura/visualization/specialized.py +190 -100
- oscura/visualization/spectral.py +670 -255
- oscura/visualization/thumbnails.py +166 -137
- oscura/visualization/waveform.py +150 -63
- oscura/workflows/__init__.py +3 -0
- oscura/{batch → workflows/batch}/__init__.py +5 -5
- oscura/{batch → workflows/batch}/advanced.py +150 -75
- oscura/workflows/batch/aggregate.py +531 -0
- oscura/workflows/batch/analyze.py +236 -0
- oscura/{batch → workflows/batch}/logging.py +2 -2
- oscura/{batch → workflows/batch}/metrics.py +1 -1
- oscura/workflows/complete_re.py +1144 -0
- oscura/workflows/compliance.py +44 -54
- oscura/workflows/digital.py +197 -51
- oscura/workflows/legacy/__init__.py +12 -0
- oscura/{workflow → workflows/legacy}/dag.py +4 -1
- oscura/workflows/multi_trace.py +9 -9
- oscura/workflows/power.py +42 -62
- oscura/workflows/protocol.py +82 -49
- oscura/workflows/reverse_engineering.py +351 -150
- oscura/workflows/signal_integrity.py +157 -82
- oscura-0.7.0.dist-info/METADATA +661 -0
- oscura-0.7.0.dist-info/RECORD +591 -0
- oscura/batch/aggregate.py +0 -300
- oscura/batch/analyze.py +0 -139
- oscura/dsl/__init__.py +0 -73
- oscura/exceptions.py +0 -59
- oscura/exploratory/fuzzy.py +0 -513
- oscura/exploratory/sync.py +0 -384
- oscura/exporters/__init__.py +0 -94
- oscura/exporters/csv.py +0 -303
- oscura/exporters/exporters.py +0 -44
- oscura/exporters/hdf5.py +0 -217
- oscura/exporters/html_export.py +0 -701
- oscura/exporters/json_export.py +0 -291
- oscura/exporters/markdown_export.py +0 -367
- oscura/exporters/matlab_export.py +0 -354
- oscura/exporters/npz_export.py +0 -219
- oscura/exporters/spice_export.py +0 -210
- oscura/search/context.py +0 -149
- oscura/session/__init__.py +0 -34
- oscura/session/annotations.py +0 -289
- oscura/session/history.py +0 -313
- oscura/session/session.py +0 -520
- oscura/workflow/__init__.py +0 -13
- oscura-0.5.1.dist-info/METADATA +0 -583
- oscura-0.5.1.dist-info/RECORD +0 -481
- /oscura/core/{config.py → config/legacy.py} +0 -0
- /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
- /oscura/{extensibility → core/extensibility}/registry.py +0 -0
- /oscura/{plugins → core/plugins}/isolation.py +0 -0
- /oscura/{schemas → core/schemas}/bus_configuration.json +0 -0
- /oscura/{builders → utils/builders}/signal_builder.py +0 -0
- /oscura/{optimization → utils/optimization}/parallel.py +0 -0
- /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
- /oscura/{streaming → utils/streaming}/__init__.py +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/WHEEL +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/entry_points.txt +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/licenses/LICENSE +0 -0
oscura/session/session.py
DELETED
|
@@ -1,520 +0,0 @@
|
|
|
1
|
-
"""Analysis session management.
|
|
2
|
-
|
|
3
|
-
This module provides session save/restore functionality for Oscura.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Example:
|
|
7
|
-
>>> session = Session()
|
|
8
|
-
>>> session.load_trace('capture.wfm')
|
|
9
|
-
>>> session.save('debug_session.tks')
|
|
10
|
-
>>>
|
|
11
|
-
>>> # Later...
|
|
12
|
-
>>> session = load_session('debug_session.tks')
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
import gzip
|
|
18
|
-
import hashlib
|
|
19
|
-
import hmac
|
|
20
|
-
import pickle
|
|
21
|
-
from dataclasses import dataclass, field
|
|
22
|
-
from datetime import datetime
|
|
23
|
-
from pathlib import Path
|
|
24
|
-
from typing import Any, cast
|
|
25
|
-
|
|
26
|
-
import numpy as np
|
|
27
|
-
|
|
28
|
-
from oscura.core.exceptions import SecurityError
|
|
29
|
-
from oscura.session.annotations import AnnotationLayer
|
|
30
|
-
from oscura.session.history import OperationHistory
|
|
31
|
-
|
|
32
|
-
# Session file format constants
|
|
33
|
-
_SESSION_MAGIC = b"OSC1" # Magic bytes for new format with signature
|
|
34
|
-
_SESSION_SIGNATURE_SIZE = 32 # SHA256 hash size in bytes
|
|
35
|
-
_SECURITY_KEY = hashlib.sha256(b"oscura-session-v1").digest()
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@dataclass
|
|
39
|
-
class Session:
|
|
40
|
-
"""Analysis session container.
|
|
41
|
-
|
|
42
|
-
Manages traces, annotations, measurements, and history for a complete
|
|
43
|
-
analysis session. Sessions can be saved and restored.
|
|
44
|
-
|
|
45
|
-
Attributes:
|
|
46
|
-
name: Session name
|
|
47
|
-
traces: Dictionary of loaded traces (name -> trace)
|
|
48
|
-
annotation_layers: Annotation layers
|
|
49
|
-
measurements: Recorded measurements
|
|
50
|
-
history: Operation history
|
|
51
|
-
metadata: Session metadata
|
|
52
|
-
created_at: Creation timestamp
|
|
53
|
-
modified_at: Last modification timestamp
|
|
54
|
-
"""
|
|
55
|
-
|
|
56
|
-
name: str = "Untitled Session"
|
|
57
|
-
traces: dict[str, Any] = field(default_factory=dict)
|
|
58
|
-
annotation_layers: dict[str, AnnotationLayer] = field(default_factory=dict)
|
|
59
|
-
measurements: dict[str, Any] = field(default_factory=dict)
|
|
60
|
-
history: OperationHistory = field(default_factory=OperationHistory)
|
|
61
|
-
metadata: dict[str, Any] = field(default_factory=dict)
|
|
62
|
-
created_at: datetime = field(default_factory=datetime.now)
|
|
63
|
-
modified_at: datetime = field(default_factory=datetime.now)
|
|
64
|
-
_file_path: Path | None = None
|
|
65
|
-
|
|
66
|
-
def __post_init__(self) -> None:
|
|
67
|
-
"""Initialize default annotation layer."""
|
|
68
|
-
if "default" not in self.annotation_layers:
|
|
69
|
-
self.annotation_layers["default"] = AnnotationLayer("Default")
|
|
70
|
-
|
|
71
|
-
def load_trace(
|
|
72
|
-
self,
|
|
73
|
-
path: str | Path,
|
|
74
|
-
name: str | None = None,
|
|
75
|
-
**load_kwargs: Any,
|
|
76
|
-
) -> Any:
|
|
77
|
-
"""Load a trace into the session.
|
|
78
|
-
|
|
79
|
-
Args:
|
|
80
|
-
path: Path to trace file.
|
|
81
|
-
name: Name for trace in session (default: filename).
|
|
82
|
-
**load_kwargs: Additional arguments for load().
|
|
83
|
-
|
|
84
|
-
Returns:
|
|
85
|
-
Loaded trace.
|
|
86
|
-
"""
|
|
87
|
-
from oscura.loaders import load
|
|
88
|
-
|
|
89
|
-
path = Path(path)
|
|
90
|
-
trace = load(str(path), **load_kwargs)
|
|
91
|
-
|
|
92
|
-
if name is None:
|
|
93
|
-
name = path.stem
|
|
94
|
-
|
|
95
|
-
self.traces[name] = trace
|
|
96
|
-
self._mark_modified()
|
|
97
|
-
|
|
98
|
-
self.history.record(
|
|
99
|
-
"load_trace",
|
|
100
|
-
{"path": str(path), "name": name},
|
|
101
|
-
result=f"Loaded {name}",
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
return trace
|
|
105
|
-
|
|
106
|
-
def add_trace(
|
|
107
|
-
self,
|
|
108
|
-
name: str,
|
|
109
|
-
trace: Any,
|
|
110
|
-
) -> None:
|
|
111
|
-
"""Add an in-memory trace to the session.
|
|
112
|
-
|
|
113
|
-
This method allows adding traces that were created programmatically
|
|
114
|
-
or loaded separately, rather than loading from a file.
|
|
115
|
-
|
|
116
|
-
Args:
|
|
117
|
-
name: Name for the trace in the session.
|
|
118
|
-
trace: Trace object (WaveformTrace, DigitalTrace, etc.).
|
|
119
|
-
|
|
120
|
-
Raises:
|
|
121
|
-
ValueError: If name is empty or already exists.
|
|
122
|
-
TypeError: If trace doesn't have expected attributes.
|
|
123
|
-
|
|
124
|
-
Example:
|
|
125
|
-
>>> session = Session()
|
|
126
|
-
>>> data = np.sin(np.linspace(0, 2*np.pi, 1000))
|
|
127
|
-
>>> trace = osc.WaveformTrace(data=data, metadata=osc.TraceMetadata(sample_rate=1e6))
|
|
128
|
-
>>> session.add_trace("my_trace", trace)
|
|
129
|
-
"""
|
|
130
|
-
if not name:
|
|
131
|
-
raise ValueError("Trace name cannot be empty")
|
|
132
|
-
|
|
133
|
-
if not hasattr(trace, "data"):
|
|
134
|
-
raise TypeError("Trace must have a 'data' attribute")
|
|
135
|
-
|
|
136
|
-
self.traces[name] = trace
|
|
137
|
-
self._mark_modified()
|
|
138
|
-
|
|
139
|
-
self.history.record(
|
|
140
|
-
"add_trace",
|
|
141
|
-
{"name": name, "type": type(trace).__name__},
|
|
142
|
-
result=f"Added {name}",
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
def remove_trace(self, name: str) -> None:
|
|
146
|
-
"""Remove a trace from the session.
|
|
147
|
-
|
|
148
|
-
Args:
|
|
149
|
-
name: Name of the trace to remove.
|
|
150
|
-
|
|
151
|
-
Raises:
|
|
152
|
-
KeyError: If trace not found.
|
|
153
|
-
"""
|
|
154
|
-
if name not in self.traces:
|
|
155
|
-
raise KeyError(f"Trace '{name}' not found in session")
|
|
156
|
-
|
|
157
|
-
del self.traces[name]
|
|
158
|
-
self._mark_modified()
|
|
159
|
-
|
|
160
|
-
self.history.record(
|
|
161
|
-
"remove_trace",
|
|
162
|
-
{"name": name},
|
|
163
|
-
result=f"Removed {name}",
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
def get_trace(self, name: str) -> Any:
|
|
167
|
-
"""Get trace by name.
|
|
168
|
-
|
|
169
|
-
Args:
|
|
170
|
-
name: Trace name.
|
|
171
|
-
|
|
172
|
-
Returns:
|
|
173
|
-
Trace object.
|
|
174
|
-
"""
|
|
175
|
-
return self.traces[name]
|
|
176
|
-
|
|
177
|
-
def list_traces(self) -> list[str]:
|
|
178
|
-
"""List all trace names."""
|
|
179
|
-
return list(self.traces.keys())
|
|
180
|
-
|
|
181
|
-
def annotate(
|
|
182
|
-
self,
|
|
183
|
-
text: str,
|
|
184
|
-
*,
|
|
185
|
-
time: float | None = None,
|
|
186
|
-
time_range: tuple[float, float] | None = None,
|
|
187
|
-
layer: str = "default",
|
|
188
|
-
**kwargs: Any,
|
|
189
|
-
) -> None:
|
|
190
|
-
"""Add annotation to session.
|
|
191
|
-
|
|
192
|
-
Args:
|
|
193
|
-
text: Annotation text.
|
|
194
|
-
time: Time point for annotation.
|
|
195
|
-
time_range: Time range for annotation.
|
|
196
|
-
layer: Annotation layer name.
|
|
197
|
-
**kwargs: Additional annotation parameters.
|
|
198
|
-
"""
|
|
199
|
-
if layer not in self.annotation_layers:
|
|
200
|
-
self.annotation_layers[layer] = AnnotationLayer(layer)
|
|
201
|
-
|
|
202
|
-
self.annotation_layers[layer].add(
|
|
203
|
-
text=text,
|
|
204
|
-
time=time,
|
|
205
|
-
time_range=time_range,
|
|
206
|
-
**kwargs,
|
|
207
|
-
)
|
|
208
|
-
self._mark_modified()
|
|
209
|
-
|
|
210
|
-
self.history.record(
|
|
211
|
-
"annotate",
|
|
212
|
-
{"text": text, "time": time, "layer": layer},
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
def get_annotations(
|
|
216
|
-
self,
|
|
217
|
-
layer: str | None = None,
|
|
218
|
-
time_range: tuple[float, float] | None = None,
|
|
219
|
-
) -> list[Any]:
|
|
220
|
-
"""Get annotations.
|
|
221
|
-
|
|
222
|
-
Args:
|
|
223
|
-
layer: Filter by layer name (None for all layers).
|
|
224
|
-
time_range: Filter by time range.
|
|
225
|
-
|
|
226
|
-
Returns:
|
|
227
|
-
List of annotations.
|
|
228
|
-
"""
|
|
229
|
-
annotations = []
|
|
230
|
-
|
|
231
|
-
layers = [self.annotation_layers[layer]] if layer else self.annotation_layers.values()
|
|
232
|
-
|
|
233
|
-
for ann_layer in layers:
|
|
234
|
-
if time_range:
|
|
235
|
-
annotations.extend(ann_layer.find_in_range(time_range[0], time_range[1]))
|
|
236
|
-
else:
|
|
237
|
-
annotations.extend(ann_layer.annotations)
|
|
238
|
-
|
|
239
|
-
return annotations
|
|
240
|
-
|
|
241
|
-
def record_measurement(
|
|
242
|
-
self,
|
|
243
|
-
name: str,
|
|
244
|
-
value: Any,
|
|
245
|
-
unit: str = "",
|
|
246
|
-
trace_name: str | None = None,
|
|
247
|
-
**metadata: Any,
|
|
248
|
-
) -> None:
|
|
249
|
-
"""Record a measurement result.
|
|
250
|
-
|
|
251
|
-
Args:
|
|
252
|
-
name: Measurement name (e.g., 'rise_time').
|
|
253
|
-
value: Measurement value.
|
|
254
|
-
unit: Unit of measurement.
|
|
255
|
-
trace_name: Associated trace name.
|
|
256
|
-
**metadata: Additional metadata.
|
|
257
|
-
"""
|
|
258
|
-
self.measurements[name] = {
|
|
259
|
-
"value": value,
|
|
260
|
-
"unit": unit,
|
|
261
|
-
"trace": trace_name,
|
|
262
|
-
"timestamp": datetime.now().isoformat(),
|
|
263
|
-
**metadata,
|
|
264
|
-
}
|
|
265
|
-
self._mark_modified()
|
|
266
|
-
|
|
267
|
-
self.history.record(
|
|
268
|
-
f"measure_{name}",
|
|
269
|
-
{"trace": trace_name},
|
|
270
|
-
result=f"{value} {unit}".strip(),
|
|
271
|
-
)
|
|
272
|
-
|
|
273
|
-
def get_measurements(self) -> dict[str, Any]:
|
|
274
|
-
"""Get all recorded measurements."""
|
|
275
|
-
return self.measurements.copy()
|
|
276
|
-
|
|
277
|
-
def save(
|
|
278
|
-
self,
|
|
279
|
-
path: str | Path | None = None,
|
|
280
|
-
*,
|
|
281
|
-
include_traces: bool = True,
|
|
282
|
-
compress: bool = True,
|
|
283
|
-
) -> Path:
|
|
284
|
-
"""Save session to file with HMAC signature for integrity verification.
|
|
285
|
-
|
|
286
|
-
Args:
|
|
287
|
-
path: Output path (default: use existing or generate).
|
|
288
|
-
include_traces: Include trace data in session file.
|
|
289
|
-
compress: Compress session file with gzip.
|
|
290
|
-
|
|
291
|
-
Returns:
|
|
292
|
-
Path to saved file.
|
|
293
|
-
|
|
294
|
-
Example:
|
|
295
|
-
>>> session.save('analysis.tks')
|
|
296
|
-
|
|
297
|
-
Security Note:
|
|
298
|
-
Session files now include HMAC signatures for integrity verification.
|
|
299
|
-
Files are still pickle-based - only load from trusted sources.
|
|
300
|
-
For secure data exchange with untrusted parties, use JSON or HDF5
|
|
301
|
-
export formats instead.
|
|
302
|
-
"""
|
|
303
|
-
if path is None:
|
|
304
|
-
path = self._file_path or Path(f"{self.name.replace(' ', '_')}.tks")
|
|
305
|
-
else:
|
|
306
|
-
path = Path(path)
|
|
307
|
-
|
|
308
|
-
self._file_path = path
|
|
309
|
-
self._mark_modified()
|
|
310
|
-
|
|
311
|
-
# Build session data
|
|
312
|
-
data = self._to_dict(include_traces=include_traces)
|
|
313
|
-
|
|
314
|
-
# Serialize with pickle
|
|
315
|
-
serialized = pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
|
|
316
|
-
|
|
317
|
-
# Compute HMAC signature
|
|
318
|
-
signature = hmac.new(_SECURITY_KEY, serialized, hashlib.sha256).digest()
|
|
319
|
-
|
|
320
|
-
# Write: magic bytes + signature + pickled data
|
|
321
|
-
if compress:
|
|
322
|
-
with gzip.open(path, "wb") as f:
|
|
323
|
-
f.write(_SESSION_MAGIC)
|
|
324
|
-
f.write(signature)
|
|
325
|
-
f.write(serialized)
|
|
326
|
-
else:
|
|
327
|
-
with open(path, "wb") as f:
|
|
328
|
-
f.write(_SESSION_MAGIC)
|
|
329
|
-
f.write(signature)
|
|
330
|
-
f.write(serialized)
|
|
331
|
-
|
|
332
|
-
self.history.record("save", {"path": str(path)})
|
|
333
|
-
|
|
334
|
-
return path
|
|
335
|
-
|
|
336
|
-
def _to_dict(self, include_traces: bool = True) -> dict[str, Any]:
|
|
337
|
-
"""Convert session to dictionary."""
|
|
338
|
-
data: dict[str, Any] = {
|
|
339
|
-
"version": "1.0",
|
|
340
|
-
"name": self.name,
|
|
341
|
-
"created_at": self.created_at.isoformat(),
|
|
342
|
-
"modified_at": self.modified_at.isoformat(),
|
|
343
|
-
"annotation_layers": {
|
|
344
|
-
name: layer.to_dict() for name, layer in self.annotation_layers.items()
|
|
345
|
-
},
|
|
346
|
-
"measurements": self.measurements,
|
|
347
|
-
"history": self.history.to_dict(),
|
|
348
|
-
"metadata": self.metadata,
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
if include_traces:
|
|
352
|
-
# Store traces with their data
|
|
353
|
-
data["traces"] = {}
|
|
354
|
-
for name, trace in self.traces.items():
|
|
355
|
-
trace_data = {
|
|
356
|
-
"type": type(trace).__name__,
|
|
357
|
-
"data": trace.data.tolist() if hasattr(trace, "data") else None,
|
|
358
|
-
"sample_rate": (
|
|
359
|
-
trace.metadata.sample_rate if hasattr(trace, "metadata") else None
|
|
360
|
-
),
|
|
361
|
-
}
|
|
362
|
-
data["traces"][name] = trace_data
|
|
363
|
-
else:
|
|
364
|
-
data["traces"] = {}
|
|
365
|
-
|
|
366
|
-
return data
|
|
367
|
-
|
|
368
|
-
@classmethod
|
|
369
|
-
def _from_dict(cls, data: dict[str, Any]) -> Session:
|
|
370
|
-
"""Create session from dictionary."""
|
|
371
|
-
session = cls(
|
|
372
|
-
name=data.get("name", "Untitled Session"),
|
|
373
|
-
metadata=data.get("metadata", {}),
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
if "created_at" in data:
|
|
377
|
-
session.created_at = datetime.fromisoformat(data["created_at"])
|
|
378
|
-
if "modified_at" in data:
|
|
379
|
-
session.modified_at = datetime.fromisoformat(data["modified_at"])
|
|
380
|
-
|
|
381
|
-
# Restore annotation layers
|
|
382
|
-
for name, layer_data in data.get("annotation_layers", {}).items():
|
|
383
|
-
session.annotation_layers[name] = AnnotationLayer.from_dict(layer_data)
|
|
384
|
-
|
|
385
|
-
# Restore measurements
|
|
386
|
-
session.measurements = data.get("measurements", {})
|
|
387
|
-
|
|
388
|
-
# Restore history
|
|
389
|
-
if "history" in data:
|
|
390
|
-
session.history = OperationHistory.from_dict(data["history"])
|
|
391
|
-
|
|
392
|
-
# Restore traces (if included)
|
|
393
|
-
if "traces" in data:
|
|
394
|
-
from oscura.core.types import WaveformTrace
|
|
395
|
-
|
|
396
|
-
for name, trace_data in data["traces"].items():
|
|
397
|
-
if trace_data.get("data") is not None:
|
|
398
|
-
session.traces[name] = WaveformTrace( # type: ignore[call-arg]
|
|
399
|
-
data=np.array(trace_data["data"]),
|
|
400
|
-
sample_rate=trace_data.get("sample_rate", 1.0),
|
|
401
|
-
)
|
|
402
|
-
|
|
403
|
-
return session
|
|
404
|
-
|
|
405
|
-
def _mark_modified(self) -> None:
|
|
406
|
-
"""Update modification timestamp."""
|
|
407
|
-
self.modified_at = datetime.now()
|
|
408
|
-
|
|
409
|
-
def summary(self) -> str:
|
|
410
|
-
"""Get session summary."""
|
|
411
|
-
lines = [
|
|
412
|
-
f"Session: {self.name}",
|
|
413
|
-
f"Created: {self.created_at.strftime('%Y-%m-%d %H:%M')}",
|
|
414
|
-
f"Modified: {self.modified_at.strftime('%Y-%m-%d %H:%M')}",
|
|
415
|
-
f"Traces: {len(self.traces)}",
|
|
416
|
-
f"Annotations: {sum(len(l.annotations) for l in self.annotation_layers.values())}", # noqa: E741
|
|
417
|
-
f"Measurements: {len(self.measurements)}",
|
|
418
|
-
f"History entries: {len(self.history.entries)}",
|
|
419
|
-
]
|
|
420
|
-
return "\n".join(lines)
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
def load_session(path: str | Path) -> Session:
|
|
424
|
-
"""Load session from file with HMAC signature verification.
|
|
425
|
-
|
|
426
|
-
Session files must be in the current OSC1 format with HMAC signature.
|
|
427
|
-
Legacy session files without signatures are not supported.
|
|
428
|
-
|
|
429
|
-
Args:
|
|
430
|
-
path: Path to session file (.tks).
|
|
431
|
-
|
|
432
|
-
Returns:
|
|
433
|
-
Loaded Session object.
|
|
434
|
-
|
|
435
|
-
Raises:
|
|
436
|
-
SecurityError: If signature verification fails or file is not in OSC1 format.
|
|
437
|
-
gzip.BadGzipFile: If file is neither valid gzip nor uncompressed session.
|
|
438
|
-
|
|
439
|
-
Example:
|
|
440
|
-
>>> session = load_session('debug_session.tks')
|
|
441
|
-
>>> print(session.list_traces())
|
|
442
|
-
|
|
443
|
-
Security Warning:
|
|
444
|
-
Session files use pickle serialization. Only load session files from
|
|
445
|
-
trusted sources. Loading a malicious .tks file could execute arbitrary
|
|
446
|
-
code. Never load session files from untrusted or unknown sources.
|
|
447
|
-
|
|
448
|
-
All session files must include HMAC signatures for integrity verification.
|
|
449
|
-
For secure data exchange with untrusted parties, consider exporting to
|
|
450
|
-
JSON or HDF5 formats instead of using pickle-based session files.
|
|
451
|
-
"""
|
|
452
|
-
path = Path(path)
|
|
453
|
-
|
|
454
|
-
def _load_with_verification(f: Any) -> dict[str, Any]:
|
|
455
|
-
"""Load and verify session file with HMAC signature.
|
|
456
|
-
|
|
457
|
-
Args:
|
|
458
|
-
f: File object (gzip or regular).
|
|
459
|
-
|
|
460
|
-
Returns:
|
|
461
|
-
Deserialized session dictionary.
|
|
462
|
-
|
|
463
|
-
Raises:
|
|
464
|
-
SecurityError: If magic bytes or signature verification fails.
|
|
465
|
-
"""
|
|
466
|
-
# Read magic bytes
|
|
467
|
-
magic = f.read(len(_SESSION_MAGIC))
|
|
468
|
-
|
|
469
|
-
if magic != _SESSION_MAGIC:
|
|
470
|
-
raise SecurityError(
|
|
471
|
-
"This is a legacy session file. Please re-save with current version.",
|
|
472
|
-
file_path=str(path),
|
|
473
|
-
check_type="Session format",
|
|
474
|
-
details="Expected OSC1 format with HMAC signature",
|
|
475
|
-
)
|
|
476
|
-
|
|
477
|
-
# Read signature and payload
|
|
478
|
-
signature = f.read(_SESSION_SIGNATURE_SIZE)
|
|
479
|
-
serialized = f.read()
|
|
480
|
-
|
|
481
|
-
if not signature or not serialized:
|
|
482
|
-
raise SecurityError(
|
|
483
|
-
"This is a legacy session file. Please re-save with current version.",
|
|
484
|
-
file_path=str(path),
|
|
485
|
-
check_type="Session format",
|
|
486
|
-
details="File is incomplete or corrupted",
|
|
487
|
-
)
|
|
488
|
-
|
|
489
|
-
# Verify HMAC signature
|
|
490
|
-
expected = hmac.new(_SECURITY_KEY, serialized, hashlib.sha256).digest()
|
|
491
|
-
if not hmac.compare_digest(signature, expected):
|
|
492
|
-
raise SecurityError(
|
|
493
|
-
"Session file signature verification failed",
|
|
494
|
-
file_path=str(path),
|
|
495
|
-
check_type="HMAC signature",
|
|
496
|
-
details="File may be corrupted or tampered with",
|
|
497
|
-
)
|
|
498
|
-
|
|
499
|
-
# Deserialize verified data
|
|
500
|
-
data = cast("dict[str, Any]", pickle.loads(serialized))
|
|
501
|
-
return data
|
|
502
|
-
|
|
503
|
-
# Try loading (compressed first, then uncompressed)
|
|
504
|
-
try:
|
|
505
|
-
with gzip.open(path, "rb") as f:
|
|
506
|
-
data = _load_with_verification(f)
|
|
507
|
-
except gzip.BadGzipFile:
|
|
508
|
-
with open(path, "rb") as f: # type: ignore[assignment]
|
|
509
|
-
data = _load_with_verification(f)
|
|
510
|
-
|
|
511
|
-
session = Session._from_dict(data)
|
|
512
|
-
session._file_path = path
|
|
513
|
-
|
|
514
|
-
return session
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
__all__ = [
|
|
518
|
-
"Session",
|
|
519
|
-
"load_session",
|
|
520
|
-
]
|
oscura/workflow/__init__.py
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"""Workflow execution and DAG-based analysis.
|
|
2
|
-
|
|
3
|
-
This module provides directed acyclic graph (DAG) execution for complex
|
|
4
|
-
multi-step analysis workflows with automatic dependency resolution and
|
|
5
|
-
parallel execution.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from oscura.workflow.dag import TaskNode, WorkflowDAG
|
|
9
|
-
|
|
10
|
-
__all__ = [
|
|
11
|
-
"TaskNode",
|
|
12
|
-
"WorkflowDAG",
|
|
13
|
-
]
|