nanoping-api 11.1.0.dev0__py3-none-any.whl

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.
@@ -0,0 +1,18 @@
1
+ """A typed client for the REST API of a NanoPing node."""
2
+
3
+ from ._client import AsyncNanopingClient, NanopingClient
4
+ from ._generated import *
5
+ from ._generated import __all__ as _generated_all
6
+ from ._runtime import ApiError, AsyncBidiStream, AsyncServerStream, BidiStream, Code, ServerStream
7
+
8
+ __all__ = [
9
+ "ApiError",
10
+ "AsyncBidiStream",
11
+ "AsyncNanopingClient",
12
+ "AsyncServerStream",
13
+ "BidiStream",
14
+ "Code",
15
+ "NanopingClient",
16
+ "ServerStream",
17
+ *_generated_all,
18
+ ]
@@ -0,0 +1,154 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ from collections.abc import AsyncIterator, Awaitable, Callable, Iterator, Mapping
5
+ from typing import Self
6
+
7
+ import httpx
8
+
9
+ from ._generated import (
10
+ AuthenticationByRequestAnswer,
11
+ AuthenticationByRequestRequest,
12
+ Message,
13
+ Options,
14
+ _AsyncServices,
15
+ _Services,
16
+ )
17
+ from ._runtime import AsyncTransport, Transport
18
+
19
+ _EVERY_LINE = Options(lines=2**63 - 1)
20
+
21
+
22
+ class NanopingClient(_Services):
23
+ """A blocking client for the REST API of a node.
24
+
25
+ `base_url` is the address of the REST API, such as "http://127.0.0.1:10565",
26
+ and `headers` are sent with every request. `timeout` limits each call, in
27
+ seconds, but not the wait for the next message of a stream. By default a call
28
+ waits as long as it takes. Pass `http_client` to send the calls with an
29
+ httpx.Client of your own.
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ base_url: str,
35
+ *,
36
+ headers: Mapping[str, str] | None = None,
37
+ timeout: float | None = None,
38
+ http_client: httpx.Client | None = None,
39
+ ) -> None:
40
+ self._transport = Transport(base_url, headers, timeout, http_client)
41
+ super().__init__(self._transport)
42
+
43
+ def close(self) -> None:
44
+ """Closes the connections of the client, unless they belong to `http_client`."""
45
+ self._transport.close()
46
+
47
+ def __enter__(self) -> Self:
48
+ return self
49
+
50
+ def __exit__(self, *_: object) -> None:
51
+ self.close()
52
+
53
+ def join_hub(self, hub_server_address: str) -> None:
54
+ """Joins the hub server at `hub_server_address` with the node hosting the API.
55
+
56
+ Returns once the hub server approved the node. Raises an ApiError with
57
+ Code.UNAUTHENTICATED when the request was rejected, Code.DEADLINE_EXCEEDED
58
+ when nobody answered in time and Code.ALREADY_EXISTS when the node already
59
+ is part of a hub.
60
+ """
61
+ self.hub_client.authenticate_by_request(hub_server_address=hub_server_address)
62
+
63
+ def answer_join_requests(self, decide: Callable[[AuthenticationByRequestRequest], bool]) -> None:
64
+ """Answers the nodes that ask to join the hub server hosted by the node hosting
65
+ the API.
66
+
67
+ `decide` is called for every request and returns whether the node may join.
68
+ Runs until the stream ends or fails.
69
+ """
70
+ with self.hub_server.authentication_by_requests() as stream:
71
+ for request in stream:
72
+ stream.send(AuthenticationByRequestAnswer(request_id=request.request_id, accept=decide(request)))
73
+
74
+ def run_pipeline(self, node_id: str, pipeline_id: str) -> Iterator[Message]:
75
+ """Starts a pipeline and follows the log of the new run from its first line.
76
+
77
+ The pipeline starts when the loop does. Yields each log message until the
78
+ log stream ends or the loop stops, which closes the stream.
79
+ """
80
+ run = self.pipelines.start_pipeline(node_id=node_id, id=pipeline_id)
81
+ with self.pipelines.open_log_stream(node_id=node_id, run_id=run.run_id, options=_EVERY_LINE) as stream:
82
+ for batch in stream:
83
+ yield from batch.messages
84
+
85
+
86
+ class AsyncNanopingClient(_AsyncServices):
87
+ """An asyncio client for the REST API of a node.
88
+
89
+ `base_url` is the address of the REST API, such as "http://127.0.0.1:10565",
90
+ and `headers` are sent with every request. `timeout` limits each call, in
91
+ seconds, but not the wait for the next message of a stream. By default a call
92
+ waits as long as it takes. Pass `http_client` to send the calls with an
93
+ httpx.AsyncClient of your own.
94
+ """
95
+
96
+ def __init__(
97
+ self,
98
+ base_url: str,
99
+ *,
100
+ headers: Mapping[str, str] | None = None,
101
+ timeout: float | None = None,
102
+ http_client: httpx.AsyncClient | None = None,
103
+ ) -> None:
104
+ self._transport = AsyncTransport(base_url, headers, timeout, http_client)
105
+ super().__init__(self._transport)
106
+
107
+ async def aclose(self) -> None:
108
+ """Closes the connections of the client, unless they belong to `http_client`."""
109
+ await self._transport.aclose()
110
+
111
+ async def __aenter__(self) -> Self:
112
+ return self
113
+
114
+ async def __aexit__(self, *_: object) -> None:
115
+ await self.aclose()
116
+
117
+ async def join_hub(self, hub_server_address: str) -> None:
118
+ """Joins the hub server at `hub_server_address` with the node hosting the API.
119
+
120
+ Returns once the hub server approved the node. Raises an ApiError with
121
+ Code.UNAUTHENTICATED when the request was rejected, Code.DEADLINE_EXCEEDED
122
+ when nobody answered in time and Code.ALREADY_EXISTS when the node already
123
+ is part of a hub.
124
+ """
125
+ await self.hub_client.authenticate_by_request(hub_server_address=hub_server_address)
126
+
127
+ async def answer_join_requests(
128
+ self, decide: Callable[[AuthenticationByRequestRequest], bool | Awaitable[bool]]
129
+ ) -> None:
130
+ """Answers the nodes that ask to join the hub server hosted by the node hosting
131
+ the API.
132
+
133
+ `decide` is called for every request and returns, or resolves to, whether
134
+ the node may join. Runs until the stream ends or fails, or the task is
135
+ cancelled.
136
+ """
137
+ async with self.hub_server.authentication_by_requests() as stream:
138
+ async for request in stream:
139
+ accept = decide(request)
140
+ if inspect.isawaitable(accept):
141
+ accept = await accept
142
+ await stream.send(AuthenticationByRequestAnswer(request_id=request.request_id, accept=accept))
143
+
144
+ async def run_pipeline(self, node_id: str, pipeline_id: str) -> AsyncIterator[Message]:
145
+ """Starts a pipeline and follows the log of the new run from its first line.
146
+
147
+ The pipeline starts when the loop does. Yields each log message until the
148
+ log stream ends or the loop stops, which closes the stream.
149
+ """
150
+ run = await self.pipelines.start_pipeline(node_id=node_id, id=pipeline_id)
151
+ async with self.pipelines.open_log_stream(node_id=node_id, run_id=run.run_id, options=_EVERY_LINE) as stream:
152
+ async for batch in stream:
153
+ for message in batch.messages:
154
+ yield message