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.
Files changed (465) hide show
  1. oscura/__init__.py +813 -8
  2. oscura/__main__.py +392 -0
  3. oscura/analyzers/__init__.py +37 -0
  4. oscura/analyzers/digital/__init__.py +177 -0
  5. oscura/analyzers/digital/bus.py +691 -0
  6. oscura/analyzers/digital/clock.py +805 -0
  7. oscura/analyzers/digital/correlation.py +720 -0
  8. oscura/analyzers/digital/edges.py +632 -0
  9. oscura/analyzers/digital/extraction.py +413 -0
  10. oscura/analyzers/digital/quality.py +878 -0
  11. oscura/analyzers/digital/signal_quality.py +877 -0
  12. oscura/analyzers/digital/thresholds.py +708 -0
  13. oscura/analyzers/digital/timing.py +1104 -0
  14. oscura/analyzers/eye/__init__.py +46 -0
  15. oscura/analyzers/eye/diagram.py +434 -0
  16. oscura/analyzers/eye/metrics.py +555 -0
  17. oscura/analyzers/jitter/__init__.py +83 -0
  18. oscura/analyzers/jitter/ber.py +333 -0
  19. oscura/analyzers/jitter/decomposition.py +759 -0
  20. oscura/analyzers/jitter/measurements.py +413 -0
  21. oscura/analyzers/jitter/spectrum.py +220 -0
  22. oscura/analyzers/measurements.py +40 -0
  23. oscura/analyzers/packet/__init__.py +171 -0
  24. oscura/analyzers/packet/daq.py +1077 -0
  25. oscura/analyzers/packet/metrics.py +437 -0
  26. oscura/analyzers/packet/parser.py +327 -0
  27. oscura/analyzers/packet/payload.py +2156 -0
  28. oscura/analyzers/packet/payload_analysis.py +1312 -0
  29. oscura/analyzers/packet/payload_extraction.py +236 -0
  30. oscura/analyzers/packet/payload_patterns.py +670 -0
  31. oscura/analyzers/packet/stream.py +359 -0
  32. oscura/analyzers/patterns/__init__.py +266 -0
  33. oscura/analyzers/patterns/clustering.py +1036 -0
  34. oscura/analyzers/patterns/discovery.py +539 -0
  35. oscura/analyzers/patterns/learning.py +797 -0
  36. oscura/analyzers/patterns/matching.py +1091 -0
  37. oscura/analyzers/patterns/periodic.py +650 -0
  38. oscura/analyzers/patterns/sequences.py +767 -0
  39. oscura/analyzers/power/__init__.py +116 -0
  40. oscura/analyzers/power/ac_power.py +391 -0
  41. oscura/analyzers/power/basic.py +383 -0
  42. oscura/analyzers/power/conduction.py +314 -0
  43. oscura/analyzers/power/efficiency.py +297 -0
  44. oscura/analyzers/power/ripple.py +356 -0
  45. oscura/analyzers/power/soa.py +372 -0
  46. oscura/analyzers/power/switching.py +479 -0
  47. oscura/analyzers/protocol/__init__.py +150 -0
  48. oscura/analyzers/protocols/__init__.py +150 -0
  49. oscura/analyzers/protocols/base.py +500 -0
  50. oscura/analyzers/protocols/can.py +620 -0
  51. oscura/analyzers/protocols/can_fd.py +448 -0
  52. oscura/analyzers/protocols/flexray.py +405 -0
  53. oscura/analyzers/protocols/hdlc.py +399 -0
  54. oscura/analyzers/protocols/i2c.py +368 -0
  55. oscura/analyzers/protocols/i2s.py +296 -0
  56. oscura/analyzers/protocols/jtag.py +393 -0
  57. oscura/analyzers/protocols/lin.py +445 -0
  58. oscura/analyzers/protocols/manchester.py +333 -0
  59. oscura/analyzers/protocols/onewire.py +501 -0
  60. oscura/analyzers/protocols/spi.py +334 -0
  61. oscura/analyzers/protocols/swd.py +325 -0
  62. oscura/analyzers/protocols/uart.py +393 -0
  63. oscura/analyzers/protocols/usb.py +495 -0
  64. oscura/analyzers/signal_integrity/__init__.py +63 -0
  65. oscura/analyzers/signal_integrity/embedding.py +294 -0
  66. oscura/analyzers/signal_integrity/equalization.py +370 -0
  67. oscura/analyzers/signal_integrity/sparams.py +484 -0
  68. oscura/analyzers/spectral/__init__.py +53 -0
  69. oscura/analyzers/spectral/chunked.py +273 -0
  70. oscura/analyzers/spectral/chunked_fft.py +571 -0
  71. oscura/analyzers/spectral/chunked_wavelet.py +391 -0
  72. oscura/analyzers/spectral/fft.py +92 -0
  73. oscura/analyzers/statistical/__init__.py +250 -0
  74. oscura/analyzers/statistical/checksum.py +923 -0
  75. oscura/analyzers/statistical/chunked_corr.py +228 -0
  76. oscura/analyzers/statistical/classification.py +778 -0
  77. oscura/analyzers/statistical/entropy.py +1113 -0
  78. oscura/analyzers/statistical/ngrams.py +614 -0
  79. oscura/analyzers/statistics/__init__.py +119 -0
  80. oscura/analyzers/statistics/advanced.py +885 -0
  81. oscura/analyzers/statistics/basic.py +263 -0
  82. oscura/analyzers/statistics/correlation.py +630 -0
  83. oscura/analyzers/statistics/distribution.py +298 -0
  84. oscura/analyzers/statistics/outliers.py +463 -0
  85. oscura/analyzers/statistics/streaming.py +93 -0
  86. oscura/analyzers/statistics/trend.py +520 -0
  87. oscura/analyzers/validation.py +598 -0
  88. oscura/analyzers/waveform/__init__.py +36 -0
  89. oscura/analyzers/waveform/measurements.py +943 -0
  90. oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
  91. oscura/analyzers/waveform/spectral.py +1689 -0
  92. oscura/analyzers/waveform/wavelets.py +298 -0
  93. oscura/api/__init__.py +62 -0
  94. oscura/api/dsl.py +538 -0
  95. oscura/api/fluent.py +571 -0
  96. oscura/api/operators.py +498 -0
  97. oscura/api/optimization.py +392 -0
  98. oscura/api/profiling.py +396 -0
  99. oscura/automotive/__init__.py +73 -0
  100. oscura/automotive/can/__init__.py +52 -0
  101. oscura/automotive/can/analysis.py +356 -0
  102. oscura/automotive/can/checksum.py +250 -0
  103. oscura/automotive/can/correlation.py +212 -0
  104. oscura/automotive/can/discovery.py +355 -0
  105. oscura/automotive/can/message_wrapper.py +375 -0
  106. oscura/automotive/can/models.py +385 -0
  107. oscura/automotive/can/patterns.py +381 -0
  108. oscura/automotive/can/session.py +452 -0
  109. oscura/automotive/can/state_machine.py +300 -0
  110. oscura/automotive/can/stimulus_response.py +461 -0
  111. oscura/automotive/dbc/__init__.py +15 -0
  112. oscura/automotive/dbc/generator.py +156 -0
  113. oscura/automotive/dbc/parser.py +146 -0
  114. oscura/automotive/dtc/__init__.py +30 -0
  115. oscura/automotive/dtc/database.py +3036 -0
  116. oscura/automotive/j1939/__init__.py +14 -0
  117. oscura/automotive/j1939/decoder.py +745 -0
  118. oscura/automotive/loaders/__init__.py +35 -0
  119. oscura/automotive/loaders/asc.py +98 -0
  120. oscura/automotive/loaders/blf.py +77 -0
  121. oscura/automotive/loaders/csv_can.py +136 -0
  122. oscura/automotive/loaders/dispatcher.py +136 -0
  123. oscura/automotive/loaders/mdf.py +331 -0
  124. oscura/automotive/loaders/pcap.py +132 -0
  125. oscura/automotive/obd/__init__.py +14 -0
  126. oscura/automotive/obd/decoder.py +707 -0
  127. oscura/automotive/uds/__init__.py +48 -0
  128. oscura/automotive/uds/decoder.py +265 -0
  129. oscura/automotive/uds/models.py +64 -0
  130. oscura/automotive/visualization.py +369 -0
  131. oscura/batch/__init__.py +55 -0
  132. oscura/batch/advanced.py +627 -0
  133. oscura/batch/aggregate.py +300 -0
  134. oscura/batch/analyze.py +139 -0
  135. oscura/batch/logging.py +487 -0
  136. oscura/batch/metrics.py +556 -0
  137. oscura/builders/__init__.py +41 -0
  138. oscura/builders/signal_builder.py +1131 -0
  139. oscura/cli/__init__.py +14 -0
  140. oscura/cli/batch.py +339 -0
  141. oscura/cli/characterize.py +273 -0
  142. oscura/cli/compare.py +775 -0
  143. oscura/cli/decode.py +551 -0
  144. oscura/cli/main.py +247 -0
  145. oscura/cli/shell.py +350 -0
  146. oscura/comparison/__init__.py +66 -0
  147. oscura/comparison/compare.py +397 -0
  148. oscura/comparison/golden.py +487 -0
  149. oscura/comparison/limits.py +391 -0
  150. oscura/comparison/mask.py +434 -0
  151. oscura/comparison/trace_diff.py +30 -0
  152. oscura/comparison/visualization.py +481 -0
  153. oscura/compliance/__init__.py +70 -0
  154. oscura/compliance/advanced.py +756 -0
  155. oscura/compliance/masks.py +363 -0
  156. oscura/compliance/reporting.py +483 -0
  157. oscura/compliance/testing.py +298 -0
  158. oscura/component/__init__.py +38 -0
  159. oscura/component/impedance.py +365 -0
  160. oscura/component/reactive.py +598 -0
  161. oscura/component/transmission_line.py +312 -0
  162. oscura/config/__init__.py +191 -0
  163. oscura/config/defaults.py +254 -0
  164. oscura/config/loader.py +348 -0
  165. oscura/config/memory.py +271 -0
  166. oscura/config/migration.py +458 -0
  167. oscura/config/pipeline.py +1077 -0
  168. oscura/config/preferences.py +530 -0
  169. oscura/config/protocol.py +875 -0
  170. oscura/config/schema.py +713 -0
  171. oscura/config/settings.py +420 -0
  172. oscura/config/thresholds.py +599 -0
  173. oscura/convenience.py +457 -0
  174. oscura/core/__init__.py +299 -0
  175. oscura/core/audit.py +457 -0
  176. oscura/core/backend_selector.py +405 -0
  177. oscura/core/cache.py +590 -0
  178. oscura/core/cancellation.py +439 -0
  179. oscura/core/confidence.py +225 -0
  180. oscura/core/config.py +506 -0
  181. oscura/core/correlation.py +216 -0
  182. oscura/core/cross_domain.py +422 -0
  183. oscura/core/debug.py +301 -0
  184. oscura/core/edge_cases.py +541 -0
  185. oscura/core/exceptions.py +535 -0
  186. oscura/core/gpu_backend.py +523 -0
  187. oscura/core/lazy.py +832 -0
  188. oscura/core/log_query.py +540 -0
  189. oscura/core/logging.py +931 -0
  190. oscura/core/logging_advanced.py +952 -0
  191. oscura/core/memoize.py +171 -0
  192. oscura/core/memory_check.py +274 -0
  193. oscura/core/memory_guard.py +290 -0
  194. oscura/core/memory_limits.py +336 -0
  195. oscura/core/memory_monitor.py +453 -0
  196. oscura/core/memory_progress.py +465 -0
  197. oscura/core/memory_warnings.py +315 -0
  198. oscura/core/numba_backend.py +362 -0
  199. oscura/core/performance.py +352 -0
  200. oscura/core/progress.py +524 -0
  201. oscura/core/provenance.py +358 -0
  202. oscura/core/results.py +331 -0
  203. oscura/core/types.py +504 -0
  204. oscura/core/uncertainty.py +383 -0
  205. oscura/discovery/__init__.py +52 -0
  206. oscura/discovery/anomaly_detector.py +672 -0
  207. oscura/discovery/auto_decoder.py +415 -0
  208. oscura/discovery/comparison.py +497 -0
  209. oscura/discovery/quality_validator.py +528 -0
  210. oscura/discovery/signal_detector.py +769 -0
  211. oscura/dsl/__init__.py +73 -0
  212. oscura/dsl/commands.py +246 -0
  213. oscura/dsl/interpreter.py +455 -0
  214. oscura/dsl/parser.py +689 -0
  215. oscura/dsl/repl.py +172 -0
  216. oscura/exceptions.py +59 -0
  217. oscura/exploratory/__init__.py +111 -0
  218. oscura/exploratory/error_recovery.py +642 -0
  219. oscura/exploratory/fuzzy.py +513 -0
  220. oscura/exploratory/fuzzy_advanced.py +786 -0
  221. oscura/exploratory/legacy.py +831 -0
  222. oscura/exploratory/parse.py +358 -0
  223. oscura/exploratory/recovery.py +275 -0
  224. oscura/exploratory/sync.py +382 -0
  225. oscura/exploratory/unknown.py +707 -0
  226. oscura/export/__init__.py +25 -0
  227. oscura/export/wireshark/README.md +265 -0
  228. oscura/export/wireshark/__init__.py +47 -0
  229. oscura/export/wireshark/generator.py +312 -0
  230. oscura/export/wireshark/lua_builder.py +159 -0
  231. oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
  232. oscura/export/wireshark/type_mapping.py +165 -0
  233. oscura/export/wireshark/validator.py +105 -0
  234. oscura/exporters/__init__.py +94 -0
  235. oscura/exporters/csv.py +303 -0
  236. oscura/exporters/exporters.py +44 -0
  237. oscura/exporters/hdf5.py +219 -0
  238. oscura/exporters/html_export.py +701 -0
  239. oscura/exporters/json_export.py +291 -0
  240. oscura/exporters/markdown_export.py +367 -0
  241. oscura/exporters/matlab_export.py +354 -0
  242. oscura/exporters/npz_export.py +219 -0
  243. oscura/exporters/spice_export.py +210 -0
  244. oscura/extensibility/__init__.py +131 -0
  245. oscura/extensibility/docs.py +752 -0
  246. oscura/extensibility/extensions.py +1125 -0
  247. oscura/extensibility/logging.py +259 -0
  248. oscura/extensibility/measurements.py +485 -0
  249. oscura/extensibility/plugins.py +414 -0
  250. oscura/extensibility/registry.py +346 -0
  251. oscura/extensibility/templates.py +913 -0
  252. oscura/extensibility/validation.py +651 -0
  253. oscura/filtering/__init__.py +89 -0
  254. oscura/filtering/base.py +563 -0
  255. oscura/filtering/convenience.py +564 -0
  256. oscura/filtering/design.py +725 -0
  257. oscura/filtering/filters.py +32 -0
  258. oscura/filtering/introspection.py +605 -0
  259. oscura/guidance/__init__.py +24 -0
  260. oscura/guidance/recommender.py +429 -0
  261. oscura/guidance/wizard.py +518 -0
  262. oscura/inference/__init__.py +251 -0
  263. oscura/inference/active_learning/README.md +153 -0
  264. oscura/inference/active_learning/__init__.py +38 -0
  265. oscura/inference/active_learning/lstar.py +257 -0
  266. oscura/inference/active_learning/observation_table.py +230 -0
  267. oscura/inference/active_learning/oracle.py +78 -0
  268. oscura/inference/active_learning/teachers/__init__.py +15 -0
  269. oscura/inference/active_learning/teachers/simulator.py +192 -0
  270. oscura/inference/adaptive_tuning.py +453 -0
  271. oscura/inference/alignment.py +653 -0
  272. oscura/inference/bayesian.py +943 -0
  273. oscura/inference/binary.py +1016 -0
  274. oscura/inference/crc_reverse.py +711 -0
  275. oscura/inference/logic.py +288 -0
  276. oscura/inference/message_format.py +1305 -0
  277. oscura/inference/protocol.py +417 -0
  278. oscura/inference/protocol_dsl.py +1084 -0
  279. oscura/inference/protocol_library.py +1230 -0
  280. oscura/inference/sequences.py +809 -0
  281. oscura/inference/signal_intelligence.py +1509 -0
  282. oscura/inference/spectral.py +215 -0
  283. oscura/inference/state_machine.py +634 -0
  284. oscura/inference/stream.py +918 -0
  285. oscura/integrations/__init__.py +59 -0
  286. oscura/integrations/llm.py +1827 -0
  287. oscura/jupyter/__init__.py +32 -0
  288. oscura/jupyter/display.py +268 -0
  289. oscura/jupyter/magic.py +334 -0
  290. oscura/loaders/__init__.py +526 -0
  291. oscura/loaders/binary.py +69 -0
  292. oscura/loaders/configurable.py +1255 -0
  293. oscura/loaders/csv.py +26 -0
  294. oscura/loaders/csv_loader.py +473 -0
  295. oscura/loaders/hdf5.py +9 -0
  296. oscura/loaders/hdf5_loader.py +510 -0
  297. oscura/loaders/lazy.py +370 -0
  298. oscura/loaders/mmap_loader.py +583 -0
  299. oscura/loaders/numpy_loader.py +436 -0
  300. oscura/loaders/pcap.py +432 -0
  301. oscura/loaders/preprocessing.py +368 -0
  302. oscura/loaders/rigol.py +287 -0
  303. oscura/loaders/sigrok.py +321 -0
  304. oscura/loaders/tdms.py +367 -0
  305. oscura/loaders/tektronix.py +711 -0
  306. oscura/loaders/validation.py +584 -0
  307. oscura/loaders/vcd.py +464 -0
  308. oscura/loaders/wav.py +233 -0
  309. oscura/math/__init__.py +45 -0
  310. oscura/math/arithmetic.py +824 -0
  311. oscura/math/interpolation.py +413 -0
  312. oscura/onboarding/__init__.py +39 -0
  313. oscura/onboarding/help.py +498 -0
  314. oscura/onboarding/tutorials.py +405 -0
  315. oscura/onboarding/wizard.py +466 -0
  316. oscura/optimization/__init__.py +19 -0
  317. oscura/optimization/parallel.py +440 -0
  318. oscura/optimization/search.py +532 -0
  319. oscura/pipeline/__init__.py +43 -0
  320. oscura/pipeline/base.py +338 -0
  321. oscura/pipeline/composition.py +242 -0
  322. oscura/pipeline/parallel.py +448 -0
  323. oscura/pipeline/pipeline.py +375 -0
  324. oscura/pipeline/reverse_engineering.py +1119 -0
  325. oscura/plugins/__init__.py +122 -0
  326. oscura/plugins/base.py +272 -0
  327. oscura/plugins/cli.py +497 -0
  328. oscura/plugins/discovery.py +411 -0
  329. oscura/plugins/isolation.py +418 -0
  330. oscura/plugins/lifecycle.py +959 -0
  331. oscura/plugins/manager.py +493 -0
  332. oscura/plugins/registry.py +421 -0
  333. oscura/plugins/versioning.py +372 -0
  334. oscura/py.typed +0 -0
  335. oscura/quality/__init__.py +65 -0
  336. oscura/quality/ensemble.py +740 -0
  337. oscura/quality/explainer.py +338 -0
  338. oscura/quality/scoring.py +616 -0
  339. oscura/quality/warnings.py +456 -0
  340. oscura/reporting/__init__.py +248 -0
  341. oscura/reporting/advanced.py +1234 -0
  342. oscura/reporting/analyze.py +448 -0
  343. oscura/reporting/argument_preparer.py +596 -0
  344. oscura/reporting/auto_report.py +507 -0
  345. oscura/reporting/batch.py +615 -0
  346. oscura/reporting/chart_selection.py +223 -0
  347. oscura/reporting/comparison.py +330 -0
  348. oscura/reporting/config.py +615 -0
  349. oscura/reporting/content/__init__.py +39 -0
  350. oscura/reporting/content/executive.py +127 -0
  351. oscura/reporting/content/filtering.py +191 -0
  352. oscura/reporting/content/minimal.py +257 -0
  353. oscura/reporting/content/verbosity.py +162 -0
  354. oscura/reporting/core.py +508 -0
  355. oscura/reporting/core_formats/__init__.py +17 -0
  356. oscura/reporting/core_formats/multi_format.py +210 -0
  357. oscura/reporting/engine.py +836 -0
  358. oscura/reporting/export.py +366 -0
  359. oscura/reporting/formatting/__init__.py +129 -0
  360. oscura/reporting/formatting/emphasis.py +81 -0
  361. oscura/reporting/formatting/numbers.py +403 -0
  362. oscura/reporting/formatting/standards.py +55 -0
  363. oscura/reporting/formatting.py +466 -0
  364. oscura/reporting/html.py +578 -0
  365. oscura/reporting/index.py +590 -0
  366. oscura/reporting/multichannel.py +296 -0
  367. oscura/reporting/output.py +379 -0
  368. oscura/reporting/pdf.py +373 -0
  369. oscura/reporting/plots.py +731 -0
  370. oscura/reporting/pptx_export.py +360 -0
  371. oscura/reporting/renderers/__init__.py +11 -0
  372. oscura/reporting/renderers/pdf.py +94 -0
  373. oscura/reporting/sections.py +471 -0
  374. oscura/reporting/standards.py +680 -0
  375. oscura/reporting/summary_generator.py +368 -0
  376. oscura/reporting/tables.py +397 -0
  377. oscura/reporting/template_system.py +724 -0
  378. oscura/reporting/templates/__init__.py +15 -0
  379. oscura/reporting/templates/definition.py +205 -0
  380. oscura/reporting/templates/index.html +649 -0
  381. oscura/reporting/templates/index.md +173 -0
  382. oscura/schemas/__init__.py +158 -0
  383. oscura/schemas/bus_configuration.json +322 -0
  384. oscura/schemas/device_mapping.json +182 -0
  385. oscura/schemas/packet_format.json +418 -0
  386. oscura/schemas/protocol_definition.json +363 -0
  387. oscura/search/__init__.py +16 -0
  388. oscura/search/anomaly.py +292 -0
  389. oscura/search/context.py +149 -0
  390. oscura/search/pattern.py +160 -0
  391. oscura/session/__init__.py +34 -0
  392. oscura/session/annotations.py +289 -0
  393. oscura/session/history.py +313 -0
  394. oscura/session/session.py +445 -0
  395. oscura/streaming/__init__.py +43 -0
  396. oscura/streaming/chunked.py +611 -0
  397. oscura/streaming/progressive.py +393 -0
  398. oscura/streaming/realtime.py +622 -0
  399. oscura/testing/__init__.py +54 -0
  400. oscura/testing/synthetic.py +808 -0
  401. oscura/triggering/__init__.py +68 -0
  402. oscura/triggering/base.py +229 -0
  403. oscura/triggering/edge.py +353 -0
  404. oscura/triggering/pattern.py +344 -0
  405. oscura/triggering/pulse.py +581 -0
  406. oscura/triggering/window.py +453 -0
  407. oscura/ui/__init__.py +48 -0
  408. oscura/ui/formatters.py +526 -0
  409. oscura/ui/progressive_display.py +340 -0
  410. oscura/utils/__init__.py +99 -0
  411. oscura/utils/autodetect.py +338 -0
  412. oscura/utils/buffer.py +389 -0
  413. oscura/utils/lazy.py +407 -0
  414. oscura/utils/lazy_imports.py +147 -0
  415. oscura/utils/memory.py +836 -0
  416. oscura/utils/memory_advanced.py +1326 -0
  417. oscura/utils/memory_extensions.py +465 -0
  418. oscura/utils/progressive.py +352 -0
  419. oscura/utils/windowing.py +362 -0
  420. oscura/visualization/__init__.py +321 -0
  421. oscura/visualization/accessibility.py +526 -0
  422. oscura/visualization/annotations.py +374 -0
  423. oscura/visualization/axis_scaling.py +305 -0
  424. oscura/visualization/colors.py +453 -0
  425. oscura/visualization/digital.py +337 -0
  426. oscura/visualization/eye.py +420 -0
  427. oscura/visualization/histogram.py +281 -0
  428. oscura/visualization/interactive.py +858 -0
  429. oscura/visualization/jitter.py +702 -0
  430. oscura/visualization/keyboard.py +394 -0
  431. oscura/visualization/layout.py +365 -0
  432. oscura/visualization/optimization.py +1028 -0
  433. oscura/visualization/palettes.py +446 -0
  434. oscura/visualization/plot.py +92 -0
  435. oscura/visualization/power.py +290 -0
  436. oscura/visualization/power_extended.py +626 -0
  437. oscura/visualization/presets.py +467 -0
  438. oscura/visualization/protocols.py +932 -0
  439. oscura/visualization/render.py +207 -0
  440. oscura/visualization/rendering.py +444 -0
  441. oscura/visualization/reverse_engineering.py +791 -0
  442. oscura/visualization/signal_integrity.py +808 -0
  443. oscura/visualization/specialized.py +553 -0
  444. oscura/visualization/spectral.py +811 -0
  445. oscura/visualization/styles.py +381 -0
  446. oscura/visualization/thumbnails.py +311 -0
  447. oscura/visualization/time_axis.py +351 -0
  448. oscura/visualization/waveform.py +367 -0
  449. oscura/workflow/__init__.py +13 -0
  450. oscura/workflow/dag.py +377 -0
  451. oscura/workflows/__init__.py +58 -0
  452. oscura/workflows/compliance.py +280 -0
  453. oscura/workflows/digital.py +272 -0
  454. oscura/workflows/multi_trace.py +502 -0
  455. oscura/workflows/power.py +178 -0
  456. oscura/workflows/protocol.py +492 -0
  457. oscura/workflows/reverse_engineering.py +639 -0
  458. oscura/workflows/signal_integrity.py +227 -0
  459. oscura-0.1.0.dist-info/METADATA +300 -0
  460. oscura-0.1.0.dist-info/RECORD +463 -0
  461. oscura-0.1.0.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/licenses/LICENSE +1 -1
  463. oscura-0.0.1.dist-info/METADATA +0 -63
  464. oscura-0.0.1.dist-info/RECORD +0 -5
  465. {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,366 @@
1
+ """Multi-format report export for TraceKit.
2
+
3
+ This module provides unified export interface for generating reports in
4
+ multiple formats (HTML, PDF, DOCX, Markdown) from a single source.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.reporting.export import export_report
9
+ >>> export_report(report, "output", formats=["pdf", "html", "markdown"])
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from pathlib import Path
15
+ from typing import TYPE_CHECKING, Any, Literal
16
+
17
+ if TYPE_CHECKING:
18
+ from oscura.reporting.core import Report
19
+
20
+
21
+ def export_report(
22
+ report: Report,
23
+ output_path: str | Path,
24
+ *,
25
+ formats: list[Literal["pdf", "html", "docx", "markdown"]] | None = None,
26
+ format_options: dict[str, Any] | None = None,
27
+ ) -> dict[str, Path]:
28
+ """Export report to multiple formats.
29
+
30
+ Args:
31
+ report: Report object to export.
32
+ output_path: Base output path (without extension).
33
+ formats: List of formats to generate (default: ["pdf", "html"]).
34
+ format_options: Format-specific options dictionary.
35
+
36
+ Returns:
37
+ Dictionary mapping format to output file path.
38
+
39
+ Raises:
40
+ ValueError: If unsupported format is specified.
41
+
42
+ Example:
43
+ >>> paths = export_report(
44
+ ... report,
45
+ ... "report",
46
+ ... formats=["pdf", "html", "markdown"],
47
+ ... format_options={
48
+ ... "pdf": {"dpi": 300, "pdfa_compliance": True},
49
+ ... "html": {"interactive": True, "dark_mode": True},
50
+ ... }
51
+ ... )
52
+
53
+ References:
54
+ REPORT-010
55
+ """
56
+ if formats is None:
57
+ formats = ["pdf", "html"]
58
+
59
+ if format_options is None:
60
+ format_options = {}
61
+
62
+ output_path = Path(output_path)
63
+ generated_files = {}
64
+
65
+ for fmt in formats:
66
+ fmt_opts = format_options.get(fmt, {})
67
+
68
+ if fmt == "pdf":
69
+ file_path = _export_pdf(report, output_path, **fmt_opts)
70
+ elif fmt == "html":
71
+ file_path = _export_html(report, output_path, **fmt_opts)
72
+ elif fmt == "docx":
73
+ file_path = _export_docx(report, output_path, **fmt_opts)
74
+ elif fmt == "markdown":
75
+ file_path = _export_markdown(report, output_path, **fmt_opts)
76
+ else:
77
+ raise ValueError(f"Unsupported format: {fmt}")
78
+
79
+ generated_files[fmt] = file_path
80
+
81
+ return generated_files # type: ignore[return-value]
82
+
83
+
84
+ def _export_pdf(
85
+ report: Report,
86
+ output_path: Path,
87
+ **options: Any,
88
+ ) -> Path:
89
+ """Export report as PDF."""
90
+ from oscura.reporting.pdf import save_pdf_report
91
+
92
+ path = output_path.with_suffix(".pdf")
93
+ save_pdf_report(report, path, **options)
94
+ return path
95
+
96
+
97
+ def _export_html(
98
+ report: Report,
99
+ output_path: Path,
100
+ **options: Any,
101
+ ) -> Path:
102
+ """Export report as HTML."""
103
+ from oscura.reporting.html import save_html_report
104
+
105
+ path = output_path.with_suffix(".html")
106
+ save_html_report(report, path, **options)
107
+ return path
108
+
109
+
110
+ def _export_markdown(
111
+ report: Report,
112
+ output_path: Path,
113
+ **options: Any,
114
+ ) -> Path:
115
+ """Export report as Markdown."""
116
+ path = output_path.with_suffix(".md")
117
+ markdown_content = report.to_markdown()
118
+ path.write_text(markdown_content, encoding="utf-8")
119
+ return path
120
+
121
+
122
+ def _export_docx(
123
+ report: Report,
124
+ output_path: Path,
125
+ **options: Any,
126
+ ) -> Path:
127
+ """Export report as DOCX.
128
+
129
+ Requires python-docx library.
130
+
131
+ Args:
132
+ report: Report object to export.
133
+ output_path: Base output path (extension will be changed to .docx).
134
+ **options: Format-specific options (currently unused).
135
+
136
+ Returns:
137
+ Path to the created DOCX file.
138
+
139
+ Raises:
140
+ ImportError: If python-docx library is not installed.
141
+
142
+ References:
143
+ REPORT-019
144
+ """
145
+ try:
146
+ from docx import Document # type: ignore[import-not-found]
147
+ from docx.enum.text import ( # type: ignore[import-not-found]
148
+ WD_ALIGN_PARAGRAPH, # type: ignore[import-not-found]
149
+ )
150
+ from docx.shared import ( # noqa: F401 # type: ignore[import-not-found]
151
+ Inches,
152
+ Pt,
153
+ RGBColor,
154
+ )
155
+ except ImportError:
156
+ raise ImportError( # noqa: B904
157
+ "python-docx is required for DOCX export. Install with: pip install python-docx"
158
+ )
159
+
160
+ path = output_path.with_suffix(".docx")
161
+ doc = Document()
162
+
163
+ # Add title
164
+ title = doc.add_heading(report.config.title, level=0)
165
+ title.alignment = WD_ALIGN_PARAGRAPH.CENTER
166
+
167
+ # Add metadata
168
+ if report.config.author:
169
+ doc.add_paragraph(f"Author: {report.config.author}")
170
+ doc.add_paragraph(f"Date: {report.config.created.strftime('%Y-%m-%d %H:%M')}")
171
+ doc.add_paragraph() # Blank line
172
+
173
+ # Add sections
174
+ for section in report.sections:
175
+ if not section.visible:
176
+ continue
177
+
178
+ # Section heading
179
+ doc.add_heading(section.title, level=section.level)
180
+
181
+ # Section content
182
+ if isinstance(section.content, str):
183
+ doc.add_paragraph(section.content)
184
+
185
+ elif isinstance(section.content, list):
186
+ for item in section.content:
187
+ if isinstance(item, dict):
188
+ if item.get("type") == "table":
189
+ _add_table_to_docx(doc, item)
190
+ elif item.get("type") == "figure":
191
+ # Placeholder for figures
192
+ doc.add_paragraph(f"[Figure: {item.get('caption', 'N/A')}]")
193
+ else:
194
+ doc.add_paragraph(str(item))
195
+
196
+ # Subsections
197
+ for subsec in section.subsections:
198
+ if not subsec.visible:
199
+ continue
200
+ doc.add_heading(subsec.title, level=subsec.level)
201
+ if isinstance(subsec.content, str):
202
+ doc.add_paragraph(subsec.content)
203
+
204
+ # Save document
205
+ doc.save(str(path))
206
+ return path
207
+
208
+
209
+ def _add_table_to_docx(doc: Any, table_dict: dict[str, Any]) -> None:
210
+ """Add table to DOCX document."""
211
+ headers = table_dict.get("headers", [])
212
+ data = table_dict.get("data", [])
213
+
214
+ if not headers and not data:
215
+ return
216
+
217
+ # Create table
218
+ num_cols = len(headers) if headers else len(data[0]) if data else 0
219
+ num_rows = len(data) + (1 if headers else 0)
220
+
221
+ if num_rows == 0 or num_cols == 0:
222
+ return
223
+
224
+ table = doc.add_table(rows=num_rows, cols=num_cols)
225
+ table.style = "Light Grid Accent 1"
226
+
227
+ # Add headers
228
+ if headers:
229
+ header_cells = table.rows[0].cells
230
+ for i, header in enumerate(headers):
231
+ header_cells[i].text = str(header)
232
+ # Make header bold
233
+ for paragraph in header_cells[i].paragraphs:
234
+ for run in paragraph.runs:
235
+ run.bold = True
236
+
237
+ # Add data
238
+ start_row = 1 if headers else 0
239
+ for i, row in enumerate(data):
240
+ row_cells = table.rows[start_row + i].cells
241
+ for j, cell in enumerate(row):
242
+ row_cells[j].text = str(cell)
243
+
244
+ # Add caption
245
+ if table_dict.get("caption"):
246
+ doc.add_paragraph(table_dict["caption"], style="Caption")
247
+
248
+
249
+ def export_multiple_reports(
250
+ reports: dict[str, Report],
251
+ output_dir: str | Path,
252
+ *,
253
+ format: Literal["pdf", "html", "docx", "markdown"] = "pdf",
254
+ **options: Any,
255
+ ) -> dict[str, Path]:
256
+ """Export multiple reports to a directory.
257
+
258
+ Args:
259
+ reports: Dictionary mapping name to Report object.
260
+ output_dir: Output directory path.
261
+ format: Export format for all reports.
262
+ **options: Format-specific options.
263
+
264
+ Returns:
265
+ Dictionary mapping report name to output path.
266
+
267
+ References:
268
+ REPORT-010
269
+ """
270
+ output_dir = Path(output_dir)
271
+ output_dir.mkdir(parents=True, exist_ok=True)
272
+
273
+ generated_files = {}
274
+
275
+ for name, report in reports.items():
276
+ output_path = output_dir / name
277
+ files = export_report(
278
+ report, output_path, formats=[format], format_options={format: options}
279
+ )
280
+ generated_files[name] = files[format]
281
+
282
+ return generated_files
283
+
284
+
285
+ def batch_export_formats(
286
+ report: Report,
287
+ output_dir: str | Path,
288
+ *,
289
+ formats: list[str] | None = None,
290
+ **options: Any,
291
+ ) -> dict[str, Path]:
292
+ """Export single report to multiple formats in a directory.
293
+
294
+ Args:
295
+ report: Report to export.
296
+ output_dir: Output directory.
297
+ formats: List of formats (default: all supported).
298
+ **options: Common options for all formats.
299
+
300
+ Returns:
301
+ Dictionary mapping format to output path.
302
+
303
+ References:
304
+ REPORT-010
305
+ """
306
+ if formats is None:
307
+ formats = ["pdf", "html", "docx", "markdown"]
308
+
309
+ output_dir = Path(output_dir)
310
+ output_dir.mkdir(parents=True, exist_ok=True)
311
+
312
+ base_name = report.config.title.lower().replace(" ", "_")
313
+ output_path = output_dir / base_name
314
+
315
+ return export_report(report, output_path, formats=formats, format_options=options) # type: ignore[arg-type]
316
+
317
+
318
+ def create_archive(
319
+ files: dict[str, Path],
320
+ archive_path: str | Path,
321
+ *,
322
+ format: Literal["zip", "tar", "tar.gz"] = "zip",
323
+ ) -> Path:
324
+ """Create archive of exported report files.
325
+
326
+ Args:
327
+ files: Dictionary of files to archive.
328
+ archive_path: Output archive path.
329
+ format: Archive format (zip, tar, tar.gz).
330
+
331
+ Returns:
332
+ Path to created archive.
333
+
334
+ Raises:
335
+ ValueError: If unsupported archive format is specified.
336
+
337
+ References:
338
+ REPORT-010
339
+ """
340
+ from pathlib import Path
341
+
342
+ archive_path = Path(archive_path)
343
+
344
+ if format == "zip":
345
+ import zipfile
346
+
347
+ with zipfile.ZipFile(archive_path.with_suffix(".zip"), "w") as zipf:
348
+ for path in files.values():
349
+ zipf.write(path, arcname=path.name)
350
+
351
+ return archive_path.with_suffix(".zip")
352
+
353
+ elif format in ("tar", "tar.gz"):
354
+ import tarfile
355
+
356
+ mode = "w:gz" if format == "tar.gz" else "w"
357
+ suffix = ".tar.gz" if format == "tar.gz" else ".tar"
358
+
359
+ with tarfile.open(archive_path.with_suffix(suffix), mode) as tar: # type: ignore[call-overload]
360
+ for path in files.values():
361
+ tar.add(path, arcname=path.name)
362
+
363
+ return archive_path.with_suffix(suffix)
364
+
365
+ else:
366
+ raise ValueError(f"Unsupported archive format: {format}")
@@ -0,0 +1,129 @@
1
+ """Formatting utilities for reports."""
2
+
3
+ from oscura.reporting.formatting.emphasis import (
4
+ VisualEmphasis,
5
+ format_callout_box,
6
+ format_severity,
7
+ )
8
+ from oscura.reporting.formatting.numbers import (
9
+ NumberFormatter,
10
+ format_percentage,
11
+ format_range,
12
+ format_value,
13
+ format_with_context,
14
+ format_with_locale,
15
+ format_with_units,
16
+ )
17
+ from oscura.reporting.formatting.standards import (
18
+ ColorScheme,
19
+ FormatStandards,
20
+ Severity,
21
+ apply_formatting_standards,
22
+ )
23
+
24
+
25
+ def format_margin(
26
+ value: float,
27
+ limit: float,
28
+ unit: str = "",
29
+ limit_type: str = "upper",
30
+ ) -> str:
31
+ """Format a margin value with pass/fail indication.
32
+
33
+ Calculates the margin between a measured value and its specification limit,
34
+ then categorizes it as good (>20%), ok (10-20%), marginal (0-10%), or violation.
35
+
36
+ Args:
37
+ value: Measured value.
38
+ limit: Limit value (specification).
39
+ unit: Unit of measurement.
40
+ limit_type: Type of limit ("upper" or "lower").
41
+
42
+ Returns:
43
+ Formatted margin string with status indication.
44
+
45
+ Examples:
46
+ >>> format_margin(70, 100, limit_type="upper")
47
+ '30.0% margin (good)'
48
+ >>> format_margin(95, 100, limit_type="upper")
49
+ '5.0% margin (marginal)'
50
+ """
51
+ # Calculate margin percentage
52
+ if limit != 0:
53
+ if limit_type == "upper":
54
+ margin = limit - value
55
+ margin_pct = (margin / limit) * 100
56
+ is_passing = value < limit
57
+ else: # lower
58
+ margin = value - limit
59
+ margin_pct = (margin / limit) * 100
60
+ is_passing = value > limit
61
+ else:
62
+ margin_pct = 0
63
+ is_passing = False
64
+
65
+ # Determine status based on margin percentage
66
+ if not is_passing:
67
+ status = "violation"
68
+ elif margin_pct >= 20:
69
+ status = "good"
70
+ elif margin_pct >= 10:
71
+ status = "ok"
72
+ else:
73
+ status = "marginal"
74
+
75
+ return f"{abs(margin_pct):.1f}% margin ({status})"
76
+
77
+
78
+ def format_pass_fail(passed: bool, message: str = "", with_symbol: bool = True) -> str:
79
+ """Format pass/fail status.
80
+
81
+ Args:
82
+ passed: Whether the test passed.
83
+ message: Optional message to append.
84
+ with_symbol: Whether to include Unicode symbol (checkmark/cross).
85
+
86
+ Returns:
87
+ Formatted pass/fail string.
88
+
89
+ Examples:
90
+ >>> format_pass_fail(True)
91
+ 'PASS ✓'
92
+ >>> format_pass_fail(False, with_symbol=False)
93
+ 'FAIL'
94
+ """
95
+ status = "PASS" if passed else "FAIL"
96
+
97
+ if with_symbol:
98
+ symbol = "\u2713" if passed else "\u2717"
99
+ result = f"{status} {symbol}"
100
+ else:
101
+ result = status
102
+
103
+ if message:
104
+ return f"{result}: {message}"
105
+ return result
106
+
107
+
108
+ __all__ = [
109
+ "ColorScheme",
110
+ # Standards
111
+ "FormatStandards",
112
+ # Numbers
113
+ "NumberFormatter",
114
+ "Severity",
115
+ # Emphasis
116
+ "VisualEmphasis",
117
+ "apply_formatting_standards",
118
+ "format_callout_box",
119
+ # Convenience
120
+ "format_margin",
121
+ "format_pass_fail",
122
+ "format_percentage",
123
+ "format_range",
124
+ "format_severity",
125
+ "format_value",
126
+ "format_with_context",
127
+ "format_with_locale",
128
+ "format_with_units",
129
+ ]
@@ -0,0 +1,81 @@
1
+ """Text emphasis formatting utilities.
2
+
3
+ Simple text formatting for terminal output.
4
+ """
5
+
6
+ from enum import Enum
7
+
8
+
9
+ class VisualEmphasis(Enum):
10
+ """Visual emphasis levels."""
11
+
12
+ NONE = "none"
13
+ SUBTLE = "subtle"
14
+ MODERATE = "moderate"
15
+ STRONG = "strong"
16
+ CRITICAL = "critical"
17
+
18
+
19
+ def bold(text: str) -> str:
20
+ """Make text bold (terminal)."""
21
+ return f"\033[1m{text}\033[0m"
22
+
23
+
24
+ def italic(text: str) -> str:
25
+ """Make text italic (terminal)."""
26
+ return f"\033[3m{text}\033[0m"
27
+
28
+
29
+ def underline(text: str) -> str:
30
+ """Underline text (terminal)."""
31
+ return f"\033[4m{text}\033[0m"
32
+
33
+
34
+ def color(text: str, color_code: str) -> str:
35
+ """Color text (terminal)."""
36
+ colors = {
37
+ "red": "31",
38
+ "green": "32",
39
+ "yellow": "33",
40
+ "blue": "34",
41
+ "magenta": "35",
42
+ "cyan": "36",
43
+ "white": "37",
44
+ }
45
+ code = colors.get(color_code.lower(), "37")
46
+ return f"\033[{code}m{text}\033[0m"
47
+
48
+
49
+ def format_severity(text: str, severity: str) -> str:
50
+ """Format text based on severity level."""
51
+ severity_colors = {
52
+ "critical": "red",
53
+ "error": "red",
54
+ "warning": "yellow",
55
+ "info": "blue",
56
+ "success": "green",
57
+ }
58
+ return color(text, severity_colors.get(severity.lower(), "white"))
59
+
60
+
61
+ def format_callout_box(title: str, content: str, severity: str = "info") -> str:
62
+ """Format a callout box."""
63
+ lines = [
64
+ "=" * 60,
65
+ format_severity(f" {title.upper()} ", severity),
66
+ "-" * 60,
67
+ content,
68
+ "=" * 60,
69
+ ]
70
+ return "\n".join(lines)
71
+
72
+
73
+ __all__ = [
74
+ "VisualEmphasis",
75
+ "bold",
76
+ "color",
77
+ "format_callout_box",
78
+ "format_severity",
79
+ "italic",
80
+ "underline",
81
+ ]