thyra 4.0.0__tar.gz → 4.2.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 (155) hide show
  1. thyra-4.2.0/PKG-INFO +195 -0
  2. thyra-4.2.0/README.md +142 -0
  3. {thyra-4.0.0 → thyra-4.2.0}/pyproject.toml +10 -3
  4. thyra-4.2.0/thyra/__init__.py +110 -0
  5. {thyra-4.0.0 → thyra-4.2.0}/thyra/__main__.py +30 -13
  6. thyra-4.2.0/thyra/converters/__init__.py +26 -0
  7. {thyra-4.0.0 → thyra-4.2.0}/thyra/converters/spatialdata/base_spatialdata_converter.py +105 -704
  8. {thyra-4.0.0 → thyra-4.2.0}/thyra/converters/spatialdata/mobility_table.py +1 -1
  9. {thyra-4.0.0 → thyra-4.2.0}/thyra/converters/spatialdata/optical_image.py +18 -7
  10. {thyra-4.0.0/thyra/metadata → thyra-4.2.0/thyra/converters/spatialdata}/root_attrs.py +5 -5
  11. thyra-4.2.0/thyra/converters/spatialdata/sibling_tables.py +977 -0
  12. {thyra-4.0.0 → thyra-4.2.0}/thyra/converters/spatialdata/streaming_converter.py +53 -20
  13. {thyra-4.0.0/thyra/metadata → thyra-4.2.0/thyra/converters/spatialdata}/uns_assembler.py +55 -18
  14. {thyra-4.0.0 → thyra-4.2.0}/thyra/core/base_extractor.py +9 -1
  15. {thyra-4.0.0 → thyra-4.2.0}/thyra/core/base_reader.py +21 -1
  16. {thyra-4.0.0 → thyra-4.2.0}/thyra/core/msms.py +39 -1
  17. {thyra-4.0.0 → thyra-4.2.0}/thyra/core/registry.py +115 -13
  18. {thyra-4.0.0 → thyra-4.2.0}/thyra/errors.py +5 -4
  19. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/__init__.py +40 -7
  20. {thyra-4.0.0/thyra/resampling → thyra-4.2.0/thyra/metadata}/constants.py +16 -9
  21. thyra-4.2.0/thyra/metadata/document.py +114 -0
  22. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/extractors/bruker_extractor.py +362 -15
  23. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/extractors/imzml_extractor.py +3 -3
  24. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/extractors/mzpeak_extractor.py +1 -1
  25. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/extractors/phi_extractor.py +61 -16
  26. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/extractors/solarix_extractor.py +2 -1
  27. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/extractors/waters_extractor.py +44 -15
  28. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/ontology/_ms.py +1172 -512
  29. thyra-4.2.0/thyra/metadata/ontology/dump_obo_files.py +98 -0
  30. thyra-4.2.0/thyra/metadata/personal_data.py +288 -0
  31. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/schema/__init__.py +18 -3
  32. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/schema/builder.py +601 -37
  33. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/schema/cli.py +83 -9
  34. thyra-4.2.0/thyra/metadata/schema/generate.py +78 -0
  35. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/schema/models.py +429 -37
  36. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/schema/msi_metadata.linkml.yaml +77 -2
  37. thyra-4.0.0/thyra/metadata/schema/msi_metadata_schema_v0_6.json → thyra-4.2.0/thyra/metadata/schema/msi_metadata_schema_v0_9.json +294 -4
  38. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/schema/store_io.py +33 -17
  39. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/schema/validate.py +30 -21
  40. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/schema/vocab.py +31 -0
  41. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/types.py +5 -2
  42. thyra-4.2.0/thyra/readers/__init__.py +50 -0
  43. thyra-4.2.0/thyra/readers/bruker/__init__.py +77 -0
  44. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/folder_structure.py +1 -1
  45. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/rapiflex/rapiflex_reader.py +2 -2
  46. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/timstof/timstof_reader.py +99 -13
  47. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/imzml/imzml_reader.py +1 -1
  48. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/phi/event_stream.py +16 -1
  49. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/phi/phi_reader.py +11 -1
  50. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/waters/masslynx_lib.py +4 -0
  51. thyra-4.2.0/thyra/resampling/constants.py +26 -0
  52. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/data_characteristics.py +2 -1
  53. thyra-4.0.0/PKG-INFO +0 -224
  54. thyra-4.0.0/README.md +0 -172
  55. thyra-4.0.0/thyra/__init__.py +0 -48
  56. thyra-4.0.0/thyra/converters/__init__.py +0 -3
  57. thyra-4.0.0/thyra/metadata/schema/generate.py +0 -34
  58. thyra-4.0.0/thyra/readers/__init__.py +0 -23
  59. thyra-4.0.0/thyra/readers/bruker/__init__.py +0 -46
  60. {thyra-4.0.0 → thyra-4.2.0}/.gitignore +0 -0
  61. {thyra-4.0.0 → thyra-4.2.0}/LICENSE +0 -0
  62. {thyra-4.0.0 → thyra-4.2.0}/thyra/alignment/__init__.py +0 -0
  63. {thyra-4.0.0 → thyra-4.2.0}/thyra/alignment/affine.py +0 -0
  64. {thyra-4.0.0 → thyra-4.2.0}/thyra/alignment/teaching_points.py +0 -0
  65. {thyra-4.0.0 → thyra-4.2.0}/thyra/config.py +0 -0
  66. {thyra-4.0.0 → thyra-4.2.0}/thyra/convert.py +0 -0
  67. {thyra-4.0.0 → thyra-4.2.0}/thyra/converters/spatialdata/__init__.py +0 -0
  68. {thyra-4.0.0 → thyra-4.2.0}/thyra/converters/spatialdata/_chunking.py +0 -0
  69. {thyra-4.0.0 → thyra-4.2.0}/thyra/converters/spatialdata/converter.py +0 -0
  70. {thyra-4.0.0 → thyra-4.2.0}/thyra/converters/spatialdata/csc_assembly.py +0 -0
  71. {thyra-4.0.0 → thyra-4.2.0}/thyra/converters/spatialdata/fused_passes.py +0 -0
  72. {thyra-4.0.0 → thyra-4.2.0}/thyra/converters/spatialdata/mobility_heatmap.py +0 -0
  73. {thyra-4.0.0 → thyra-4.2.0}/thyra/converters/spatialdata/msms_table.py +0 -0
  74. {thyra-4.0.0 → thyra-4.2.0}/thyra/core/__init__.py +0 -0
  75. {thyra-4.0.0 → thyra-4.2.0}/thyra/core/base_converter.py +0 -0
  76. {thyra-4.0.0 → thyra-4.2.0}/thyra/core/conversion_state.py +0 -0
  77. {thyra-4.0.0 → thyra-4.2.0}/thyra/core/drop_tally.py +0 -0
  78. {thyra-4.0.0 → thyra-4.2.0}/thyra/core/frames.py +0 -0
  79. {thyra-4.0.0 → thyra-4.2.0}/thyra/core/mass_axis.py +0 -0
  80. {thyra-4.0.0 → thyra-4.2.0}/thyra/core/mobility.py +0 -0
  81. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/extractors/__init__.py +0 -0
  82. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/extractors/imzml_header_extractor.py +0 -0
  83. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/ontology/__init__.py +0 -0
  84. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/ontology/_ims.py +0 -0
  85. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/ontology/_uo.py +0 -0
  86. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/ontology/cache.py +0 -0
  87. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/schema/metaspace.py +0 -0
  88. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/uns_compat.py +0 -0
  89. {thyra-4.0.0 → thyra-4.2.0}/thyra/metadata/validator.py +0 -0
  90. {thyra-4.0.0 → thyra-4.2.0}/thyra/preview.py +0 -0
  91. {thyra-4.0.0 → thyra-4.2.0}/thyra/py.typed +0 -0
  92. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/base_bruker_reader.py +0 -0
  93. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/mis_parser.py +0 -0
  94. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/rapiflex/__init__.py +0 -0
  95. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/solarix/__init__.py +0 -0
  96. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/solarix/solarix_reader.py +0 -0
  97. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/timstof/__init__.py +0 -0
  98. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/timstof/sdk/__init__.py +0 -0
  99. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/timstof/sdk/dll/LICENCE-BRUKER.txt +0 -0
  100. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/timstof/sdk/dll/README.md +0 -0
  101. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/timstof/sdk/dll/timsdata.dll +0 -0
  102. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/timstof/sdk/dll/timsdata.so +0 -0
  103. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/timstof/sdk/dll_manager.py +0 -0
  104. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/timstof/sdk/platform_detector.py +0 -0
  105. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/timstof/sdk/sdk_functions.py +0 -0
  106. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/bruker/vendor_db.py +0 -0
  107. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/imzml/__init__.py +0 -0
  108. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/imzml/_pyimzml_compat.py +0 -0
  109. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/imzml/header.py +0 -0
  110. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/imzml/mobility_array.py +0 -0
  111. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/mzpeak/__init__.py +0 -0
  112. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/mzpeak/mzpeak_reader.py +0 -0
  113. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/phi/__init__.py +0 -0
  114. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/phi/mass_axis.py +0 -0
  115. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/phi/phi_header.py +0 -0
  116. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/waters/__init__.py +0 -0
  117. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/waters/imaging_grid.py +0 -0
  118. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/waters/instrument.py +0 -0
  119. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/waters/lib/MLReader.dll +0 -0
  120. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/waters/lib/MassLynxRaw.dll +0 -0
  121. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/waters/lib/libMLReader.so +0 -0
  122. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/waters/lib/libMassLynxRaw.so +0 -0
  123. {thyra-4.0.0 → thyra-4.2.0}/thyra/readers/waters/waters_reader.py +0 -0
  124. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/__init__.py +0 -0
  125. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/axis_planner.py +0 -0
  126. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/binning.py +0 -0
  127. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/common_axis.py +0 -0
  128. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/decision_tree.py +0 -0
  129. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/gaps.py +0 -0
  130. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/instrument_detectors.py +0 -0
  131. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/interpolation.py +0 -0
  132. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/mass_axis/__init__.py +0 -0
  133. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/mass_axis/base_generator.py +0 -0
  134. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/mass_axis/fticr_generator.py +0 -0
  135. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/mass_axis/linear_generator.py +0 -0
  136. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/mass_axis/linear_tof_generator.py +0 -0
  137. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/mass_axis/orbitrap_generator.py +0 -0
  138. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/mass_axis/reflector_tof_generator.py +0 -0
  139. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/mass_axis/tof_generator.py +0 -0
  140. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/mobility_grid.py +0 -0
  141. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/strategies/__init__.py +0 -0
  142. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/strategies/base.py +0 -0
  143. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/strategies/nearest_neighbor.py +0 -0
  144. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/strategies/tic_preserving.py +0 -0
  145. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/tic.py +0 -0
  146. {thyra-4.0.0 → thyra-4.2.0}/thyra/resampling/types.py +0 -0
  147. {thyra-4.0.0 → thyra-4.2.0}/thyra/tools/__init__.py +0 -0
  148. {thyra-4.0.0 → thyra-4.2.0}/thyra/tools/check_ontology.py +0 -0
  149. {thyra-4.0.0 → thyra-4.2.0}/thyra/tools/make_example_data.py +0 -0
  150. {thyra-4.0.0 → thyra-4.2.0}/thyra/utils/__init__.py +0 -0
  151. {thyra-4.0.0 → thyra-4.2.0}/thyra/utils/bruker_exceptions.py +0 -0
  152. {thyra-4.0.0 → thyra-4.2.0}/thyra/utils/imzml_coordinate_base.py +0 -0
  153. {thyra-4.0.0 → thyra-4.2.0}/thyra/utils/logging_config.py +0 -0
  154. {thyra-4.0.0 → thyra-4.2.0}/thyra/utils/pyimzml_direct.py +0 -0
  155. {thyra-4.0.0 → thyra-4.2.0}/thyra/utils/windows_paths.py +0 -0
thyra-4.2.0/PKG-INFO ADDED
@@ -0,0 +1,195 @@
1
+ Metadata-Version: 2.5
2
+ Name: thyra
3
+ Version: 4.2.0
4
+ Summary: A modern Python library for converting Mass Spectrometry Imaging (MSI) data into SpatialData/Zarr format - your portal to spatial omics
5
+ Project-URL: Homepage, https://github.com/M4i-Imaging-Mass-Spectrometry/thyra
6
+ Project-URL: Repository, https://github.com/M4i-Imaging-Mass-Spectrometry/thyra
7
+ Project-URL: Documentation, https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/
8
+ Project-URL: Bug Tracker, https://github.com/M4i-Imaging-Mass-Spectrometry/thyra/issues
9
+ Project-URL: Discussions, https://github.com/M4i-Imaging-Mass-Spectrometry/thyra/discussions
10
+ Project-URL: Changelog, https://github.com/M4i-Imaging-Mass-Spectrometry/thyra/blob/main/CHANGELOG.md
11
+ Project-URL: Contributing, https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/contributing/
12
+ Author-email: Theodoros Visvikis <t.visvikis@maastrichtuniversity.nl>
13
+ Maintainer-email: Theodoros Visvikis <t.visvikis@maastrichtuniversity.nl>
14
+ License-Expression: MIT
15
+ License-File: LICENSE
16
+ Keywords: bioinformatics,bruker,data-conversion,imaging,imzml,mass-spectrometry,msi,omics,scientific-computing,spatial-omics,spatialdata,zarr
17
+ Classifier: Development Status :: 4 - Beta
18
+ Classifier: Environment :: Console
19
+ Classifier: Intended Audience :: Science/Research
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Topic :: File Formats
26
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
27
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
28
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
29
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
30
+ Classifier: Typing :: Typed
31
+ Requires-Python: <3.15,>=3.12
32
+ Requires-Dist: anndata<0.14,>=0.13.2
33
+ Requires-Dist: click<9,>=8.1
34
+ Requires-Dist: dask<2026.8,>=2026.3.0
35
+ Requires-Dist: defusedxml>=0.7.1
36
+ Requires-Dist: geopandas>=0.9.0
37
+ Requires-Dist: imagecodecs>=2024.1.1
38
+ Requires-Dist: numpy<3,>=2.0.0
39
+ Requires-Dist: ome-zarr<0.19,>=0.16.0
40
+ Requires-Dist: pandas<4,>=2.0.0
41
+ Requires-Dist: pillow>=10.1
42
+ Requires-Dist: psutil>=5.0.0
43
+ Requires-Dist: pydantic<3,>=2.7
44
+ Requires-Dist: pyimzml>=1.4.0
45
+ Requires-Dist: scipy>=1.7.0
46
+ Requires-Dist: shapely>=2.0.0
47
+ Requires-Dist: spatialdata<0.9,>=0.8.0
48
+ Requires-Dist: tifffile>=2026.5.2
49
+ Requires-Dist: tqdm>=4.50.0
50
+ Requires-Dist: xarray>=2024.10.0
51
+ Requires-Dist: zarr<3.5,>=3.4.0
52
+ Description-Content-Type: text/markdown
53
+
54
+ <p align="center">
55
+ <img src="docs/assets/thyra-logotype.svg" alt="Thyra" width="420">
56
+ </p>
57
+
58
+ [![Tests](https://img.shields.io/github/actions/workflow/status/M4i-Imaging-Mass-Spectrometry/thyra/tests.yml?branch=main&logo=github)](https://github.com/M4i-Imaging-Mass-Spectrometry/thyra/actions/workflows/tests.yml)
59
+ [![PyPI](https://img.shields.io/pypi/v/thyra?logo=pypi&logoColor=white)](https://pypi.org/project/thyra/)
60
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
61
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
62
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
63
+ [![Docs](https://img.shields.io/badge/docs-mkdocs-blue)](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra)
64
+ [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/M4i-Imaging-Mass-Spectrometry/thyra/blob/main/notebooks/Thyra_Validation_Workflow.ipynb)
65
+
66
+ **Thyra converts imaging mass spectrometry data into one open format.** It
67
+ reads the files your instrument writes (Bruker, Waters, PHI, or imzML from any
68
+ vendor) and saves them in [SpatialData](https://spatialdata.scverse.org/),
69
+ which analysis and viewing tools can read. You do not need to know how to
70
+ program.
71
+
72
+ **[Documentation](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra)** | [Install](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/install/) | [Your first conversion](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/getting-started/) | [Tutorial](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/tutorial/) | [Technical reference](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/technical-reference/)
73
+
74
+ ![The total ion current and two ion images of a sagittal mouse brain section, converted with Thyra](docs/assets/mouse-brain-ion-images.webp)
75
+
76
+ <sub>A sagittal mouse brain section converted with Thyra: the total ion current
77
+ and two ion images. MALDI-MSI data from
78
+ [10.5281/zenodo.18326569](https://doi.org/10.5281/zenodo.18326569),
79
+ CC-BY-4.0.</sub>
80
+
81
+ ## Quick start
82
+
83
+ ```bash
84
+ uv tool install --python 3.13 thyra # or: pip install thyra (Python 3.12 to 3.14)
85
+ thyra-example-data example_data/synthetic_brain.imzML # makes a small example dataset
86
+ thyra example_data/synthetic_brain.imzML example_data/synthetic_brain.zarr
87
+ ```
88
+
89
+ New to the terminal or to uv? The
90
+ [Install](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/install/) page
91
+ walks you through every step. To try Thyra with nothing installed, open the
92
+ [notebook in Google Colab](https://colab.research.google.com/github/M4i-Imaging-Mass-Spectrometry/thyra/blob/main/notebooks/Thyra_Validation_Workflow.ipynb).
93
+ The [Tutorial](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/tutorial/)
94
+ also converts the published example dataset
95
+ ([10.5281/zenodo.18326569](https://doi.org/10.5281/zenodo.18326569)).
96
+
97
+ ## What you can convert
98
+
99
+ | Your instrument | Point Thyra at |
100
+ |---|---|
101
+ | Bruker timsTOF or solariX | the folder ending in `.d` |
102
+ | Bruker rapifleX | the acquisition folder |
103
+ | Waters (MassLynx) | the folder ending in `.raw` |
104
+ | PHI nanoTOF (ToF-SIMS) | the file ending in `.raw` |
105
+ | Any instrument that exports imzML | the `.imzML` file, with its `.ibd` file next to it |
106
+ | mzPeak (experimental) | the `.mzpeak` file |
107
+
108
+ Thyra reads timsTOF and Waters data on Windows and Linux; everything else
109
+ also works on macOS. Shimadzu support is in development: until then, export
110
+ imzML from IMAGEREVEAL MS.
111
+
112
+ PHI mosaic, MS/MS and depth-profiling acquisitions have so far been tested
113
+ only on synthetic files. If you have real data in those modes, please get in
114
+ touch.
115
+
116
+ ## What you get
117
+
118
+ One folder ending in `.zarr`, holding:
119
+
120
+ - the spectrum of every pixel, on one shared m/z axis
121
+ - an image of the total ion current (TIC)
122
+ - the optical image of the slide, when the acquisition has one
123
+ - ion mobility and MS/MS data from Bruker timsTOF, when the acquisition has them
124
+ - a validated description of the acquisition, ready for METASPACE
125
+
126
+ Thyra handles datasets larger than your computer's memory, slides with
127
+ several regions, and 3D data.
128
+
129
+ ## From Python
130
+
131
+ ```python
132
+ from thyra import convert_msi
133
+ import spatialdata as sd
134
+
135
+ convert_msi("data/sample.imzML", "output/sample.zarr")
136
+
137
+ sdata = sd.read_zarr("output/sample.zarr")
138
+ table = sdata.tables["msi_dataset_z0"] # one row per pixel, one column per m/z bin
139
+ ```
140
+
141
+ The acquisition description has its own commands:
142
+
143
+ ```bash
144
+ thyra metadata raw_data.d # describe a dataset without converting it
145
+ thyra validate output.zarr # check a result against the schema
146
+ thyra export-metaspace output.zarr --merge sample.json # METASPACE submission file
147
+ ```
148
+
149
+ Every option, format and output detail is in the
150
+ [Technical reference](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/technical-reference/).
151
+
152
+ ## Development
153
+
154
+ ```bash
155
+ git clone https://github.com/M4i-Imaging-Mass-Spectrometry/thyra.git
156
+ cd thyra
157
+ uv sync
158
+ uv run pre-commit install
159
+ uv run pytest
160
+ ```
161
+
162
+ CI runs the same hooks on every pull request. Installing them locally shows
163
+ you a failure before you push.
164
+
165
+ ## Contributing
166
+
167
+ See [CONTRIBUTING.md](docs/contributing.md) for guidelines.
168
+
169
+ ## License
170
+
171
+ MIT -- see [LICENSE](LICENSE).
172
+
173
+ ## Citation
174
+
175
+ ```bibtex
176
+ @software{thyra2024,
177
+ title = {Thyra: Modern Mass Spectrometry Imaging Data Conversion},
178
+ author = {Visvikis, Theodoros},
179
+ year = {2024},
180
+ url = {https://github.com/M4i-Imaging-Mass-Spectrometry/thyra}
181
+ }
182
+ ```
183
+
184
+ ## Acknowledgments
185
+
186
+ - Built with [SpatialData](https://spatialdata.scverse.org/) ecosystem
187
+ - Powered by [Zarr](https://zarr.readthedocs.io/) for efficient storage
188
+ - Uses [pyimzML](https://github.com/alexandrovteam/pyimzML) for ImzML parsing
189
+
190
+ ### Visual identity
191
+
192
+ Logomark and logotype designed by **Nepsis Scriptorium**.
193
+
194
+ [![Instagram @nepsis.scriptorium](https://img.shields.io/badge/Instagram-%40nepsis.scriptorium-E4405F?logo=instagram&logoColor=white)](https://www.instagram.com/nepsis.scriptorium/)
195
+ [![Email nepsisscriptorium@gmail.com](https://img.shields.io/badge/Email-nepsisscriptorium%40gmail.com-EA4335?logo=gmail&logoColor=white)](mailto:nepsisscriptorium@gmail.com)
thyra-4.2.0/README.md ADDED
@@ -0,0 +1,142 @@
1
+ <p align="center">
2
+ <img src="docs/assets/thyra-logotype.svg" alt="Thyra" width="420">
3
+ </p>
4
+
5
+ [![Tests](https://img.shields.io/github/actions/workflow/status/M4i-Imaging-Mass-Spectrometry/thyra/tests.yml?branch=main&logo=github)](https://github.com/M4i-Imaging-Mass-Spectrometry/thyra/actions/workflows/tests.yml)
6
+ [![PyPI](https://img.shields.io/pypi/v/thyra?logo=pypi&logoColor=white)](https://pypi.org/project/thyra/)
7
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
10
+ [![Docs](https://img.shields.io/badge/docs-mkdocs-blue)](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra)
11
+ [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/M4i-Imaging-Mass-Spectrometry/thyra/blob/main/notebooks/Thyra_Validation_Workflow.ipynb)
12
+
13
+ **Thyra converts imaging mass spectrometry data into one open format.** It
14
+ reads the files your instrument writes (Bruker, Waters, PHI, or imzML from any
15
+ vendor) and saves them in [SpatialData](https://spatialdata.scverse.org/),
16
+ which analysis and viewing tools can read. You do not need to know how to
17
+ program.
18
+
19
+ **[Documentation](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra)** | [Install](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/install/) | [Your first conversion](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/getting-started/) | [Tutorial](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/tutorial/) | [Technical reference](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/technical-reference/)
20
+
21
+ ![The total ion current and two ion images of a sagittal mouse brain section, converted with Thyra](docs/assets/mouse-brain-ion-images.webp)
22
+
23
+ <sub>A sagittal mouse brain section converted with Thyra: the total ion current
24
+ and two ion images. MALDI-MSI data from
25
+ [10.5281/zenodo.18326569](https://doi.org/10.5281/zenodo.18326569),
26
+ CC-BY-4.0.</sub>
27
+
28
+ ## Quick start
29
+
30
+ ```bash
31
+ uv tool install --python 3.13 thyra # or: pip install thyra (Python 3.12 to 3.14)
32
+ thyra-example-data example_data/synthetic_brain.imzML # makes a small example dataset
33
+ thyra example_data/synthetic_brain.imzML example_data/synthetic_brain.zarr
34
+ ```
35
+
36
+ New to the terminal or to uv? The
37
+ [Install](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/install/) page
38
+ walks you through every step. To try Thyra with nothing installed, open the
39
+ [notebook in Google Colab](https://colab.research.google.com/github/M4i-Imaging-Mass-Spectrometry/thyra/blob/main/notebooks/Thyra_Validation_Workflow.ipynb).
40
+ The [Tutorial](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/tutorial/)
41
+ also converts the published example dataset
42
+ ([10.5281/zenodo.18326569](https://doi.org/10.5281/zenodo.18326569)).
43
+
44
+ ## What you can convert
45
+
46
+ | Your instrument | Point Thyra at |
47
+ |---|---|
48
+ | Bruker timsTOF or solariX | the folder ending in `.d` |
49
+ | Bruker rapifleX | the acquisition folder |
50
+ | Waters (MassLynx) | the folder ending in `.raw` |
51
+ | PHI nanoTOF (ToF-SIMS) | the file ending in `.raw` |
52
+ | Any instrument that exports imzML | the `.imzML` file, with its `.ibd` file next to it |
53
+ | mzPeak (experimental) | the `.mzpeak` file |
54
+
55
+ Thyra reads timsTOF and Waters data on Windows and Linux; everything else
56
+ also works on macOS. Shimadzu support is in development: until then, export
57
+ imzML from IMAGEREVEAL MS.
58
+
59
+ PHI mosaic, MS/MS and depth-profiling acquisitions have so far been tested
60
+ only on synthetic files. If you have real data in those modes, please get in
61
+ touch.
62
+
63
+ ## What you get
64
+
65
+ One folder ending in `.zarr`, holding:
66
+
67
+ - the spectrum of every pixel, on one shared m/z axis
68
+ - an image of the total ion current (TIC)
69
+ - the optical image of the slide, when the acquisition has one
70
+ - ion mobility and MS/MS data from Bruker timsTOF, when the acquisition has them
71
+ - a validated description of the acquisition, ready for METASPACE
72
+
73
+ Thyra handles datasets larger than your computer's memory, slides with
74
+ several regions, and 3D data.
75
+
76
+ ## From Python
77
+
78
+ ```python
79
+ from thyra import convert_msi
80
+ import spatialdata as sd
81
+
82
+ convert_msi("data/sample.imzML", "output/sample.zarr")
83
+
84
+ sdata = sd.read_zarr("output/sample.zarr")
85
+ table = sdata.tables["msi_dataset_z0"] # one row per pixel, one column per m/z bin
86
+ ```
87
+
88
+ The acquisition description has its own commands:
89
+
90
+ ```bash
91
+ thyra metadata raw_data.d # describe a dataset without converting it
92
+ thyra validate output.zarr # check a result against the schema
93
+ thyra export-metaspace output.zarr --merge sample.json # METASPACE submission file
94
+ ```
95
+
96
+ Every option, format and output detail is in the
97
+ [Technical reference](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/technical-reference/).
98
+
99
+ ## Development
100
+
101
+ ```bash
102
+ git clone https://github.com/M4i-Imaging-Mass-Spectrometry/thyra.git
103
+ cd thyra
104
+ uv sync
105
+ uv run pre-commit install
106
+ uv run pytest
107
+ ```
108
+
109
+ CI runs the same hooks on every pull request. Installing them locally shows
110
+ you a failure before you push.
111
+
112
+ ## Contributing
113
+
114
+ See [CONTRIBUTING.md](docs/contributing.md) for guidelines.
115
+
116
+ ## License
117
+
118
+ MIT -- see [LICENSE](LICENSE).
119
+
120
+ ## Citation
121
+
122
+ ```bibtex
123
+ @software{thyra2024,
124
+ title = {Thyra: Modern Mass Spectrometry Imaging Data Conversion},
125
+ author = {Visvikis, Theodoros},
126
+ year = {2024},
127
+ url = {https://github.com/M4i-Imaging-Mass-Spectrometry/thyra}
128
+ }
129
+ ```
130
+
131
+ ## Acknowledgments
132
+
133
+ - Built with [SpatialData](https://spatialdata.scverse.org/) ecosystem
134
+ - Powered by [Zarr](https://zarr.readthedocs.io/) for efficient storage
135
+ - Uses [pyimzML](https://github.com/alexandrovteam/pyimzML) for ImzML parsing
136
+
137
+ ### Visual identity
138
+
139
+ Logomark and logotype designed by **Nepsis Scriptorium**.
140
+
141
+ [![Instagram @nepsis.scriptorium](https://img.shields.io/badge/Instagram-%40nepsis.scriptorium-E4405F?logo=instagram&logoColor=white)](https://www.instagram.com/nepsis.scriptorium/)
142
+ [![Email nepsisscriptorium@gmail.com](https://img.shields.io/badge/Email-nepsisscriptorium%40gmail.com-EA4335?logo=gmail&logoColor=white)](mailto:nepsisscriptorium@gmail.com)
@@ -4,14 +4,14 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "thyra"
7
- version = "4.0.0"
7
+ version = "4.2.0"
8
8
  description = "A modern Python library for converting Mass Spectrometry Imaging (MSI) data into SpatialData/Zarr format - your portal to spatial omics"
9
9
  authors = [{ name = "Theodoros Visvikis", email = "t.visvikis@maastrichtuniversity.nl" }]
10
10
  maintainers = [{ name = "Theodoros Visvikis", email = "t.visvikis@maastrichtuniversity.nl" }]
11
11
  license = "MIT"
12
12
  license-files = ["LICENSE"]
13
13
  readme = "README.md"
14
- requires-python = ">=3.12, <3.14"
14
+ requires-python = ">=3.12, <3.15"
15
15
  keywords = [
16
16
  "mass-spectrometry",
17
17
  "imaging",
@@ -34,6 +34,7 @@ classifiers = [
34
34
  "Programming Language :: Python :: 3",
35
35
  "Programming Language :: Python :: 3.12",
36
36
  "Programming Language :: Python :: 3.13",
37
+ "Programming Language :: Python :: 3.14",
37
38
  "Topic :: Scientific/Engineering :: Bio-Informatics",
38
39
  "Topic :: Scientific/Engineering :: Information Analysis",
39
40
  "Topic :: Scientific/Engineering :: Medical Science Apps.",
@@ -329,7 +330,7 @@ dev = [
329
330
  ]
330
331
  docs = [
331
332
  "mkdocs>=1.6, <2",
332
- "mkdocs-material>=9.5",
333
+ "mkdocs-material>=9.7", # navigation.path, content.tooltips
333
334
  "mkdocs-jupyter>=0.25, <1", # renders docs/*.ipynb as site pages
334
335
  "mkdocstrings[python]>=0.27",
335
336
  ]
@@ -480,6 +481,12 @@ module = [
480
481
  ignore_missing_imports = true
481
482
 
482
483
 
484
+ [tool.black]
485
+ # Black otherwise infers every version in `requires-python` and, whenever the
486
+ # newest is above the interpreter running it, prints a warning on each run.
487
+ # Formatting follows the oldest target either way, so this changes no output.
488
+ target-version = ["py312"]
489
+
483
490
  [tool.isort]
484
491
  profile = "black"
485
492
  multi_line_output = 3
@@ -0,0 +1,110 @@
1
+ """MSIConverter - Convert Mass Spectrometry Imaging data to SpatialData/Zarr format.
2
+
3
+ This package provides tools for converting MSI data from various formats
4
+ (ImzML, Bruker) into the modern SpatialData/Zarr format with automatic
5
+ pixel size detection.
6
+
7
+ **Nothing heavy is imported here** (issue #381). This module used to import
8
+ the reader and converter packages for their registration side effects and
9
+ bind the public API eagerly, which cost 3.5 s and 3,580 modules --
10
+ ``spatialdata``, ``dask`` and ``anndata`` among them -- before any caller
11
+ did any work. A parent package always initialises first, so that was also
12
+ the price of ``import thyra.metadata.schema``: the metadata layer depends
13
+ on none of it in source (every reference from ``thyra/metadata/`` to the
14
+ converter is ``TYPE_CHECKING``-guarded) and could not be used without it.
15
+
16
+ Two changes replaced it. The registry imports a format's module the first
17
+ time that format is looked up, from the table in
18
+ :mod:`thyra.core.registry`, so registration no longer needs a front door.
19
+ The public names below resolve on first attribute access (:pep:`562`), so
20
+ ``thyra.convert_msi`` and ``from thyra import preview_msi`` still work and
21
+ still cost what they always did -- paid by the caller who asks for them.
22
+
23
+ ``__version__`` stays an ordinary module attribute rather than joining
24
+ them. It is what ``python-semantic-release`` rewrites at release
25
+ (``version_variables`` in ``pyproject.toml``), and
26
+ ``thyra.metadata.schema.builder`` reads it through ``from thyra import
27
+ __version__`` while building a document -- which is only affordable
28
+ because reaching this module is now free.
29
+
30
+ **Where a broken install now fails.** Issue #310 made ``spatialdata`` and
31
+ ``defusedxml`` abort ``import thyra``, because both had been treated as
32
+ optional and the branch that fired swallowed the cause: the user's first
33
+ sign of trouble was ``No converter for format 'spatialdata'. Available:
34
+ []``, naming no package and no reason. The swallowing is still gone and
35
+ nothing rewrites either ImportError; what moved is where it surfaces.
36
+ ``spatialdata`` is imported by :mod:`thyra.converters`, which a conversion
37
+ reaches while resolving its output format, before it opens the input;
38
+ ``defusedxml`` by ``thyra.readers.bruker.mis_parser``, which every Bruker
39
+ path reaches during format detection. Each raises at its own import with
40
+ its own traceback. Checking for them here instead would have cost the
41
+ metadata layer the very thing #381 is for: an implementation that only
42
+ writes the document would have to have ``spatialdata`` installed to import
43
+ the schema.
44
+ """
45
+
46
+ # Suppress known warnings from dependencies
47
+ import importlib
48
+ import warnings
49
+ from typing import TYPE_CHECKING, Any, List
50
+
51
+ if TYPE_CHECKING: # pragma: no cover - for type checkers, not at runtime
52
+ from .convert import convert_msi
53
+ from .converters.spatialdata.converter import SpatialDataConverter
54
+ from .metadata.document import read_metadata_document
55
+ from .preview import MsiPreview, preview_msi
56
+
57
+ # Suppress remaining dependency warnings
58
+ warnings.filterwarnings("ignore", category=FutureWarning, module="dask")
59
+ warnings.filterwarnings("ignore", category=FutureWarning, module="spatialdata")
60
+ warnings.filterwarnings("ignore", category=RuntimeWarning, module="numba")
61
+ warnings.filterwarnings(
62
+ "ignore", message="pkg_resources is deprecated", category=UserWarning
63
+ )
64
+ warnings.filterwarnings(
65
+ "ignore",
66
+ message="The legacy Dask DataFrame implementation is deprecated",
67
+ category=FutureWarning,
68
+ )
69
+
70
+ __version__ = "4.2.0"
71
+
72
+ #: Public name -> the module that defines it, imported on first access.
73
+ _LAZY_ATTRS = {
74
+ "MsiPreview": ".preview",
75
+ "SpatialDataConverter": ".converters.spatialdata.converter",
76
+ "convert_msi": ".convert",
77
+ "preview_msi": ".preview",
78
+ "read_metadata_document": ".metadata.document",
79
+ }
80
+
81
+ #: Subpackages ``import thyra`` used to bind as attributes on its way to
82
+ #: registering their contents. Kept reachable as ``thyra.readers`` and
83
+ #: ``thyra.converters`` for code that never imported them by name.
84
+ _LAZY_SUBMODULES = ("converters", "readers")
85
+
86
+ # Expose main API
87
+ __all__ = [
88
+ "__version__",
89
+ "convert_msi",
90
+ "preview_msi",
91
+ "read_metadata_document",
92
+ "MsiPreview",
93
+ "SpatialDataConverter",
94
+ ]
95
+
96
+
97
+ def __getattr__(name: str) -> Any:
98
+ """Resolve a public name by importing the module that defines it."""
99
+ if name in _LAZY_SUBMODULES:
100
+ return importlib.import_module(f".{name}", __name__)
101
+ module_path = _LAZY_ATTRS.get(name)
102
+ if module_path is None:
103
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
104
+ value = getattr(importlib.import_module(module_path, __name__), name)
105
+ globals()[name] = value
106
+ return value
107
+
108
+
109
+ def __dir__() -> List[str]:
110
+ return sorted(set(globals()) | set(__all__) | set(_LAZY_SUBMODULES))
@@ -16,6 +16,15 @@ The import block is ordered deliberately and carries ``# noqa: E402``: the
16
16
  dependency warning configuration has to run before the libraries that emit
17
17
  those warnings are imported, so the usual "imports first" rule is
18
18
  inverted on purpose here.
19
+
20
+ **What it does not import at module scope is ``thyra.convert``** (issue
21
+ #381). Binding ``convert_msi`` here meant every invocation paid for the
22
+ converter, ``pandas`` and ``spatialdata`` before click had parsed a single
23
+ argument -- including ``thyra --version`` and the two subcommands,
24
+ ``validate`` and ``metadata``, that never reach a converter at all. The
25
+ conversion command imports it where it calls it. Tests that replace the
26
+ conversion patch ``thyra.convert.convert_msi``, which is the function
27
+ rather than a second name for it.
19
28
  """
20
29
 
21
30
  # Configure dependencies to suppress warnings BEFORE any imports
@@ -30,12 +39,6 @@ from uuid import uuid4 # noqa: E402
30
39
  import click # noqa: E402
31
40
 
32
41
  from thyra import __version__ # noqa: E402
33
- from thyra.convert import ( # noqa: E402
34
- convert_msi,
35
- dataset_id_problem,
36
- quarantine_partial_output,
37
- )
38
- from thyra.core.registry import detect_format # noqa: E402
39
42
  from thyra.resampling.mobility_grid import MOBILITY_CHANNELS # noqa: E402
40
43
  from thyra.utils.logging_config import setup_logging # noqa: E402
41
44
 
@@ -80,6 +83,8 @@ def _validate_basic_params(pixel_size: Optional[float], dataset_id: str) -> None
80
83
  raise click.BadParameter(
81
84
  "Pixel size must be a finite positive number", param_hint="pixel_size"
82
85
  )
86
+ from thyra.convert import dataset_id_problem
87
+
83
88
  # Emptiness used to be the whole check, so an id with a space or a
84
89
  # slash was refused by SpatialData only at the write, after both
85
90
  # passes over the source (issue #250).
@@ -207,6 +212,8 @@ def _validate_input_path(input: Path) -> None:
207
212
  f"found: {ibd_path}"
208
213
  )
209
214
  elif input.is_dir() and input.suffix.lower() == ".d":
215
+ from thyra.core.registry import detect_format
216
+
210
217
  # Several Bruker formats share the .d extension (timsTOF via
211
218
  # analysis.tsf/.tdf, solariX via peaks.sqlite), so delegate to the
212
219
  # registry's detection rather than duplicating the marker files
@@ -335,7 +342,11 @@ def _display_calibration_info(input: Path, use_recalibrated: bool) -> None:
335
342
  else:
336
343
  click.echo("\nUsing original calibration (--no-recalibrated flag set)")
337
344
 
338
- click.echo("\nNote: Interactive selection not yet available. See GitHub issue #54.")
345
+ click.echo(
346
+ "\nNote: this lists the states only; one cannot be chosen here. "
347
+ "Thyra uses the active state, or the original calibration with "
348
+ "--no-recalibrated."
349
+ )
339
350
  click.echo("=" * 60 + "\n")
340
351
 
341
352
 
@@ -563,6 +574,8 @@ def _quarantine_partial_output(output: Path) -> None:
563
574
  store already moved aside does not exist here, so this returns
564
575
  immediately.
565
576
  """
577
+ from thyra.convert import quarantine_partial_output
578
+
566
579
  quarantine_partial_output(output)
567
580
 
568
581
 
@@ -1048,9 +1061,10 @@ def main(
1048
1061
  INPUT: Path to input MSI file or directory
1049
1062
  OUTPUT: Path for output file
1050
1063
 
1051
- Subcommands (each has its own --help): 'thyra validate PATH'
1052
- validates MSI metadata against the schema; 'thyra export-metaspace
1053
- PATH' writes the METASPACE submission JSON.
1064
+ Subcommands (each has its own --help): 'thyra metadata INPUT'
1065
+ writes a raw source's metadata document without converting it;
1066
+ 'thyra validate PATH' validates MSI metadata against the schema;
1067
+ 'thyra export-metaspace PATH' writes the METASPACE submission JSON.
1054
1068
  """
1055
1069
  # Validate all parameters
1056
1070
  _validate_basic_params(pixel_size, dataset_id)
@@ -1090,6 +1104,9 @@ def main(
1090
1104
  # If input folder has multiple .d datasets, let the user choose
1091
1105
  input = _select_bruker_dataset(input)
1092
1106
 
1107
+ from thyra.convert import convert_msi
1108
+ from thyra.core.registry import detect_format
1109
+
1093
1110
  # Before any work: say which of the given options this source ignores.
1094
1111
  # Detection failures are left to the conversion, which reports them
1095
1112
  # with the guidance the registry writes for each format.
@@ -1178,9 +1195,9 @@ def cli() -> None:
1178
1195
  """Console-script entry point: dispatch subcommands, else convert.
1179
1196
 
1180
1197
  ``thyra INPUT OUTPUT`` converts exactly as it always has; the
1181
- metadata subcommands (``thyra validate``, ``thyra
1182
- export-metaspace``) are picked off by their first argument before
1183
- click sees it. Dispatch is hand-rolled rather than a
1198
+ metadata subcommands (``thyra metadata``, ``thyra validate``,
1199
+ ``thyra export-metaspace``) are picked off by their first argument
1200
+ before click sees it. Dispatch is hand-rolled rather than a
1184
1201
  ``click.Group`` because a group cannot carry the two positional
1185
1202
  arguments the conversion interface is documented with.
1186
1203
  """
@@ -0,0 +1,26 @@
1
+ """Converter plugins for MSI data output formats.
2
+
3
+ Importing this package is what pulls in ``spatialdata``, and through it
4
+ ``dask`` and ``anndata``. It used to happen during ``import thyra``, for
5
+ the ``@register_converter`` side effect; the registry now imports
6
+ :mod:`thyra.converters.spatialdata.converter` the first time the
7
+ ``spatialdata`` output format is resolved (issue #381), so this runs when
8
+ a conversion is actually being set up.
9
+
10
+ The Dask configuration below moved here from ``thyra/__init__.py`` for the
11
+ same reason: reading it there meant importing ``dask`` to silence a
12
+ ``dask`` warning, on every import of anything under ``thyra``. It has to
13
+ be set before ``dask.dataframe`` is first imported, which is why it comes
14
+ before the subpackage import rather than after.
15
+ """
16
+
17
+ try:
18
+ import dask
19
+
20
+ # Configure Dask to use new query planning (silences legacy DataFrame
21
+ # warning)
22
+ dask.config.set({"dataframe.query-planning": True})
23
+ except ImportError:
24
+ pass
25
+
26
+ from . import spatialdata # noqa: E402,F401