specmod 0.2.3__tar.gz → 0.3.0__tar.gz

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 (268) hide show
  1. {specmod-0.2.3 → specmod-0.3.0}/.github/workflows/build.yml +7 -3
  2. {specmod-0.2.3 → specmod-0.3.0}/.github/workflows/docs.yml +7 -3
  3. {specmod-0.2.3 → specmod-0.3.0}/.github/workflows/release.yml +6 -2
  4. {specmod-0.2.3 → specmod-0.3.0}/.github/workflows/test.yml +31 -11
  5. {specmod-0.2.3 → specmod-0.3.0}/.pre-commit-config.yaml +9 -3
  6. specmod-0.3.0/.release-please-manifest.json +3 -0
  7. {specmod-0.2.3 → specmod-0.3.0}/CHANGELOG.md +36 -0
  8. {specmod-0.2.3 → specmod-0.3.0}/PKG-INFO +6 -5
  9. {specmod-0.2.3 → specmod-0.3.0}/README.md +3 -3
  10. {specmod-0.2.3 → specmod-0.3.0}/docs/REFACTOR_PLAN.md +1 -1
  11. specmod-0.3.0/docs/_builders/_notebook.py +162 -0
  12. specmod-0.2.3/docs/notebooks/_build_notebook.py → specmod-0.3.0/docs/_builders/choosing_a_transform.py +132 -103
  13. specmod-0.3.0/docs/_builders/specmod_tutorial.py +980 -0
  14. specmod-0.3.0/docs/_static/academic.css +70 -0
  15. specmod-0.3.0/docs/_static/fonts/OFL-fira-code.txt +93 -0
  16. specmod-0.3.0/docs/_static/fonts/OFL-merriweather.txt +93 -0
  17. specmod-0.3.0/docs/_static/fonts/OFL-open-sans.txt +88 -0
  18. specmod-0.3.0/docs/_static/fonts/README.md +7 -0
  19. specmod-0.3.0/docs/_static/fonts/fira-code-400-greek.woff2 +0 -0
  20. specmod-0.3.0/docs/_static/fonts/fira-code-400-latin-ext.woff2 +0 -0
  21. specmod-0.3.0/docs/_static/fonts/fira-code-400-latin.woff2 +0 -0
  22. specmod-0.3.0/docs/_static/fonts/merriweather-latin-ext.woff2 +0 -0
  23. specmod-0.3.0/docs/_static/fonts/merriweather-latin.woff2 +0 -0
  24. specmod-0.3.0/docs/_static/fonts/open-sans-greek.woff2 +0 -0
  25. specmod-0.3.0/docs/_static/fonts/open-sans-latin-ext.woff2 +0 -0
  26. specmod-0.3.0/docs/_static/fonts/open-sans-latin.woff2 +0 -0
  27. specmod-0.3.0/docs/_static/fonts.css +134 -0
  28. specmod-0.3.0/docs/_static/katex/LICENSE +21 -0
  29. specmod-0.3.0/docs/_static/katex/README.md +9 -0
  30. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  31. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  32. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  33. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  34. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  35. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  36. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  37. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  38. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  39. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  40. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  41. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  42. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  43. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  44. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  45. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  46. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  47. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  48. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  49. specmod-0.3.0/docs/_static/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  50. specmod-0.3.0/docs/_static/katex/katex.min.css +1 -0
  51. specmod-0.3.0/docs/_static/specmod-logo-academic-dark-mark.svg +340 -0
  52. specmod-0.3.0/docs/_static/specmod-logo-academic-dark.svg +333 -0
  53. specmod-0.3.0/docs/_static/specmod-logo-academic-mark.svg +340 -0
  54. specmod-0.3.0/docs/_static/specmod-logo-academic.svg +333 -0
  55. {specmod-0.2.3 → specmod-0.3.0}/docs/api.md +22 -9
  56. specmod-0.3.0/docs/branding.md +163 -0
  57. {specmod-0.2.3 → specmod-0.3.0}/docs/conf.py +87 -25
  58. {specmod-0.2.3 → specmod-0.3.0}/docs/documentation.md +114 -1
  59. {specmod-0.2.3 → specmod-0.3.0}/docs/getting-started.md +3 -3
  60. {specmod-0.2.3 → specmod-0.3.0}/docs/index.md +2 -2
  61. {specmod-0.2.3 → specmod-0.3.0}/docs/notebooks/choosing-a-transform.ipynb +4 -4
  62. {specmod-0.2.3 → specmod-0.3.0}/docs/notes/api-audit.md +2 -2
  63. {specmod-0.2.3 → specmod-0.3.0}/docs/pick-formats.md +9 -8
  64. {specmod-0.2.3 → specmod-0.3.0}/docs/processing.md +44 -5
  65. {specmod-0.2.3 → specmod-0.3.0}/docs/roadmap.md +23 -13
  66. {specmod-0.2.3 → specmod-0.3.0}/docs/upgrading.md +106 -27
  67. {specmod-0.2.3 → specmod-0.3.0}/pyproject.toml +5 -1
  68. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/api.py +6 -2
  69. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/config/sections.py +23 -1
  70. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/core/bandwidth.py +122 -4
  71. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/core/collection.py +127 -12
  72. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/core/noise.py +1 -0
  73. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/datasets.py +1 -1
  74. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/distance.py +2 -2
  75. specmod-0.3.0/src/specmod/io/__init__.py +54 -0
  76. specmod-0.3.0/src/specmod/io/backend.py +28 -0
  77. specmod-0.3.0/src/specmod/io/hdf5.py +119 -0
  78. specmod-0.3.0/src/specmod/io/layout.py +85 -0
  79. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/pipeline.py +26 -1
  80. specmod-0.3.0/src/specmod/plotting/__init__.py +31 -0
  81. specmod-0.3.0/src/specmod/plotting/grid.py +60 -0
  82. specmod-0.2.3/src/specmod/plotting.py → specmod-0.3.0/src/specmod/plotting/pair.py +6 -78
  83. specmod-0.3.0/src/specmod/plotting/style.py +24 -0
  84. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/preprocess.py +160 -117
  85. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/smoothing/__init__.py +6 -1
  86. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/transforms/__init__.py +16 -1
  87. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/utils.py +2 -1
  88. {specmod-0.2.3 → specmod-0.3.0}/tests/conftest.py +5 -10
  89. {specmod-0.2.3 → specmod-0.3.0}/tests/test_api_surface.py +1 -3
  90. {specmod-0.2.3 → specmod-0.3.0}/tests/test_collection.py +228 -4
  91. {specmod-0.2.3 → specmod-0.3.0}/tests/test_config.py +42 -0
  92. specmod-0.3.0/tests/test_docs_are_current.py +152 -0
  93. {specmod-0.2.3 → specmod-0.3.0}/tests/test_end_to_end.py +3 -8
  94. {specmod-0.2.3 → specmod-0.3.0}/tests/test_golden_reference.py +4 -9
  95. {specmod-0.2.3 → specmod-0.3.0}/tests/test_io_and_plotting.py +8 -3
  96. {specmod-0.2.3 → specmod-0.3.0}/tests/test_legacy_fixes.py +10 -10
  97. {specmod-0.2.3 → specmod-0.3.0}/tests/test_pick_plugins.py +1 -1
  98. {specmod-0.2.3 → specmod-0.3.0}/tests/test_picks.py +4 -4
  99. {specmod-0.2.3 → specmod-0.3.0}/tests/test_preprocess.py +169 -74
  100. {specmod-0.2.3 → specmod-0.3.0}/tests/test_staged.py +1 -1
  101. {specmod-0.2.3 → specmod-0.3.0}/tests/test_stubs.py +2 -2
  102. {specmod-0.2.3 → specmod-0.3.0}/tests/test_tutorial.py +3 -1
  103. specmod-0.3.0/tools/make_logo.py +158 -0
  104. {specmod-0.2.3 → specmod-0.3.0}/tools/measure_docs.py +1 -1
  105. specmod-0.3.0/tools/vendor_fonts.py +175 -0
  106. specmod-0.3.0/tools/vendor_katex.py +122 -0
  107. specmod-0.2.3/tutorial/SpecModTutorial.ipynb → specmod-0.3.0/tutorial/specmod-tutorial.ipynb +241 -54
  108. specmod-0.2.3/.release-please-manifest.json +0 -3
  109. specmod-0.2.3/src/specmod/io.py +0 -241
  110. specmod-0.2.3/tests/test_docs_are_current.py +0 -80
  111. {specmod-0.2.3 → specmod-0.3.0}/.git-blame-ignore-revs +0 -0
  112. {specmod-0.2.3 → specmod-0.3.0}/.gitignore +0 -0
  113. {specmod-0.2.3 → specmod-0.3.0}/.readthedocs.yaml +0 -0
  114. {specmod-0.2.3 → specmod-0.3.0}/AGENTS.md +0 -0
  115. {specmod-0.2.3 → specmod-0.3.0}/CITATION.cff +0 -0
  116. {specmod-0.2.3 → specmod-0.3.0}/CLAUDE.md +0 -0
  117. {specmod-0.2.3 → specmod-0.3.0}/CONTRIBUTING.md +0 -0
  118. {specmod-0.2.3 → specmod-0.3.0}/LICENSE +0 -0
  119. {specmod-0.2.3 → specmod-0.3.0}/datasets/magna_2020.toml +0 -0
  120. {specmod-0.2.3 → specmod-0.3.0}/datasets/pnr_2019.toml +0 -0
  121. {specmod-0.2.3 → specmod-0.3.0}/docs/choosing-a-transform.md +0 -0
  122. {specmod-0.2.3 → specmod-0.3.0}/docs/contributing.md +0 -0
  123. {specmod-0.2.3 → specmod-0.3.0}/docs/development.md +0 -0
  124. {specmod-0.2.3 → specmod-0.3.0}/docs/guides.md +0 -0
  125. {specmod-0.2.3 → specmod-0.3.0}/docs/notes/window-position.md +0 -0
  126. {specmod-0.2.3 → specmod-0.3.0}/docs/releasing-data.md +0 -0
  127. {specmod-0.2.3 → specmod-0.3.0}/docs/releasing.md +0 -0
  128. {specmod-0.2.3 → specmod-0.3.0}/release-please-config.json +0 -0
  129. {specmod-0.2.3 → specmod-0.3.0}/requirements.txt +0 -0
  130. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/__init__.py +0 -0
  131. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/_vendor/__init__.py +0 -0
  132. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/_vendor/qiinv.py +0 -0
  133. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/acquire.py +0 -0
  134. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/cli.py +0 -0
  135. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/config/__init__.py +0 -0
  136. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/config/layers.py +0 -0
  137. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/config/provenance.py +0 -0
  138. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/config/serialize.py +0 -0
  139. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/core/__init__.py +0 -0
  140. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/core/scalogram.py +0 -0
  141. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/core/spectrum.py +0 -0
  142. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/core/units.py +0 -0
  143. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/exceptions.py +0 -0
  144. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/fitting/__init__.py +0 -0
  145. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/fitting/base.py +0 -0
  146. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/fitting/event.py +0 -0
  147. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/fitting/guess.py +0 -0
  148. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/fitting/spectrum.py +0 -0
  149. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/magnitude.py +0 -0
  150. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/picks/__init__.py +0 -0
  151. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/picks/base.py +0 -0
  152. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/picks/delimited.py +0 -0
  153. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/picks/events.py +0 -0
  154. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/picks/resolution.py +0 -0
  155. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/picks/snuffler.py +0 -0
  156. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/smoothing/base.py +0 -0
  157. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/smoothing/konno_ohmachi.py +0 -0
  158. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/smoothing/log_bins.py +0 -0
  159. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/sources/__init__.py +0 -0
  160. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/sources/attenuation.py +0 -0
  161. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/sources/composite.py +0 -0
  162. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/sources/motion.py +0 -0
  163. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/sources/source.py +0 -0
  164. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/spreading.py +0 -0
  165. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/staged.py +0 -0
  166. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/tables.py +0 -0
  167. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/transforms/base.py +0 -0
  168. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/transforms/cwt.py +0 -0
  169. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/transforms/fft.py +0 -0
  170. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/transforms/multitaper.py +0 -0
  171. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/transforms/prieto.py +0 -0
  172. {specmod-0.2.3 → specmod-0.3.0}/src/specmod/transforms/quadratic.py +0 -0
  173. {specmod-0.2.3 → specmod-0.3.0}/stubs/README.md +0 -0
  174. {specmod-0.2.3 → specmod-0.3.0}/stubs/lmfit/__init__.pyi +0 -0
  175. {specmod-0.2.3 → specmod-0.3.0}/stubs/lmfit/model.pyi +0 -0
  176. {specmod-0.2.3 → specmod-0.3.0}/stubs/lmfit/parameter.pyi +0 -0
  177. {specmod-0.2.3 → specmod-0.3.0}/stubs/obspy/__init__.pyi +0 -0
  178. {specmod-0.2.3 → specmod-0.3.0}/stubs/obspy/clients/__init__.pyi +0 -0
  179. {specmod-0.2.3 → specmod-0.3.0}/stubs/obspy/clients/fdsn/__init__.pyi +0 -0
  180. {specmod-0.2.3 → specmod-0.3.0}/stubs/obspy/core/__init__.pyi +0 -0
  181. {specmod-0.2.3 → specmod-0.3.0}/stubs/obspy/core/event.pyi +0 -0
  182. {specmod-0.2.3 → specmod-0.3.0}/stubs/obspy/core/inventory.pyi +0 -0
  183. {specmod-0.2.3 → specmod-0.3.0}/stubs/obspy/core/stream.pyi +0 -0
  184. {specmod-0.2.3 → specmod-0.3.0}/stubs/obspy/core/trace.pyi +0 -0
  185. {specmod-0.2.3 → specmod-0.3.0}/stubs/obspy/core/utcdatetime.pyi +0 -0
  186. {specmod-0.2.3 → specmod-0.3.0}/stubs/obspy/core/util/__init__.pyi +0 -0
  187. {specmod-0.2.3 → specmod-0.3.0}/stubs/obspy/core/util/base.pyi +0 -0
  188. {specmod-0.2.3 → specmod-0.3.0}/stubs/obspy/geodetics/__init__.pyi +0 -0
  189. {specmod-0.2.3 → specmod-0.3.0}/stubs/obspy/signal/__init__.pyi +0 -0
  190. {specmod-0.2.3 → specmod-0.3.0}/stubs/obspy/signal/konnoohmachismoothing.pyi +0 -0
  191. {specmod-0.2.3 → specmod-0.3.0}/studies/magna_2020_paper.toml +0 -0
  192. {specmod-0.2.3 → specmod-0.3.0}/tests/__init__.py +0 -0
  193. {specmod-0.2.3 → specmod-0.3.0}/tests/golden/motion_reference.json +0 -0
  194. {specmod-0.2.3 → specmod-0.3.0}/tests/golden/pipeline_reference.json +0 -0
  195. {specmod-0.2.3 → specmod-0.3.0}/tests/golden/window_reference.json +0 -0
  196. {specmod-0.2.3 → specmod-0.3.0}/tests/test_acquire.py +0 -0
  197. {specmod-0.2.3 → specmod-0.3.0}/tests/test_ambient_state.py +0 -0
  198. {specmod-0.2.3 → specmod-0.3.0}/tests/test_cwt.py +0 -0
  199. {specmod-0.2.3 → specmod-0.3.0}/tests/test_datasets.py +0 -0
  200. {specmod-0.2.3 → specmod-0.3.0}/tests/test_fitting_defaults.py +0 -0
  201. {specmod-0.2.3 → specmod-0.3.0}/tests/test_import.py +0 -0
  202. {specmod-0.2.3 → specmod-0.3.0}/tests/test_magnitude.py +0 -0
  203. {specmod-0.2.3 → specmod-0.3.0}/tests/test_make_golden.py +0 -0
  204. {specmod-0.2.3 → specmod-0.3.0}/tests/test_pick_readers.py +0 -0
  205. {specmod-0.2.3 → specmod-0.3.0}/tests/test_pipeline.py +0 -0
  206. {specmod-0.2.3 → specmod-0.3.0}/tests/test_pipeline_smoke.py +0 -0
  207. {specmod-0.2.3 → specmod-0.3.0}/tests/test_prieto.py +0 -0
  208. {specmod-0.2.3 → specmod-0.3.0}/tests/test_quadratic.py +0 -0
  209. {specmod-0.2.3 → specmod-0.3.0}/tests/test_release_config.py +0 -0
  210. {specmod-0.2.3 → specmod-0.3.0}/tests/test_smoothing.py +0 -0
  211. {specmod-0.2.3 → specmod-0.3.0}/tests/test_sources.py +0 -0
  212. {specmod-0.2.3 → specmod-0.3.0}/tests/test_spectral_wiring.py +0 -0
  213. {specmod-0.2.3 → specmod-0.3.0}/tests/test_transforms.py +0 -0
  214. {specmod-0.2.3 → specmod-0.3.0}/tests/test_typing_backlog.py +0 -0
  215. {specmod-0.2.3 → specmod-0.3.0}/tests/test_utils.py +0 -0
  216. {specmod-0.2.3 → specmod-0.3.0}/tests/test_versioning.py +0 -0
  217. {specmod-0.2.3 → specmod-0.3.0}/tools/check_built_version.py +0 -0
  218. {specmod-0.2.3 → specmod-0.3.0}/tools/check_floors.py +0 -0
  219. {specmod-0.2.3 → specmod-0.3.0}/tools/make_golden.py +0 -0
  220. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/picks/2019-08-26T07:30:47.000000Z.picks +0 -0
  221. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/picks/2019-08-26T07:30:47.000000Z.xml +0 -0
  222. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/stations/inventory.xml +0 -0
  223. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L001..HHE_2019-08-26T07:30:47.000000Z +0 -0
  224. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L001..HHN_2019-08-26T07:30:47.000000Z +0 -0
  225. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L001..HHZ_2019-08-26T07:30:47.000000Z +0 -0
  226. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L002..HHE_2019-08-26T07:30:47.000000Z +0 -0
  227. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L002..HHN_2019-08-26T07:30:47.000000Z +0 -0
  228. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L002..HHZ_2019-08-26T07:30:47.000000Z +0 -0
  229. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L006..HHE_2019-08-26T07:30:47.000000Z +0 -0
  230. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L006..HHN_2019-08-26T07:30:47.000000Z +0 -0
  231. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L006..HHZ_2019-08-26T07:30:47.000000Z +0 -0
  232. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L007..HHE_2019-08-26T07:30:47.000000Z +0 -0
  233. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L007..HHN_2019-08-26T07:30:47.000000Z +0 -0
  234. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L007..HHZ_2019-08-26T07:30:47.000000Z +0 -0
  235. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L008..HHE_2019-08-26T07:30:47.000000Z +0 -0
  236. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L008..HHN_2019-08-26T07:30:47.000000Z +0 -0
  237. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L008..HHZ_2019-08-26T07:30:47.000000Z +0 -0
  238. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L009..HHE_2019-08-26T07:30:47.000000Z +0 -0
  239. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L009..HHN_2019-08-26T07:30:47.000000Z +0 -0
  240. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.L009..HHZ_2019-08-26T07:30:47.000000Z +0 -0
  241. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.LD06..HH1_2019-08-26T07:30:47.000000Z +0 -0
  242. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.LD06..HH2_2019-08-26T07:30:47.000000Z +0 -0
  243. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/LV.LD06..HH3_2019-08-26T07:30:47.000000Z +0 -0
  244. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ01.00.HHE_2019-08-26T07:30:47.000000Z +0 -0
  245. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ01.00.HHN_2019-08-26T07:30:47.000000Z +0 -0
  246. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ01.00.HHZ_2019-08-26T07:30:47.000000Z +0 -0
  247. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ02.00.HHZ_2019-08-26T07:30:47.000000Z +0 -0
  248. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ03.00.HHE_2019-08-26T07:30:47.000000Z +0 -0
  249. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ03.00.HHN_2019-08-26T07:30:47.000000Z +0 -0
  250. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ03.00.HHZ_2019-08-26T07:30:47.000000Z +0 -0
  251. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ04.00.HHE_2019-08-26T07:30:47.000000Z +0 -0
  252. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ04.00.HHN_2019-08-26T07:30:47.000000Z +0 -0
  253. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ04.00.HHZ_2019-08-26T07:30:47.000000Z +0 -0
  254. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ05.00.HHE_2019-08-26T07:30:47.000000Z +0 -0
  255. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ05.00.HHN_2019-08-26T07:30:47.000000Z +0 -0
  256. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ05.00.HHZ_2019-08-26T07:30:47.000000Z +0 -0
  257. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ06.00.HHE_2019-08-26T07:30:47.000000Z +0 -0
  258. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ06.00.HHN_2019-08-26T07:30:47.000000Z +0 -0
  259. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ06.00.HHZ_2019-08-26T07:30:47.000000Z +0 -0
  260. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ07.00.HHE_2019-08-26T07:30:47.000000Z +0 -0
  261. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ07.00.HHN_2019-08-26T07:30:47.000000Z +0 -0
  262. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ07.00.HHZ_2019-08-26T07:30:47.000000Z +0 -0
  263. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ09.00.HHE_2019-08-26T07:30:47.000000Z +0 -0
  264. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ09.00.HHN_2019-08-26T07:30:47.000000Z +0 -0
  265. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ09.00.HHZ_2019-08-26T07:30:47.000000Z +0 -0
  266. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ10.00.HHE_2019-08-26T07:30:47.000000Z +0 -0
  267. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ10.00.HHN_2019-08-26T07:30:47.000000Z +0 -0
  268. {specmod-0.2.3 → specmod-0.3.0}/tutorial/data/events/2019-08-26T07:30:47.000000Z/waveforms/UR.AQ10.00.HHZ_2019-08-26T07:30:47.000000Z +0 -0
@@ -10,10 +10,14 @@ jobs:
10
10
  build:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
- - uses: actions/checkout@v4
13
+ - uses: actions/checkout@v7
14
14
  with:
15
15
  fetch-depth: 0 # hatch-vcs derives the version from tags
16
- - uses: astral-sh/setup-uv@v5
16
+ # Pinned to a full version, unlike the other three. `astral-sh/setup-uv`
17
+ # publishes no bare major tag — `@v10` does not resolve, and using it
18
+ # fails every job at "Getting action download info", before a step runs.
19
+ # Bump this deliberately; do not "tidy" it to `@v10`.
20
+ - uses: astral-sh/setup-uv@v10.0.1
17
21
  - run: uv python install 3.11
18
22
 
19
23
  - name: Build sdist and wheel
@@ -34,7 +38,7 @@ jobs:
34
38
  print('specmod', specmod.__version__, '- wheel imports cleanly')
35
39
  "
36
40
 
37
- - uses: actions/upload-artifact@v4
41
+ - uses: actions/upload-artifact@v7
38
42
  with:
39
43
  name: dist
40
44
  path: dist/
@@ -24,10 +24,14 @@ jobs:
24
24
  docs:
25
25
  runs-on: ubuntu-latest
26
26
  steps:
27
- - uses: actions/checkout@v4
27
+ - uses: actions/checkout@v7
28
28
  with:
29
29
  fetch-depth: 0 # hatch-vcs derives the version from tags
30
- - uses: astral-sh/setup-uv@v5
30
+ # Pinned to a full version, unlike the other three. `astral-sh/setup-uv`
31
+ # publishes no bare major tag — `@v10` does not resolve, and using it
32
+ # fails every job at "Getting action download info", before a step runs.
33
+ # Bump this deliberately; do not "tidy" it to `@v10`.
34
+ - uses: astral-sh/setup-uv@v10.0.1
31
35
  - run: uv python install 3.11
32
36
  - run: uv venv # create a venv to install into
33
37
 
@@ -50,7 +54,7 @@ jobs:
50
54
 
51
55
  # Not a Pages artefact: this is a plain zip anyone can download from the
52
56
  # run and open locally, and it stays useful if Read the Docs is down.
53
- - uses: actions/upload-artifact@v4
57
+ - uses: actions/upload-artifact@v7
54
58
  with:
55
59
  name: docs-html
56
60
  path: docs/_build/html
@@ -77,7 +77,7 @@ jobs:
77
77
  id-token: write # mandatory for Trusted Publishing
78
78
  contents: read
79
79
  steps:
80
- - uses: actions/checkout@v4
80
+ - uses: actions/checkout@v7
81
81
  with:
82
82
  # The tag, not the branch. main has already moved on by one commit
83
83
  # (the release PR merge), and hatch-vcs would derive a .postN.devN
@@ -85,7 +85,11 @@ jobs:
85
85
  ref: ${{ needs.release-please.outputs.tag_name }}
86
86
  fetch-depth: 0 # hatch-vcs derives the version by describing tags
87
87
 
88
- - uses: astral-sh/setup-uv@v5
88
+ # Pinned to a full version, unlike the other three. `astral-sh/setup-uv`
89
+ # publishes no bare major tag — `@v10` does not resolve, and using it
90
+ # fails every job at "Getting action download info", before a step runs.
91
+ # Bump this deliberately; do not "tidy" it to `@v10`.
92
+ - uses: astral-sh/setup-uv@v10.0.1
89
93
  - run: uv python install 3.11
90
94
  - run: uv build
91
95
 
@@ -14,8 +14,12 @@ jobs:
14
14
  lint:
15
15
  runs-on: ubuntu-latest
16
16
  steps:
17
- - uses: actions/checkout@v4
18
- - uses: astral-sh/setup-uv@v5
17
+ - uses: actions/checkout@v7
18
+ # Pinned to a full version, unlike the other three. `astral-sh/setup-uv`
19
+ # publishes no bare major tag — `@v10` does not resolve, and using it
20
+ # fails every job at "Getting action download info", before a step runs.
21
+ # Bump this deliberately; do not "tidy" it to `@v10`.
22
+ - uses: astral-sh/setup-uv@v10.0.1
19
23
  with:
20
24
  enable-cache: true
21
25
  - run: uv python install 3.11
@@ -28,10 +32,14 @@ jobs:
28
32
  typecheck:
29
33
  runs-on: ubuntu-latest
30
34
  steps:
31
- - uses: actions/checkout@v4
35
+ - uses: actions/checkout@v7
32
36
  with:
33
37
  fetch-depth: 0 # hatch-vcs needs history to derive the version
34
- - uses: astral-sh/setup-uv@v5
38
+ # Pinned to a full version, unlike the other three. `astral-sh/setup-uv`
39
+ # publishes no bare major tag — `@v10` does not resolve, and using it
40
+ # fails every job at "Getting action download info", before a step runs.
41
+ # Bump this deliberately; do not "tidy" it to `@v10`.
42
+ - uses: astral-sh/setup-uv@v10.0.1
35
43
  with:
36
44
  enable-cache: true
37
45
  - run: uv python install 3.11
@@ -48,10 +56,14 @@ jobs:
48
56
  os: [ubuntu-latest, macos-latest]
49
57
  python-version: ["3.11", "3.12", "3.13"]
50
58
  steps:
51
- - uses: actions/checkout@v4
59
+ - uses: actions/checkout@v7
52
60
  with:
53
61
  fetch-depth: 0
54
- - uses: astral-sh/setup-uv@v5
62
+ # Pinned to a full version, unlike the other three. `astral-sh/setup-uv`
63
+ # publishes no bare major tag — `@v10` does not resolve, and using it
64
+ # fails every job at "Getting action download info", before a step runs.
65
+ # Bump this deliberately; do not "tidy" it to `@v10`.
66
+ - uses: astral-sh/setup-uv@v10.0.1
55
67
  with:
56
68
  enable-cache: true
57
69
  - run: uv python install ${{ matrix.python-version }}
@@ -64,7 +76,7 @@ jobs:
64
76
  # execution is excluded because it costs ~40s and a Jupyter kernel, and
65
77
  # is worth paying once rather than six times — see the notebook job.
66
78
  run: uv run pytest -m "not dataset and not notebook" --cov=specmod --cov-report=xml --cov-report=term
67
- - uses: codecov/codecov-action@v4
79
+ - uses: codecov/codecov-action@v7
68
80
  if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
69
81
  with:
70
82
  files: coverage.xml
@@ -73,10 +85,14 @@ jobs:
73
85
  floors:
74
86
  runs-on: ubuntu-latest
75
87
  steps:
76
- - uses: actions/checkout@v4
88
+ - uses: actions/checkout@v7
77
89
  with:
78
90
  fetch-depth: 0
79
- - uses: astral-sh/setup-uv@v5
91
+ # Pinned to a full version, unlike the other three. `astral-sh/setup-uv`
92
+ # publishes no bare major tag — `@v10` does not resolve, and using it
93
+ # fails every job at "Getting action download info", before a step runs.
94
+ # Bump this deliberately; do not "tidy" it to `@v10`.
95
+ - uses: astral-sh/setup-uv@v10.0.1
80
96
  - run: uv python install 3.11
81
97
  - run: uv venv
82
98
  # Exercise the declared minimums. CI otherwise installs the newest of
@@ -92,10 +108,14 @@ jobs:
92
108
  # third kind, so it runs per-PR rather than at release.
93
109
  runs-on: ubuntu-latest
94
110
  steps:
95
- - uses: actions/checkout@v4
111
+ - uses: actions/checkout@v7
96
112
  with:
97
113
  fetch-depth: 0
98
- - uses: astral-sh/setup-uv@v5
114
+ # Pinned to a full version, unlike the other three. `astral-sh/setup-uv`
115
+ # publishes no bare major tag — `@v10` does not resolve, and using it
116
+ # fails every job at "Getting action download info", before a step runs.
117
+ # Bump this deliberately; do not "tidy" it to `@v10`.
118
+ - uses: astral-sh/setup-uv@v10.0.1
99
119
  with:
100
120
  enable-cache: true
101
121
  - run: uv python install 3.11
@@ -36,12 +36,18 @@ repos:
36
36
  # running the hooks over one silently stripped six bytes from the middle
37
37
  # of it — a corrupted pickle that still looks like a plausible diff.
38
38
  # Anything binary has to be excluded by hand; the hooks do not detect it.
39
+ #
40
+ # The two vendored directories under `docs/_static/` are excluded for
41
+ # both reasons at once. Their `.woff2` files are binary, and the licences
42
+ # beside them have to stay byte identical to what upstream publishes —
43
+ # `trailing-whitespace` edited Merriweather's OFL the first time it was
44
+ # staged, which is a licence rewritten to satisfy a linter.
39
45
  - id: end-of-file-fixer
40
- exclude: '\.spec$'
46
+ exclude: &verbatim '(\.spec$|^docs/_static/(fonts|katex)/)'
41
47
  - id: trailing-whitespace
42
- exclude: '\.spec$'
48
+ exclude: *verbatim
43
49
  - id: mixed-line-ending
44
- exclude: '\.spec$'
50
+ exclude: *verbatim
45
51
 
46
52
  - repo: https://github.com/kynan/nbstripout
47
53
  rev: 0.8.1
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.3.0"
3
+ }
@@ -9,6 +9,42 @@ release could have this problem — every later one has a real predecessor tag
9
9
  so leave subsequent headings as generated. release-please only prepends to this
10
10
  file, so this edit survives. -->
11
11
 
12
+ ## [0.3.0](https://github.com/sgjholt/SpecMod/compare/v0.2.3...v0.3.0) (2026-09-06)
13
+
14
+
15
+ ### ⚠ BREAKING CHANGES
16
+
17
+ * the tutorial page moves from `/tutorial/SpecModTutorial.html` to `/tutorial/specmod-tutorial.html`.
18
+ * the ten mutating functions in `specmod.preprocess` are renamed and now return a new stream or trace. `get_signal` is removed, and the two undated deprecation shims are gone.
19
+
20
+ ### Features
21
+
22
+ * let the fitted band be capped, fixed or corrected by hand ([ba241a9](https://github.com/sgjholt/SpecMod/commit/ba241a95d94a298418b3c6f414a0026b9f310926))
23
+ * preprocess returns new streams instead of mutating ([78aabeb](https://github.com/sgjholt/SpecMod/commit/78aabeb4856eda4208787fcd53b0df77bc50bd1c))
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * give the noise and bandwidth registries a documented home ([daeb2cc](https://github.com/sgjholt/SpecMod/commit/daeb2cc43eff2869e9617e4111f281f9bd4d33bb))
29
+ * **transforms:** re-export every public name from base ([67c24c3](https://github.com/sgjholt/SpecMod/commit/67c24c3dda80f7fbc1bd51861861bde48d943eac))
30
+
31
+
32
+ ### Code Refactoring
33
+
34
+ * **docs:** build the tutorial from a script too ([de846c6](https://github.com/sgjholt/SpecMod/commit/de846c6128fc7922df8e1e3c2c1c01f2b8bf95bb))
35
+ * **docs:** one notebook builder per notebook, named after it ([0175f89](https://github.com/sgjholt/SpecMod/commit/0175f890f5f92668341a17bece7149f7d8f8c6b7))
36
+ * split io and plotting into packages ([027b77f](https://github.com/sgjholt/SpecMod/commit/027b77f7d050716b7d98fc873a8a4e93098d1d68))
37
+
38
+
39
+ ### Documentation
40
+
41
+ * adopt the academic theme from the branding manual ([76f0aa0](https://github.com/sgjholt/SpecMod/commit/76f0aa0a40a134eeb91b8ada04c8f5fa66243c54))
42
+ * kebab-case the tutorial notebook, snake_case its builder ([5a65b73](https://github.com/sgjholt/SpecMod/commit/5a65b7336980bf5d7163c4cad28e7c273715c0a3))
43
+ * measure the MathJax figure instead of asserting it ([cba7e01](https://github.com/sgjholt/SpecMod/commit/cba7e0116a09ba8d4565308ee8c0227d138b20cd))
44
+ * render maths with KaTeX, served from this site ([902d3fe](https://github.com/sgjholt/SpecMod/commit/902d3fec4230e6af17ea1b7a9d32d9e9c153fd8e))
45
+ * self-host the branding fonts ([c1e6f9f](https://github.com/sgjholt/SpecMod/commit/c1e6f9fe16f4ec48c5e788e6b29181c5ffc01bbf))
46
+ * the decomposition is finished ([bbb2b71](https://github.com/sgjholt/SpecMod/commit/bbb2b7189b9f98c3baa48fe907bc2c414455f5d9))
47
+
12
48
  ## [0.2.3](https://github.com/sgjholt/SpecMod/compare/v0.2.2...v0.2.3) (2026-09-05)
13
49
 
14
50
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: specmod
3
- Version: 0.2.3
3
+ Version: 0.3.0
4
4
  Summary: A Python toolbox for processing and modelling seismic spectra
5
5
  Project-URL: Homepage, https://github.com/sgjholt/SpecMod
6
6
  Project-URL: Repository, https://github.com/sgjholt/SpecMod
@@ -35,11 +35,12 @@ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
35
35
  Requires-Dist: pytest>=8.0; extra == 'dev'
36
36
  Requires-Dist: ruff>=0.6; extra == 'dev'
37
37
  Provides-Extra: docs
38
+ Requires-Dist: furo>=2024.1; extra == 'docs'
38
39
  Requires-Dist: myst-nb>=1.1; extra == 'docs'
39
40
  Requires-Dist: myst-parser>=3.0; extra == 'docs'
40
- Requires-Dist: pydata-sphinx-theme>=0.15; extra == 'docs'
41
41
  Requires-Dist: sphinx-togglebutton>=0.3; extra == 'docs'
42
42
  Requires-Dist: sphinx>=7.3; extra == 'docs'
43
+ Requires-Dist: sphinxcontrib-katex>=0.9; extra == 'docs'
43
44
  Provides-Extra: io
44
45
  Requires-Dist: h5py>=3.11; extra == 'io'
45
46
  Requires-Dist: pyarrow>=16.0; extra == 'io'
@@ -179,13 +180,13 @@ changes nothing else.
179
180
 
180
181
  ## Reading picks
181
182
 
182
- Arrivals are attached to traces by `set_picks`, which detects the format from
183
- the file rather than from its name:
183
+ `with_picks` returns a stream with arrivals attached, detecting the format
184
+ from the file rather than from its name:
184
185
 
185
186
  ```python
186
187
  import specmod.preprocess as pre
187
188
 
188
- pre.set_picks(stream, "event.xml")
189
+ stream = pre.with_picks(stream, "event.xml")
189
190
  ```
190
191
 
191
192
  Everything `obspy.read_events` parses is read through one delegate — QuakeML,
@@ -122,13 +122,13 @@ changes nothing else.
122
122
 
123
123
  ## Reading picks
124
124
 
125
- Arrivals are attached to traces by `set_picks`, which detects the format from
126
- the file rather than from its name:
125
+ `with_picks` returns a stream with arrivals attached, detecting the format
126
+ from the file rather than from its name:
127
127
 
128
128
  ```python
129
129
  import specmod.preprocess as pre
130
130
 
131
- pre.set_picks(stream, "event.xml")
131
+ stream = pre.with_picks(stream, "event.xml")
132
132
  ```
133
133
 
134
134
  Everything `obspy.read_events` parses is read through one delegate — QuakeML,
@@ -3571,7 +3571,7 @@ Each phase ends green on CI and is independently mergeable.
3571
3571
  | **2b. Release plumbing** ✅ | ~~Sphinx skeleton + `pydata-sphinx-theme` + autodoc/napoleon/intersphinx~~ ✅; ~~`docs.yml` → a published site~~ ✅ — Read the Docs rather than GH Pages, for versions (§6.3); ~~release-please + PyPI Trusted Publishing~~ ✅ — one `release.yml`, not two workflows (§6.5); ~~Zenodo webhook~~ ✅ documented. Five repository settings have to be turned on by hand: `docs/releasing.md` lists them. The `ci/` mirror that staged workflow files for hand-copying is gone — it existed because an agent token could not push `.github/workflows/`, and that permission is now granted. Parallel with 2 | 1 | 1–2 days |
3572
3572
  | **3. Transform layer** | `SpectralEstimator` protocol; `FFTEstimator`, `WelchEstimator`, `MultitaperEstimator`; `smoothing/` incl. Konno–Ohmachi and `LogBinner`; mtspec demoted to optional legacy backend; Tier 1 + Tier 2 tests; theory docs page | 2 | 5–7 days |
3573
3573
  | **4. CWT** | `CWTEstimator` + `Scalogram`; COI handling; the Parseval/units calibration and its test; `time_average()`; `ScalogramQC` + the four QC checks; COI floor into `BandwidthSelector`; scalogram plotting; HDF5 scalogram storage | 3 | 6–8 days |
3574
- | **5. Decompose** | Split `Spectral.py` (655 lines) into `core/` + `snr/` ✅; ~~`Fitting.py` → `fitting/`~~ ✅ — `base`/`guess`/`spectrum`/`event`, 745 lines to four modules of 30–300, public names unchanged; models as objects ✅; `io/`; `viz/`; non-mutating operations; mypy override list → empty ✅; `picks/` per §4.9 ✅ | 3 | 4–6 days |
3574
+ | **5. Decompose** ✅ | Split `Spectral.py` (655 lines) into `core/` + `snr/` ✅; ~~`Fitting.py` → `fitting/`~~ ✅ — `base`/`guess`/`spectrum`/`event`, 745 lines to four modules of 30–300, public names unchanged; models as objects ✅; ~~`io/`~~ ✅ — `layout`/`hdf5`/`backend`; ~~`viz/`~~ ✅ — shipped as `plotting/`, `pair`/`grid`/`style`; ~~non-mutating operations~~ ✅ — `preprocess` was the last holdout, and the ten functions were renamed rather than only re-signatured so the break is loud; mypy override list → empty ✅; `picks/` per §4.9 ✅ | 3 | 4–6 days |
3575
3575
  | **6. Ship** | Full docs content, tutorial rewritten as an executed `myst-nb` page with no `os.chdir`, 0.1→1.0 "what changed" page, **1.0 release** | 4, 5 | 2–3 days |
3576
3576
 
3577
3577
  **Executing the tutorial was pulled forward out of Phase 6.** The `myst-nb`
@@ -0,0 +1,162 @@
1
+ """Shared machinery for the notebook builders in this directory.
2
+
3
+ Every notebook in this repository is written by a script here, named after the
4
+ notebook it writes. One copy of ``md``, ``code`` and the notebook envelope,
5
+ rather than one per builder: the first builder carried all three inline, which
6
+ was fine while there was one, and a second would have started by copying them.
7
+ The two copies would then have drifted on cell metadata, ``nbformat_minor``, or
8
+ the trailing newline — none of it visible in a rendered notebook, all of it
9
+ noise in the diff of a regenerated one.
10
+
11
+ The two notebooks do not live in the same place, and should not:
12
+
13
+ ``docs/notebooks/choosing-a-transform.ipynb``
14
+ Not published. ``choosing-a-transform.md`` is the page that carries this
15
+ material, and ``conf.py`` excludes ``notebooks/**``.
16
+
17
+ ``tutorial/specmod-tutorial.ipynb``
18
+ Published, and executed on every docs build. It stays outside ``docs/``
19
+ because it reads ``tutorial/data/events/`` through paths relative to
20
+ itself, and because eight other files name that directory.
21
+
22
+ So a builder says where its notebook goes, and only the name is derived. This
23
+ directory is the one place to look for all of them.
24
+
25
+ Builders are **not** part of the published site — ``conf.py`` excludes
26
+ ``_builders/**`` — and are deliberately outside the ruff and mypy scopes,
27
+ which ``.pre-commit-config.yaml`` pins to ``src/``, ``tests/`` and ``tools/``.
28
+ They carry long prose lines and mathematical unicode that the source rules
29
+ reject, so keep new builders here rather than under ``tools/``.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import json
35
+ import pathlib
36
+ from typing import Any
37
+
38
+ #: The repository root: this file is ``<root>/docs/_builders/_notebook.py``.
39
+ ROOT = pathlib.Path(__file__).resolve().parent.parent.parent
40
+
41
+ #: Where a builder writes unless it says otherwise.
42
+ NOTEBOOKS = ROOT / "docs" / "notebooks"
43
+
44
+ #: The envelope a notebook gets when its builder does not supply one.
45
+ DEFAULT_METADATA: dict[str, Any] = {
46
+ "kernelspec": {
47
+ "display_name": "Python 3",
48
+ "language": "python",
49
+ "name": "python3",
50
+ },
51
+ "language_info": {"name": "python", "version": "3.11"},
52
+ }
53
+
54
+
55
+ class NotebookBuilder:
56
+ """Cells in, one ``.ipynb`` out.
57
+
58
+ Prefer :func:`builder_for`, which takes the output name from the calling
59
+ script's own filename so the two cannot drift apart.
60
+ """
61
+
62
+ def __init__(
63
+ self,
64
+ name: str,
65
+ *,
66
+ into: pathlib.Path = NOTEBOOKS,
67
+ metadata: dict[str, Any] | None = None,
68
+ ) -> None:
69
+ self.name = name
70
+ self.into = into
71
+ self.metadata = DEFAULT_METADATA if metadata is None else metadata
72
+ self.cells: list[dict[str, Any]] = []
73
+
74
+ def md(self, text: str) -> None:
75
+ """A markdown cell."""
76
+ self.cells.append(
77
+ {
78
+ "cell_type": "markdown",
79
+ "id": None,
80
+ "metadata": {},
81
+ "source": text.strip().splitlines(True),
82
+ }
83
+ )
84
+
85
+ def code(self, text: str) -> None:
86
+ """A code cell, with no outputs — the docs build executes it."""
87
+ self.cells.append(
88
+ {
89
+ "cell_type": "code",
90
+ "execution_count": None,
91
+ "id": None,
92
+ "metadata": {},
93
+ "outputs": [],
94
+ "source": text.strip().splitlines(True),
95
+ }
96
+ )
97
+
98
+ def write(self) -> pathlib.Path:
99
+ """Write the notebook and report where it went.
100
+
101
+ The output is what is committed, byte for byte —
102
+ ``tests/test_docs_are_current.py`` asserts it. That is why the cell
103
+ sources in a builder are written already formatted rather than being
104
+ passed through ``ruff format`` here: ruff is not importable from this
105
+ environment, and the pre-commit hook, CI and a local checkout pin
106
+ three different versions, so a formatting step at build time would
107
+ make "rebuilding changes nothing" depend on which ruff you had.
108
+ """
109
+ for position, cell in enumerate(self.cells):
110
+ # Sequential rather than the random hex `nbformat` assigns, so a
111
+ # regenerated notebook diffs against the committed one only where
112
+ # its content changed. Random ids would rewrite every cell.
113
+ #
114
+ # Filled in here, but the key is created in `md`/`code` so it lands
115
+ # in the position `nbformat` writes it. Key order is not meaningful
116
+ # to a notebook and is entirely meaningful to `git diff`.
117
+ cell["id"] = str(position)
118
+ notebook = {
119
+ "cells": self.cells,
120
+ "metadata": self.metadata,
121
+ "nbformat": 4,
122
+ "nbformat_minor": 5,
123
+ }
124
+ # Relative to this file, not to whoever happened to run it: the path
125
+ # was once hardcoded to one machine's checkout, so the script only
126
+ # worked there.
127
+ out = self.into / f"{self.name}.ipynb"
128
+ out.parent.mkdir(parents=True, exist_ok=True)
129
+ # `ensure_ascii=False`: the prose is full of em dashes and mathematical
130
+ # symbols, and `\\u2014` in a source file is unreadable and unsearchable.
131
+ out.write_text(json.dumps(notebook, indent=1, ensure_ascii=False) + "\n")
132
+ code_cells = sum(c["cell_type"] == "code" for c in self.cells)
133
+ print(f"wrote {out} — {len(self.cells)} cells ({code_cells} code)")
134
+ return out
135
+
136
+
137
+ def builder_for(
138
+ script: str,
139
+ *,
140
+ into: pathlib.Path = NOTEBOOKS,
141
+ metadata: dict[str, Any] | None = None,
142
+ ) -> NotebookBuilder:
143
+ """The builder writing the notebook this script is named after.
144
+
145
+ Pass ``__file__``. ``choosing_a_transform.py`` writes
146
+ ``choosing-a-transform.ipynb``: underscores become hyphens, so each side
147
+ keeps the convention of its own language — ``snake_case`` for a Python
148
+ module, ``kebab-case`` for a file that becomes a URL on the published
149
+ site.
150
+
151
+ Deriving the name rather than repeating it is what makes the convention
152
+ hold. A builder and its notebook cannot be given different names without
153
+ renaming the file, so there is no way to end up with
154
+ ``_build_notebook.py`` again — a name that said nothing about which of
155
+ several notebooks it built.
156
+
157
+ ``into`` is the directory the notebook goes in, for the one that cannot
158
+ live beside the others.
159
+ """
160
+ return NotebookBuilder(
161
+ pathlib.Path(script).stem.replace("_", "-"), into=into, metadata=metadata
162
+ )