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
|
@@ -5,7 +5,7 @@ including API reference, usage examples, and metadata extraction from docstrings
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
Example:
|
|
8
|
-
>>> from oscura.extensibility.docs import generate_extension_docs
|
|
8
|
+
>>> from oscura.core.extensibility.docs import generate_extension_docs
|
|
9
9
|
>>> from pathlib import Path
|
|
10
10
|
>>>
|
|
11
11
|
>>> # Generate documentation for an extension
|
|
@@ -561,39 +561,65 @@ def _process_section(
|
|
|
561
561
|
include_examples: Extract examples
|
|
562
562
|
"""
|
|
563
563
|
if section in ["args", "arguments", "parameters"]:
|
|
564
|
-
|
|
565
|
-
for line in content:
|
|
566
|
-
line = line.strip()
|
|
567
|
-
if ":" in line:
|
|
568
|
-
parts = line.split(":", 1)
|
|
569
|
-
param_name = parts[0].strip()
|
|
570
|
-
param_desc = parts[1].strip()
|
|
571
|
-
func_doc.parameters.append((param_name, param_desc))
|
|
572
|
-
|
|
564
|
+
_process_parameters_section(func_doc, content)
|
|
573
565
|
elif section in ["returns", "return"]:
|
|
574
|
-
func_doc
|
|
575
|
-
|
|
566
|
+
_process_returns_section(func_doc, content)
|
|
576
567
|
elif section in ["example", "examples"] and include_examples:
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
568
|
+
_process_examples_section(func_doc, content)
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def _process_parameters_section(func_doc: FunctionDoc, content: list[str]) -> None:
|
|
572
|
+
"""Process parameters section.
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
func_doc: FunctionDoc to populate.
|
|
576
|
+
content: Section content lines.
|
|
577
|
+
"""
|
|
578
|
+
for line in content:
|
|
579
|
+
line = line.strip()
|
|
580
|
+
if ":" in line:
|
|
581
|
+
parts = line.split(":", 1)
|
|
582
|
+
param_name = parts[0].strip()
|
|
583
|
+
param_desc = parts[1].strip()
|
|
584
|
+
func_doc.parameters.append((param_name, param_desc))
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def _process_returns_section(func_doc: FunctionDoc, content: list[str]) -> None:
|
|
588
|
+
"""Process returns section.
|
|
589
|
+
|
|
590
|
+
Args:
|
|
591
|
+
func_doc: FunctionDoc to populate.
|
|
592
|
+
content: Section content lines.
|
|
593
|
+
"""
|
|
594
|
+
func_doc.returns = "\n".join(content).strip()
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
def _process_examples_section(func_doc: FunctionDoc, content: list[str]) -> None:
|
|
598
|
+
"""Process examples section.
|
|
599
|
+
|
|
600
|
+
Args:
|
|
601
|
+
func_doc: FunctionDoc to populate.
|
|
602
|
+
content: Section content lines.
|
|
603
|
+
"""
|
|
604
|
+
in_code = False
|
|
605
|
+
code_lines = []
|
|
606
|
+
|
|
607
|
+
for line in content:
|
|
608
|
+
if ">>>" in line or "..." in line:
|
|
609
|
+
in_code = True
|
|
610
|
+
code_lines.append(line.strip())
|
|
611
|
+
elif in_code:
|
|
612
|
+
if line.strip() and not line.strip().startswith("#"):
|
|
613
|
+
if not (">>>" in line or "..." in line):
|
|
614
|
+
in_code = False
|
|
615
|
+
if code_lines:
|
|
616
|
+
func_doc.examples.append("\n".join(code_lines))
|
|
617
|
+
code_lines = []
|
|
618
|
+
else:
|
|
619
|
+
code_lines.append(line.strip())
|
|
620
|
+
|
|
621
|
+
if code_lines:
|
|
622
|
+
func_doc.examples.append("\n".join(code_lines))
|
|
597
623
|
|
|
598
624
|
|
|
599
625
|
def _get_name_from_ast(node: ast.expr) -> str:
|
|
@@ -622,13 +648,30 @@ def _generate_markdown(docs: ExtensionDocs) -> str:
|
|
|
622
648
|
Returns:
|
|
623
649
|
Markdown string
|
|
624
650
|
"""
|
|
625
|
-
lines = []
|
|
651
|
+
lines: list[str] = []
|
|
652
|
+
|
|
653
|
+
# Title and metadata
|
|
654
|
+
_add_markdown_header(lines, docs)
|
|
626
655
|
|
|
627
|
-
#
|
|
656
|
+
# Dependencies
|
|
657
|
+
_add_markdown_dependencies(lines, docs)
|
|
658
|
+
|
|
659
|
+
# Modules
|
|
660
|
+
_add_markdown_modules(lines, docs)
|
|
661
|
+
|
|
662
|
+
return "\n".join(lines)
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
def _add_markdown_header(lines: list[str], docs: ExtensionDocs) -> None:
|
|
666
|
+
"""Add header section to markdown.
|
|
667
|
+
|
|
668
|
+
Args:
|
|
669
|
+
lines: List to append lines to.
|
|
670
|
+
docs: ExtensionDocs to render.
|
|
671
|
+
"""
|
|
628
672
|
lines.append(f"# {docs.name}")
|
|
629
673
|
lines.append("")
|
|
630
674
|
|
|
631
|
-
# Metadata
|
|
632
675
|
if docs.version:
|
|
633
676
|
lines.append(f"**Version:** {docs.version}")
|
|
634
677
|
lines.append("")
|
|
@@ -639,7 +682,14 @@ def _generate_markdown(docs: ExtensionDocs) -> str:
|
|
|
639
682
|
lines.append(docs.description)
|
|
640
683
|
lines.append("")
|
|
641
684
|
|
|
642
|
-
|
|
685
|
+
|
|
686
|
+
def _add_markdown_dependencies(lines: list[str], docs: ExtensionDocs) -> None:
|
|
687
|
+
"""Add dependencies section to markdown.
|
|
688
|
+
|
|
689
|
+
Args:
|
|
690
|
+
lines: List to append lines to.
|
|
691
|
+
docs: ExtensionDocs to render.
|
|
692
|
+
"""
|
|
643
693
|
if "dependencies" in docs.metadata:
|
|
644
694
|
lines.append("## Dependencies")
|
|
645
695
|
lines.append("")
|
|
@@ -647,7 +697,14 @@ def _generate_markdown(docs: ExtensionDocs) -> str:
|
|
|
647
697
|
lines.append(f"- {dep}")
|
|
648
698
|
lines.append("")
|
|
649
699
|
|
|
650
|
-
|
|
700
|
+
|
|
701
|
+
def _add_markdown_modules(lines: list[str], docs: ExtensionDocs) -> None:
|
|
702
|
+
"""Add modules section to markdown.
|
|
703
|
+
|
|
704
|
+
Args:
|
|
705
|
+
lines: List to append lines to.
|
|
706
|
+
docs: ExtensionDocs to render.
|
|
707
|
+
"""
|
|
651
708
|
for module in docs.modules:
|
|
652
709
|
lines.append(f"## Module: {module.name}")
|
|
653
710
|
lines.append("")
|
|
@@ -656,43 +713,74 @@ def _generate_markdown(docs: ExtensionDocs) -> str:
|
|
|
656
713
|
lines.append("")
|
|
657
714
|
|
|
658
715
|
# Classes
|
|
659
|
-
|
|
660
|
-
lines.append(f"### Class: {cls.name}")
|
|
661
|
-
lines.append("")
|
|
662
|
-
if cls.docstring:
|
|
663
|
-
lines.append(cls.docstring)
|
|
664
|
-
lines.append("")
|
|
665
|
-
|
|
666
|
-
# Methods
|
|
667
|
-
if cls.methods:
|
|
668
|
-
lines.append("#### Methods")
|
|
669
|
-
lines.append("")
|
|
670
|
-
for method in cls.methods:
|
|
671
|
-
lines.append(f"##### {method.name}")
|
|
672
|
-
lines.append("")
|
|
673
|
-
if method.signature:
|
|
674
|
-
lines.append("```python")
|
|
675
|
-
lines.append(method.signature)
|
|
676
|
-
lines.append("```")
|
|
677
|
-
lines.append("")
|
|
678
|
-
if method.docstring:
|
|
679
|
-
lines.append(method.docstring)
|
|
680
|
-
lines.append("")
|
|
716
|
+
_add_markdown_classes(lines, module)
|
|
681
717
|
|
|
682
718
|
# Functions
|
|
683
|
-
|
|
684
|
-
|
|
719
|
+
_add_markdown_functions(lines, module)
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
def _add_markdown_classes(lines: list[str], module: ModuleDoc) -> None:
|
|
723
|
+
"""Add classes section to markdown.
|
|
724
|
+
|
|
725
|
+
Args:
|
|
726
|
+
lines: List to append lines to.
|
|
727
|
+
module: ModuleDoc to render.
|
|
728
|
+
"""
|
|
729
|
+
for cls in module.classes:
|
|
730
|
+
lines.append(f"### Class: {cls.name}")
|
|
731
|
+
lines.append("")
|
|
732
|
+
if cls.docstring:
|
|
733
|
+
lines.append(cls.docstring)
|
|
685
734
|
lines.append("")
|
|
686
|
-
if func.signature:
|
|
687
|
-
lines.append("```python")
|
|
688
|
-
lines.append(func.signature)
|
|
689
|
-
lines.append("```")
|
|
690
|
-
lines.append("")
|
|
691
|
-
if func.docstring:
|
|
692
|
-
lines.append(func.docstring)
|
|
693
|
-
lines.append("")
|
|
694
735
|
|
|
695
|
-
|
|
736
|
+
# Methods
|
|
737
|
+
if cls.methods:
|
|
738
|
+
lines.append("#### Methods")
|
|
739
|
+
lines.append("")
|
|
740
|
+
_add_markdown_methods(lines, cls.methods)
|
|
741
|
+
|
|
742
|
+
|
|
743
|
+
def _add_markdown_methods(lines: list[str], methods: list[FunctionDoc]) -> None:
|
|
744
|
+
"""Add methods section to markdown.
|
|
745
|
+
|
|
746
|
+
Args:
|
|
747
|
+
lines: List to append lines to.
|
|
748
|
+
methods: List of method documentation.
|
|
749
|
+
"""
|
|
750
|
+
for method in methods:
|
|
751
|
+
lines.append(f"##### {method.name}")
|
|
752
|
+
lines.append("")
|
|
753
|
+
_add_markdown_function_details(lines, method)
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
def _add_markdown_functions(lines: list[str], module: ModuleDoc) -> None:
|
|
757
|
+
"""Add functions section to markdown.
|
|
758
|
+
|
|
759
|
+
Args:
|
|
760
|
+
lines: List to append lines to.
|
|
761
|
+
module: ModuleDoc to render.
|
|
762
|
+
"""
|
|
763
|
+
for func in module.functions:
|
|
764
|
+
lines.append(f"### Function: {func.name}")
|
|
765
|
+
lines.append("")
|
|
766
|
+
_add_markdown_function_details(lines, func)
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
def _add_markdown_function_details(lines: list[str], func: FunctionDoc) -> None:
|
|
770
|
+
"""Add function/method details to markdown.
|
|
771
|
+
|
|
772
|
+
Args:
|
|
773
|
+
lines: List to append lines to.
|
|
774
|
+
func: FunctionDoc to render.
|
|
775
|
+
"""
|
|
776
|
+
if func.signature:
|
|
777
|
+
lines.append("```python")
|
|
778
|
+
lines.append(func.signature)
|
|
779
|
+
lines.append("```")
|
|
780
|
+
lines.append("")
|
|
781
|
+
if func.docstring:
|
|
782
|
+
lines.append(func.docstring)
|
|
783
|
+
lines.append("")
|
|
696
784
|
|
|
697
785
|
|
|
698
786
|
def _generate_html(docs: ExtensionDocs) -> str:
|
|
@@ -250,7 +250,7 @@ class ExtensionPointRegistry:
|
|
|
250
250
|
cls._instance._hook_error_policy = HookErrorPolicy.CONTINUE
|
|
251
251
|
cls._instance._log_hook_errors = True
|
|
252
252
|
cls._instance._initialized = False # type: ignore[has-type]
|
|
253
|
-
cls._instance._registration_counter = 0 # type: ignore[misc, attr-defined]
|
|
253
|
+
cls._instance._registration_counter: int = 0 # type: ignore[misc, attr-defined]
|
|
254
254
|
return cls._instance
|
|
255
255
|
|
|
256
256
|
def initialize(self) -> None:
|
|
@@ -530,99 +530,183 @@ class ExtensionPointRegistry:
|
|
|
530
530
|
if name:
|
|
531
531
|
return self.get_algorithm(category, name)
|
|
532
532
|
|
|
533
|
-
#
|
|
533
|
+
# Get and filter candidates
|
|
534
534
|
candidates = list(self._algorithms[category].values()) # type: ignore[attr-defined]
|
|
535
|
-
|
|
536
535
|
if not candidates:
|
|
537
536
|
raise KeyError(f"No algorithms registered in category '{category}'")
|
|
538
537
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
filtered = []
|
|
542
|
-
for algo in candidates:
|
|
543
|
-
if all(algo.can(cap) for cap in required_capabilities):
|
|
544
|
-
filtered.append(algo)
|
|
545
|
-
candidates = filtered
|
|
538
|
+
candidates = self._filter_by_capabilities(candidates, required_capabilities, category)
|
|
539
|
+
candidates = self._filter_by_constraints(candidates, constraints, category)
|
|
546
540
|
|
|
547
|
-
|
|
541
|
+
# Select best match
|
|
542
|
+
sort_key = self._get_sort_key(optimize_for)
|
|
543
|
+
candidates.sort(key=sort_key)
|
|
544
|
+
return candidates[0]
|
|
545
|
+
|
|
546
|
+
def _filter_by_capabilities(
|
|
547
|
+
self,
|
|
548
|
+
candidates: list[RegisteredAlgorithm],
|
|
549
|
+
required_capabilities: list[str] | None,
|
|
550
|
+
category: str,
|
|
551
|
+
) -> list[RegisteredAlgorithm]:
|
|
552
|
+
"""Filter algorithms by required capabilities.
|
|
553
|
+
|
|
554
|
+
Args:
|
|
555
|
+
candidates: List of candidate algorithms.
|
|
556
|
+
required_capabilities: Required capabilities.
|
|
557
|
+
category: Category name for error messages.
|
|
558
|
+
|
|
559
|
+
Returns:
|
|
560
|
+
Filtered list of algorithms.
|
|
561
|
+
|
|
562
|
+
Raises:
|
|
563
|
+
KeyError: If no algorithms match capabilities.
|
|
564
|
+
"""
|
|
565
|
+
if not required_capabilities:
|
|
566
|
+
return candidates
|
|
567
|
+
|
|
568
|
+
filtered = [
|
|
569
|
+
algo for algo in candidates if all(algo.can(cap) for cap in required_capabilities)
|
|
570
|
+
]
|
|
571
|
+
|
|
572
|
+
if not filtered:
|
|
548
573
|
raise KeyError(f"No algorithms match required capabilities in category '{category}'")
|
|
549
574
|
|
|
550
|
-
|
|
551
|
-
if constraints:
|
|
552
|
-
filtered = []
|
|
553
|
-
for algo in candidates:
|
|
554
|
-
match = True
|
|
555
|
-
for key, value in constraints.items():
|
|
556
|
-
if key.startswith("performance."):
|
|
557
|
-
perf_key = key.split(".", 1)[1]
|
|
558
|
-
if algo.performance.get(perf_key) != value:
|
|
559
|
-
match = False
|
|
560
|
-
break
|
|
561
|
-
elif key.startswith("capabilities."):
|
|
562
|
-
cap_key = key.split(".", 1)[1]
|
|
563
|
-
if algo.capabilities.get(cap_key) != value:
|
|
564
|
-
match = False
|
|
565
|
-
break
|
|
566
|
-
elif key == "supports":
|
|
567
|
-
if isinstance(value, list):
|
|
568
|
-
if not any(s in algo.supports for s in value):
|
|
569
|
-
match = False
|
|
570
|
-
break
|
|
571
|
-
elif value not in algo.supports:
|
|
572
|
-
match = False
|
|
573
|
-
break
|
|
574
|
-
elif key == "memory_usage":
|
|
575
|
-
if algo.memory_usage != value:
|
|
576
|
-
match = False
|
|
577
|
-
break
|
|
578
|
-
if match:
|
|
579
|
-
filtered.append(algo)
|
|
580
|
-
candidates = filtered
|
|
575
|
+
return filtered
|
|
581
576
|
|
|
582
|
-
|
|
577
|
+
def _filter_by_constraints(
|
|
578
|
+
self,
|
|
579
|
+
candidates: list[RegisteredAlgorithm],
|
|
580
|
+
constraints: dict[str, Any] | None,
|
|
581
|
+
category: str,
|
|
582
|
+
) -> list[RegisteredAlgorithm]:
|
|
583
|
+
"""Filter algorithms by constraints.
|
|
584
|
+
|
|
585
|
+
Args:
|
|
586
|
+
candidates: List of candidate algorithms.
|
|
587
|
+
constraints: Constraint dictionary.
|
|
588
|
+
category: Category name for error messages.
|
|
589
|
+
|
|
590
|
+
Returns:
|
|
591
|
+
Filtered list of algorithms.
|
|
592
|
+
|
|
593
|
+
Raises:
|
|
594
|
+
KeyError: If no algorithms match constraints.
|
|
595
|
+
"""
|
|
596
|
+
if not constraints:
|
|
597
|
+
return candidates
|
|
598
|
+
|
|
599
|
+
filtered = [algo for algo in candidates if self._matches_constraints(algo, constraints)]
|
|
600
|
+
|
|
601
|
+
if not filtered:
|
|
583
602
|
raise KeyError(f"No algorithms match constraints in category '{category}'")
|
|
584
603
|
|
|
585
|
-
|
|
604
|
+
return filtered
|
|
605
|
+
|
|
606
|
+
def _matches_constraints(
|
|
607
|
+
self,
|
|
608
|
+
algo: RegisteredAlgorithm,
|
|
609
|
+
constraints: dict[str, Any],
|
|
610
|
+
) -> bool:
|
|
611
|
+
"""Check if algorithm matches all constraints.
|
|
612
|
+
|
|
613
|
+
Args:
|
|
614
|
+
algo: Algorithm to check.
|
|
615
|
+
constraints: Constraint dictionary.
|
|
616
|
+
|
|
617
|
+
Returns:
|
|
618
|
+
True if all constraints match.
|
|
619
|
+
"""
|
|
620
|
+
for key, value in constraints.items():
|
|
621
|
+
if not self._check_single_constraint(algo, key, value):
|
|
622
|
+
return False
|
|
623
|
+
return True
|
|
624
|
+
|
|
625
|
+
def _check_single_constraint(
|
|
626
|
+
self,
|
|
627
|
+
algo: RegisteredAlgorithm,
|
|
628
|
+
key: str,
|
|
629
|
+
value: Any,
|
|
630
|
+
) -> bool:
|
|
631
|
+
"""Check if algorithm matches a single constraint.
|
|
632
|
+
|
|
633
|
+
Args:
|
|
634
|
+
algo: Algorithm to check.
|
|
635
|
+
key: Constraint key.
|
|
636
|
+
value: Expected value.
|
|
637
|
+
|
|
638
|
+
Returns:
|
|
639
|
+
True if constraint matches.
|
|
640
|
+
"""
|
|
641
|
+
if key.startswith("performance."):
|
|
642
|
+
return self._check_performance_constraint(algo, key, value)
|
|
643
|
+
elif key.startswith("capabilities."):
|
|
644
|
+
return self._check_capabilities_constraint(algo, key, value)
|
|
645
|
+
elif key == "supports":
|
|
646
|
+
return self._check_supports_constraint(algo, value)
|
|
647
|
+
elif key == "memory_usage":
|
|
648
|
+
memory_check: bool = bool(algo.memory_usage == value)
|
|
649
|
+
return memory_check
|
|
650
|
+
return True
|
|
651
|
+
|
|
652
|
+
def _check_performance_constraint(
|
|
653
|
+
self, algo: RegisteredAlgorithm, key: str, value: Any
|
|
654
|
+
) -> bool:
|
|
655
|
+
"""Check performance constraint."""
|
|
656
|
+
perf_key = key.split(".", 1)[1]
|
|
657
|
+
result: bool = bool(algo.performance.get(perf_key) == value)
|
|
658
|
+
return result
|
|
659
|
+
|
|
660
|
+
def _check_capabilities_constraint(
|
|
661
|
+
self, algo: RegisteredAlgorithm, key: str, value: Any
|
|
662
|
+
) -> bool:
|
|
663
|
+
"""Check capabilities constraint."""
|
|
664
|
+
cap_key = key.split(".", 1)[1]
|
|
665
|
+
result: bool = bool(algo.capabilities.get(cap_key) == value)
|
|
666
|
+
return result
|
|
667
|
+
|
|
668
|
+
def _check_supports_constraint(self, algo: RegisteredAlgorithm, value: Any) -> bool:
|
|
669
|
+
"""Check supports constraint."""
|
|
670
|
+
if isinstance(value, list):
|
|
671
|
+
result: bool = bool(any(s in algo.supports for s in value))
|
|
672
|
+
return result
|
|
673
|
+
else:
|
|
674
|
+
in_supports: bool = bool(value in algo.supports)
|
|
675
|
+
return in_supports
|
|
676
|
+
|
|
677
|
+
def _get_sort_key(self, optimize_for: str) -> Any:
|
|
678
|
+
"""Get sort key function for optimization criterion.
|
|
679
|
+
|
|
680
|
+
Args:
|
|
681
|
+
optimize_for: Optimization target.
|
|
682
|
+
|
|
683
|
+
Returns:
|
|
684
|
+
Sort key function.
|
|
685
|
+
"""
|
|
586
686
|
if optimize_for == "speed":
|
|
587
687
|
|
|
588
|
-
def sort_key(a
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
else 1
|
|
593
|
-
if a.performance.get("speed") == "medium"
|
|
594
|
-
else 2,
|
|
595
|
-
-a.priority,
|
|
596
|
-
)
|
|
688
|
+
def sort_key(a: RegisteredAlgorithm) -> tuple[int, int]:
|
|
689
|
+
speed = a.performance.get("speed")
|
|
690
|
+
rank = 0 if speed == "fast" else 1 if speed == "medium" else 2
|
|
691
|
+
return (rank, -a.priority)
|
|
597
692
|
elif optimize_for == "accuracy":
|
|
598
693
|
|
|
599
|
-
def sort_key(a
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
else 1
|
|
604
|
-
if a.performance.get("accuracy") == "medium"
|
|
605
|
-
else 2,
|
|
606
|
-
-a.priority,
|
|
607
|
-
)
|
|
694
|
+
def sort_key(a: RegisteredAlgorithm) -> tuple[int, int]:
|
|
695
|
+
acc = a.performance.get("accuracy")
|
|
696
|
+
rank = 0 if acc == "high" else 1 if acc == "medium" else 2
|
|
697
|
+
return (rank, -a.priority)
|
|
608
698
|
elif optimize_for == "memory":
|
|
609
699
|
|
|
610
|
-
def sort_key(a
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
else 1
|
|
615
|
-
if a.performance.get("memory") == "medium"
|
|
616
|
-
else 2,
|
|
617
|
-
-a.priority,
|
|
618
|
-
)
|
|
700
|
+
def sort_key(a: RegisteredAlgorithm) -> tuple[int, int]:
|
|
701
|
+
mem = a.performance.get("memory")
|
|
702
|
+
rank = 0 if mem == "low" else 1 if mem == "medium" else 2
|
|
703
|
+
return (rank, -a.priority)
|
|
619
704
|
else:
|
|
620
|
-
# Default to priority only
|
|
621
|
-
def sort_key(a): # type: ignore[no-untyped-def]
|
|
622
|
-
return -a.priority
|
|
623
705
|
|
|
624
|
-
|
|
625
|
-
|
|
706
|
+
def sort_key(a: RegisteredAlgorithm) -> tuple[int, int]:
|
|
707
|
+
return (-a.priority, 0)
|
|
708
|
+
|
|
709
|
+
return sort_key
|
|
626
710
|
|
|
627
711
|
def list_algorithms(
|
|
628
712
|
self,
|
|
@@ -5,7 +5,7 @@ and per-plugin configuration for proper log management.
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
Example:
|
|
8
|
-
>>> from oscura.extensibility.logging import get_plugin_logger
|
|
8
|
+
>>> from oscura.core.extensibility.logging import get_plugin_logger
|
|
9
9
|
>>> logger = get_plugin_logger("my_plugin")
|
|
10
10
|
>>> logger.info("Plugin initialized")
|
|
11
11
|
|
|
@@ -312,7 +312,41 @@ def _write_readme(template: PluginTemplate) -> None:
|
|
|
312
312
|
class_name = _get_class_name(template)
|
|
313
313
|
entry_point_group = _get_entry_point_group(template.plugin_type)
|
|
314
314
|
|
|
315
|
-
content =
|
|
315
|
+
content = _generate_readme_content(template, class_name, entry_point_group)
|
|
316
|
+
(template.output_dir / "README.md").write_text(content)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _generate_readme_content(
|
|
320
|
+
template: PluginTemplate, class_name: str, entry_point_group: str
|
|
321
|
+
) -> str:
|
|
322
|
+
"""Generate README.md content.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
template: Plugin template configuration.
|
|
326
|
+
class_name: PascalCase class name.
|
|
327
|
+
entry_point_group: Entry point group name.
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
README content as string.
|
|
331
|
+
"""
|
|
332
|
+
header = _generate_readme_header(template, class_name)
|
|
333
|
+
usage = _generate_readme_usage(template, class_name)
|
|
334
|
+
dev = _generate_readme_development(template)
|
|
335
|
+
metadata = _generate_readme_metadata(template, entry_point_group)
|
|
336
|
+
return f"{header}\n{usage}\n{dev}\n{metadata}"
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _generate_readme_header(template: PluginTemplate, class_name: str) -> str:
|
|
340
|
+
"""Generate README header and installation section.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
template: Plugin template configuration.
|
|
344
|
+
class_name: Class name.
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
Header section content.
|
|
348
|
+
"""
|
|
349
|
+
return textwrap.dedent(f"""\
|
|
316
350
|
# {class_name}
|
|
317
351
|
|
|
318
352
|
{template.description}
|
|
@@ -325,7 +359,20 @@ def _write_readme(template: PluginTemplate) -> None:
|
|
|
325
359
|
cd {template.output_dir.name}
|
|
326
360
|
pip install -e .
|
|
327
361
|
```
|
|
362
|
+
""")
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def _generate_readme_usage(template: PluginTemplate, class_name: str) -> str:
|
|
366
|
+
"""Generate usage section.
|
|
328
367
|
|
|
368
|
+
Args:
|
|
369
|
+
template: Plugin template configuration.
|
|
370
|
+
class_name: Class name.
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
Usage section content.
|
|
374
|
+
"""
|
|
375
|
+
return textwrap.dedent(f"""\
|
|
329
376
|
## Usage
|
|
330
377
|
|
|
331
378
|
The plugin integrates automatically with Oscura via entry points:
|
|
@@ -359,7 +406,19 @@ def _write_readme(template: PluginTemplate) -> None:
|
|
|
359
406
|
# Show plugin info
|
|
360
407
|
oscura plugin info {template.name}
|
|
361
408
|
```
|
|
409
|
+
""")
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _generate_readme_development(template: PluginTemplate) -> str:
|
|
413
|
+
"""Generate development section.
|
|
362
414
|
|
|
415
|
+
Args:
|
|
416
|
+
template: Plugin template configuration.
|
|
417
|
+
|
|
418
|
+
Returns:
|
|
419
|
+
Development section content.
|
|
420
|
+
"""
|
|
421
|
+
return textwrap.dedent(f"""\
|
|
363
422
|
## Development
|
|
364
423
|
|
|
365
424
|
### Running Tests
|
|
@@ -380,7 +439,20 @@ def _write_readme(template: PluginTemplate) -> None:
|
|
|
380
439
|
# Type checking
|
|
381
440
|
mypy {template.name}.py
|
|
382
441
|
```
|
|
442
|
+
""")
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _generate_readme_metadata(template: PluginTemplate, entry_point_group: str) -> str:
|
|
446
|
+
"""Generate metadata section.
|
|
447
|
+
|
|
448
|
+
Args:
|
|
449
|
+
template: Plugin template configuration.
|
|
450
|
+
entry_point_group: Entry point group name.
|
|
383
451
|
|
|
452
|
+
Returns:
|
|
453
|
+
Metadata section content.
|
|
454
|
+
"""
|
|
455
|
+
return textwrap.dedent(f"""\
|
|
384
456
|
## Plugin Type: {template.plugin_type}
|
|
385
457
|
|
|
386
458
|
This is a **{template.plugin_type}** plugin for Oscura.
|
|
@@ -407,8 +479,6 @@ def _write_readme(template: PluginTemplate) -> None:
|
|
|
407
479
|
{template.version}
|
|
408
480
|
""")
|
|
409
481
|
|
|
410
|
-
(template.output_dir / "README.md").write_text(content)
|
|
411
|
-
|
|
412
482
|
|
|
413
483
|
def _write_pyproject_toml(template: PluginTemplate) -> None:
|
|
414
484
|
"""Write pyproject.toml for plugin packaging.
|