@@ -1776,6 +1776,7 @@ def test_nonfirst_partition_transform_directly_addresses_its_array() -> None:
17761776
17771777
17781778def test_partition_token_encodes_its_public_global_transform () -> None :
1779+ pytest .importorskip ("dask.base" )
17791780 source = np .arange (8 )
17801781 base = LazyArray .from_numpy (source )
17811782 partition_view = list (base .with_parts ((4 ,)).parts ())[1 ].view
@@ -1907,6 +1908,7 @@ def test_with_parts_validates_strictly(parts: Any, match: str) -> None:
19071908
19081909def test_dask_token_is_deterministic_and_discriminating () -> None :
19091910 """Same data and same view token alike; a different selection differs."""
1911+ pytest .importorskip ("dask.base" )
19101912 data = reference ()
19111913 base = LazyArray (data )
19121914 assert base .__dask_tokenize__ () == LazyArray (reference ()).__dask_tokenize__ ()
@@ -1923,6 +1925,7 @@ def test_dask_token_is_deterministic_and_discriminating() -> None:
19231925
19241926
19251927def test_reader_and_partitioning_do_not_change_dask_identity () -> None :
1928+ pytest .importorskip ("dask.base" )
19261929 base = LazyArray (reference ())
19271930 token = base .__dask_tokenize__ ()
19281931 assert base .with_reader (numpy_reader ).__dask_tokenize__ () == token
@@ -2000,7 +2003,6 @@ def test_pickle_round_trip() -> None:
20002003 view = LazyArray (reference ()).with_parts ((2 , 2 , 2 )).lazy [1 :6 , ::2 ].lazy .oindex [[3 , 0 , 0 ], :, :]
20012004 restored = pickle .loads (pickle .dumps (view ))
20022005 assert restored .shape == view .shape
2003- assert restored .__dask_tokenize__ () == view .__dask_tokenize__ ()
20042006 np .testing .assert_array_equal (np .asarray (restored .result ()), np .asarray (view .result ()))
20052007
20062008
@@ -2010,7 +2012,7 @@ def test_pickle_round_trip() -> None:
20102012
20112013
20122014def test_dask_from_array_roundtrip () -> None :
2013- """A `LazyArray` is a drop-in dask source — no translation ceremony ."""
2015+ """Dask can tokenize and read a wrapper over a Zarr source ."""
20142016 da = pytest .importorskip ("dask.array" )
20152017 source = make_source ("zarr" )
20162018
@@ -2435,25 +2437,6 @@ def test_a_masked_source_keeps_its_mask_when_the_view_is_empty(parts: Any) -> No
24352437 assert np .asarray (got ).shape == (3 , 0 ), parts
24362438
24372439
2438- def test_a_large_array_without_dask_refuses_to_claim_equality (
2439- monkeypatch : pytest .MonkeyPatch ,
2440- ) -> None :
2441- """Without Dask, arrays above the digest limit receive distinct fallback tokens."""
2442- import sys
2443-
2444- monkeypatch .setitem (sys .modules , "dask.base" , None )
2445- big = np .zeros (1 << 19 , dtype = np .int64 )
2446- other = big .copy ()
2447- other [0 ] = 1
2448-
2449- assert LazyArray (big ).__dask_tokenize__ () != LazyArray (other ).__dask_tokenize__ ()
2450- assert LazyArray (big ).__dask_tokenize__ () != LazyArray (big ).__dask_tokenize__ ()
2451-
2452- # Below the limit the contents are digested, so equal data still tokens alike.
2453- small = np .zeros (8 , dtype = np .int64 )
2454- assert LazyArray (small ).__dask_tokenize__ () == LazyArray (small .copy ()).__dask_tokenize__ ()
2455-
2456-
24572440# ---------------------------------------------------------------------------
24582441# Completeness and partition spellings
24592442# ---------------------------------------------------------------------------
@@ -2576,3 +2559,63 @@ def test_fancy_composition_over_an_empty_axis() -> None:
25762559 scalar = composed .lazy .vindex [..., np .array (1 )]
25772560 assert scalar .shape == (2 , 0 )
25782561 assert np .asarray (scalar .result ()).shape == (2 , 0 )
2562+
2563+
2564+ @pytest .mark .parametrize ("kind" , ["numpy" , "object" , "masked" , "registered" , "hook" ])
2565+ def test_source_token_uses_dask_policy (kind : str ) -> None :
2566+ dask_base = pytest .importorskip ("dask.base" )
2567+
2568+ class RegisteredArray (ForeignArray ):
2569+ pass
2570+
2571+ class VersionedArray (ForeignArray ):
2572+ def __dask_tokenize__ (self ) -> Any :
2573+ return ("versioned-source" , 1 )
2574+
2575+ dask_base .normalize_token .register (RegisteredArray , lambda source : ("registered-source" , 1 ))
2576+ data = np .arange (4 )
2577+ sources = {
2578+ "numpy" : data ,
2579+ "object" : data .astype (object ),
2580+ "masked" : np .ma .masked_greater (data , 2 ),
2581+ "registered" : RegisteredArray (data , None ),
2582+ "hook" : VersionedArray (data , None ),
2583+ }
2584+ source = sources [kind ]
2585+ assert LazyArray (source ).__dask_tokenize__ ()[1 ] == dask_base .tokenize (source )
2586+
2587+
2588+ def test_source_token_preserves_dask_determinism_requirement () -> None :
2589+ dask_base = pytest .importorskip ("dask.base" )
2590+ dask_tokenize = pytest .importorskip ("dask.tokenize" )
2591+
2592+ class UnserializableArray (ForeignArray ):
2593+ def __reduce_ex__ (self , protocol : int ) -> Any :
2594+ raise TypeError ("cannot serialize source" )
2595+
2596+ source = UnserializableArray (np .arange (4 ), None )
2597+ for value in (source , LazyArray (source )):
2598+ with pytest .raises (dask_tokenize .TokenizationError ):
2599+ dask_base .tokenize (value , ensure_deterministic = True )
2600+
2601+
2602+ def test_source_token_preserves_hook_failure () -> None :
2603+ pytest .importorskip ("dask.base" )
2604+
2605+ class RefusingArray (ForeignArray ):
2606+ def __dask_tokenize__ (self ) -> Any :
2607+ raise RuntimeError ("source version unavailable" )
2608+
2609+ with pytest .raises (RuntimeError , match = "source version unavailable" ):
2610+ LazyArray (RefusingArray (np .arange (4 ), None )).__dask_tokenize__ ()
2611+
2612+
2613+ def test_dask_is_only_required_for_tokenization (monkeypatch : pytest .MonkeyPatch ) -> None :
2614+ import sys
2615+
2616+ monkeypatch .setitem (sys .modules , "dask.base" , None )
2617+ data = np .arange (4 )
2618+ view = LazyArray (data ).lazy [1 :]
2619+ np .testing .assert_array_equal (view .result (), data [1 :])
2620+ with pytest .raises (ModuleNotFoundError ):
2621+ view .__dask_tokenize__ ()
0 commit comments