@@ -290,33 +290,31 @@ Install the server dependencies with:
290290pip install zarr[server]
291291```
292292
293- ### Serving a Store
293+ ### Building an ASGI App
294294
295- [ ` zarr.experimental.serve.serve_store ` ] [ ] creates an ASGI app that exposes every key
295+ [ ` zarr.experimental.serve.store_app ` ] [ ] creates an ASGI app that exposes every key
296296in a store:
297297
298298``` python
299299import zarr
300- from zarr.experimental.serve import serve_store
300+ from zarr.experimental.serve import store_app
301301
302302store = zarr.storage.MemoryStore()
303303zarr.create_array(store, shape = (100 , 100 ), chunks = (10 , 10 ), dtype = " float64" )
304304
305- app = serve_store (store)
305+ app = store_app (store)
306306
307- # Run with Uvicorn:
307+ # Run with any ASGI server, e.g. Uvicorn:
308308# uvicorn my_module:app --host 0.0.0.0 --port 8000
309309```
310310
311- ### Serving a Node
312-
313- [ ` zarr.experimental.serve.serve_node ` ] [ ] creates an ASGI app that only serves keys
311+ [ ` zarr.experimental.serve.node_app ` ] [ ] creates an ASGI app that only serves keys
314312belonging to a specific ` Array ` or ` Group ` . Requests for keys outside the node
315313receive a 404, even if those keys exist in the underlying store:
316314
317315``` python
318316import zarr
319- from zarr.experimental.serve import serve_node
317+ from zarr.experimental.serve import node_app
320318
321319store = zarr.storage.MemoryStore()
322320root = zarr.open_group(store)
@@ -325,19 +323,57 @@ root.create_array("b", shape=(20,), dtype="float64")
325323
326324# Only serve the array at "a" — requests for "b" will return 404.
327325arr = root[" a" ]
328- app = serve_node(arr)
326+ app = node_app(arr)
327+ ```
328+
329+ ### Running the Server
330+
331+ [ ` zarr.experimental.serve.serve_store ` ] [ ] and [ ` zarr.experimental.serve.serve_node ` ] [ ]
332+ build an ASGI app * and* start a [ Uvicorn] ( https://www.uvicorn.org/ ) server.
333+ By default they block until the server is shut down:
334+
335+ ``` python
336+ from zarr.experimental.serve import serve_store
337+
338+ serve_store(store, host = " 127.0.0.1" , port = 8000 )
339+ ```
340+
341+ Pass ` background=True ` to start the server in a daemon thread and return
342+ immediately. The returned ` uvicorn.Server ` object can be used to shut down
343+ the server:
344+
345+ ``` python
346+ import numpy as np
347+
348+ import zarr
349+ from zarr.experimental.serve import serve_node
350+ from zarr.storage import MemoryStore
351+
352+ store = MemoryStore()
353+ arr = zarr.create_array(store, shape = (100 ,), chunks = (10 ,), dtype = " float64" )
354+ arr[:] = np.arange(100 , dtype = " float64" )
355+
356+ server = serve_node(arr, host = " 127.0.0.1" , port = 8000 , background = True )
357+
358+ # Now open the served array from another zarr client.
359+ remote = zarr.open_array(" http://127.0.0.1:8000" , mode = " r" )
360+ np.testing.assert_array_equal(remote[:], arr[:])
361+
362+ # Shut down when finished.
363+ server.should_exit = True
329364```
330365
331366### CORS Support
332367
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:
368+ Both ` store_app ` and ` node_app ` (and their ` serve_* ` counterparts) accept a
369+ [ ` CorsOptions ` ] [ zarr.experimental.serve.CorsOptions ] parameter to enable
370+ [ CORS] ( https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS ) middleware for
371+ browser-based clients:
336372
337373``` python
338- from zarr.experimental.serve import CorsOptions, serve_store
374+ from zarr.experimental.serve import CorsOptions, store_app
339375
340- app = serve_store (
376+ app = store_app (
341377 store,
342378 cors_options = CorsOptions(
343379 allow_origins = [" *" ],
@@ -365,44 +401,7 @@ By default only `GET` requests are accepted. To enable writes, pass
365401` methods={"GET", "PUT"} ` :
366402
367403``` python
368- app = serve_store (store, methods = {" GET" , " PUT" })
404+ app = store_app (store, methods = {" GET" , " PUT" })
369405```
370406
371407A ` 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- ```
0 commit comments