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.
- nanoping_api/__init__.py +18 -0
- nanoping_api/_client.py +154 -0
- nanoping_api/_generated.py +4182 -0
- nanoping_api/_runtime.py +685 -0
- nanoping_api/py.typed +0 -0
- nanoping_api-11.1.0.dev0.dist-info/METADATA +63 -0
- nanoping_api-11.1.0.dev0.dist-info/RECORD +8 -0
- nanoping_api-11.1.0.dev0.dist-info/WHEEL +4 -0
nanoping_api/__init__.py
ADDED
|
@@ -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
|
+
]
|
nanoping_api/_client.py
ADDED
|
@@ -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
|