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,420 @@
|
|
|
1
|
+
"""Application settings management system.
|
|
2
|
+
|
|
3
|
+
This module provides application-wide settings management for TraceKit,
|
|
4
|
+
including feature flags, CLI defaults, output formats, and runtime
|
|
5
|
+
configuration options.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
>>> from oscura.config.settings import get_settings, Settings
|
|
10
|
+
>>> settings = get_settings()
|
|
11
|
+
>>> settings.enable_feature("advanced_analysis")
|
|
12
|
+
>>> if settings.is_feature_enabled("advanced_analysis"):
|
|
13
|
+
... perform_advanced_analysis()
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import logging
|
|
20
|
+
from dataclasses import dataclass, field
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from oscura.core.exceptions import ConfigurationError
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class CLIDefaults:
|
|
31
|
+
"""CLI default settings.
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
output_format: Default output format (json, yaml, text)
|
|
35
|
+
verbosity: Default verbosity level (0-3)
|
|
36
|
+
color_output: Enable colored output
|
|
37
|
+
progress_bar: Show progress bars
|
|
38
|
+
parallel_workers: Number of parallel workers
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
output_format: str = "text"
|
|
42
|
+
verbosity: int = 1
|
|
43
|
+
color_output: bool = True
|
|
44
|
+
progress_bar: bool = True
|
|
45
|
+
parallel_workers: int = 4
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class AnalysisSettings:
|
|
50
|
+
"""Analysis configuration settings.
|
|
51
|
+
|
|
52
|
+
Attributes:
|
|
53
|
+
max_trace_size: Maximum trace size in bytes (0 = unlimited)
|
|
54
|
+
enable_caching: Enable result caching
|
|
55
|
+
cache_dir: Cache directory path
|
|
56
|
+
timeout: Default timeout for analysis in seconds
|
|
57
|
+
streaming_mode: Enable streaming mode for large files
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
max_trace_size: int = 0
|
|
61
|
+
enable_caching: bool = True
|
|
62
|
+
cache_dir: str | None = None
|
|
63
|
+
timeout: float = 300.0
|
|
64
|
+
streaming_mode: bool = False
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class OutputSettings:
|
|
69
|
+
"""Output and export configuration.
|
|
70
|
+
|
|
71
|
+
Attributes:
|
|
72
|
+
default_format: Default export format (csv, json, hdf5)
|
|
73
|
+
include_raw_data: Include raw waveform data in exports
|
|
74
|
+
compress_output: Compress output files
|
|
75
|
+
decimal_places: Decimal precision for numeric output
|
|
76
|
+
timestamp_format: Format for timestamps
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
default_format: str = "csv"
|
|
80
|
+
include_raw_data: bool = False
|
|
81
|
+
compress_output: bool = False
|
|
82
|
+
decimal_places: int = 6
|
|
83
|
+
timestamp_format: str = "iso8601"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass
|
|
87
|
+
class Settings:
|
|
88
|
+
"""Application-wide settings.
|
|
89
|
+
|
|
90
|
+
Attributes:
|
|
91
|
+
cli: CLI defaults
|
|
92
|
+
analysis: Analysis settings
|
|
93
|
+
output: Output settings
|
|
94
|
+
features: Feature flags
|
|
95
|
+
custom: Custom user-defined settings
|
|
96
|
+
|
|
97
|
+
Example:
|
|
98
|
+
>>> settings = Settings()
|
|
99
|
+
>>> settings.cli.verbosity = 2
|
|
100
|
+
>>> settings.analysis.max_trace_size = 1024**3 # 1 GB
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
cli: CLIDefaults = field(default_factory=CLIDefaults)
|
|
104
|
+
analysis: AnalysisSettings = field(default_factory=AnalysisSettings)
|
|
105
|
+
output: OutputSettings = field(default_factory=OutputSettings)
|
|
106
|
+
features: dict[str, bool] = field(default_factory=dict)
|
|
107
|
+
custom: dict[str, Any] = field(default_factory=dict)
|
|
108
|
+
|
|
109
|
+
def enable_feature(self, name: str) -> None:
|
|
110
|
+
"""Enable a feature flag.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
name: Feature name
|
|
114
|
+
|
|
115
|
+
Example:
|
|
116
|
+
>>> settings.enable_feature("advanced_analysis")
|
|
117
|
+
"""
|
|
118
|
+
self.features[name] = True
|
|
119
|
+
logger.debug(f"Feature enabled: {name}")
|
|
120
|
+
|
|
121
|
+
def disable_feature(self, name: str) -> None:
|
|
122
|
+
"""Disable a feature flag.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
name: Feature name
|
|
126
|
+
|
|
127
|
+
Example:
|
|
128
|
+
>>> settings.disable_feature("experimental_mode")
|
|
129
|
+
"""
|
|
130
|
+
self.features[name] = False
|
|
131
|
+
logger.debug(f"Feature disabled: {name}")
|
|
132
|
+
|
|
133
|
+
def is_feature_enabled(self, name: str) -> bool:
|
|
134
|
+
"""Check if a feature is enabled.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
name: Feature name
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
True if feature is enabled, False otherwise
|
|
141
|
+
|
|
142
|
+
Example:
|
|
143
|
+
>>> if settings.is_feature_enabled("advanced_analysis"):
|
|
144
|
+
... perform_analysis()
|
|
145
|
+
"""
|
|
146
|
+
return self.features.get(name, False)
|
|
147
|
+
|
|
148
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
149
|
+
"""Get setting value by dot-notation key.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
key: Key path (e.g., "cli.verbosity" or "custom.my_setting")
|
|
153
|
+
default: Default value if not found
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
Setting value
|
|
157
|
+
|
|
158
|
+
Example:
|
|
159
|
+
>>> settings.get("cli.verbosity")
|
|
160
|
+
1
|
|
161
|
+
>>> settings.get("custom.undefined", "fallback")
|
|
162
|
+
'fallback'
|
|
163
|
+
"""
|
|
164
|
+
parts = key.split(".")
|
|
165
|
+
obj: Any = self
|
|
166
|
+
|
|
167
|
+
for part in parts:
|
|
168
|
+
if isinstance(obj, dict):
|
|
169
|
+
obj = obj.get(part, default)
|
|
170
|
+
if obj is default:
|
|
171
|
+
return default
|
|
172
|
+
elif hasattr(obj, part):
|
|
173
|
+
obj = getattr(obj, part)
|
|
174
|
+
else:
|
|
175
|
+
return default
|
|
176
|
+
|
|
177
|
+
return obj
|
|
178
|
+
|
|
179
|
+
def set(self, key: str, value: Any) -> None:
|
|
180
|
+
"""Set setting value by dot-notation key.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
key: Key path (e.g., "cli.verbosity" or "custom.my_setting")
|
|
184
|
+
value: Value to set
|
|
185
|
+
|
|
186
|
+
Raises:
|
|
187
|
+
KeyError: If path is invalid
|
|
188
|
+
|
|
189
|
+
Example:
|
|
190
|
+
>>> settings.set("cli.verbosity", 2)
|
|
191
|
+
>>> settings.set("custom.my_setting", 42)
|
|
192
|
+
"""
|
|
193
|
+
parts = key.split(".")
|
|
194
|
+
|
|
195
|
+
if parts[0] == "custom":
|
|
196
|
+
# Custom settings are a dict
|
|
197
|
+
if len(parts) == 2:
|
|
198
|
+
self.custom[parts[1]] = value
|
|
199
|
+
else:
|
|
200
|
+
# Nested custom settings
|
|
201
|
+
current = self.custom
|
|
202
|
+
for part in parts[1:-1]:
|
|
203
|
+
if part not in current:
|
|
204
|
+
current[part] = {}
|
|
205
|
+
current = current[part]
|
|
206
|
+
current[parts[-1]] = value
|
|
207
|
+
return
|
|
208
|
+
|
|
209
|
+
# Navigate to parent
|
|
210
|
+
obj: Any = self
|
|
211
|
+
for part in parts[:-1]:
|
|
212
|
+
if hasattr(obj, part):
|
|
213
|
+
obj = getattr(obj, part)
|
|
214
|
+
else:
|
|
215
|
+
raise KeyError(f"Invalid setting path: {key}")
|
|
216
|
+
|
|
217
|
+
# Set value
|
|
218
|
+
final_part = parts[-1]
|
|
219
|
+
if hasattr(obj, final_part):
|
|
220
|
+
setattr(obj, final_part, value)
|
|
221
|
+
else:
|
|
222
|
+
raise KeyError(f"Unknown setting: {key}")
|
|
223
|
+
|
|
224
|
+
def to_dict(self) -> dict[str, Any]:
|
|
225
|
+
"""Convert settings to dictionary.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Dictionary representation
|
|
229
|
+
"""
|
|
230
|
+
return {
|
|
231
|
+
"cli": {
|
|
232
|
+
"output_format": self.cli.output_format,
|
|
233
|
+
"verbosity": self.cli.verbosity,
|
|
234
|
+
"color_output": self.cli.color_output,
|
|
235
|
+
"progress_bar": self.cli.progress_bar,
|
|
236
|
+
"parallel_workers": self.cli.parallel_workers,
|
|
237
|
+
},
|
|
238
|
+
"analysis": {
|
|
239
|
+
"max_trace_size": self.analysis.max_trace_size,
|
|
240
|
+
"enable_caching": self.analysis.enable_caching,
|
|
241
|
+
"cache_dir": self.analysis.cache_dir,
|
|
242
|
+
"timeout": self.analysis.timeout,
|
|
243
|
+
"streaming_mode": self.analysis.streaming_mode,
|
|
244
|
+
},
|
|
245
|
+
"output": {
|
|
246
|
+
"default_format": self.output.default_format,
|
|
247
|
+
"include_raw_data": self.output.include_raw_data,
|
|
248
|
+
"compress_output": self.output.compress_output,
|
|
249
|
+
"decimal_places": self.output.decimal_places,
|
|
250
|
+
"timestamp_format": self.output.timestamp_format,
|
|
251
|
+
},
|
|
252
|
+
"features": self.features,
|
|
253
|
+
"custom": self.custom,
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
@classmethod
|
|
257
|
+
def from_dict(cls, data: dict[str, Any]) -> Settings:
|
|
258
|
+
"""Create settings from dictionary.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
data: Dictionary representation
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Settings instance
|
|
265
|
+
"""
|
|
266
|
+
settings = cls()
|
|
267
|
+
|
|
268
|
+
# CLI settings
|
|
269
|
+
if "cli" in data:
|
|
270
|
+
c = data["cli"]
|
|
271
|
+
settings.cli = CLIDefaults(
|
|
272
|
+
output_format=c.get("output_format", settings.cli.output_format),
|
|
273
|
+
verbosity=c.get("verbosity", settings.cli.verbosity),
|
|
274
|
+
color_output=c.get("color_output", settings.cli.color_output),
|
|
275
|
+
progress_bar=c.get("progress_bar", settings.cli.progress_bar),
|
|
276
|
+
parallel_workers=c.get("parallel_workers", settings.cli.parallel_workers),
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# Analysis settings
|
|
280
|
+
if "analysis" in data:
|
|
281
|
+
a = data["analysis"]
|
|
282
|
+
settings.analysis = AnalysisSettings(
|
|
283
|
+
max_trace_size=a.get("max_trace_size", settings.analysis.max_trace_size),
|
|
284
|
+
enable_caching=a.get("enable_caching", settings.analysis.enable_caching),
|
|
285
|
+
cache_dir=a.get("cache_dir", settings.analysis.cache_dir),
|
|
286
|
+
timeout=a.get("timeout", settings.analysis.timeout),
|
|
287
|
+
streaming_mode=a.get("streaming_mode", settings.analysis.streaming_mode),
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# Output settings
|
|
291
|
+
if "output" in data:
|
|
292
|
+
o = data["output"]
|
|
293
|
+
settings.output = OutputSettings(
|
|
294
|
+
default_format=o.get("default_format", settings.output.default_format),
|
|
295
|
+
include_raw_data=o.get("include_raw_data", settings.output.include_raw_data),
|
|
296
|
+
compress_output=o.get("compress_output", settings.output.compress_output),
|
|
297
|
+
decimal_places=o.get("decimal_places", settings.output.decimal_places),
|
|
298
|
+
timestamp_format=o.get("timestamp_format", settings.output.timestamp_format),
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
settings.features = data.get("features", {})
|
|
302
|
+
settings.custom = data.get("custom", {})
|
|
303
|
+
|
|
304
|
+
return settings
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# Global settings instance
|
|
308
|
+
_global_settings: Settings | None = None
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def get_settings() -> Settings:
|
|
312
|
+
"""Get global application settings.
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
Global Settings instance
|
|
316
|
+
"""
|
|
317
|
+
global _global_settings
|
|
318
|
+
if _global_settings is None:
|
|
319
|
+
_global_settings = Settings()
|
|
320
|
+
return _global_settings
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def set_settings(settings: Settings) -> None:
|
|
324
|
+
"""Set global application settings.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
settings: Settings instance to use globally
|
|
328
|
+
"""
|
|
329
|
+
global _global_settings
|
|
330
|
+
_global_settings = settings
|
|
331
|
+
logger.debug("Global settings updated")
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def reset_settings() -> None:
|
|
335
|
+
"""Reset settings to defaults."""
|
|
336
|
+
global _global_settings
|
|
337
|
+
_global_settings = Settings()
|
|
338
|
+
logger.debug("Settings reset to defaults")
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def load_settings(path: Path | str) -> Settings:
|
|
342
|
+
"""Load settings from a JSON file.
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
path: Path to settings file
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
Loaded Settings instance
|
|
349
|
+
|
|
350
|
+
Raises:
|
|
351
|
+
ConfigurationError: If file cannot be read or parsed
|
|
352
|
+
|
|
353
|
+
Example:
|
|
354
|
+
>>> settings = load_settings("settings.json")
|
|
355
|
+
"""
|
|
356
|
+
try:
|
|
357
|
+
path_obj = Path(path).expanduser()
|
|
358
|
+
|
|
359
|
+
if not path_obj.exists():
|
|
360
|
+
raise ConfigurationError(f"Settings file not found: {path}")
|
|
361
|
+
|
|
362
|
+
with open(path_obj, encoding="utf-8") as f:
|
|
363
|
+
data = json.load(f)
|
|
364
|
+
|
|
365
|
+
if not isinstance(data, dict):
|
|
366
|
+
raise ConfigurationError("Settings file must contain a JSON object")
|
|
367
|
+
|
|
368
|
+
logger.debug(f"Loaded settings from {path_obj}")
|
|
369
|
+
return Settings.from_dict(data)
|
|
370
|
+
|
|
371
|
+
except json.JSONDecodeError as e:
|
|
372
|
+
raise ConfigurationError(f"Failed to parse settings JSON: {e}") from e
|
|
373
|
+
except OSError as e:
|
|
374
|
+
raise ConfigurationError(f"Failed to read settings file: {e}") from e
|
|
375
|
+
except Exception as e:
|
|
376
|
+
raise ConfigurationError(f"Error loading settings: {e}") from e
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def save_settings(settings: Settings, path: Path | str) -> None:
|
|
380
|
+
"""Save settings to a JSON file.
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
settings: Settings to save
|
|
384
|
+
path: Path to save settings to
|
|
385
|
+
|
|
386
|
+
Raises:
|
|
387
|
+
ConfigurationError: If file cannot be written
|
|
388
|
+
|
|
389
|
+
Example:
|
|
390
|
+
>>> settings = get_settings()
|
|
391
|
+
>>> save_settings(settings, "settings.json")
|
|
392
|
+
"""
|
|
393
|
+
try:
|
|
394
|
+
path_obj = Path(path).expanduser()
|
|
395
|
+
|
|
396
|
+
# Create parent directory if needed
|
|
397
|
+
path_obj.parent.mkdir(parents=True, exist_ok=True)
|
|
398
|
+
|
|
399
|
+
with open(path_obj, "w", encoding="utf-8") as f:
|
|
400
|
+
json.dump(settings.to_dict(), f, indent=2)
|
|
401
|
+
|
|
402
|
+
logger.debug(f"Saved settings to {path_obj}")
|
|
403
|
+
|
|
404
|
+
except OSError as e:
|
|
405
|
+
raise ConfigurationError(f"Failed to write settings file: {e}") from e
|
|
406
|
+
except Exception as e:
|
|
407
|
+
raise ConfigurationError(f"Error saving settings: {e}") from e
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
__all__ = [
|
|
411
|
+
"AnalysisSettings",
|
|
412
|
+
"CLIDefaults",
|
|
413
|
+
"OutputSettings",
|
|
414
|
+
"Settings",
|
|
415
|
+
"get_settings",
|
|
416
|
+
"load_settings",
|
|
417
|
+
"reset_settings",
|
|
418
|
+
"save_settings",
|
|
419
|
+
"set_settings",
|
|
420
|
+
]
|