oscura 0.5.0__py3-none-any.whl → 0.6.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/__init__.py +0 -48
- oscura/analyzers/digital/edges.py +325 -65
- oscura/analyzers/digital/extraction.py +0 -195
- 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/__init__.py +1 -22
- 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 +2763 -0
- 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/core/schemas/bus_configuration.json +322 -0
- oscura/core/schemas/device_mapping.json +182 -0
- oscura/core/schemas/packet_format.json +418 -0
- oscura/core/schemas/protocol_definition.json +363 -0
- 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 -20
- oscura/export/kaitai_struct.py +513 -0
- oscura/export/scapy_layer.py +801 -0
- oscura/export/wireshark/README.md +15 -15
- 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/README.md +7 -7
- 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 +171 -63
- 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/vcd.py +215 -117
- oscura/loaders/wav.py +155 -68
- oscura/reporting/__init__.py +9 -7
- 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/reporting/templates/index.md +13 -13
- 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/autodetect.py +1 -5
- 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 +11 -3
- 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.6.0.dist-info/METADATA +643 -0
- oscura-0.6.0.dist-info/RECORD +590 -0
- oscura/analyzers/digital/ic_database.py +0 -498
- oscura/analyzers/digital/timing_paths.py +0 -339
- oscura/analyzers/digital/vintage.py +0 -377
- oscura/analyzers/digital/vintage_result.py +0 -148
- oscura/analyzers/protocols/parallel_bus.py +0 -449
- 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/export/wavedrom.py +0 -430
- 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 -338
- 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/exporters/vintage_logic_csv.py +0 -247
- oscura/reporting/vintage_logic_report.py +0 -523
- 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/visualization/digital_advanced.py +0 -718
- oscura/visualization/figure_manager.py +0 -156
- oscura/workflow/__init__.py +0 -13
- oscura-0.5.0.dist-info/METADATA +0 -407
- oscura-0.5.0.dist-info/RECORD +0 -486
- /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/{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.0.dist-info → oscura-0.6.0.dist-info}/WHEEL +0 -0
- {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/entry_points.txt +0 -0
- {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/licenses/LICENSE +0 -0
oscura/batch/aggregate.py
DELETED
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
"""Result aggregation for batch analysis.
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
This module provides statistical aggregation and reporting for batch
|
|
5
|
-
analysis results, including outlier detection and export capabilities.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
from typing import Any
|
|
10
|
-
|
|
11
|
-
import numpy as np
|
|
12
|
-
import pandas as pd
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def aggregate_results(
|
|
16
|
-
results: pd.DataFrame,
|
|
17
|
-
*,
|
|
18
|
-
metrics: list[str] | None = None,
|
|
19
|
-
outlier_threshold: float = 3.0,
|
|
20
|
-
include_plots: bool = False,
|
|
21
|
-
output_format: str = "dict",
|
|
22
|
-
output_file: str | Path | None = None,
|
|
23
|
-
) -> dict[str, Any] | pd.DataFrame:
|
|
24
|
-
"""Aggregate results from batch analysis into summary statistics.
|
|
25
|
-
|
|
26
|
-
: Computes comprehensive statistics (mean, std, min, max,
|
|
27
|
-
outliers) for each metric in the batch results. Supports export to various
|
|
28
|
-
formats and optional visualization generation.
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
results: DataFrame from batch_analyze() containing analysis results
|
|
32
|
-
metrics: List of column names to aggregate (default: all numeric columns)
|
|
33
|
-
outlier_threshold: Z-score threshold for outlier detection (default: 3.0)
|
|
34
|
-
include_plots: Generate comparison plots across files (default: False)
|
|
35
|
-
output_format: Output format - 'dict', 'dataframe', 'csv', 'excel', 'html'
|
|
36
|
-
output_file: Optional output file path for export formats
|
|
37
|
-
|
|
38
|
-
Returns:
|
|
39
|
-
Dictionary or DataFrame with summary statistics:
|
|
40
|
-
- count: Number of valid values
|
|
41
|
-
- mean: Mean value
|
|
42
|
-
- std: Standard deviation
|
|
43
|
-
- min: Minimum value
|
|
44
|
-
- max: Maximum value
|
|
45
|
-
- median: Median value
|
|
46
|
-
- q25: 25th percentile
|
|
47
|
-
- q75: 75th percentile
|
|
48
|
-
- outliers: List of outlier values
|
|
49
|
-
- outlier_files: List of files containing outliers
|
|
50
|
-
|
|
51
|
-
Raises:
|
|
52
|
-
ValueError: If no numeric metrics are found in results.
|
|
53
|
-
|
|
54
|
-
Examples:
|
|
55
|
-
>>> results = osc.batch_analyze(files, osc.characterize_buffer)
|
|
56
|
-
>>> summary = osc.aggregate_results(
|
|
57
|
-
... results,
|
|
58
|
-
... metrics=['rise_time', 'fall_time'],
|
|
59
|
-
... outlier_threshold=2.5
|
|
60
|
-
... )
|
|
61
|
-
>>> print(summary['rise_time']['mean'])
|
|
62
|
-
>>> print(summary['rise_time']['outlier_files'])
|
|
63
|
-
|
|
64
|
-
Notes:
|
|
65
|
-
- Outliers detected using IQR method: values outside [Q1 - k*IQR, Q3 + k*IQR]
|
|
66
|
-
where k = (threshold / 3.0) * 1.5 (more robust than z-score for heavy-tailed data)
|
|
67
|
-
- Non-numeric columns are automatically skipped
|
|
68
|
-
- Missing values (NaN) are excluded from statistics
|
|
69
|
-
- CSV/Excel/HTML export requires output_file parameter
|
|
70
|
-
|
|
71
|
-
References:
|
|
72
|
-
BATCH-002: Result Aggregation
|
|
73
|
-
"""
|
|
74
|
-
if results.empty:
|
|
75
|
-
return {} if output_format == "dict" else pd.DataFrame()
|
|
76
|
-
|
|
77
|
-
# Determine metrics to analyze
|
|
78
|
-
if metrics is None:
|
|
79
|
-
# Auto-select all numeric columns except 'file' and 'error'
|
|
80
|
-
metrics = results.select_dtypes(include=[np.number]).columns.tolist()
|
|
81
|
-
metrics = [m for m in metrics if m not in ["file", "error"]]
|
|
82
|
-
|
|
83
|
-
if not metrics:
|
|
84
|
-
raise ValueError("No numeric metrics found in results")
|
|
85
|
-
|
|
86
|
-
# Compute aggregated statistics
|
|
87
|
-
aggregated: dict[str, dict[str, Any]] = {}
|
|
88
|
-
|
|
89
|
-
for metric in metrics:
|
|
90
|
-
if metric not in results.columns:
|
|
91
|
-
continue
|
|
92
|
-
|
|
93
|
-
# Extract valid (non-null) values
|
|
94
|
-
values = results[metric].dropna()
|
|
95
|
-
|
|
96
|
-
if values.empty:
|
|
97
|
-
aggregated[metric] = {
|
|
98
|
-
"count": 0,
|
|
99
|
-
"mean": np.nan,
|
|
100
|
-
"std": np.nan,
|
|
101
|
-
"min": np.nan,
|
|
102
|
-
"max": np.nan,
|
|
103
|
-
"median": np.nan,
|
|
104
|
-
"q25": np.nan,
|
|
105
|
-
"q75": np.nan,
|
|
106
|
-
"outliers": [],
|
|
107
|
-
"outlier_files": [],
|
|
108
|
-
}
|
|
109
|
-
continue
|
|
110
|
-
|
|
111
|
-
# Basic statistics
|
|
112
|
-
stats = {
|
|
113
|
-
"count": len(values),
|
|
114
|
-
"mean": float(values.mean()),
|
|
115
|
-
"std": float(values.std()),
|
|
116
|
-
"min": float(values.min()),
|
|
117
|
-
"max": float(values.max()),
|
|
118
|
-
"median": float(values.median()),
|
|
119
|
-
"q25": float(values.quantile(0.25)),
|
|
120
|
-
"q75": float(values.quantile(0.75)),
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
# Outlier detection using IQR method (more robust than z-score)
|
|
124
|
-
# IQR method: outliers are values outside [Q1 - k*IQR, Q3 + k*IQR]
|
|
125
|
-
# where k = outlier_threshold * 1.5 (standard is k=1.5, we scale by threshold)
|
|
126
|
-
if len(values) > 3: # Need at least 4 values for meaningful IQR
|
|
127
|
-
q1 = stats["q25"]
|
|
128
|
-
q3 = stats["q75"]
|
|
129
|
-
iqr = q3 - q1
|
|
130
|
-
|
|
131
|
-
# Scale IQR multiplier by threshold (default 3.0 -> 2.0 * 1.5 = 3.0)
|
|
132
|
-
k = (outlier_threshold / 3.0) * 1.5
|
|
133
|
-
|
|
134
|
-
lower_bound = q1 - k * iqr
|
|
135
|
-
upper_bound = q3 + k * iqr
|
|
136
|
-
|
|
137
|
-
outlier_mask = (values < lower_bound) | (values > upper_bound)
|
|
138
|
-
outlier_indices = values[outlier_mask].index.tolist()
|
|
139
|
-
stats["outliers"] = values[outlier_mask].tolist()
|
|
140
|
-
|
|
141
|
-
# Get corresponding filenames if available
|
|
142
|
-
if "file" in results.columns:
|
|
143
|
-
stats["outlier_files"] = results.loc[outlier_indices, "file"].tolist()
|
|
144
|
-
else:
|
|
145
|
-
stats["outlier_files"] = outlier_indices
|
|
146
|
-
else:
|
|
147
|
-
stats["outliers"] = [] # type: ignore[assignment]
|
|
148
|
-
stats["outlier_files"] = [] # type: ignore[assignment]
|
|
149
|
-
|
|
150
|
-
aggregated[metric] = stats
|
|
151
|
-
|
|
152
|
-
# Generate plots if requested
|
|
153
|
-
if include_plots:
|
|
154
|
-
# Import here to avoid circular dependency
|
|
155
|
-
try:
|
|
156
|
-
import matplotlib.pyplot as plt
|
|
157
|
-
|
|
158
|
-
for metric in metrics:
|
|
159
|
-
if metric not in aggregated:
|
|
160
|
-
continue
|
|
161
|
-
|
|
162
|
-
_fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
|
|
163
|
-
|
|
164
|
-
# Histogram
|
|
165
|
-
results[metric].dropna().hist(ax=ax1, bins=30)
|
|
166
|
-
ax1.axvline(
|
|
167
|
-
aggregated[metric]["mean"],
|
|
168
|
-
color="r",
|
|
169
|
-
linestyle="--",
|
|
170
|
-
label="Mean",
|
|
171
|
-
)
|
|
172
|
-
ax1.axvline(
|
|
173
|
-
aggregated[metric]["median"],
|
|
174
|
-
color="g",
|
|
175
|
-
linestyle="--",
|
|
176
|
-
label="Median",
|
|
177
|
-
)
|
|
178
|
-
ax1.set_xlabel(metric)
|
|
179
|
-
ax1.set_ylabel("Count")
|
|
180
|
-
ax1.legend()
|
|
181
|
-
ax1.set_title(f"{metric} Distribution")
|
|
182
|
-
|
|
183
|
-
# Box plot
|
|
184
|
-
ax2.boxplot(results[metric].dropna())
|
|
185
|
-
ax2.set_ylabel(metric)
|
|
186
|
-
ax2.set_title(f"{metric} Box Plot")
|
|
187
|
-
|
|
188
|
-
plt.tight_layout()
|
|
189
|
-
|
|
190
|
-
# Save or show based on output_file
|
|
191
|
-
if output_file:
|
|
192
|
-
plot_file = Path(output_file).with_suffix("") / f"{metric}_plot.png"
|
|
193
|
-
plot_file.parent.mkdir(parents=True, exist_ok=True)
|
|
194
|
-
plt.savefig(plot_file)
|
|
195
|
-
else:
|
|
196
|
-
plt.show()
|
|
197
|
-
|
|
198
|
-
plt.close()
|
|
199
|
-
|
|
200
|
-
except ImportError:
|
|
201
|
-
pass # Silently skip plotting if matplotlib not available
|
|
202
|
-
|
|
203
|
-
# Format output
|
|
204
|
-
if output_format == "dict":
|
|
205
|
-
return aggregated
|
|
206
|
-
|
|
207
|
-
elif output_format == "dataframe":
|
|
208
|
-
# Convert to DataFrame with metrics as rows
|
|
209
|
-
df = pd.DataFrame(aggregated).T
|
|
210
|
-
# Drop list columns for DataFrame format
|
|
211
|
-
df = df.drop(columns=["outliers", "outlier_files"], errors="ignore")
|
|
212
|
-
return df
|
|
213
|
-
|
|
214
|
-
elif output_format in ["csv", "excel", "html"]:
|
|
215
|
-
if not output_file:
|
|
216
|
-
raise ValueError(f"{output_format} format requires output_file parameter")
|
|
217
|
-
|
|
218
|
-
df = pd.DataFrame(aggregated).T
|
|
219
|
-
df = df.drop(columns=["outliers", "outlier_files"], errors="ignore")
|
|
220
|
-
|
|
221
|
-
if output_format == "csv":
|
|
222
|
-
df.to_csv(output_file)
|
|
223
|
-
elif output_format == "excel":
|
|
224
|
-
df.to_excel(output_file)
|
|
225
|
-
elif output_format == "html":
|
|
226
|
-
# Generate HTML report
|
|
227
|
-
html = _generate_html_report(results, aggregated, metrics)
|
|
228
|
-
Path(output_file).write_text(html)
|
|
229
|
-
|
|
230
|
-
return df
|
|
231
|
-
|
|
232
|
-
else:
|
|
233
|
-
raise ValueError(f"Unknown output_format: {output_format}")
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
def _generate_html_report(
|
|
237
|
-
results: pd.DataFrame,
|
|
238
|
-
aggregated: dict[str, dict[str, Any]],
|
|
239
|
-
metrics: list[str],
|
|
240
|
-
) -> str:
|
|
241
|
-
"""Generate HTML report for batch analysis results."""
|
|
242
|
-
html = """
|
|
243
|
-
<!DOCTYPE html>
|
|
244
|
-
<html>
|
|
245
|
-
<head>
|
|
246
|
-
<title>Batch Analysis Report</title>
|
|
247
|
-
<style>
|
|
248
|
-
body { font-family: Arial, sans-serif; margin: 20px; }
|
|
249
|
-
h1 { color: #333; }
|
|
250
|
-
h2 { color: #666; margin-top: 30px; }
|
|
251
|
-
table { border-collapse: collapse; width: 100%; margin: 20px 0; }
|
|
252
|
-
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
|
253
|
-
th { background-color: #4CAF50; color: white; }
|
|
254
|
-
tr:nth-child(even) { background-color: #f2f2f2; }
|
|
255
|
-
.outlier { background-color: #ffcccc; }
|
|
256
|
-
</style>
|
|
257
|
-
</head>
|
|
258
|
-
<body>
|
|
259
|
-
<h1>Batch Analysis Report</h1>
|
|
260
|
-
"""
|
|
261
|
-
# Summary statistics table
|
|
262
|
-
html += "<h2>Summary Statistics</h2>\n<table>\n"
|
|
263
|
-
html += "<tr><th>Metric</th><th>Count</th><th>Mean</th><th>Std</th>"
|
|
264
|
-
html += "<th>Min</th><th>Median</th><th>Max</th><th>Outliers</th></tr>\n"
|
|
265
|
-
|
|
266
|
-
for metric in metrics:
|
|
267
|
-
if metric not in aggregated:
|
|
268
|
-
continue
|
|
269
|
-
stats = aggregated[metric]
|
|
270
|
-
html += "<tr>"
|
|
271
|
-
html += f"<td>{metric}</td>"
|
|
272
|
-
html += f"<td>{stats['count']}</td>"
|
|
273
|
-
html += f"<td>{stats['mean']:.4g}</td>"
|
|
274
|
-
html += f"<td>{stats['std']:.4g}</td>"
|
|
275
|
-
html += f"<td>{stats['min']:.4g}</td>"
|
|
276
|
-
html += f"<td>{stats['median']:.4g}</td>"
|
|
277
|
-
html += f"<td>{stats['max']:.4g}</td>"
|
|
278
|
-
html += f"<td>{len(stats['outliers'])}</td>"
|
|
279
|
-
html += "</tr>\n"
|
|
280
|
-
|
|
281
|
-
html += "</table>\n"
|
|
282
|
-
|
|
283
|
-
# Outlier details
|
|
284
|
-
has_outliers = any(len(aggregated[m]["outliers"]) > 0 for m in metrics if m in aggregated)
|
|
285
|
-
|
|
286
|
-
if has_outliers:
|
|
287
|
-
html += "<h2>Outliers Detected</h2>\n"
|
|
288
|
-
for metric in metrics:
|
|
289
|
-
if metric not in aggregated:
|
|
290
|
-
continue
|
|
291
|
-
stats = aggregated[metric]
|
|
292
|
-
if stats["outliers"]:
|
|
293
|
-
html += f"<h3>{metric}</h3>\n<table>\n"
|
|
294
|
-
html += "<tr><th>File</th><th>Value</th></tr>\n"
|
|
295
|
-
for file, value in zip(stats["outlier_files"], stats["outliers"], strict=False):
|
|
296
|
-
html += f"<tr class='outlier'><td>{file}</td><td>{value:.4g}</td></tr>\n"
|
|
297
|
-
html += "</table>\n"
|
|
298
|
-
|
|
299
|
-
html += "</body>\n</html>"
|
|
300
|
-
return html
|
oscura/batch/analyze.py
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
"""Multi-file batch analysis with parallel execution support.
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
This module provides parallel batch processing of signal files using
|
|
5
|
-
concurrent.futures for efficient multi-core utilization.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from collections.abc import Callable
|
|
9
|
-
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_completed
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
from typing import Any
|
|
12
|
-
|
|
13
|
-
import pandas as pd
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def batch_analyze(
|
|
17
|
-
files: list[str | Path],
|
|
18
|
-
analysis_fn: Callable[[str | Path], dict[str, Any]],
|
|
19
|
-
*,
|
|
20
|
-
parallel: bool = False,
|
|
21
|
-
workers: int | None = None,
|
|
22
|
-
progress_callback: Callable[[int, int, str], None] | None = None,
|
|
23
|
-
use_threads: bool = False,
|
|
24
|
-
**config: Any,
|
|
25
|
-
) -> pd.DataFrame:
|
|
26
|
-
"""Analyze multiple files with the same analysis configuration.
|
|
27
|
-
|
|
28
|
-
: Multi-file analysis with parallel execution support
|
|
29
|
-
via concurrent.futures. Returns aggregated results as a DataFrame for
|
|
30
|
-
easy statistical analysis and export.
|
|
31
|
-
|
|
32
|
-
Args:
|
|
33
|
-
files: List of file paths to analyze
|
|
34
|
-
analysis_fn: Analysis function to apply to each file.
|
|
35
|
-
Must accept a file path and return a dict of results.
|
|
36
|
-
parallel: Enable parallel processing (default: False)
|
|
37
|
-
workers: Number of parallel workers (default: CPU count)
|
|
38
|
-
progress_callback: Optional callback for progress updates.
|
|
39
|
-
Called with (current, total, filename) after each file.
|
|
40
|
-
use_threads: Use ThreadPoolExecutor instead of ProcessPoolExecutor
|
|
41
|
-
(useful for I/O-bound tasks, default: False)
|
|
42
|
-
**config: Additional keyword arguments passed to analysis_fn
|
|
43
|
-
|
|
44
|
-
Returns:
|
|
45
|
-
DataFrame with one row per file, columns from analysis results.
|
|
46
|
-
Always includes a 'file' column with the input filename.
|
|
47
|
-
|
|
48
|
-
Examples:
|
|
49
|
-
>>> import oscura as osc
|
|
50
|
-
>>> import glob
|
|
51
|
-
>>> files = glob.glob('captures/*.wfm')
|
|
52
|
-
>>> results = osc.batch_analyze(
|
|
53
|
-
... files,
|
|
54
|
-
... analysis_fn=osc.characterize_buffer,
|
|
55
|
-
... parallel=True,
|
|
56
|
-
... workers=4
|
|
57
|
-
... )
|
|
58
|
-
>>> print(results[['file', 'rise_time', 'fall_time', 'status']])
|
|
59
|
-
>>> results.to_csv('batch_results.csv')
|
|
60
|
-
|
|
61
|
-
Notes:
|
|
62
|
-
- Use parallel=True for CPU-bound analysis functions
|
|
63
|
-
- Use use_threads=True for I/O-bound operations (file loading)
|
|
64
|
-
- Progress callback is called from worker threads/processes
|
|
65
|
-
- All exceptions during analysis are caught and stored in 'error' column
|
|
66
|
-
|
|
67
|
-
References:
|
|
68
|
-
BATCH-001: Multi-File Analysis
|
|
69
|
-
"""
|
|
70
|
-
if not files:
|
|
71
|
-
return pd.DataFrame()
|
|
72
|
-
|
|
73
|
-
# Wrapper to include config in analysis calls
|
|
74
|
-
def _wrapped_analysis(filepath: str | Path) -> dict[str, Any]:
|
|
75
|
-
try:
|
|
76
|
-
result = analysis_fn(filepath, **config)
|
|
77
|
-
# Ensure result is a dict
|
|
78
|
-
if not isinstance(result, dict):
|
|
79
|
-
result = {"result": result} # type: ignore[unreachable]
|
|
80
|
-
result["file"] = str(filepath)
|
|
81
|
-
result["error"] = None
|
|
82
|
-
return result
|
|
83
|
-
except Exception as e:
|
|
84
|
-
# Return error info on failure
|
|
85
|
-
return {
|
|
86
|
-
"file": str(filepath),
|
|
87
|
-
"error": str(e),
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
results: list[dict[str, Any]] = []
|
|
91
|
-
total = len(files)
|
|
92
|
-
|
|
93
|
-
if parallel:
|
|
94
|
-
# Use concurrent.futures for parallel execution
|
|
95
|
-
executor_class = ThreadPoolExecutor if use_threads else ProcessPoolExecutor
|
|
96
|
-
with executor_class(max_workers=workers) as executor:
|
|
97
|
-
# Submit all tasks
|
|
98
|
-
future_to_file = {executor.submit(_wrapped_analysis, f): f for f in files}
|
|
99
|
-
|
|
100
|
-
# Process results as they complete
|
|
101
|
-
for i, future in enumerate(as_completed(future_to_file), 1):
|
|
102
|
-
filepath = future_to_file[future]
|
|
103
|
-
try:
|
|
104
|
-
result = future.result()
|
|
105
|
-
results.append(result)
|
|
106
|
-
|
|
107
|
-
if progress_callback:
|
|
108
|
-
progress_callback(i, total, str(filepath))
|
|
109
|
-
except Exception as e:
|
|
110
|
-
# Catch execution errors
|
|
111
|
-
results.append(
|
|
112
|
-
{
|
|
113
|
-
"file": str(filepath),
|
|
114
|
-
"error": f"Execution error: {e}",
|
|
115
|
-
}
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
else:
|
|
119
|
-
# Sequential processing
|
|
120
|
-
for i, filepath in enumerate(files, 1):
|
|
121
|
-
result = _wrapped_analysis(filepath)
|
|
122
|
-
results.append(result)
|
|
123
|
-
|
|
124
|
-
if progress_callback:
|
|
125
|
-
progress_callback(i, total, str(filepath))
|
|
126
|
-
|
|
127
|
-
# Convert to DataFrame
|
|
128
|
-
df = pd.DataFrame(results)
|
|
129
|
-
|
|
130
|
-
# Reorder columns: file first, error last
|
|
131
|
-
cols = df.columns.tolist()
|
|
132
|
-
if "file" in cols:
|
|
133
|
-
cols.remove("file")
|
|
134
|
-
cols = ["file", *cols]
|
|
135
|
-
if "error" in cols:
|
|
136
|
-
cols.remove("error")
|
|
137
|
-
cols = [*cols, "error"]
|
|
138
|
-
|
|
139
|
-
return df[cols]
|
oscura/dsl/__init__.py
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"""Oscura DSL - Domain-Specific Language for trace analysis.
|
|
2
|
-
|
|
3
|
-
Provides a simple, declarative language for defining trace analysis workflows.
|
|
4
|
-
|
|
5
|
-
Example usage:
|
|
6
|
-
```python
|
|
7
|
-
from oscura.dsl import execute_dsl
|
|
8
|
-
|
|
9
|
-
# Execute DSL script
|
|
10
|
-
script = '''
|
|
11
|
-
$data = load "capture.csv"
|
|
12
|
-
$filtered = $data | filter lowpass 1000
|
|
13
|
-
$rise = $filtered | measure rise_time
|
|
14
|
-
'''
|
|
15
|
-
|
|
16
|
-
env = execute_dsl(script)
|
|
17
|
-
print(f"Rise time: {env['$rise']}")
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Or start interactive REPL:
|
|
21
|
-
```python
|
|
22
|
-
from oscura.dsl import start_repl
|
|
23
|
-
start_repl()
|
|
24
|
-
```
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
from oscura.dsl.commands import BUILTIN_COMMANDS
|
|
28
|
-
from oscura.dsl.interpreter import Interpreter, InterpreterError, execute_dsl
|
|
29
|
-
from oscura.dsl.parser import (
|
|
30
|
-
Assignment,
|
|
31
|
-
Command,
|
|
32
|
-
Expression,
|
|
33
|
-
ForLoop,
|
|
34
|
-
FunctionCall,
|
|
35
|
-
Lexer,
|
|
36
|
-
Literal,
|
|
37
|
-
Parser,
|
|
38
|
-
Pipeline,
|
|
39
|
-
Statement,
|
|
40
|
-
Token,
|
|
41
|
-
TokenType,
|
|
42
|
-
Variable,
|
|
43
|
-
parse_dsl,
|
|
44
|
-
)
|
|
45
|
-
from oscura.dsl.repl import REPL, start_repl
|
|
46
|
-
|
|
47
|
-
__all__ = [
|
|
48
|
-
# Commands
|
|
49
|
-
"BUILTIN_COMMANDS",
|
|
50
|
-
# REPL
|
|
51
|
-
"REPL",
|
|
52
|
-
# AST nodes
|
|
53
|
-
"Assignment",
|
|
54
|
-
"Command",
|
|
55
|
-
"Expression",
|
|
56
|
-
"ForLoop",
|
|
57
|
-
"FunctionCall",
|
|
58
|
-
# Interpreter
|
|
59
|
-
"Interpreter",
|
|
60
|
-
"InterpreterError",
|
|
61
|
-
# Parser
|
|
62
|
-
"Lexer",
|
|
63
|
-
"Literal",
|
|
64
|
-
"Parser",
|
|
65
|
-
"Pipeline",
|
|
66
|
-
"Statement",
|
|
67
|
-
"Token",
|
|
68
|
-
"TokenType",
|
|
69
|
-
"Variable",
|
|
70
|
-
"execute_dsl",
|
|
71
|
-
"parse_dsl",
|
|
72
|
-
"start_repl",
|
|
73
|
-
]
|
oscura/exceptions.py
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"""Oscura exception hierarchy - DEPRECATED compatibility module.
|
|
2
|
-
|
|
3
|
-
.. deprecated:: 1.0.0
|
|
4
|
-
This module is deprecated for backward compatibility only.
|
|
5
|
-
New code MUST import from `oscura.core.exceptions` directly.
|
|
6
|
-
This module will be removed in a future major version.
|
|
7
|
-
|
|
8
|
-
This module re-exports exceptions from oscura.core.exceptions.
|
|
9
|
-
The canonical location for all exception classes is `oscura.core.exceptions`.
|
|
10
|
-
|
|
11
|
-
Why two files exist:
|
|
12
|
-
- `oscura/core/exceptions.py`: Canonical implementation of all exception classes
|
|
13
|
-
- `oscura/exceptions.py` (this file): Deprecated re-export for backward compatibility
|
|
14
|
-
|
|
15
|
-
Migration guide:
|
|
16
|
-
Old (deprecated):
|
|
17
|
-
from oscura.exceptions import LoaderError
|
|
18
|
-
|
|
19
|
-
New (preferred):
|
|
20
|
-
from oscura.core.exceptions import LoaderError
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
import warnings
|
|
24
|
-
|
|
25
|
-
# Issue deprecation warning on import
|
|
26
|
-
warnings.warn(
|
|
27
|
-
"oscura.exceptions is deprecated. "
|
|
28
|
-
"Import from oscura.core.exceptions instead. "
|
|
29
|
-
"This module will be removed in a future major version.",
|
|
30
|
-
DeprecationWarning,
|
|
31
|
-
stacklevel=2,
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
# Re-export all exceptions from core.exceptions
|
|
35
|
-
from oscura.core.exceptions import ( # noqa: E402
|
|
36
|
-
AnalysisError,
|
|
37
|
-
ConfigurationError,
|
|
38
|
-
ExportError,
|
|
39
|
-
FormatError,
|
|
40
|
-
InsufficientDataError,
|
|
41
|
-
LoaderError,
|
|
42
|
-
OscuraError,
|
|
43
|
-
SampleRateError,
|
|
44
|
-
UnsupportedFormatError,
|
|
45
|
-
ValidationError,
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
__all__ = [
|
|
49
|
-
"AnalysisError",
|
|
50
|
-
"ConfigurationError",
|
|
51
|
-
"ExportError",
|
|
52
|
-
"FormatError",
|
|
53
|
-
"InsufficientDataError",
|
|
54
|
-
"LoaderError",
|
|
55
|
-
"OscuraError",
|
|
56
|
-
"SampleRateError",
|
|
57
|
-
"UnsupportedFormatError",
|
|
58
|
-
"ValidationError",
|
|
59
|
-
]
|