oscura 0.0.1__py3-none-any.whl → 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oscura/__init__.py +813 -8
- oscura/__main__.py +392 -0
- oscura/analyzers/__init__.py +37 -0
- oscura/analyzers/digital/__init__.py +177 -0
- oscura/analyzers/digital/bus.py +691 -0
- oscura/analyzers/digital/clock.py +805 -0
- oscura/analyzers/digital/correlation.py +720 -0
- oscura/analyzers/digital/edges.py +632 -0
- oscura/analyzers/digital/extraction.py +413 -0
- oscura/analyzers/digital/quality.py +878 -0
- oscura/analyzers/digital/signal_quality.py +877 -0
- oscura/analyzers/digital/thresholds.py +708 -0
- oscura/analyzers/digital/timing.py +1104 -0
- oscura/analyzers/eye/__init__.py +46 -0
- oscura/analyzers/eye/diagram.py +434 -0
- oscura/analyzers/eye/metrics.py +555 -0
- oscura/analyzers/jitter/__init__.py +83 -0
- oscura/analyzers/jitter/ber.py +333 -0
- oscura/analyzers/jitter/decomposition.py +759 -0
- oscura/analyzers/jitter/measurements.py +413 -0
- oscura/analyzers/jitter/spectrum.py +220 -0
- oscura/analyzers/measurements.py +40 -0
- oscura/analyzers/packet/__init__.py +171 -0
- oscura/analyzers/packet/daq.py +1077 -0
- oscura/analyzers/packet/metrics.py +437 -0
- oscura/analyzers/packet/parser.py +327 -0
- oscura/analyzers/packet/payload.py +2156 -0
- oscura/analyzers/packet/payload_analysis.py +1312 -0
- oscura/analyzers/packet/payload_extraction.py +236 -0
- oscura/analyzers/packet/payload_patterns.py +670 -0
- oscura/analyzers/packet/stream.py +359 -0
- oscura/analyzers/patterns/__init__.py +266 -0
- oscura/analyzers/patterns/clustering.py +1036 -0
- oscura/analyzers/patterns/discovery.py +539 -0
- oscura/analyzers/patterns/learning.py +797 -0
- oscura/analyzers/patterns/matching.py +1091 -0
- oscura/analyzers/patterns/periodic.py +650 -0
- oscura/analyzers/patterns/sequences.py +767 -0
- oscura/analyzers/power/__init__.py +116 -0
- oscura/analyzers/power/ac_power.py +391 -0
- oscura/analyzers/power/basic.py +383 -0
- oscura/analyzers/power/conduction.py +314 -0
- oscura/analyzers/power/efficiency.py +297 -0
- oscura/analyzers/power/ripple.py +356 -0
- oscura/analyzers/power/soa.py +372 -0
- oscura/analyzers/power/switching.py +479 -0
- oscura/analyzers/protocol/__init__.py +150 -0
- oscura/analyzers/protocols/__init__.py +150 -0
- oscura/analyzers/protocols/base.py +500 -0
- oscura/analyzers/protocols/can.py +620 -0
- oscura/analyzers/protocols/can_fd.py +448 -0
- oscura/analyzers/protocols/flexray.py +405 -0
- oscura/analyzers/protocols/hdlc.py +399 -0
- oscura/analyzers/protocols/i2c.py +368 -0
- oscura/analyzers/protocols/i2s.py +296 -0
- oscura/analyzers/protocols/jtag.py +393 -0
- oscura/analyzers/protocols/lin.py +445 -0
- oscura/analyzers/protocols/manchester.py +333 -0
- oscura/analyzers/protocols/onewire.py +501 -0
- oscura/analyzers/protocols/spi.py +334 -0
- oscura/analyzers/protocols/swd.py +325 -0
- oscura/analyzers/protocols/uart.py +393 -0
- oscura/analyzers/protocols/usb.py +495 -0
- oscura/analyzers/signal_integrity/__init__.py +63 -0
- oscura/analyzers/signal_integrity/embedding.py +294 -0
- oscura/analyzers/signal_integrity/equalization.py +370 -0
- oscura/analyzers/signal_integrity/sparams.py +484 -0
- oscura/analyzers/spectral/__init__.py +53 -0
- oscura/analyzers/spectral/chunked.py +273 -0
- oscura/analyzers/spectral/chunked_fft.py +571 -0
- oscura/analyzers/spectral/chunked_wavelet.py +391 -0
- oscura/analyzers/spectral/fft.py +92 -0
- oscura/analyzers/statistical/__init__.py +250 -0
- oscura/analyzers/statistical/checksum.py +923 -0
- oscura/analyzers/statistical/chunked_corr.py +228 -0
- oscura/analyzers/statistical/classification.py +778 -0
- oscura/analyzers/statistical/entropy.py +1113 -0
- oscura/analyzers/statistical/ngrams.py +614 -0
- oscura/analyzers/statistics/__init__.py +119 -0
- oscura/analyzers/statistics/advanced.py +885 -0
- oscura/analyzers/statistics/basic.py +263 -0
- oscura/analyzers/statistics/correlation.py +630 -0
- oscura/analyzers/statistics/distribution.py +298 -0
- oscura/analyzers/statistics/outliers.py +463 -0
- oscura/analyzers/statistics/streaming.py +93 -0
- oscura/analyzers/statistics/trend.py +520 -0
- oscura/analyzers/validation.py +598 -0
- oscura/analyzers/waveform/__init__.py +36 -0
- oscura/analyzers/waveform/measurements.py +943 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
- oscura/analyzers/waveform/spectral.py +1689 -0
- oscura/analyzers/waveform/wavelets.py +298 -0
- oscura/api/__init__.py +62 -0
- oscura/api/dsl.py +538 -0
- oscura/api/fluent.py +571 -0
- oscura/api/operators.py +498 -0
- oscura/api/optimization.py +392 -0
- oscura/api/profiling.py +396 -0
- oscura/automotive/__init__.py +73 -0
- oscura/automotive/can/__init__.py +52 -0
- oscura/automotive/can/analysis.py +356 -0
- oscura/automotive/can/checksum.py +250 -0
- oscura/automotive/can/correlation.py +212 -0
- oscura/automotive/can/discovery.py +355 -0
- oscura/automotive/can/message_wrapper.py +375 -0
- oscura/automotive/can/models.py +385 -0
- oscura/automotive/can/patterns.py +381 -0
- oscura/automotive/can/session.py +452 -0
- oscura/automotive/can/state_machine.py +300 -0
- oscura/automotive/can/stimulus_response.py +461 -0
- oscura/automotive/dbc/__init__.py +15 -0
- oscura/automotive/dbc/generator.py +156 -0
- oscura/automotive/dbc/parser.py +146 -0
- oscura/automotive/dtc/__init__.py +30 -0
- oscura/automotive/dtc/database.py +3036 -0
- oscura/automotive/j1939/__init__.py +14 -0
- oscura/automotive/j1939/decoder.py +745 -0
- oscura/automotive/loaders/__init__.py +35 -0
- oscura/automotive/loaders/asc.py +98 -0
- oscura/automotive/loaders/blf.py +77 -0
- oscura/automotive/loaders/csv_can.py +136 -0
- oscura/automotive/loaders/dispatcher.py +136 -0
- oscura/automotive/loaders/mdf.py +331 -0
- oscura/automotive/loaders/pcap.py +132 -0
- oscura/automotive/obd/__init__.py +14 -0
- oscura/automotive/obd/decoder.py +707 -0
- oscura/automotive/uds/__init__.py +48 -0
- oscura/automotive/uds/decoder.py +265 -0
- oscura/automotive/uds/models.py +64 -0
- oscura/automotive/visualization.py +369 -0
- oscura/batch/__init__.py +55 -0
- oscura/batch/advanced.py +627 -0
- oscura/batch/aggregate.py +300 -0
- oscura/batch/analyze.py +139 -0
- oscura/batch/logging.py +487 -0
- oscura/batch/metrics.py +556 -0
- oscura/builders/__init__.py +41 -0
- oscura/builders/signal_builder.py +1131 -0
- oscura/cli/__init__.py +14 -0
- oscura/cli/batch.py +339 -0
- oscura/cli/characterize.py +273 -0
- oscura/cli/compare.py +775 -0
- oscura/cli/decode.py +551 -0
- oscura/cli/main.py +247 -0
- oscura/cli/shell.py +350 -0
- oscura/comparison/__init__.py +66 -0
- oscura/comparison/compare.py +397 -0
- oscura/comparison/golden.py +487 -0
- oscura/comparison/limits.py +391 -0
- oscura/comparison/mask.py +434 -0
- oscura/comparison/trace_diff.py +30 -0
- oscura/comparison/visualization.py +481 -0
- oscura/compliance/__init__.py +70 -0
- oscura/compliance/advanced.py +756 -0
- oscura/compliance/masks.py +363 -0
- oscura/compliance/reporting.py +483 -0
- oscura/compliance/testing.py +298 -0
- oscura/component/__init__.py +38 -0
- oscura/component/impedance.py +365 -0
- oscura/component/reactive.py +598 -0
- oscura/component/transmission_line.py +312 -0
- oscura/config/__init__.py +191 -0
- oscura/config/defaults.py +254 -0
- oscura/config/loader.py +348 -0
- oscura/config/memory.py +271 -0
- oscura/config/migration.py +458 -0
- oscura/config/pipeline.py +1077 -0
- oscura/config/preferences.py +530 -0
- oscura/config/protocol.py +875 -0
- oscura/config/schema.py +713 -0
- oscura/config/settings.py +420 -0
- oscura/config/thresholds.py +599 -0
- oscura/convenience.py +457 -0
- oscura/core/__init__.py +299 -0
- oscura/core/audit.py +457 -0
- oscura/core/backend_selector.py +405 -0
- oscura/core/cache.py +590 -0
- oscura/core/cancellation.py +439 -0
- oscura/core/confidence.py +225 -0
- oscura/core/config.py +506 -0
- oscura/core/correlation.py +216 -0
- oscura/core/cross_domain.py +422 -0
- oscura/core/debug.py +301 -0
- oscura/core/edge_cases.py +541 -0
- oscura/core/exceptions.py +535 -0
- oscura/core/gpu_backend.py +523 -0
- oscura/core/lazy.py +832 -0
- oscura/core/log_query.py +540 -0
- oscura/core/logging.py +931 -0
- oscura/core/logging_advanced.py +952 -0
- oscura/core/memoize.py +171 -0
- oscura/core/memory_check.py +274 -0
- oscura/core/memory_guard.py +290 -0
- oscura/core/memory_limits.py +336 -0
- oscura/core/memory_monitor.py +453 -0
- oscura/core/memory_progress.py +465 -0
- oscura/core/memory_warnings.py +315 -0
- oscura/core/numba_backend.py +362 -0
- oscura/core/performance.py +352 -0
- oscura/core/progress.py +524 -0
- oscura/core/provenance.py +358 -0
- oscura/core/results.py +331 -0
- oscura/core/types.py +504 -0
- oscura/core/uncertainty.py +383 -0
- oscura/discovery/__init__.py +52 -0
- oscura/discovery/anomaly_detector.py +672 -0
- oscura/discovery/auto_decoder.py +415 -0
- oscura/discovery/comparison.py +497 -0
- oscura/discovery/quality_validator.py +528 -0
- oscura/discovery/signal_detector.py +769 -0
- oscura/dsl/__init__.py +73 -0
- oscura/dsl/commands.py +246 -0
- oscura/dsl/interpreter.py +455 -0
- oscura/dsl/parser.py +689 -0
- oscura/dsl/repl.py +172 -0
- oscura/exceptions.py +59 -0
- oscura/exploratory/__init__.py +111 -0
- oscura/exploratory/error_recovery.py +642 -0
- oscura/exploratory/fuzzy.py +513 -0
- oscura/exploratory/fuzzy_advanced.py +786 -0
- oscura/exploratory/legacy.py +831 -0
- oscura/exploratory/parse.py +358 -0
- oscura/exploratory/recovery.py +275 -0
- oscura/exploratory/sync.py +382 -0
- oscura/exploratory/unknown.py +707 -0
- oscura/export/__init__.py +25 -0
- oscura/export/wireshark/README.md +265 -0
- oscura/export/wireshark/__init__.py +47 -0
- oscura/export/wireshark/generator.py +312 -0
- oscura/export/wireshark/lua_builder.py +159 -0
- oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
- oscura/export/wireshark/type_mapping.py +165 -0
- oscura/export/wireshark/validator.py +105 -0
- oscura/exporters/__init__.py +94 -0
- oscura/exporters/csv.py +303 -0
- oscura/exporters/exporters.py +44 -0
- oscura/exporters/hdf5.py +219 -0
- oscura/exporters/html_export.py +701 -0
- oscura/exporters/json_export.py +291 -0
- oscura/exporters/markdown_export.py +367 -0
- oscura/exporters/matlab_export.py +354 -0
- oscura/exporters/npz_export.py +219 -0
- oscura/exporters/spice_export.py +210 -0
- oscura/extensibility/__init__.py +131 -0
- oscura/extensibility/docs.py +752 -0
- oscura/extensibility/extensions.py +1125 -0
- oscura/extensibility/logging.py +259 -0
- oscura/extensibility/measurements.py +485 -0
- oscura/extensibility/plugins.py +414 -0
- oscura/extensibility/registry.py +346 -0
- oscura/extensibility/templates.py +913 -0
- oscura/extensibility/validation.py +651 -0
- oscura/filtering/__init__.py +89 -0
- oscura/filtering/base.py +563 -0
- oscura/filtering/convenience.py +564 -0
- oscura/filtering/design.py +725 -0
- oscura/filtering/filters.py +32 -0
- oscura/filtering/introspection.py +605 -0
- oscura/guidance/__init__.py +24 -0
- oscura/guidance/recommender.py +429 -0
- oscura/guidance/wizard.py +518 -0
- oscura/inference/__init__.py +251 -0
- oscura/inference/active_learning/README.md +153 -0
- oscura/inference/active_learning/__init__.py +38 -0
- oscura/inference/active_learning/lstar.py +257 -0
- oscura/inference/active_learning/observation_table.py +230 -0
- oscura/inference/active_learning/oracle.py +78 -0
- oscura/inference/active_learning/teachers/__init__.py +15 -0
- oscura/inference/active_learning/teachers/simulator.py +192 -0
- oscura/inference/adaptive_tuning.py +453 -0
- oscura/inference/alignment.py +653 -0
- oscura/inference/bayesian.py +943 -0
- oscura/inference/binary.py +1016 -0
- oscura/inference/crc_reverse.py +711 -0
- oscura/inference/logic.py +288 -0
- oscura/inference/message_format.py +1305 -0
- oscura/inference/protocol.py +417 -0
- oscura/inference/protocol_dsl.py +1084 -0
- oscura/inference/protocol_library.py +1230 -0
- oscura/inference/sequences.py +809 -0
- oscura/inference/signal_intelligence.py +1509 -0
- oscura/inference/spectral.py +215 -0
- oscura/inference/state_machine.py +634 -0
- oscura/inference/stream.py +918 -0
- oscura/integrations/__init__.py +59 -0
- oscura/integrations/llm.py +1827 -0
- oscura/jupyter/__init__.py +32 -0
- oscura/jupyter/display.py +268 -0
- oscura/jupyter/magic.py +334 -0
- oscura/loaders/__init__.py +526 -0
- oscura/loaders/binary.py +69 -0
- oscura/loaders/configurable.py +1255 -0
- oscura/loaders/csv.py +26 -0
- oscura/loaders/csv_loader.py +473 -0
- oscura/loaders/hdf5.py +9 -0
- oscura/loaders/hdf5_loader.py +510 -0
- oscura/loaders/lazy.py +370 -0
- oscura/loaders/mmap_loader.py +583 -0
- oscura/loaders/numpy_loader.py +436 -0
- oscura/loaders/pcap.py +432 -0
- oscura/loaders/preprocessing.py +368 -0
- oscura/loaders/rigol.py +287 -0
- oscura/loaders/sigrok.py +321 -0
- oscura/loaders/tdms.py +367 -0
- oscura/loaders/tektronix.py +711 -0
- oscura/loaders/validation.py +584 -0
- oscura/loaders/vcd.py +464 -0
- oscura/loaders/wav.py +233 -0
- oscura/math/__init__.py +45 -0
- oscura/math/arithmetic.py +824 -0
- oscura/math/interpolation.py +413 -0
- oscura/onboarding/__init__.py +39 -0
- oscura/onboarding/help.py +498 -0
- oscura/onboarding/tutorials.py +405 -0
- oscura/onboarding/wizard.py +466 -0
- oscura/optimization/__init__.py +19 -0
- oscura/optimization/parallel.py +440 -0
- oscura/optimization/search.py +532 -0
- oscura/pipeline/__init__.py +43 -0
- oscura/pipeline/base.py +338 -0
- oscura/pipeline/composition.py +242 -0
- oscura/pipeline/parallel.py +448 -0
- oscura/pipeline/pipeline.py +375 -0
- oscura/pipeline/reverse_engineering.py +1119 -0
- oscura/plugins/__init__.py +122 -0
- oscura/plugins/base.py +272 -0
- oscura/plugins/cli.py +497 -0
- oscura/plugins/discovery.py +411 -0
- oscura/plugins/isolation.py +418 -0
- oscura/plugins/lifecycle.py +959 -0
- oscura/plugins/manager.py +493 -0
- oscura/plugins/registry.py +421 -0
- oscura/plugins/versioning.py +372 -0
- oscura/py.typed +0 -0
- oscura/quality/__init__.py +65 -0
- oscura/quality/ensemble.py +740 -0
- oscura/quality/explainer.py +338 -0
- oscura/quality/scoring.py +616 -0
- oscura/quality/warnings.py +456 -0
- oscura/reporting/__init__.py +248 -0
- oscura/reporting/advanced.py +1234 -0
- oscura/reporting/analyze.py +448 -0
- oscura/reporting/argument_preparer.py +596 -0
- oscura/reporting/auto_report.py +507 -0
- oscura/reporting/batch.py +615 -0
- oscura/reporting/chart_selection.py +223 -0
- oscura/reporting/comparison.py +330 -0
- oscura/reporting/config.py +615 -0
- oscura/reporting/content/__init__.py +39 -0
- oscura/reporting/content/executive.py +127 -0
- oscura/reporting/content/filtering.py +191 -0
- oscura/reporting/content/minimal.py +257 -0
- oscura/reporting/content/verbosity.py +162 -0
- oscura/reporting/core.py +508 -0
- oscura/reporting/core_formats/__init__.py +17 -0
- oscura/reporting/core_formats/multi_format.py +210 -0
- oscura/reporting/engine.py +836 -0
- oscura/reporting/export.py +366 -0
- oscura/reporting/formatting/__init__.py +129 -0
- oscura/reporting/formatting/emphasis.py +81 -0
- oscura/reporting/formatting/numbers.py +403 -0
- oscura/reporting/formatting/standards.py +55 -0
- oscura/reporting/formatting.py +466 -0
- oscura/reporting/html.py +578 -0
- oscura/reporting/index.py +590 -0
- oscura/reporting/multichannel.py +296 -0
- oscura/reporting/output.py +379 -0
- oscura/reporting/pdf.py +373 -0
- oscura/reporting/plots.py +731 -0
- oscura/reporting/pptx_export.py +360 -0
- oscura/reporting/renderers/__init__.py +11 -0
- oscura/reporting/renderers/pdf.py +94 -0
- oscura/reporting/sections.py +471 -0
- oscura/reporting/standards.py +680 -0
- oscura/reporting/summary_generator.py +368 -0
- oscura/reporting/tables.py +397 -0
- oscura/reporting/template_system.py +724 -0
- oscura/reporting/templates/__init__.py +15 -0
- oscura/reporting/templates/definition.py +205 -0
- oscura/reporting/templates/index.html +649 -0
- oscura/reporting/templates/index.md +173 -0
- oscura/schemas/__init__.py +158 -0
- oscura/schemas/bus_configuration.json +322 -0
- oscura/schemas/device_mapping.json +182 -0
- oscura/schemas/packet_format.json +418 -0
- oscura/schemas/protocol_definition.json +363 -0
- oscura/search/__init__.py +16 -0
- oscura/search/anomaly.py +292 -0
- oscura/search/context.py +149 -0
- oscura/search/pattern.py +160 -0
- oscura/session/__init__.py +34 -0
- oscura/session/annotations.py +289 -0
- oscura/session/history.py +313 -0
- oscura/session/session.py +445 -0
- oscura/streaming/__init__.py +43 -0
- oscura/streaming/chunked.py +611 -0
- oscura/streaming/progressive.py +393 -0
- oscura/streaming/realtime.py +622 -0
- oscura/testing/__init__.py +54 -0
- oscura/testing/synthetic.py +808 -0
- oscura/triggering/__init__.py +68 -0
- oscura/triggering/base.py +229 -0
- oscura/triggering/edge.py +353 -0
- oscura/triggering/pattern.py +344 -0
- oscura/triggering/pulse.py +581 -0
- oscura/triggering/window.py +453 -0
- oscura/ui/__init__.py +48 -0
- oscura/ui/formatters.py +526 -0
- oscura/ui/progressive_display.py +340 -0
- oscura/utils/__init__.py +99 -0
- oscura/utils/autodetect.py +338 -0
- oscura/utils/buffer.py +389 -0
- oscura/utils/lazy.py +407 -0
- oscura/utils/lazy_imports.py +147 -0
- oscura/utils/memory.py +836 -0
- oscura/utils/memory_advanced.py +1326 -0
- oscura/utils/memory_extensions.py +465 -0
- oscura/utils/progressive.py +352 -0
- oscura/utils/windowing.py +362 -0
- oscura/visualization/__init__.py +321 -0
- oscura/visualization/accessibility.py +526 -0
- oscura/visualization/annotations.py +374 -0
- oscura/visualization/axis_scaling.py +305 -0
- oscura/visualization/colors.py +453 -0
- oscura/visualization/digital.py +337 -0
- oscura/visualization/eye.py +420 -0
- oscura/visualization/histogram.py +281 -0
- oscura/visualization/interactive.py +858 -0
- oscura/visualization/jitter.py +702 -0
- oscura/visualization/keyboard.py +394 -0
- oscura/visualization/layout.py +365 -0
- oscura/visualization/optimization.py +1028 -0
- oscura/visualization/palettes.py +446 -0
- oscura/visualization/plot.py +92 -0
- oscura/visualization/power.py +290 -0
- oscura/visualization/power_extended.py +626 -0
- oscura/visualization/presets.py +467 -0
- oscura/visualization/protocols.py +932 -0
- oscura/visualization/render.py +207 -0
- oscura/visualization/rendering.py +444 -0
- oscura/visualization/reverse_engineering.py +791 -0
- oscura/visualization/signal_integrity.py +808 -0
- oscura/visualization/specialized.py +553 -0
- oscura/visualization/spectral.py +811 -0
- oscura/visualization/styles.py +381 -0
- oscura/visualization/thumbnails.py +311 -0
- oscura/visualization/time_axis.py +351 -0
- oscura/visualization/waveform.py +367 -0
- oscura/workflow/__init__.py +13 -0
- oscura/workflow/dag.py +377 -0
- oscura/workflows/__init__.py +58 -0
- oscura/workflows/compliance.py +280 -0
- oscura/workflows/digital.py +272 -0
- oscura/workflows/multi_trace.py +502 -0
- oscura/workflows/power.py +178 -0
- oscura/workflows/protocol.py +492 -0
- oscura/workflows/reverse_engineering.py +639 -0
- oscura/workflows/signal_integrity.py +227 -0
- oscura-0.1.0.dist-info/METADATA +300 -0
- oscura-0.1.0.dist-info/RECORD +463 -0
- oscura-0.1.0.dist-info/entry_points.txt +2 -0
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/licenses/LICENSE +1 -1
- oscura-0.0.1.dist-info/METADATA +0 -63
- oscura-0.0.1.dist-info/RECORD +0 -5
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
"""Plugin registry and management.
|
|
2
|
+
|
|
3
|
+
This module provides the central plugin registry for loading,
|
|
4
|
+
registering, and accessing plugins.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> from oscura.plugins.registry import register_plugin, get_plugin
|
|
9
|
+
>>> register_plugin(MyDecoder)
|
|
10
|
+
>>> decoder = get_plugin("my_decoder")
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
from typing import TYPE_CHECKING, Any
|
|
17
|
+
|
|
18
|
+
from oscura.plugins.discovery import (
|
|
19
|
+
TRACEKIT_API_VERSION,
|
|
20
|
+
DiscoveredPlugin,
|
|
21
|
+
discover_plugins,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from oscura.plugins.base import PluginBase, PluginCapability, PluginMetadata
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class PluginConflictError(Exception):
|
|
31
|
+
"""Plugin registration conflict.
|
|
32
|
+
|
|
33
|
+
Raised when registering a plugin with a name that already exists.
|
|
34
|
+
|
|
35
|
+
Attributes:
|
|
36
|
+
existing: Metadata of existing plugin.
|
|
37
|
+
new: Metadata of new plugin.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
message: str,
|
|
43
|
+
existing: PluginMetadata,
|
|
44
|
+
new: PluginMetadata,
|
|
45
|
+
) -> None:
|
|
46
|
+
super().__init__(message)
|
|
47
|
+
self.existing = existing
|
|
48
|
+
self.new = new
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class PluginVersionError(Exception):
|
|
52
|
+
"""Plugin version incompatibility.
|
|
53
|
+
|
|
54
|
+
Raised when a plugin is not compatible with the current API.
|
|
55
|
+
|
|
56
|
+
Attributes:
|
|
57
|
+
plugin_api_version: Plugin's required API version.
|
|
58
|
+
oscura_api_version: Current TraceKit API version.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
message: str,
|
|
64
|
+
plugin_api_version: str,
|
|
65
|
+
oscura_api_version: str,
|
|
66
|
+
) -> None:
|
|
67
|
+
super().__init__(message)
|
|
68
|
+
self.plugin_api_version = plugin_api_version
|
|
69
|
+
self.oscura_api_version = oscura_api_version
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class PluginDependencyError(Exception):
|
|
73
|
+
"""Plugin dependency not satisfied.
|
|
74
|
+
|
|
75
|
+
Attributes:
|
|
76
|
+
plugin: Plugin name that has unmet dependency.
|
|
77
|
+
dependency: Missing dependency name.
|
|
78
|
+
required_version: Required dependency version.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
message: str,
|
|
84
|
+
plugin: str,
|
|
85
|
+
dependency: str,
|
|
86
|
+
required_version: str,
|
|
87
|
+
) -> None:
|
|
88
|
+
super().__init__(message)
|
|
89
|
+
self.plugin = plugin
|
|
90
|
+
self.dependency = dependency
|
|
91
|
+
self.required_version = required_version
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class PluginRegistry:
|
|
95
|
+
"""Central registry for plugins.
|
|
96
|
+
|
|
97
|
+
Manages plugin registration, loading, and lookup.
|
|
98
|
+
|
|
99
|
+
Example:
|
|
100
|
+
>>> registry = PluginRegistry()
|
|
101
|
+
>>> registry.register(MyDecoder)
|
|
102
|
+
>>> plugin = registry.get("my_decoder")
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def __init__(self) -> None:
|
|
106
|
+
"""Initialize empty registry."""
|
|
107
|
+
self._plugins: dict[str, PluginBase] = {}
|
|
108
|
+
self._metadata: dict[str, PluginMetadata] = {}
|
|
109
|
+
self._by_capability: dict[PluginCapability, list[str]] = {}
|
|
110
|
+
self._discovered: list[DiscoveredPlugin] = []
|
|
111
|
+
|
|
112
|
+
def register(
|
|
113
|
+
self,
|
|
114
|
+
plugin: type[PluginBase] | PluginBase,
|
|
115
|
+
*,
|
|
116
|
+
check_compatibility: bool = True,
|
|
117
|
+
check_conflicts: bool = True,
|
|
118
|
+
config: dict[str, Any] | None = None,
|
|
119
|
+
) -> None:
|
|
120
|
+
"""Register a plugin with the registry.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
plugin: Plugin class or instance to register.
|
|
124
|
+
check_compatibility: Verify API compatibility.
|
|
125
|
+
check_conflicts: Check for duplicate names.
|
|
126
|
+
config: Optional plugin configuration.
|
|
127
|
+
|
|
128
|
+
Raises:
|
|
129
|
+
PluginConflictError: If plugin name already registered.
|
|
130
|
+
PluginVersionError: If plugin is not compatible.
|
|
131
|
+
"""
|
|
132
|
+
# Get or create instance
|
|
133
|
+
instance = plugin() if isinstance(plugin, type) else plugin
|
|
134
|
+
|
|
135
|
+
metadata = instance.metadata
|
|
136
|
+
|
|
137
|
+
# Check compatibility
|
|
138
|
+
if check_compatibility and not metadata.is_compatible_with(TRACEKIT_API_VERSION):
|
|
139
|
+
raise PluginVersionError(
|
|
140
|
+
f"Plugin '{metadata.name}' requires API v{metadata.api_version}, "
|
|
141
|
+
f"but TraceKit API is v{TRACEKIT_API_VERSION}",
|
|
142
|
+
plugin_api_version=metadata.api_version,
|
|
143
|
+
oscura_api_version=TRACEKIT_API_VERSION,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Check conflicts (PLUG-002: conflict detection for duplicate plugins)
|
|
147
|
+
if check_conflicts and metadata.name in self._plugins:
|
|
148
|
+
existing = self._metadata[metadata.name]
|
|
149
|
+
|
|
150
|
+
# Provide detailed conflict information
|
|
151
|
+
conflict_msg = (
|
|
152
|
+
f"Plugin '{metadata.name}' already registered:\n"
|
|
153
|
+
f" Existing: v{existing.version} at {existing.path}\n"
|
|
154
|
+
f" New: v{metadata.version}"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Check if same version
|
|
158
|
+
if existing.version == metadata.version:
|
|
159
|
+
conflict_msg += " (same version)"
|
|
160
|
+
|
|
161
|
+
raise PluginConflictError(
|
|
162
|
+
conflict_msg,
|
|
163
|
+
existing=existing,
|
|
164
|
+
new=metadata,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Register
|
|
168
|
+
self._plugins[metadata.name] = instance
|
|
169
|
+
self._metadata[metadata.name] = metadata
|
|
170
|
+
|
|
171
|
+
# Index by capability
|
|
172
|
+
for cap in metadata.capabilities:
|
|
173
|
+
if cap not in self._by_capability:
|
|
174
|
+
self._by_capability[cap] = []
|
|
175
|
+
self._by_capability[cap].append(metadata.name)
|
|
176
|
+
|
|
177
|
+
# Configure and load
|
|
178
|
+
if config:
|
|
179
|
+
instance.on_configure(config)
|
|
180
|
+
|
|
181
|
+
instance.on_load()
|
|
182
|
+
|
|
183
|
+
logger.info(f"Registered plugin: {metadata.name} v{metadata.version}")
|
|
184
|
+
|
|
185
|
+
def unregister(self, name: str) -> None:
|
|
186
|
+
"""Unregister a plugin.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
name: Plugin name to unregister.
|
|
190
|
+
"""
|
|
191
|
+
if name not in self._plugins:
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
instance = self._plugins[name]
|
|
195
|
+
metadata = self._metadata[name]
|
|
196
|
+
|
|
197
|
+
# Call unload hook
|
|
198
|
+
instance.on_unload()
|
|
199
|
+
|
|
200
|
+
# Remove from capability index
|
|
201
|
+
for cap in metadata.capabilities:
|
|
202
|
+
if cap in self._by_capability and name in self._by_capability[cap]:
|
|
203
|
+
self._by_capability[cap].remove(name)
|
|
204
|
+
|
|
205
|
+
# Remove from registry
|
|
206
|
+
del self._plugins[name]
|
|
207
|
+
del self._metadata[name]
|
|
208
|
+
|
|
209
|
+
logger.info(f"Unregistered plugin: {name}")
|
|
210
|
+
|
|
211
|
+
def get(self, name: str) -> PluginBase | None:
|
|
212
|
+
"""Get plugin by name.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
name: Plugin name.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
Plugin instance or None.
|
|
219
|
+
"""
|
|
220
|
+
return self._plugins.get(name)
|
|
221
|
+
|
|
222
|
+
def get_metadata(self, name: str) -> PluginMetadata | None:
|
|
223
|
+
"""Get plugin metadata by name.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
name: Plugin name.
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Plugin metadata or None.
|
|
230
|
+
"""
|
|
231
|
+
return self._metadata.get(name)
|
|
232
|
+
|
|
233
|
+
def list_plugins(
|
|
234
|
+
self,
|
|
235
|
+
*,
|
|
236
|
+
capability: PluginCapability | None = None,
|
|
237
|
+
) -> list[PluginMetadata]:
|
|
238
|
+
"""List registered plugins.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
capability: Filter by capability.
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
List of plugin metadata.
|
|
245
|
+
"""
|
|
246
|
+
if capability is not None:
|
|
247
|
+
names = self._by_capability.get(capability, [])
|
|
248
|
+
return [self._metadata[name] for name in names]
|
|
249
|
+
|
|
250
|
+
return list(self._metadata.values())
|
|
251
|
+
|
|
252
|
+
def has_plugin(self, name: str) -> bool:
|
|
253
|
+
"""Check if plugin is registered.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
name: Plugin name.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
True if registered.
|
|
260
|
+
"""
|
|
261
|
+
return name in self._plugins
|
|
262
|
+
|
|
263
|
+
def is_compatible(self, name: str) -> bool:
|
|
264
|
+
"""Check if plugin is compatible with current API.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
name: Plugin name.
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
True if compatible.
|
|
271
|
+
"""
|
|
272
|
+
metadata = self._metadata.get(name)
|
|
273
|
+
if metadata is None:
|
|
274
|
+
return False
|
|
275
|
+
return metadata.is_compatible_with(TRACEKIT_API_VERSION)
|
|
276
|
+
|
|
277
|
+
def discover_and_load(
|
|
278
|
+
self,
|
|
279
|
+
*,
|
|
280
|
+
compatible_only: bool = True,
|
|
281
|
+
config: dict[str, dict[str, Any]] | None = None,
|
|
282
|
+
) -> list[PluginMetadata]:
|
|
283
|
+
"""Discover and load all available plugins.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
compatible_only: Only load compatible plugins.
|
|
287
|
+
config: Configuration dict keyed by plugin name.
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
List of loaded plugin metadata.
|
|
291
|
+
"""
|
|
292
|
+
self._discovered = discover_plugins(compatible_only=compatible_only)
|
|
293
|
+
loaded: list[PluginMetadata] = []
|
|
294
|
+
|
|
295
|
+
for discovered in self._discovered:
|
|
296
|
+
if discovered.load_error:
|
|
297
|
+
logger.warning(
|
|
298
|
+
f"Skipping plugin {discovered.metadata.name}: {discovered.load_error}"
|
|
299
|
+
)
|
|
300
|
+
continue
|
|
301
|
+
|
|
302
|
+
if not discovered.compatible and compatible_only:
|
|
303
|
+
logger.debug(f"Skipping incompatible plugin: {discovered.metadata.name}")
|
|
304
|
+
continue
|
|
305
|
+
|
|
306
|
+
try:
|
|
307
|
+
if config and discovered.metadata.name in config:
|
|
308
|
+
config[discovered.metadata.name]
|
|
309
|
+
|
|
310
|
+
# For now, just store the metadata
|
|
311
|
+
# Full loading requires importing the plugin module
|
|
312
|
+
self._metadata[discovered.metadata.name] = discovered.metadata
|
|
313
|
+
loaded.append(discovered.metadata)
|
|
314
|
+
|
|
315
|
+
except Exception as e:
|
|
316
|
+
logger.error(f"Failed to load plugin {discovered.metadata.name}: {e}")
|
|
317
|
+
|
|
318
|
+
return loaded
|
|
319
|
+
|
|
320
|
+
def get_providers(self, item_type: str, item_name: str) -> list[str]:
|
|
321
|
+
"""Find plugins that provide a specific capability.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
item_type: Type of item (e.g., "protocols", "algorithms").
|
|
325
|
+
item_name: Name of item.
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
List of plugin names that provide the item.
|
|
329
|
+
"""
|
|
330
|
+
providers: list[str] = []
|
|
331
|
+
|
|
332
|
+
for name, metadata in self._metadata.items():
|
|
333
|
+
if item_type in metadata.provides:
|
|
334
|
+
if item_name in metadata.provides[item_type]:
|
|
335
|
+
providers.append(name)
|
|
336
|
+
|
|
337
|
+
return providers
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
# Global registry instance
|
|
341
|
+
_global_registry: PluginRegistry | None = None
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def get_plugin_registry() -> PluginRegistry:
|
|
345
|
+
"""Get the global plugin registry.
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
Global PluginRegistry instance.
|
|
349
|
+
"""
|
|
350
|
+
global _global_registry
|
|
351
|
+
|
|
352
|
+
if _global_registry is None:
|
|
353
|
+
_global_registry = PluginRegistry()
|
|
354
|
+
|
|
355
|
+
return _global_registry
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def register_plugin(
|
|
359
|
+
plugin: type[PluginBase] | PluginBase,
|
|
360
|
+
*,
|
|
361
|
+
config: dict[str, Any] | None = None,
|
|
362
|
+
) -> None:
|
|
363
|
+
"""Register plugin with global registry.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
plugin: Plugin class or instance.
|
|
367
|
+
config: Plugin configuration.
|
|
368
|
+
"""
|
|
369
|
+
get_plugin_registry().register(plugin, config=config)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def get_plugin(name: str) -> PluginBase | None:
|
|
373
|
+
"""Get plugin from global registry.
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
name: Plugin name.
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
Plugin instance or None.
|
|
380
|
+
"""
|
|
381
|
+
return get_plugin_registry().get(name)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def list_plugins(
|
|
385
|
+
*,
|
|
386
|
+
capability: PluginCapability | None = None,
|
|
387
|
+
) -> list[PluginMetadata]:
|
|
388
|
+
"""List plugins from global registry.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
capability: Filter by capability.
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
List of plugin metadata.
|
|
395
|
+
"""
|
|
396
|
+
return get_plugin_registry().list_plugins(capability=capability)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def is_compatible(name: str) -> bool:
|
|
400
|
+
"""Check plugin compatibility.
|
|
401
|
+
|
|
402
|
+
Args:
|
|
403
|
+
name: Plugin name.
|
|
404
|
+
|
|
405
|
+
Returns:
|
|
406
|
+
True if compatible.
|
|
407
|
+
"""
|
|
408
|
+
return get_plugin_registry().is_compatible(name)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
__all__ = [
|
|
412
|
+
"PluginConflictError",
|
|
413
|
+
"PluginDependencyError",
|
|
414
|
+
"PluginRegistry",
|
|
415
|
+
"PluginVersionError",
|
|
416
|
+
"get_plugin",
|
|
417
|
+
"get_plugin_registry",
|
|
418
|
+
"is_compatible",
|
|
419
|
+
"list_plugins",
|
|
420
|
+
"register_plugin",
|
|
421
|
+
]
|