thyra 3.7.2__tar.gz → 3.7.3__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 (117) hide show
  1. {thyra-3.7.2 → thyra-3.7.3}/.gitignore +2 -0
  2. {thyra-3.7.2 → thyra-3.7.3}/PKG-INFO +2 -1
  3. {thyra-3.7.2 → thyra-3.7.3}/README.md +1 -0
  4. {thyra-3.7.2 → thyra-3.7.3}/pyproject.toml +1 -1
  5. {thyra-3.7.2 → thyra-3.7.3}/thyra/__init__.py +1 -1
  6. {thyra-3.7.2 → thyra-3.7.3}/thyra/converters/spatialdata/base_spatialdata_converter.py +55 -3
  7. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/__init__.py +4 -0
  8. thyra-3.7.3/thyra/metadata/uns_compat.py +57 -0
  9. {thyra-3.7.2 → thyra-3.7.3}/LICENSE +0 -0
  10. {thyra-3.7.2 → thyra-3.7.3}/thyra/__main__.py +0 -0
  11. {thyra-3.7.2 → thyra-3.7.3}/thyra/alignment/__init__.py +0 -0
  12. {thyra-3.7.2 → thyra-3.7.3}/thyra/alignment/affine.py +0 -0
  13. {thyra-3.7.2 → thyra-3.7.3}/thyra/alignment/teaching_points.py +0 -0
  14. {thyra-3.7.2 → thyra-3.7.3}/thyra/config.py +0 -0
  15. {thyra-3.7.2 → thyra-3.7.3}/thyra/convert.py +0 -0
  16. {thyra-3.7.2 → thyra-3.7.3}/thyra/converters/__init__.py +0 -0
  17. {thyra-3.7.2 → thyra-3.7.3}/thyra/converters/spatialdata/__init__.py +0 -0
  18. {thyra-3.7.2 → thyra-3.7.3}/thyra/converters/spatialdata/_chunking.py +0 -0
  19. {thyra-3.7.2 → thyra-3.7.3}/thyra/converters/spatialdata/converter.py +0 -0
  20. {thyra-3.7.2 → thyra-3.7.3}/thyra/converters/spatialdata/spatialdata_2d_converter.py +0 -0
  21. {thyra-3.7.2 → thyra-3.7.3}/thyra/converters/spatialdata/spatialdata_3d_converter.py +0 -0
  22. {thyra-3.7.2 → thyra-3.7.3}/thyra/converters/spatialdata/streaming_converter.py +0 -0
  23. {thyra-3.7.2 → thyra-3.7.3}/thyra/core/__init__.py +0 -0
  24. {thyra-3.7.2 → thyra-3.7.3}/thyra/core/base_converter.py +0 -0
  25. {thyra-3.7.2 → thyra-3.7.3}/thyra/core/base_extractor.py +0 -0
  26. {thyra-3.7.2 → thyra-3.7.3}/thyra/core/base_reader.py +0 -0
  27. {thyra-3.7.2 → thyra-3.7.3}/thyra/core/registry.py +0 -0
  28. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/extractors/__init__.py +0 -0
  29. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/extractors/bruker_extractor.py +0 -0
  30. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/extractors/imzml_extractor.py +0 -0
  31. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/extractors/phi_extractor.py +0 -0
  32. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/extractors/waters_extractor.py +0 -0
  33. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/ontology/__init__.py +0 -0
  34. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/ontology/_ims.py +0 -0
  35. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/ontology/_ms.py +0 -0
  36. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/ontology/_uo.py +0 -0
  37. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/ontology/cache.py +0 -0
  38. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/schema/__init__.py +0 -0
  39. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/schema/builder.py +0 -0
  40. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/schema/cli.py +0 -0
  41. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/schema/generate.py +0 -0
  42. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/schema/metaspace.py +0 -0
  43. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/schema/models.py +0 -0
  44. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/schema/msi_metadata.linkml.yaml +0 -0
  45. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/schema/msi_metadata_schema_v0_1.json +0 -0
  46. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/schema/store_io.py +0 -0
  47. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/schema/validate.py +0 -0
  48. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/schema/vocab.py +0 -0
  49. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/types.py +0 -0
  50. {thyra-3.7.2 → thyra-3.7.3}/thyra/metadata/validator.py +0 -0
  51. {thyra-3.7.2 → thyra-3.7.3}/thyra/preview.py +0 -0
  52. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/__init__.py +0 -0
  53. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/__init__.py +0 -0
  54. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/base_bruker_reader.py +0 -0
  55. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/folder_structure.py +0 -0
  56. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/mis_parser.py +0 -0
  57. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/rapiflex/__init__.py +0 -0
  58. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/rapiflex/rapiflex_reader.py +0 -0
  59. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/__init__.py +0 -0
  60. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/sdk/__init__.py +0 -0
  61. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/sdk/dll/LICENCE-BRUKER.txt +0 -0
  62. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/sdk/dll/README.md +0 -0
  63. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/sdk/dll/timsdata.dll +0 -0
  64. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/sdk/dll/timsdata.so +0 -0
  65. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/sdk/dll_manager.py +0 -0
  66. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/sdk/platform_detector.py +0 -0
  67. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/sdk/sdk_functions.py +0 -0
  68. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/timstof_reader.py +0 -0
  69. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/utils/__init__.py +0 -0
  70. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/utils/batch_processor.py +0 -0
  71. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/utils/coordinate_cache.py +0 -0
  72. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/utils/mass_axis_builder.py +0 -0
  73. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/bruker/timstof/utils/memory_manager.py +0 -0
  74. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/imzml/__init__.py +0 -0
  75. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/imzml/imzml_reader.py +0 -0
  76. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/phi/__init__.py +0 -0
  77. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/phi/event_stream.py +0 -0
  78. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/phi/mass_axis.py +0 -0
  79. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/phi/phi_header.py +0 -0
  80. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/phi/phi_reader.py +0 -0
  81. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/waters/__init__.py +0 -0
  82. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/waters/imaging_grid.py +0 -0
  83. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/waters/lib/MLReader.dll +0 -0
  84. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/waters/lib/MassLynxRaw.dll +0 -0
  85. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/waters/lib/libMLReader.so +0 -0
  86. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/waters/lib/libMassLynxRaw.so +0 -0
  87. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/waters/masslynx_lib.py +0 -0
  88. {thyra-3.7.2 → thyra-3.7.3}/thyra/readers/waters/waters_reader.py +0 -0
  89. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/__init__.py +0 -0
  90. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/common_axis.py +0 -0
  91. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/constants.py +0 -0
  92. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/data_characteristics.py +0 -0
  93. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/decision_tree.py +0 -0
  94. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/gaps.py +0 -0
  95. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/instrument_detectors.py +0 -0
  96. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/mass_axis/__init__.py +0 -0
  97. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/mass_axis/base_generator.py +0 -0
  98. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/mass_axis/fticr_generator.py +0 -0
  99. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/mass_axis/linear_generator.py +0 -0
  100. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/mass_axis/linear_tof_generator.py +0 -0
  101. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/mass_axis/orbitrap_generator.py +0 -0
  102. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/mass_axis/reflector_tof_generator.py +0 -0
  103. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/strategies/__init__.py +0 -0
  104. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/strategies/base.py +0 -0
  105. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/strategies/nearest_neighbor.py +0 -0
  106. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/strategies/tic_preserving.py +0 -0
  107. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/tic.py +0 -0
  108. {thyra-3.7.2 → thyra-3.7.3}/thyra/resampling/types.py +0 -0
  109. {thyra-3.7.2 → thyra-3.7.3}/thyra/tools/__init__.py +0 -0
  110. {thyra-3.7.2 → thyra-3.7.3}/thyra/tools/check_ontology.py +0 -0
  111. {thyra-3.7.2 → thyra-3.7.3}/thyra/tools/make_example_data.py +0 -0
  112. {thyra-3.7.2 → thyra-3.7.3}/thyra/utils/__init__.py +0 -0
  113. {thyra-3.7.2 → thyra-3.7.3}/thyra/utils/bruker_exceptions.py +0 -0
  114. {thyra-3.7.2 → thyra-3.7.3}/thyra/utils/logging_config.py +0 -0
  115. {thyra-3.7.2 → thyra-3.7.3}/thyra/utils/pyimzml_direct.py +0 -0
  116. {thyra-3.7.2 → thyra-3.7.3}/thyra/utils/windows_paths.py +0 -0
  117. {thyra-3.7.2 → thyra-3.7.3}/thyra/utils/zarr_atomic_write.py +0 -0
@@ -53,6 +53,8 @@ Archive/
53
53
  # ...except documentation notebooks, which are published as part of the docs
54
54
  # site and must be committed with their outputs.
55
55
  !docs/*.ipynb
56
+ # ...and the shared workflow notebooks (e.g. the Colab validation workflow).
57
+ !notebooks/*.ipynb
56
58
 
57
59
  # Temporary files
58
60
  temp_*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: thyra
3
- Version: 3.7.2
3
+ Version: 3.7.3
4
4
  Summary: A modern Python library for converting Mass Spectrometry Imaging (MSI) data into SpatialData/Zarr format - your portal to spatial omics
5
5
  Project-URL: Homepage, https://github.com/M4i-Imaging-Mass-Spectrometry/thyra
6
6
  Project-URL: Repository, https://github.com/M4i-Imaging-Mass-Spectrometry/thyra
@@ -60,6 +60,7 @@ Description-Content-Type: text/markdown
60
60
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
61
61
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
62
62
  [![Docs](https://img.shields.io/badge/docs-mkdocs-blue)](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra)
63
+ [![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)
63
64
 
64
65
  **Thyra** (from Greek thyra, meaning "door" or "portal") -- a modern Python library for converting Mass Spectrometry Imaging (MSI) data into the standardized **SpatialData/Zarr format**, serving as your portal to spatial omics analysis workflows.
65
66
 
@@ -8,6 +8,7 @@
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
9
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
10
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)
11
12
 
12
13
  **Thyra** (from Greek thyra, meaning "door" or "portal") -- a modern Python library for converting Mass Spectrometry Imaging (MSI) data into the standardized **SpatialData/Zarr format**, serving as your portal to spatial omics analysis workflows.
13
14
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "thyra"
7
- version = "3.7.2"
7
+ version = "3.7.3"
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" }]
@@ -36,7 +36,7 @@ warnings.filterwarnings(
36
36
  category=FutureWarning,
37
37
  )
38
38
 
39
- __version__ = "3.7.2"
39
+ __version__ = "3.7.3"
40
40
 
41
41
  # Import key components - avoid wildcard imports
42
42
  try:
@@ -46,6 +46,49 @@ def _suppress_upstream_warnings():
46
46
  yield
47
47
 
48
48
 
49
+ def _numeric_only(value: Any) -> bool:
50
+ """True when a list round-trips through AnnData/zarr as a numeric array."""
51
+ if isinstance(value, list):
52
+ return all(_numeric_only(item) for item in value)
53
+ if isinstance(value, np.ndarray):
54
+ return value.dtype.kind in "iufb"
55
+ return isinstance(value, (bool, int, float, np.integer, np.floating, np.bool_))
56
+
57
+
58
+ def _json_fallback(obj: Any) -> Any:
59
+ """Last-resort encoder for values ``json.dumps`` does not know."""
60
+ if isinstance(obj, np.ndarray):
61
+ return obj.tolist()
62
+ if isinstance(obj, np.generic):
63
+ return obj.item()
64
+ return str(obj)
65
+
66
+
67
+ def _jsonify_string_lists(obj: Any) -> Any:
68
+ """Replace any list that is not purely numeric with its JSON encoding.
69
+
70
+ AnnData/zarr cannot round-trip such lists: a list of dicts is
71
+ stringified entry by entry into Python ``repr`` strings, and any list
72
+ of strings comes back as a numpy string array -- whose ``deepcopy``
73
+ segfaults outright on numpy 2.1-2.2 (numpy#28609). Since every table
74
+ copy deepcopies ``uns`` (``AnnData.copy``, spatial queries, joins),
75
+ one such array in ``uns`` kills the reader's process with no
76
+ traceback. JSON side-steps both: it stores a single scalar string,
77
+ and hands consumers the actual structure back through
78
+ ``json.loads`` instead of ``repr`` output.
79
+
80
+ Purely numeric lists (nested included) are kept: they become plain
81
+ numeric arrays, which are safe and more useful as arrays.
82
+ """
83
+ if isinstance(obj, dict):
84
+ return {key: _jsonify_string_lists(value) for key, value in obj.items()}
85
+ if isinstance(obj, list):
86
+ if _numeric_only(obj):
87
+ return obj
88
+ return json.dumps(obj, default=_json_fallback)
89
+ return obj
90
+
91
+
49
92
  def _resolve_config_enum(raw: Any, by_name: Dict[str, Any], key: str) -> Any:
50
93
  """Resolve one resampling-config value to its enum member.
51
94
 
@@ -792,15 +835,24 @@ class BaseSpatialDataConverter(BaseMSIConverter, ABC):
792
835
  }
793
836
 
794
837
  def _collect_optional_sections(self, uns: Dict[str, Any], comp_meta: Any) -> None:
795
- """Add the vendor sections the reader actually populated."""
838
+ """Add the vendor sections the reader actually populated.
839
+
840
+ Lists that are not purely numeric (imzML ``cvParams``, or any
841
+ string list a vendor extractor reports) are stored as JSON
842
+ strings -- see :func:`_jsonify_string_lists` for why letting
843
+ them reach the writer as lists corrupts them and crashes
844
+ readers on numpy 2.1-2.2.
845
+ """
796
846
  for key in ("format_specific", "acquisition_params", "instrument_info"):
797
847
  value = getattr(comp_meta, key, None)
798
848
  if value:
799
- uns[key] = self._serialize_for_zarr(value)
849
+ uns[key] = _jsonify_string_lists(self._serialize_for_zarr(value))
800
850
 
801
851
  raw_metadata = getattr(comp_meta, "raw_metadata", None)
802
852
  if raw_metadata:
803
- uns["raw_metadata"] = self._serialize_for_zarr(raw_metadata)
853
+ uns["raw_metadata"] = _jsonify_string_lists(
854
+ self._serialize_for_zarr(raw_metadata)
855
+ )
804
856
 
805
857
  def _collect_region_info(self, uns: Dict[str, Any]) -> None:
806
858
  """Add the acquisition region summary as JSON.
@@ -53,6 +53,9 @@ from .extractors import (
53
53
  # Core data types
54
54
  from .types import ComprehensiveMetadata, EssentialMetadata
55
55
 
56
+ # Read-side compatibility for stores written by earlier versions
57
+ from .uns_compat import sanitize_uns_string_arrays
58
+
56
59
  # Base classes - import delayed to avoid circular imports
57
60
 
58
61
 
@@ -67,6 +70,7 @@ __all__ = [
67
70
  # Utility functions
68
71
  "get_extractor_for_format",
69
72
  "list_supported_formats",
73
+ "sanitize_uns_string_arrays",
70
74
  ]
71
75
 
72
76
  # Version information for the metadata system
@@ -0,0 +1,57 @@
1
+ # thyra/metadata/uns_compat.py
2
+ """Read-side defense for ``uns`` blocks written by earlier Thyra versions.
3
+
4
+ Stores written before the JSON rule in
5
+ ``BaseSpatialDataConverter._collect_optional_sections`` carry string
6
+ lists in ``uns`` (notably ``raw_metadata["cvParams"]``) that materialize
7
+ as numpy ``StringDType`` arrays when read back through AnnData/zarr.
8
+ ``copy.deepcopy`` of such an array segfaults the process on numpy
9
+ 2.1.x-2.2.x (numpy#28609, fixed in 2.3.0) -- and every table copy
10
+ deepcopies ``uns``: ``AnnData.copy``, ``spatialdata.polygon_query``,
11
+ ``bounding_box_query``, and the join paths behind them.
12
+
13
+ New stores no longer contain these arrays. This module exists for
14
+ already-written stores read in an environment pinned to the affected
15
+ numpy range.
16
+ """
17
+
18
+ from collections.abc import Mapping
19
+ from typing import Any, Dict
20
+
21
+ import numpy as np
22
+
23
+ __all__ = ["sanitize_uns_string_arrays"]
24
+
25
+
26
+ def sanitize_uns_string_arrays(uns: Mapping[str, Any]) -> Dict[str, Any]:
27
+ """Return ``uns`` with numpy string arrays replaced by plain lists.
28
+
29
+ Recurses through nested dicts and lists; every other value is kept
30
+ as-is, so the result is safe to assign straight back::
31
+
32
+ import spatialdata as sd
33
+ from thyra.metadata import sanitize_uns_string_arrays
34
+
35
+ sdata = sd.read_zarr("store.zarr")
36
+ for table in sdata.tables.values():
37
+ table.uns = sanitize_uns_string_arrays(table.uns)
38
+
39
+ Needed only for stores written by Thyra versions that stored string
40
+ lists in ``uns`` as arrays, read under numpy 2.1.x-2.2.x, where
41
+ deepcopying a ``StringDType`` array kills the process (see module
42
+ docstring). Calling it anywhere else is harmless.
43
+ """
44
+ return {key: _sanitize(value) for key, value in uns.items()}
45
+
46
+
47
+ def _sanitize(value: Any) -> Any:
48
+ if isinstance(value, Mapping):
49
+ return {key: _sanitize(item) for key, item in value.items()}
50
+ if isinstance(value, list):
51
+ return [_sanitize(item) for item in value]
52
+ # ``kind == "T"`` is StringDType, the one dtype whose deepcopy is
53
+ # broken. On numpy builds without StringDType no array has this
54
+ # kind, so the walk is a no-op there.
55
+ if isinstance(value, np.ndarray) and value.dtype.kind == "T":
56
+ return value.tolist()
57
+ return value
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes