Skip to content

Commit 71e303f

Browse files
committed
add docs and changelog
1 parent a3d0358 commit 71e303f

5 files changed

Lines changed: 150 additions & 2 deletions

File tree

changes/3732.feature.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Adds an experimental HTTP server that can expose `Store`, `Array`, or `Group` instances over HTTP.
2+
See the [user guide](https://zarr.readthedocs.io/en/latest/user-guide/experimental.html#http-server)
3+
and the [Serve v2 as v3 example](https://zarr.readthedocs.io/en/latest/user-guide/examples/serve_v2_v3.html).

docs/api/zarr/experimental.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ title: experimental
44

55
Experimental functionality is not stable and may change or be removed at any point.
66

7-
## Classes
7+
## Cache Store
88

99
::: zarr.experimental.cache_store
10+
11+
## HTTP Server
12+
13+
::: zarr.experimental.serve
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
--8<-- "examples/serve_v2_v3/README.md"
2+
3+
## Source Code
4+
5+
```python
6+
--8<-- "examples/serve_v2_v3/serve_v2_v3.py"
7+
```

docs/user-guide/experimental.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,136 @@ print(f"Cache contains {info['cached_keys']} keys with {info['current_size']} by
273273
This example shows how the CacheStore can significantly reduce access times for repeated
274274
data reads, particularly important when working with remote data sources. The dual-store
275275
architecture allows for flexible cache persistence and management.
276+
277+
## HTTP Server
278+
279+
Zarr Python provides an experimental HTTP server that exposes a Zarr `Store`, `Array`,
280+
or `Group` over HTTP as an [ASGI](https://asgi.readthedocs.io/) application.
281+
This makes it possible to serve zarr data to any HTTP-capable client (including
282+
another Zarr Python process backed by an `HTTPStore`).
283+
284+
The server is built on [Starlette](https://www.starlette.io/) and can be run with
285+
any ASGI server such as [Uvicorn](https://www.uvicorn.org/).
286+
287+
Install the server dependencies with:
288+
289+
```bash
290+
pip install zarr[server]
291+
```
292+
293+
### Serving a Store
294+
295+
[`zarr.experimental.serve.serve_store`][] creates an ASGI app that exposes every key
296+
in a store:
297+
298+
```python
299+
import zarr
300+
from zarr.experimental.serve import serve_store
301+
302+
store = zarr.storage.MemoryStore()
303+
zarr.create_array(store, shape=(100, 100), chunks=(10, 10), dtype="float64")
304+
305+
app = serve_store(store)
306+
307+
# Run with Uvicorn:
308+
# uvicorn my_module:app --host 0.0.0.0 --port 8000
309+
```
310+
311+
### Serving a Node
312+
313+
[`zarr.experimental.serve.serve_node`][] creates an ASGI app that only serves keys
314+
belonging to a specific `Array` or `Group`. Requests for keys outside the node
315+
receive a 404, even if those keys exist in the underlying store:
316+
317+
```python
318+
import zarr
319+
from zarr.experimental.serve import serve_node
320+
321+
store = zarr.storage.MemoryStore()
322+
root = zarr.open_group(store)
323+
root.create_array("a", shape=(10,), dtype="int32")
324+
root.create_array("b", shape=(20,), dtype="float64")
325+
326+
# Only serve the array at "a" — requests for "b" will return 404.
327+
arr = root["a"]
328+
app = serve_node(arr)
329+
```
330+
331+
### CORS Support
332+
333+
Both `serve_store` and `serve_node` accept a [`CorsOptions`][zarr.experimental.serve.CorsOptions]
334+
parameter to enable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
335+
middleware for browser-based clients:
336+
337+
```python
338+
from zarr.experimental.serve import CorsOptions, serve_store
339+
340+
app = serve_store(
341+
store,
342+
cors_options=CorsOptions(
343+
allow_origins=["*"],
344+
allow_methods=["GET"],
345+
),
346+
)
347+
```
348+
349+
### HTTP Range Requests
350+
351+
The server supports the standard `Range` header for partial reads. The three
352+
forms defined by [RFC 7233](https://httpwg.org/specs/rfc7233.html) are supported:
353+
354+
| Header | Meaning |
355+
| -------------------- | ------------------------------ |
356+
| `bytes=0-99` | First 100 bytes |
357+
| `bytes=100-` | Everything from byte 100 |
358+
| `bytes=-50` | Last 50 bytes |
359+
360+
A successful range request returns HTTP 206 (Partial Content).
361+
362+
### Write Support
363+
364+
By default only `GET` requests are accepted. To enable writes, pass
365+
`methods={"GET", "PUT"}`:
366+
367+
```python
368+
app = serve_store(store, methods={"GET", "PUT"})
369+
```
370+
371+
A `PUT` request stores the request body at the given path and returns 204 (No Content).
372+
373+
### Running the Server in a Background Thread
374+
375+
Because `serve_store` and `serve_node` return a standard ASGI app, you can run the
376+
server in a daemon thread and interact with it from the same process. This is
377+
useful for notebooks, scripts, and interactive exploration:
378+
379+
```python
380+
import threading
381+
382+
import numpy as np
383+
import uvicorn
384+
385+
import zarr
386+
from zarr.experimental.serve import serve_node
387+
from zarr.storage import MemoryStore
388+
389+
# Create an array with some data.
390+
store = MemoryStore()
391+
arr = zarr.create_array(store, shape=(100,), chunks=(10,), dtype="float64")
392+
arr[:] = np.arange(100, dtype="float64")
393+
394+
# Build the ASGI app and launch Uvicorn in a daemon thread.
395+
app = serve_node(arr)
396+
config = uvicorn.Config(app, host="127.0.0.1", port=8000)
397+
server = uvicorn.Server(config)
398+
thread = threading.Thread(target=server.run, daemon=True)
399+
thread.start()
400+
401+
# Now open the served array from another zarr client.
402+
remote = zarr.open_array("http://127.0.0.1:8000", mode="r")
403+
np.testing.assert_array_equal(remote[:], arr[:])
404+
405+
# Shut down when finished.
406+
server.should_exit = True
407+
thread.join()
408+
```

examples/serve_v2_v3/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ The example shows how to:
1010
existing v2 store
1111
- Translate v2 metadata (`.zarray` + `.zattrs`) to v3 `zarr.json` using
1212
the same `_convert_array_metadata` helper that powers `zarr migrate v3`
13-
- Map v3 default chunk keys (`c/0/0`) to their v2 equivalents (`0.0`)
13+
- Pass chunk keys through unchanged (the converted metadata preserves
14+
`V2ChunkKeyEncoding`, so keys like `0.0` work in both formats)
1415
- Serve the translated store over HTTP so that any v3-compatible client
1516
can read v2 data without knowing the original format
1617

0 commit comments

Comments
 (0)