File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -30,7 +30,9 @@ def _normalize_path(path: UPath | Path | str) -> UPath:
3030
3131
3232def _normalize_storage_options (path : UPath ) -> dict [str , Any ] | None :
33- return None if len (path .storage_options ) == 0 else path .storage_options
33+ # UPath.storage_options returns a read-only mappingproxy which cannot be pickled. Copy it into a
34+ # plain dict so callers can safely pass it across process boundaries (e.g. spawned workers).
35+ return None if len (path .storage_options ) == 0 else dict (path .storage_options )
3436
3537
3638def open_mdio (input_path : UPath | Path | str , chunks : T_Chunks = None ) -> xr_Dataset :
Original file line number Diff line number Diff line change 1+ """Tests for low-level MDIO API I/O helpers."""
2+
3+ from __future__ import annotations
4+
5+ from types import MappingProxyType
6+
7+ from upath import UPath
8+
9+ from mdio .api .io import _normalize_storage_options
10+
11+
12+ def test_normalize_storage_options_is_not_mappingproxy () -> None :
13+ """Storage options must not be a mappingproxy.
14+
15+ `UPath.storage_options` returns a read-only ``mappingproxy`` that cannot be pickled. Blocked-I/O
16+ ingestion passes these options into ``ProcessPoolExecutor`` initargs, so a mappingproxy breaks
17+ spawned workers with ``TypeError: cannot pickle 'mappingproxy' object``.
18+ """
19+ storage_options = _normalize_storage_options (UPath ("s3://bucket/key" , key = "access" , secret = "secret" ))
20+
21+ assert not isinstance (storage_options , MappingProxyType )
You can’t perform that action at this time.
0 commit comments