worldarray 0.10.1__tar.gz → 0.10.2__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.10.1 → worldarray-0.10.2}/.github/workflows/ci.yml +16 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/CHANGELOG.md +14 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/PKG-INFO +1 -1
- {worldarray-0.10.1 → worldarray-0.10.2}/docs/spec.md +10 -4
- {worldarray-0.10.1 → worldarray-0.10.2}/pyproject.toml +1 -1
- {worldarray-0.10.1 → worldarray-0.10.2}/src/worldarray/__init__.py +1 -1
- {worldarray-0.10.1 → worldarray-0.10.2}/src/worldarray/store.py +13 -3
- {worldarray-0.10.1 → worldarray-0.10.2}/tests/test_access.py +4 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/uv.lock +1 -1
- {worldarray-0.10.1 → worldarray-0.10.2}/.github/workflows/docs.yml +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/.github/workflows/publish.yml +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/.gitignore +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/LICENSE +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/README.md +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/docs/changelog.md +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/docs/index.md +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/mkdocs.yml +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/src/worldarray/array.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/src/worldarray/context.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/src/worldarray/geometry.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/src/worldarray/grid.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/src/worldarray/levels.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/src/worldarray/placement.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/src/worldarray/resample.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/src/worldarray/retry.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/src/worldarray/units.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/src/worldarray/write.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/tests/conftest.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/tests/fixtures/metadata.json +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/tests/fixtures/write.json +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/tests/test_axes_order.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/tests/test_geometry.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/tests/test_grid.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/tests/test_lost_wakeup.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/tests/test_metadata.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/tests/test_read.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/tests/test_resample.py +0 -0
- {worldarray-0.10.1 → worldarray-0.10.2}/tests/test_write.py +0 -0
|
@@ -3,6 +3,8 @@ on:
|
|
|
3
3
|
push:
|
|
4
4
|
branches: [main]
|
|
5
5
|
pull_request:
|
|
6
|
+
schedule:
|
|
7
|
+
- cron: "0 6 * * 1" # weekly, so a new dependency release is tried without a push
|
|
6
8
|
jobs:
|
|
7
9
|
test:
|
|
8
10
|
strategy:
|
|
@@ -20,3 +22,17 @@ jobs:
|
|
|
20
22
|
- run: uv run ruff format --check src tests
|
|
21
23
|
- run: uv run pytest -q
|
|
22
24
|
- run: uv run mkdocs build --strict
|
|
25
|
+
# what a fresh install gets: the newest release of every dependency, not the lock's
|
|
26
|
+
newest:
|
|
27
|
+
strategy:
|
|
28
|
+
matrix:
|
|
29
|
+
os: [ubuntu-latest, macos-latest]
|
|
30
|
+
runs-on: ${{ matrix.os }}
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
- uses: astral-sh/setup-uv@v5
|
|
34
|
+
with:
|
|
35
|
+
python-version: "3.12"
|
|
36
|
+
- run: uv sync --group dev --upgrade
|
|
37
|
+
- run: uv pip list | grep -E "^(tensorstore|numpy) "
|
|
38
|
+
- run: uv run pytest -q
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.2 (2026-11-13)
|
|
4
|
+
|
|
5
|
+
0.10.1's fix, on tensorstore 0.1.86. No placement change, no unit table change, and no
|
|
6
|
+
default changes.
|
|
7
|
+
|
|
8
|
+
- A `gs://` open with a stale Google login falls back to the public https URL on
|
|
9
|
+
tensorstore 0.1.86 too. 0.10.1 knew the failed login by the source locations in
|
|
10
|
+
tensorstore's error, which 0.1.86 leaves out, so with the newest tensorstore (what a
|
|
11
|
+
fresh install gets) the open still raised. The token endpoint's own error code
|
|
12
|
+
(`invalid_grant`, and the other OAuth 2.0 codes) is now matched as well. On 0.1.86 a
|
|
13
|
+
token endpoint out of reach is raised as any unreachable host is, not passed over.
|
|
14
|
+
- CI also runs the tests on the newest release of every dependency, weekly and on every
|
|
15
|
+
pull request; 0.10.1 passed on the lock's tensorstore 0.1.78.
|
|
16
|
+
|
|
3
17
|
## 0.10.1 (2026-11-13)
|
|
4
18
|
|
|
5
19
|
A fix for `gs://` with a stale Google login. No placement change, no unit table change,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: worldarray
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.2
|
|
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/
|
|
@@ -540,10 +540,13 @@ this document and that one differ, fix one of them.
|
|
|
540
540
|
is read as that unit on every axis; tensorstore is given only the array members of
|
|
541
541
|
`attributes.json`, so attributes it would reject do not stop the open. The next way is tried only when the error
|
|
542
542
|
starts `PERMISSION_DENIED` or `UNAUTHENTICATED`, or came from getting an access token,
|
|
543
|
-
which tensorstore marks with its `internal/oauth2/` code among the error's
|
|
544
|
-
locations (0.10.1)
|
|
543
|
+
which tensorstore up to 0.1.85 marks with its `internal/oauth2/` code among the error's
|
|
544
|
+
source locations (0.10.1), and which is otherwise known by the token endpoint's own error
|
|
545
|
+
code, an OAuth 2.0 one (RFC 6749 §5.2) as the body's string `error`, where a store's error
|
|
546
|
+
body holds an object (0.10.2: tensorstore 0.1.86 leaves source locations out): Google refuses an expired gcloud login with HTTP 400 `invalid_grant`,
|
|
545
547
|
which starts neither (cellmap-flow, on David's Mac), and a token endpoint out of reach
|
|
546
|
-
leaves no token either, so the public URL is worth trying
|
|
548
|
+
leaves no token either, so the public URL is worth trying; from 0.1.86 an unreachable
|
|
549
|
+
token endpoint carries no such mark and is raised as any unreachable host is. When the last way fails too
|
|
547
550
|
after a refusal, its error carries the first refusal as its cause, so a stale login is
|
|
548
551
|
still in the traceback of a private bucket's 403. The way that worked is remembered per
|
|
549
552
|
(driver, bucket) for the process. Format probing runs all probe keys under one way before
|
|
@@ -1058,7 +1061,10 @@ chunkmirage derives from raw attributes carries a `layered` note beside the pack
|
|
|
1058
1061
|
fallback; detection ignores the directory suffix and finds a precomputed scale key.
|
|
1059
1062
|
0.10.1: a failed login counts as a refusal and is the cause of a later failure; end to
|
|
1060
1063
|
end, a `gs://` open whose token refresh is answered 400 `invalid_grant` (an https proxy
|
|
1061
|
-
playing www.googleapis.com under a test CA) reads through the public URL.
|
|
1064
|
+
playing www.googleapis.com under a test CA) reads through the public URL. 0.10.2: the
|
|
1065
|
+
same on tensorstore 0.1.86's messages, and CI runs the suite on the newest release of
|
|
1066
|
+
every dependency as well as the lock's, weekly and on every pull request: 0.10.1 passed
|
|
1067
|
+
on the lock's tensorstore 0.1.78 and failed on 0.1.86, which a fresh install got.
|
|
1062
1068
|
|
|
1063
1069
|
Reads: every statement in Behaviour 1, 2, 3 and 9; a document-less group probed through
|
|
1064
1070
|
`s0`, integer and `"first"` levels, `detect(missing_ok=True)`, `index_box`; funlib keys on
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "worldarray"
|
|
3
|
-
version = "0.10.
|
|
3
|
+
version = "0.10.2"
|
|
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" }
|
|
@@ -158,12 +158,22 @@ class Refused(Exception):
|
|
|
158
158
|
"""A read was refused for want of credentials (not a missing key, not a network error)."""
|
|
159
159
|
|
|
160
160
|
|
|
161
|
+
# An OAuth 2.0 token endpoint's error codes (RFC 6749, 5.2), the string "error" member of the
|
|
162
|
+
# body it answers; a store's own error bodies hold an object there. Quotes may come escaped.
|
|
163
|
+
_TOKEN_ERROR = re.compile(
|
|
164
|
+
r'error\\*"\s*:\s*\\*"(?:invalid_grant|invalid_client|invalid_request|invalid_scope'
|
|
165
|
+
r'|unauthorized_client|unsupported_grant_type)\\*"'
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
161
169
|
def login_failed(error: BaseException) -> bool:
|
|
162
170
|
"""``error`` came from getting an access token, before any request reached the store: a
|
|
163
171
|
stale Google login (HTTP 400 ``invalid_grant``, which starts neither refusal code), a
|
|
164
|
-
rejected client, or a token endpoint out of reach.
|
|
165
|
-
among the error's source locations.
|
|
166
|
-
|
|
172
|
+
rejected client, or (up to tensorstore 0.1.85) a token endpoint out of reach. Up to
|
|
173
|
+
0.1.85 tensorstore names its oauth2 code among the error's source locations; 0.1.86
|
|
174
|
+
leaves source locations out, so the token endpoint's own error code is matched too."""
|
|
175
|
+
message = str(error)
|
|
176
|
+
return "tensorstore/internal/oauth2/" in message or bool(_TOKEN_ERROR.search(message))
|
|
167
177
|
|
|
168
178
|
|
|
169
179
|
def is_refused(error: BaseException) -> bool:
|
|
@@ -116,6 +116,10 @@ _STALE_LOGIN = ( # tensorstore's error for gcloud's login after a reauth policy
|
|
|
116
116
|
|
|
117
117
|
|
|
118
118
|
def test_a_failed_login_is_a_refusal_and_the_cause_of_a_later_one():
|
|
119
|
+
# tensorstore 0.1.86 leaves source locations out; the token endpoint's error code remains
|
|
120
|
+
login = r"Bad Request [HTTP code 400] [http_response_body='{\"error\": \"invalid_grant\"}']"
|
|
121
|
+
store_400 = r"Bad Request [HTTP code 400] [http_response_body='{\"error\": {\"code\": 400}}']"
|
|
122
|
+
assert store.is_refused(ValueError(login)) and not store.is_refused(ValueError(store_400))
|
|
119
123
|
unreachable = (
|
|
120
124
|
"UNAVAILABLE: CURL error Could not resolve host [source locations='"
|
|
121
125
|
"tensorstore/internal/curl/curl_transport.cc:475\n"
|
|
@@ -1247,7 +1247,7 @@ wheels = [
|
|
|
1247
1247
|
|
|
1248
1248
|
[[package]]
|
|
1249
1249
|
name = "worldarray"
|
|
1250
|
-
version = "0.10.
|
|
1250
|
+
version = "0.10.2"
|
|
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
|
|
File without changes
|
|
File without changes
|