worldarray 0.5.0__tar.gz → 0.6.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.
- {worldarray-0.5.0 → worldarray-0.6.0}/CHANGELOG.md +17 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/PKG-INFO +4 -3
- {worldarray-0.5.0 → worldarray-0.6.0}/README.md +3 -2
- {worldarray-0.5.0 → worldarray-0.6.0}/docs/spec.md +29 -10
- {worldarray-0.5.0 → worldarray-0.6.0}/pyproject.toml +1 -1
- {worldarray-0.5.0 → worldarray-0.6.0}/src/worldarray/__init__.py +1 -1
- {worldarray-0.5.0 → worldarray-0.6.0}/src/worldarray/context.py +36 -26
- {worldarray-0.5.0 → worldarray-0.6.0}/tests/test_access.py +20 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/tests/test_write.py +2 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/uv.lock +1 -1
- {worldarray-0.5.0 → worldarray-0.6.0}/.github/workflows/ci.yml +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/.github/workflows/docs.yml +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/.github/workflows/publish.yml +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/.gitignore +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/LICENSE +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/docs/changelog.md +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/docs/index.md +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/mkdocs.yml +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/src/worldarray/array.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/src/worldarray/geometry.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/src/worldarray/grid.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/src/worldarray/levels.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/src/worldarray/placement.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/src/worldarray/resample.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/src/worldarray/retry.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/src/worldarray/store.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/src/worldarray/units.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/src/worldarray/write.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/tests/conftest.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/tests/fixtures/metadata.json +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/tests/fixtures/write.json +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/tests/test_geometry.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/tests/test_grid.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/tests/test_lost_wakeup.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/tests/test_metadata.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/tests/test_read.py +0 -0
- {worldarray-0.5.0 → worldarray-0.6.0}/tests/test_resample.py +0 -0
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0 (2026-10-23)
|
|
4
|
+
|
|
5
|
+
One new argument, at chunkmirage's request. No placement change and no unit table change;
|
|
6
|
+
nothing changes for a caller that does not pass it.
|
|
7
|
+
|
|
8
|
+
- `wa.shared_context(cache_bytes, threads=)`: `None` (the default) limits the decoding and
|
|
9
|
+
file I/O pools to `thread_budget()` as in 0.5, a positive number limits them to that many,
|
|
10
|
+
and `0` writes no limit, so tensorstore sizes them itself (every CPU on the node). Each
|
|
11
|
+
`(cache_bytes, threads)` is one watched context for the process, so a server can share
|
|
12
|
+
one cache pool across every store it opens at the thread count it chose, without building
|
|
13
|
+
and watching its own context. A pickled handle on such a context comes back on the same
|
|
14
|
+
one; `threads=None` still lets the other process size by its own job.
|
|
15
|
+
- Documented what a pool of one thread costs (spec Behaviour 7, `capped_context_spec`):
|
|
16
|
+
nothing on one-chunk blocks; on 8-chunk blocks, half the speed of 4 in a two-slot job
|
|
17
|
+
but only 7 to 19% slower in a one-slot one (c-elegans on /nrs), so a worker per slot
|
|
18
|
+
loses little at the default.
|
|
19
|
+
|
|
3
20
|
## 0.5.0 (2026-10-23)
|
|
4
21
|
|
|
5
22
|
Two bugs that hung or crashed cellmap-analyze 0.6 on /nrs (salamander's and c-elegans'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: worldarray
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Read zarr, N5 and Neuroglancer precomputed volumes in world coordinates through tensorstore: correct voxel placement, pyramid levels, padded region reads, resampling. numpy + tensorstore only.
|
|
5
5
|
Project-URL: Homepage, https://janeliascicomp.github.io/worldarray/
|
|
6
6
|
Project-URL: Documentation, https://janeliascicomp.github.io/worldarray/
|
|
@@ -208,8 +208,9 @@ wa.open_pyramid(path, ...) -> Pyramid # .names .levels .by_name() .sele
|
|
|
208
208
|
wa.metadata(path, level=None, units=None, open=True) -> ArrayMeta # open=False: from documents alone
|
|
209
209
|
wa.detect(path, missing_ok=False) -> Node # format, array or group, raw attrs
|
|
210
210
|
wa.open_tensorstore(path, level=None, context=None)
|
|
211
|
-
wa.shared_context(cache_bytes=0) ; wa.capped_context(cache_bytes=0, concurrency=1, network=True) ; wa.Retry(...)
|
|
212
|
-
wa.thread_budget() # the shared context's thread limit: OMP_NUM_THREADS, the job's slots, or CPUs
|
|
211
|
+
wa.shared_context(cache_bytes=0, threads=None) ; wa.capped_context(cache_bytes=0, concurrency=1, network=True) ; wa.Retry(...)
|
|
212
|
+
wa.thread_budget() # the shared context's default thread limit: OMP_NUM_THREADS, the job's slots, or CPUs;
|
|
213
|
+
# threads=N limits it to N, threads=0 leaves tensorstore's own pool sizes
|
|
213
214
|
wa.list_chunk_keys(path, separator=".") # chunk indices from keys alone, no metadata needed
|
|
214
215
|
wa.Box ; wa.units.TABLE
|
|
215
216
|
|
|
@@ -149,8 +149,9 @@ wa.open_pyramid(path, ...) -> Pyramid # .names .levels .by_name() .sele
|
|
|
149
149
|
wa.metadata(path, level=None, units=None, open=True) -> ArrayMeta # open=False: from documents alone
|
|
150
150
|
wa.detect(path, missing_ok=False) -> Node # format, array or group, raw attrs
|
|
151
151
|
wa.open_tensorstore(path, level=None, context=None)
|
|
152
|
-
wa.shared_context(cache_bytes=0) ; wa.capped_context(cache_bytes=0, concurrency=1, network=True) ; wa.Retry(...)
|
|
153
|
-
wa.thread_budget() # the shared context's thread limit: OMP_NUM_THREADS, the job's slots, or CPUs
|
|
152
|
+
wa.shared_context(cache_bytes=0, threads=None) ; wa.capped_context(cache_bytes=0, concurrency=1, network=True) ; wa.Retry(...)
|
|
153
|
+
wa.thread_budget() # the shared context's default thread limit: OMP_NUM_THREADS, the job's slots, or CPUs;
|
|
154
|
+
# threads=N limits it to N, threads=0 leaves tensorstore's own pool sizes
|
|
154
155
|
wa.list_chunk_keys(path, separator=".") # chunk indices from keys alone, no metadata needed
|
|
155
156
|
wa.Box ; wa.units.TABLE
|
|
156
157
|
|
|
@@ -185,7 +185,8 @@ wa.open_tensorstore(path, level=None, context=None) -> tensorstore.TensorStore
|
|
|
185
185
|
wa.corner_to_centre(corner, voxel_size, axes=None) ; wa.default_axes(ndim) ; wa.index_range(begin, end, voxel_size, translation)
|
|
186
186
|
wa.units.TABLE # the unit tables, public: {"length": {...metres per unit}, "time": {...seconds per unit}}
|
|
187
187
|
wa.select_level(levels, voxel_size, mode) ; wa.Box ; wa.Retry(max_attempts=10, timeout=None, base_delay=1.0)
|
|
188
|
-
wa.shared_context(cache_bytes=0) ; wa.capped_context(cache_bytes=0, concurrency=1, network=True) ; wa.capped_context_spec(...)
|
|
188
|
+
wa.shared_context(cache_bytes=0, threads=None) ; wa.capped_context(cache_bytes=0, concurrency=1, network=True) ; wa.capped_context_spec(...)
|
|
189
|
+
# threads: None = thread_budget(), N = that many, 0 = tensorstore's own pool sizes (0.6)
|
|
189
190
|
# network=False caps decoding and local file I/O only, leaving tensorstore's request pools alone
|
|
190
191
|
wa.list_chunk_keys(path, separator=".", ndim=None) # chunk indices present under a path, from its keys alone: no
|
|
191
192
|
# metadata document needed, so a directory of chunk files with no .zarray lists too
|
|
@@ -229,9 +230,11 @@ returned one would raise on inputs cellmap-analyze reads every day. A consumer t
|
|
|
229
230
|
in integer Rois builds them from `corner`, `voxel_size` and `shape` in its own subclass,
|
|
230
231
|
where it also chooses the integer scale factor that makes the corner whole.
|
|
231
232
|
|
|
232
|
-
`shared_context(cache_bytes)` is one `tensorstore.Context` per budget
|
|
233
|
-
the levels of a pyramid and repeat opens share one cache
|
|
234
|
-
decoding and file I/O pools are sized by
|
|
233
|
+
`shared_context(cache_bytes, threads=None)` is one `tensorstore.Context` per budget and
|
|
234
|
+
thread limit for the process, so the levels of a pyramid and repeat opens share one cache
|
|
235
|
+
pool; the default budget is 0. Its decoding and file I/O pools are sized by
|
|
236
|
+
`thread_budget()` (0.5), or by `threads` when given, `0` leaving tensorstore's own sizes
|
|
237
|
+
(0.6, Behaviour 7).
|
|
235
238
|
`capped_context` is mesh-n-bone's worker-process context (every tensorstore concurrency
|
|
236
239
|
resource limited, cache off); it returns a **new** context per call, as those workers
|
|
237
240
|
inline the spec into every open today, and `capped_context_spec` exports the dict for
|
|
@@ -684,13 +687,27 @@ this document and that one differ, fix one of them.
|
|
|
684
687
|
task (a 256^3 block write took 2.1 s against 1.3 s uncapped) but not a job of many
|
|
685
688
|
workers, whose throughput its cores bound either way (four writers: 1.8 s against
|
|
686
689
|
1.9 s). A process that wants more threads than its job has slots, a server under
|
|
687
|
-
`bsub -n 1` say, asks for more slots or
|
|
688
|
-
|
|
690
|
+
`bsub -n 1` say, asks for more slots or says so: `shared_context(cache_bytes,
|
|
691
|
+
threads=N)` limits both pools to `N`, and `threads=0` writes no limit, leaving
|
|
692
|
+
tensorstore's own sizes (decided 2026-10-23, over chunkmirage's objection, once the
|
|
693
|
+
watchdog had removed the hang risk; the argument is 0.6, at chunkmirage's request, so
|
|
694
|
+
its server keeps one watched context per cache budget for every store it opens
|
|
695
|
+
instead of building its own). Each `(cache_bytes, threads)` is one context for the
|
|
696
|
+
process, made on first use and watched; `threads=None` and the number
|
|
697
|
+
`thread_budget()` returns are two contexts, as a pickle carries `None` for the other
|
|
698
|
+
process to size by its own job and a number as that number.
|
|
689
699
|
`capped_context` sets `data_copy_concurrency`, `file_io_concurrency`,
|
|
690
700
|
`gcs_request_concurrency`, `s3_request_concurrency` and `http_request_concurrency` to
|
|
691
701
|
`concurrency` and the cache to `cache_bytes`, and returns a new context each call, so two
|
|
692
702
|
handles never share one capped pool unless the caller passes the same object twice; a
|
|
693
|
-
`concurrency` of 1 is safe under Behaviour 6's watchdog
|
|
703
|
+
`concurrency` of 1 is safe under Behaviour 6's watchdog, but a read or write spanning
|
|
704
|
+
several chunks then handles them one at a time. On /nrs, from one process alternating
|
|
705
|
+
limits op by op (c-elegans, 2026-10-23): a one-chunk block (128^3 uint64) read in 18 to
|
|
706
|
+
19 ms and wrote in 51 ms at both 1 and 4. An 8-chunk block (256^3, what a downsampling
|
|
707
|
+
step reads) read in 157 ms against 73 and wrote in 312 ms against 158 in a two-slot
|
|
708
|
+
job, but in a one-slot job read in 147 ms against 119 and wrote in 325 ms against 302
|
|
709
|
+
(and that job still used 1.3 cores, LSF not pinning it). Most of the gain is a core the
|
|
710
|
+
worker was not given, so a worker per slot, as a dask job runs, loses little at 1. Both are plain tensorstore contexts; a caller may pass its own, at construction or through
|
|
694
711
|
`reopen(context=)` when a long-lived worker changes its concurrency (cellmap-analyze
|
|
695
712
|
reopens tensorstore on every read today for that reason).
|
|
696
713
|
|
|
@@ -769,8 +786,8 @@ Behaviours 10 to 16 are 0.2; 17 to 19 are 0.3.
|
|
|
769
786
|
unchanged, and a handle pickles by its arguments and its context's spec, so each worker
|
|
770
787
|
process opens its own cheaply (a local path goes resolved against the working directory
|
|
771
788
|
at open, so a worker that changes directory before unpickling, as cellmap-analyze's dask
|
|
772
|
-
workers do, opens the same store; `reopen` uses it too), on the shared context when the handle had one
|
|
773
|
-
a context rebuilt once per process from the same spec, so a capped context stays
|
|
789
|
+
workers do, opens the same store; `reopen` uses it too), on the shared context when the handle had one (the same
|
|
790
|
+
`threads`, and the same cache budget), else on a context rebuilt once per process from the same spec, so a capped context stays
|
|
774
791
|
capped (mesh-n-bone's per-process cache and thread caps).
|
|
775
792
|
|
|
776
793
|
12. **Group attributes belong to everyone.** The package rewrites only the `multiscales`
|
|
@@ -1000,7 +1017,9 @@ nothing resolves) cancelled and retried, the warning naming `TimeoutError`, and
|
|
|
1000
1017
|
after the last attempt; a capped context and a caller's context passed to an open both
|
|
1001
1018
|
kicked by the watchdog, a dead context no longer watched, and a kick landing on a capped
|
|
1002
1019
|
context's own pools; `thread_budget` from each variable, a bad value skipped, and the
|
|
1003
|
-
shared context's limits.
|
|
1020
|
+
shared context's limits. 0.6: `shared_context(threads=)` at `None`, `N` and `0`, one
|
|
1021
|
+
context per pair, a negative count refused, and a pickled handle coming back on the same
|
|
1022
|
+
shared context. Opt-in (`WORLDARRAY_STRESS=1`, about a minute,
|
|
1004
1023
|
`tests/test_lost_wakeup.py`): worker processes aim tasks at the moment a one-thread pool's
|
|
1005
1024
|
worker exits idle, so a plain context strands some for good (13 of about 7,000 on
|
|
1006
1025
|
tensorstore 0.1.78; 40 to 44 of 12,000 to 15,000 with 16 workers on 0.1.75 and 0.1.85)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "worldarray"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.0"
|
|
4
4
|
description = "Read zarr, N5 and Neuroglancer precomputed volumes in world coordinates through tensorstore: correct voxel placement, pyramid levels, padded region reads, resampling. numpy + tensorstore only."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { file = "LICENSE" }
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"""Tensorstore contexts: one shared per cache budget, or a capped one per call.
|
|
2
2
|
|
|
3
|
-
``shared_context(cache_bytes)`` is one ``tensorstore.Context`` per budget
|
|
4
|
-
process, so the levels of a pyramid and repeat opens of an
|
|
5
|
-
The default budget is 0: tensorstore's decoded-chunk cache
|
|
6
|
-
either reads each region once or caches decoded chunks
|
|
7
|
-
pools are sized by ``thread_budget()``, from
|
|
8
|
-
tensorstore alone would size them to every CPU on
|
|
9
|
-
|
|
3
|
+
``shared_context(cache_bytes, threads=)`` is one ``tensorstore.Context`` per budget and
|
|
4
|
+
thread limit for the whole process, so the levels of a pyramid and repeat opens of an
|
|
5
|
+
array draw on one cache pool. The default budget is 0: tensorstore's decoded-chunk cache
|
|
6
|
+
is off, because every consumer either reads each region once or caches decoded chunks
|
|
7
|
+
itself. By default its decoding and file I/O pools are sized by ``thread_budget()``, from
|
|
8
|
+
the variables a cluster job sets, where tensorstore alone would size them to every CPU on
|
|
9
|
+
the node; ``threads=0`` leaves them at tensorstore's sizes (chunkmirage's server). Every
|
|
10
|
+
context here, and every one a store is opened on, is watched for tensorstore's lost wakeup
|
|
11
|
+
(``watch``).
|
|
10
12
|
|
|
11
13
|
``capped_context`` is mesh-n-bone's worker-process context: every tensorstore concurrency
|
|
12
14
|
resource limited, so dozens of worker processes on one host do not multiply into thousands
|
|
@@ -26,7 +28,7 @@ from typing import Any
|
|
|
26
28
|
|
|
27
29
|
import tensorstore as ts
|
|
28
30
|
|
|
29
|
-
_shared: dict[int, ts.Context] = {}
|
|
31
|
+
_shared: dict[tuple[int, int | None], ts.Context] = {}
|
|
30
32
|
_rebuilt: dict[str, ts.Context] = {}
|
|
31
33
|
_lock = threading.Lock()
|
|
32
34
|
|
|
@@ -136,20 +138,23 @@ def thread_budget() -> int:
|
|
|
136
138
|
return os.cpu_count() or 1
|
|
137
139
|
|
|
138
140
|
|
|
139
|
-
def shared_context(cache_bytes: int = 0) -> ts.Context:
|
|
141
|
+
def shared_context(cache_bytes: int = 0, *, threads: int | None = None) -> ts.Context:
|
|
142
|
+
"""The process's one context for ``cache_bytes`` and ``threads``, made on first use and
|
|
143
|
+
watched. ``threads`` limits its decoding and file I/O pools: ``None`` to
|
|
144
|
+
``thread_budget()``, a positive number to that many, ``0`` not at all (tensorstore's own
|
|
145
|
+
sizes, every CPU on the node)."""
|
|
146
|
+
key = (int(cache_bytes), None if threads is None else int(threads))
|
|
147
|
+
if key[1] is not None and key[1] < 0:
|
|
148
|
+
raise ValueError(f"threads must be None, 0 or positive, not {threads!r}")
|
|
140
149
|
with _lock:
|
|
141
|
-
ctx = _shared.get(
|
|
150
|
+
ctx = _shared.get(key)
|
|
142
151
|
if ctx is None:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
)
|
|
151
|
-
watch(ctx)
|
|
152
|
-
_shared[int(cache_bytes)] = ctx
|
|
152
|
+
spec: dict = {"cache_pool": {"total_bytes_limit": key[0]}}
|
|
153
|
+
limit = thread_budget() if threads is None else key[1]
|
|
154
|
+
if limit:
|
|
155
|
+
spec["data_copy_concurrency"] = spec["file_io_concurrency"] = {"limit": limit}
|
|
156
|
+
ctx = watch(ts.Context(spec))
|
|
157
|
+
_shared[key] = ctx
|
|
153
158
|
return ctx
|
|
154
159
|
|
|
155
160
|
|
|
@@ -157,7 +162,9 @@ def capped_context_spec(cache_bytes: int = 0, concurrency: int = 1, network: boo
|
|
|
157
162
|
"""The spec dict behind ``capped_context``, for callers building their own opens.
|
|
158
163
|
``network=False`` caps only decoding and local file I/O, leaving tensorstore's request
|
|
159
164
|
pools alone (cellmap-analyze's choice for remote reads). A ``concurrency`` of 1 is safe:
|
|
160
|
-
the context is watched (see ``KICK_EVERY``).
|
|
165
|
+
the context is watched (see ``KICK_EVERY``). It is not free: a read or write spanning
|
|
166
|
+
several chunks then decodes and stores them one at a time, half the speed of 4 on
|
|
167
|
+
8-chunk blocks in a two-slot job, 7 to 19% slower in a one-slot one (spec, Behaviour 7)."""
|
|
161
168
|
limit = {"limit": int(concurrency)}
|
|
162
169
|
spec = {
|
|
163
170
|
"data_copy_concurrency": limit,
|
|
@@ -179,12 +186,14 @@ def capped_context(cache_bytes: int = 0, concurrency: int = 1, network: bool = T
|
|
|
179
186
|
|
|
180
187
|
def recipe_of(context: ts.Context) -> tuple[str, Any]:
|
|
181
188
|
"""How another process rebuilds ``context``: ``("shared", cache_bytes)`` for a shared
|
|
182
|
-
context
|
|
183
|
-
|
|
189
|
+
context at the default limit (the other process takes its own ``thread_budget()``),
|
|
190
|
+
``("shared", (cache_bytes, threads))`` for one with ``threads`` given, else
|
|
191
|
+
``("spec", json)`` from the context's own spec, so a capped context stays capped across
|
|
192
|
+
a pickle (Behaviour 11, mesh-n-bone's workers)."""
|
|
184
193
|
with _lock:
|
|
185
|
-
for cache_bytes, ctx in _shared.items():
|
|
194
|
+
for (cache_bytes, threads), ctx in _shared.items():
|
|
186
195
|
if ctx is context:
|
|
187
|
-
return ("shared", cache_bytes)
|
|
196
|
+
return ("shared", cache_bytes if threads is None else (cache_bytes, threads))
|
|
188
197
|
spec = context.spec
|
|
189
198
|
return ("spec", json.dumps(spec.to_json() if spec is not None else {}, sort_keys=True))
|
|
190
199
|
|
|
@@ -196,7 +205,8 @@ def from_recipe(recipe: tuple[str, Any] | None) -> ts.Context | None:
|
|
|
196
205
|
return None
|
|
197
206
|
kind, value = recipe
|
|
198
207
|
if kind == "shared":
|
|
199
|
-
|
|
208
|
+
cache_bytes, threads = value if isinstance(value, (tuple, list)) else (value, None)
|
|
209
|
+
return shared_context(int(cache_bytes), threads=threads)
|
|
200
210
|
with _lock:
|
|
201
211
|
ctx = _rebuilt.get(value)
|
|
202
212
|
if ctx is None:
|
|
@@ -301,3 +301,23 @@ def test_gce_guard_is_left_alone_on_a_google_machine(monkeypatch):
|
|
|
301
301
|
monkeypatch.setattr(store, "_on_google_cloud", lambda: False)
|
|
302
302
|
store.kvstore_specs("gs://bucket/vol")
|
|
303
303
|
assert os.environ["GCE_METADATA_ROOT"] == "metadata.google.internal.invalid"
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def test_a_shared_context_takes_the_callers_thread_count(monkeypatch):
|
|
307
|
+
monkeypatch.setenv("OMP_NUM_THREADS", "1")
|
|
308
|
+
monkeypatch.setattr(wa.context, "_shared", {})
|
|
309
|
+
default = wa.shared_context(7)
|
|
310
|
+
assert default.spec.to_json()["data_copy_concurrency"] == {"limit": 1}
|
|
311
|
+
four = wa.shared_context(7, threads=4).spec.to_json()
|
|
312
|
+
assert four["data_copy_concurrency"] == four["file_io_concurrency"] == {"limit": 4}
|
|
313
|
+
machine = wa.shared_context(7, threads=0)
|
|
314
|
+
assert machine is wa.shared_context(7, threads=0) and machine is not default
|
|
315
|
+
spec = machine.spec.to_json()
|
|
316
|
+
assert "data_copy_concurrency" not in spec and "file_io_concurrency" not in spec
|
|
317
|
+
assert spec["cache_pool"] == {"total_bytes_limit": 7}
|
|
318
|
+
assert machine in wa.context._watched
|
|
319
|
+
assert wa.context.recipe_of(machine) == ("shared", (7, 0))
|
|
320
|
+
assert wa.context.from_recipe(("shared", (7, 0))) is machine
|
|
321
|
+
assert wa.context.from_recipe(("shared", 7)) is default
|
|
322
|
+
with pytest.raises(ValueError):
|
|
323
|
+
wa.shared_context(threads=-1)
|
|
@@ -826,6 +826,8 @@ def test_a_pickled_handle_keeps_its_context(tmp_path):
|
|
|
826
826
|
) # one rebuilt context per spec
|
|
827
827
|
shared = pickle.loads(pickle.dumps(wa.open(path)))
|
|
828
828
|
assert shared.context is wa.shared_context(0)
|
|
829
|
+
machine = wa.shared_context(0, threads=0)
|
|
830
|
+
assert pickle.loads(pickle.dumps(wa.open(path, context=machine))).context is machine
|
|
829
831
|
|
|
830
832
|
|
|
831
833
|
def test_codecs_of_a_path_and_the_fill_write_that_removes_a_key(tmp_path):
|
|
@@ -1247,7 +1247,7 @@ wheels = [
|
|
|
1247
1247
|
|
|
1248
1248
|
[[package]]
|
|
1249
1249
|
name = "worldarray"
|
|
1250
|
-
version = "0.
|
|
1250
|
+
version = "0.6.0"
|
|
1251
1251
|
source = { editable = "." }
|
|
1252
1252
|
dependencies = [
|
|
1253
1253
|
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
|
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
|
|
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
|