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,346 @@
|
|
|
1
|
+
"""Algorithm registry for custom algorithm injection.
|
|
2
|
+
|
|
3
|
+
This module implements a registry pattern that allows users to register
|
|
4
|
+
custom algorithms and implementations at extension points throughout TraceKit.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import inspect
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from collections.abc import Callable
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AlgorithmRegistry:
|
|
17
|
+
"""Singleton registry for custom algorithm implementations.
|
|
18
|
+
|
|
19
|
+
Allows users to register custom algorithms for various categories
|
|
20
|
+
(edge detection, peak finding, window functions, etc.) that can be
|
|
21
|
+
used throughout TraceKit.
|
|
22
|
+
|
|
23
|
+
The registry validates algorithm signatures on registration and provides
|
|
24
|
+
lookup by category and name.
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
>>> import oscura as tk
|
|
28
|
+
>>> def my_edge_detector(data, threshold=0.5):
|
|
29
|
+
... '''Custom Schmitt trigger edge detector'''
|
|
30
|
+
... edges = []
|
|
31
|
+
... state = data[0] > threshold
|
|
32
|
+
... for i, val in enumerate(data):
|
|
33
|
+
... new_state = val > threshold
|
|
34
|
+
... if new_state != state:
|
|
35
|
+
... edges.append(i)
|
|
36
|
+
... state = new_state
|
|
37
|
+
... return edges
|
|
38
|
+
>>> # Register in algorithm registry
|
|
39
|
+
>>> tk.register_algorithm('my_schmitt', my_edge_detector, category='edge_detector')
|
|
40
|
+
>>> # Use custom algorithm
|
|
41
|
+
>>> edges = tk.find_edges(trace, method='my_schmitt', threshold=0.7)
|
|
42
|
+
|
|
43
|
+
Advanced Example:
|
|
44
|
+
>>> # Register custom window function
|
|
45
|
+
>>> import numpy as np
|
|
46
|
+
>>> def custom_window(n, alpha=0.5):
|
|
47
|
+
... x = np.linspace(0, 1, n)
|
|
48
|
+
... return 0.5 * (1 + np.cos(2 * np.pi * alpha * (x - 0.5)))
|
|
49
|
+
>>> tk.register_algorithm('custom_tukey', custom_window, category='window_func')
|
|
50
|
+
>>> # Use in FFT
|
|
51
|
+
>>> result = tk.fft(trace, nfft=8192, window='custom_tukey', alpha=0.3)
|
|
52
|
+
>>> # List available algorithms
|
|
53
|
+
>>> available = tk.get_algorithms('window_func')
|
|
54
|
+
|
|
55
|
+
References:
|
|
56
|
+
API-006: Algorithm Override Hooks
|
|
57
|
+
pytest plugin system
|
|
58
|
+
https://docs.pytest.org/en/stable/how-to/writing_plugins.html
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
_instance: AlgorithmRegistry | None = None
|
|
62
|
+
_registries: dict[str, dict[str, Callable[..., Any]]]
|
|
63
|
+
|
|
64
|
+
def __new__(cls) -> AlgorithmRegistry:
|
|
65
|
+
"""Ensure singleton instance."""
|
|
66
|
+
if cls._instance is None:
|
|
67
|
+
cls._instance = super().__new__(cls)
|
|
68
|
+
cls._instance._registries = {}
|
|
69
|
+
return cls._instance
|
|
70
|
+
|
|
71
|
+
def register(
|
|
72
|
+
self,
|
|
73
|
+
name: str,
|
|
74
|
+
func: Callable[..., Any],
|
|
75
|
+
category: str,
|
|
76
|
+
validate: bool = True,
|
|
77
|
+
) -> None:
|
|
78
|
+
"""Register a custom algorithm.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
name: Unique name for the algorithm within its category.
|
|
82
|
+
func: Callable implementing the algorithm.
|
|
83
|
+
category: Category of algorithm (e.g., 'edge_detector', 'peak_finder').
|
|
84
|
+
validate: Whether to validate function signature. Default True.
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
ValueError: If name already exists in category.
|
|
88
|
+
TypeError: If func is not callable or signature is invalid.
|
|
89
|
+
|
|
90
|
+
Example:
|
|
91
|
+
>>> def my_algorithm(data, param1=1.0, param2=2.0):
|
|
92
|
+
... return data * param1 + param2
|
|
93
|
+
>>> registry = AlgorithmRegistry()
|
|
94
|
+
>>> registry.register('my_algo', my_algorithm, 'preprocessor')
|
|
95
|
+
"""
|
|
96
|
+
if not callable(func):
|
|
97
|
+
raise TypeError(f"Algorithm must be callable, got {type(func).__name__}")
|
|
98
|
+
|
|
99
|
+
# Initialize category if needed
|
|
100
|
+
if category not in self._registries:
|
|
101
|
+
self._registries[category] = {}
|
|
102
|
+
|
|
103
|
+
# Check for duplicates
|
|
104
|
+
if name in self._registries[category]:
|
|
105
|
+
raise ValueError(f"Algorithm '{name}' already registered in category '{category}'")
|
|
106
|
+
|
|
107
|
+
# Validate signature if requested
|
|
108
|
+
if validate:
|
|
109
|
+
self._validate_signature(func, category)
|
|
110
|
+
|
|
111
|
+
# Register algorithm
|
|
112
|
+
self._registries[category][name] = func
|
|
113
|
+
|
|
114
|
+
def get(self, category: str, name: str) -> Callable[..., Any]:
|
|
115
|
+
"""Get algorithm by category and name.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
category: Algorithm category.
|
|
119
|
+
name: Algorithm name.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
The registered algorithm function.
|
|
123
|
+
|
|
124
|
+
Raises:
|
|
125
|
+
KeyError: If category or name not found.
|
|
126
|
+
|
|
127
|
+
Example:
|
|
128
|
+
>>> registry = AlgorithmRegistry()
|
|
129
|
+
>>> edge_detector = registry.get('edge_detector', 'my_schmitt')
|
|
130
|
+
>>> edges = edge_detector(data, threshold=0.5)
|
|
131
|
+
"""
|
|
132
|
+
if category not in self._registries:
|
|
133
|
+
raise KeyError(
|
|
134
|
+
f"Category '{category}' not found. Available: {list(self._registries.keys())}"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
if name not in self._registries[category]:
|
|
138
|
+
raise KeyError(
|
|
139
|
+
f"Algorithm '{name}' not found in category '{category}'. "
|
|
140
|
+
f"Available: {list(self._registries[category].keys())}"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return self._registries[category][name]
|
|
144
|
+
|
|
145
|
+
def list_categories(self) -> list[str]:
|
|
146
|
+
"""List all registered algorithm categories.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
List of category names.
|
|
150
|
+
|
|
151
|
+
Example:
|
|
152
|
+
>>> registry = AlgorithmRegistry()
|
|
153
|
+
>>> categories = registry.list_categories()
|
|
154
|
+
>>> print(categories)
|
|
155
|
+
['edge_detector', 'peak_finder', 'window_func']
|
|
156
|
+
"""
|
|
157
|
+
return list(self._registries.keys())
|
|
158
|
+
|
|
159
|
+
def list_algorithms(self, category: str) -> list[str]:
|
|
160
|
+
"""List all algorithms in a category.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
category: Algorithm category.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
List of algorithm names in that category.
|
|
167
|
+
|
|
168
|
+
Raises:
|
|
169
|
+
KeyError: If category not found.
|
|
170
|
+
|
|
171
|
+
Example:
|
|
172
|
+
>>> registry = AlgorithmRegistry()
|
|
173
|
+
>>> algorithms = registry.list_algorithms('edge_detector')
|
|
174
|
+
>>> print(algorithms)
|
|
175
|
+
['threshold', 'hysteresis', 'my_schmitt']
|
|
176
|
+
"""
|
|
177
|
+
if category not in self._registries:
|
|
178
|
+
raise KeyError(
|
|
179
|
+
f"Category '{category}' not found. Available: {list(self._registries.keys())}"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
return list(self._registries[category].keys())
|
|
183
|
+
|
|
184
|
+
def has_algorithm(self, category: str, name: str) -> bool:
|
|
185
|
+
"""Check if algorithm is registered.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
category: Algorithm category.
|
|
189
|
+
name: Algorithm name.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
True if algorithm is registered.
|
|
193
|
+
|
|
194
|
+
Example:
|
|
195
|
+
>>> if registry.has_algorithm('edge_detector', 'my_schmitt'):
|
|
196
|
+
... detector = registry.get('edge_detector', 'my_schmitt')
|
|
197
|
+
"""
|
|
198
|
+
return category in self._registries and name in self._registries[category]
|
|
199
|
+
|
|
200
|
+
def unregister(self, category: str, name: str) -> None:
|
|
201
|
+
"""Remove algorithm from registry.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
category: Algorithm category.
|
|
205
|
+
name: Algorithm name.
|
|
206
|
+
|
|
207
|
+
Raises:
|
|
208
|
+
KeyError: If algorithm not found.
|
|
209
|
+
|
|
210
|
+
Example:
|
|
211
|
+
>>> registry.unregister('edge_detector', 'my_schmitt')
|
|
212
|
+
"""
|
|
213
|
+
if not self.has_algorithm(category, name):
|
|
214
|
+
raise KeyError(f"Algorithm '{name}' not found in category '{category}'")
|
|
215
|
+
|
|
216
|
+
del self._registries[category][name]
|
|
217
|
+
|
|
218
|
+
def clear_category(self, category: str) -> None:
|
|
219
|
+
"""Clear all algorithms in a category.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
category: Category to clear.
|
|
223
|
+
|
|
224
|
+
Example:
|
|
225
|
+
>>> registry.clear_category('edge_detector')
|
|
226
|
+
"""
|
|
227
|
+
if category in self._registries:
|
|
228
|
+
self._registries[category].clear()
|
|
229
|
+
|
|
230
|
+
def clear_all(self) -> None:
|
|
231
|
+
"""Clear all registered algorithms.
|
|
232
|
+
|
|
233
|
+
Example:
|
|
234
|
+
>>> registry.clear_all()
|
|
235
|
+
"""
|
|
236
|
+
self._registries.clear()
|
|
237
|
+
|
|
238
|
+
def _validate_signature(self, func: Callable[..., Any], category: str) -> None:
|
|
239
|
+
"""Validate function signature for category.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
func: Function to validate.
|
|
243
|
+
category: Category to validate against.
|
|
244
|
+
|
|
245
|
+
Raises:
|
|
246
|
+
TypeError: If signature is invalid for category.
|
|
247
|
+
"""
|
|
248
|
+
sig = inspect.signature(func)
|
|
249
|
+
|
|
250
|
+
# Check that function accepts **kwargs for extensibility
|
|
251
|
+
has_var_keyword = any(
|
|
252
|
+
p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
if not has_var_keyword:
|
|
256
|
+
# Check if function has at least one parameter
|
|
257
|
+
if len(sig.parameters) == 0:
|
|
258
|
+
raise TypeError(
|
|
259
|
+
f"Algorithm function must accept at least one parameter "
|
|
260
|
+
f"(got {func.__name__} with no parameters)"
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
def __repr__(self) -> str:
|
|
264
|
+
"""String representation of registry."""
|
|
265
|
+
total_algos = sum(len(algos) for algos in self._registries.values())
|
|
266
|
+
return f"AlgorithmRegistry(categories={len(self._registries)}, algorithms={total_algos})"
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# Global registry instance
|
|
270
|
+
_registry = AlgorithmRegistry()
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def register_algorithm(
|
|
274
|
+
name: str,
|
|
275
|
+
func: Callable[..., Any],
|
|
276
|
+
category: str,
|
|
277
|
+
validate: bool = True,
|
|
278
|
+
) -> None:
|
|
279
|
+
"""Register a custom algorithm in the global registry.
|
|
280
|
+
|
|
281
|
+
Convenience function for registering algorithms without accessing
|
|
282
|
+
the registry instance directly.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
name: Unique name for the algorithm.
|
|
286
|
+
func: Callable implementing the algorithm.
|
|
287
|
+
category: Algorithm category.
|
|
288
|
+
validate: Whether to validate signature. Default True.
|
|
289
|
+
|
|
290
|
+
Example:
|
|
291
|
+
>>> import oscura as tk
|
|
292
|
+
>>> def my_edge_detector(data, threshold=0.5):
|
|
293
|
+
... return find_edges_custom(data, threshold)
|
|
294
|
+
>>> tk.register_algorithm('my_edges', my_edge_detector, 'edge_detector')
|
|
295
|
+
|
|
296
|
+
References:
|
|
297
|
+
API-006: Algorithm Override Hooks
|
|
298
|
+
"""
|
|
299
|
+
_registry.register(name, func, category, validate)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def get_algorithm(category: str, name: str) -> Callable[..., Any]:
|
|
303
|
+
"""Get algorithm from global registry.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
category: Algorithm category.
|
|
307
|
+
name: Algorithm name.
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
The registered algorithm function.
|
|
311
|
+
|
|
312
|
+
Example:
|
|
313
|
+
>>> edge_detector = tk.get_algorithm('edge_detector', 'my_edges')
|
|
314
|
+
>>> edges = edge_detector(data)
|
|
315
|
+
|
|
316
|
+
References:
|
|
317
|
+
API-006: Algorithm Override Hooks
|
|
318
|
+
"""
|
|
319
|
+
return _registry.get(category, name)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def get_algorithms(category: str) -> list[str]:
|
|
323
|
+
"""List all algorithms in a category from global registry.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
category: Algorithm category.
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
List of algorithm names.
|
|
330
|
+
|
|
331
|
+
Example:
|
|
332
|
+
>>> available = tk.get_algorithms('window_func')
|
|
333
|
+
>>> print(f"Available windows: {available}")
|
|
334
|
+
|
|
335
|
+
References:
|
|
336
|
+
API-006: Algorithm Override Hooks
|
|
337
|
+
"""
|
|
338
|
+
return _registry.list_algorithms(category)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
__all__ = [
|
|
342
|
+
"AlgorithmRegistry",
|
|
343
|
+
"get_algorithm",
|
|
344
|
+
"get_algorithms",
|
|
345
|
+
"register_algorithm",
|
|
346
|
+
]
|