koi-net 1.0.0b19__py3-none-any.whl → 1.1.0b2__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.
Potentially problematic release.
This version of koi-net might be problematic. Click here for more details.
- koi_net/config.py +44 -18
- koi_net/context.py +55 -0
- koi_net/core.py +134 -84
- koi_net/default_actions.py +15 -0
- koi_net/effector.py +139 -0
- koi_net/identity.py +4 -22
- koi_net/lifecycle.py +76 -0
- koi_net/network/__init__.py +0 -1
- koi_net/network/behavior.py +42 -0
- koi_net/network/error_handler.py +50 -0
- koi_net/network/{interface.py → event_queue.py} +52 -131
- koi_net/network/graph.py +18 -33
- koi_net/network/request_handler.py +112 -66
- koi_net/network/resolver.py +150 -0
- koi_net/network/response_handler.py +11 -3
- koi_net/poller.py +45 -0
- koi_net/processor/__init__.py +0 -1
- koi_net/processor/default_handlers.py +57 -41
- koi_net/processor/handler.py +3 -7
- koi_net/processor/interface.py +15 -214
- koi_net/processor/knowledge_object.py +10 -17
- koi_net/processor/knowledge_pipeline.py +220 -0
- koi_net/protocol/api_models.py +7 -1
- koi_net/protocol/edge.py +23 -1
- koi_net/protocol/envelope.py +54 -0
- koi_net/protocol/errors.py +23 -0
- koi_net/protocol/node.py +2 -1
- koi_net/protocol/secure.py +106 -0
- koi_net/secure.py +117 -0
- koi_net/server.py +128 -0
- {koi_net-1.0.0b19.dist-info → koi_net-1.1.0b2.dist-info}/METADATA +5 -4
- koi_net-1.1.0b2.dist-info/RECORD +38 -0
- koi_net/protocol/helpers.py +0 -25
- koi_net-1.0.0b19.dist-info/RECORD +0 -25
- {koi_net-1.0.0b19.dist-info → koi_net-1.1.0b2.dist-info}/WHEEL +0 -0
- {koi_net-1.0.0b19.dist-info → koi_net-1.1.0b2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import httpx
|
|
3
3
|
from rid_lib import RID
|
|
4
|
-
from rid_lib.ext import Cache
|
|
5
4
|
from rid_lib.types.koi_net_node import KoiNetNode
|
|
5
|
+
|
|
6
|
+
from ..identity import NodeIdentity
|
|
6
7
|
from ..protocol.api_models import (
|
|
7
8
|
RidsPayload,
|
|
8
9
|
ManifestsPayload,
|
|
@@ -13,8 +14,10 @@ from ..protocol.api_models import (
|
|
|
13
14
|
FetchBundles,
|
|
14
15
|
PollEvents,
|
|
15
16
|
RequestModels,
|
|
16
|
-
ResponseModels
|
|
17
|
+
ResponseModels,
|
|
18
|
+
ErrorResponse
|
|
17
19
|
)
|
|
20
|
+
from ..protocol.envelope import SignedEnvelope
|
|
18
21
|
from ..protocol.consts import (
|
|
19
22
|
BROADCAST_EVENTS_PATH,
|
|
20
23
|
POLL_EVENTS_PATH,
|
|
@@ -22,8 +25,13 @@ from ..protocol.consts import (
|
|
|
22
25
|
FETCH_MANIFESTS_PATH,
|
|
23
26
|
FETCH_BUNDLES_PATH
|
|
24
27
|
)
|
|
25
|
-
from ..protocol.node import NodeType
|
|
26
|
-
from
|
|
28
|
+
from ..protocol.node import NodeProfile, NodeType
|
|
29
|
+
from ..secure import Secure
|
|
30
|
+
from ..effector import Effector
|
|
31
|
+
|
|
32
|
+
from typing import TYPE_CHECKING
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from .error_handler import ErrorHandler
|
|
27
35
|
|
|
28
36
|
|
|
29
37
|
logger = logging.getLogger(__name__)
|
|
@@ -32,118 +40,156 @@ logger = logging.getLogger(__name__)
|
|
|
32
40
|
class RequestHandler:
|
|
33
41
|
"""Handles making requests to other KOI nodes."""
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
effector: Effector
|
|
44
|
+
identity: NodeIdentity
|
|
45
|
+
secure: Secure
|
|
46
|
+
error_handler: "ErrorHandler"
|
|
37
47
|
|
|
38
|
-
def __init__(
|
|
39
|
-
self
|
|
40
|
-
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
effector: Effector,
|
|
51
|
+
identity: NodeIdentity,
|
|
52
|
+
secure: Secure
|
|
53
|
+
):
|
|
54
|
+
self.effector = effector
|
|
55
|
+
self.identity = identity
|
|
56
|
+
self.secure = secure
|
|
57
|
+
|
|
58
|
+
def set_error_handler(self, error_handler: "ErrorHandler"):
|
|
59
|
+
self.error_handler = error_handler
|
|
60
|
+
|
|
61
|
+
def get_url(self, node_rid: KoiNetNode) -> str:
|
|
62
|
+
"""Retrieves URL of a node."""
|
|
63
|
+
|
|
64
|
+
logger.debug(f"Getting URL for {node_rid!r}")
|
|
65
|
+
node_url = None
|
|
66
|
+
|
|
67
|
+
if node_rid == self.identity.rid:
|
|
68
|
+
raise Exception("Don't talk to yourself")
|
|
69
|
+
|
|
70
|
+
node_bundle = self.effector.deref(node_rid)
|
|
41
71
|
|
|
72
|
+
if node_bundle:
|
|
73
|
+
node_profile = node_bundle.validate_contents(NodeProfile)
|
|
74
|
+
logger.debug(f"Found node profile: {node_profile}")
|
|
75
|
+
if node_profile.node_type != NodeType.FULL:
|
|
76
|
+
raise Exception("Can't query partial node")
|
|
77
|
+
node_url = node_profile.base_url
|
|
78
|
+
|
|
79
|
+
else:
|
|
80
|
+
if node_rid == self.identity.config.koi_net.first_contact.rid:
|
|
81
|
+
logger.debug("Found URL of first contact")
|
|
82
|
+
node_url = self.identity.config.koi_net.first_contact.url
|
|
83
|
+
|
|
84
|
+
if not node_url:
|
|
85
|
+
raise Exception("Node not found")
|
|
86
|
+
|
|
87
|
+
logger.debug(f"Resolved {node_rid!r} to {node_url}")
|
|
88
|
+
return node_url
|
|
89
|
+
|
|
42
90
|
def make_request(
|
|
43
|
-
self,
|
|
44
|
-
|
|
91
|
+
self,
|
|
92
|
+
node: KoiNetNode,
|
|
93
|
+
path: str,
|
|
45
94
|
request: RequestModels,
|
|
46
|
-
response_model: type[ResponseModels] | None = None
|
|
47
95
|
) -> ResponseModels | None:
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
96
|
+
url = self.get_url(node) + path
|
|
97
|
+
logger.info(f"Making request to {url}")
|
|
98
|
+
|
|
99
|
+
signed_envelope = self.secure.create_envelope(
|
|
100
|
+
payload=request,
|
|
101
|
+
target=node
|
|
52
102
|
)
|
|
53
|
-
if response_model:
|
|
54
|
-
return response_model.model_validate_json(resp.text)
|
|
55
|
-
|
|
56
|
-
def get_url(self, node_rid: KoiNetNode, url: str) -> str:
|
|
57
|
-
"""Retrieves URL of a node, or returns provided URL."""
|
|
58
103
|
|
|
59
|
-
|
|
60
|
-
|
|
104
|
+
try:
|
|
105
|
+
result = httpx.post(url, data=signed_envelope.model_dump_json())
|
|
106
|
+
except httpx.ConnectError as err:
|
|
107
|
+
logger.debug("Failed to connect")
|
|
108
|
+
self.error_handler.handle_connection_error(node)
|
|
109
|
+
raise err
|
|
61
110
|
|
|
62
|
-
if
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
111
|
+
if result.status_code != 200:
|
|
112
|
+
resp = ErrorResponse.model_validate_json(result.text)
|
|
113
|
+
self.error_handler.handle_protocol_error(resp.error, node)
|
|
114
|
+
return resp
|
|
115
|
+
|
|
116
|
+
if path == BROADCAST_EVENTS_PATH:
|
|
117
|
+
return None
|
|
118
|
+
elif path == POLL_EVENTS_PATH:
|
|
119
|
+
EnvelopeModel = SignedEnvelope[EventsPayload]
|
|
120
|
+
elif path == FETCH_RIDS_PATH:
|
|
121
|
+
EnvelopeModel = SignedEnvelope[RidsPayload]
|
|
122
|
+
elif path == FETCH_MANIFESTS_PATH:
|
|
123
|
+
EnvelopeModel = SignedEnvelope[ManifestsPayload]
|
|
124
|
+
elif path == FETCH_BUNDLES_PATH:
|
|
125
|
+
EnvelopeModel = SignedEnvelope[BundlesPayload]
|
|
70
126
|
else:
|
|
71
|
-
|
|
127
|
+
raise Exception(f"Unknown path '{path}'")
|
|
128
|
+
|
|
129
|
+
resp_envelope = EnvelopeModel.model_validate_json(result.text)
|
|
130
|
+
self.secure.validate_envelope(resp_envelope)
|
|
131
|
+
|
|
132
|
+
return resp_envelope.payload
|
|
72
133
|
|
|
73
134
|
def broadcast_events(
|
|
74
135
|
self,
|
|
75
|
-
node: RID
|
|
76
|
-
url: str = None,
|
|
136
|
+
node: RID,
|
|
77
137
|
req: EventsPayload | None = None,
|
|
78
138
|
**kwargs
|
|
79
139
|
) -> None:
|
|
80
140
|
"""See protocol.api_models.EventsPayload for available kwargs."""
|
|
81
141
|
request = req or EventsPayload.model_validate(kwargs)
|
|
82
|
-
self.make_request(
|
|
83
|
-
|
|
84
|
-
)
|
|
85
|
-
logger.info(f"Broadcasted {len(request.events)} event(s) to {node or url!r}")
|
|
142
|
+
self.make_request(node, BROADCAST_EVENTS_PATH, request)
|
|
143
|
+
logger.info(f"Broadcasted {len(request.events)} event(s) to {node!r}")
|
|
86
144
|
|
|
87
145
|
def poll_events(
|
|
88
146
|
self,
|
|
89
|
-
node: RID
|
|
90
|
-
url: str = None,
|
|
147
|
+
node: RID,
|
|
91
148
|
req: PollEvents | None = None,
|
|
92
149
|
**kwargs
|
|
93
150
|
) -> EventsPayload:
|
|
94
151
|
"""See protocol.api_models.PollEvents for available kwargs."""
|
|
95
152
|
request = req or PollEvents.model_validate(kwargs)
|
|
96
|
-
resp = self.make_request(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
)
|
|
100
|
-
logger.info(f"Polled {len(resp.events)} events from {node or url!r}")
|
|
153
|
+
resp = self.make_request(node, POLL_EVENTS_PATH, request)
|
|
154
|
+
if type(resp) != ErrorResponse:
|
|
155
|
+
logger.info(f"Polled {len(resp.events)} events from {node!r}")
|
|
101
156
|
return resp
|
|
102
157
|
|
|
103
158
|
def fetch_rids(
|
|
104
159
|
self,
|
|
105
|
-
node: RID
|
|
106
|
-
url: str = None,
|
|
160
|
+
node: RID,
|
|
107
161
|
req: FetchRids | None = None,
|
|
108
162
|
**kwargs
|
|
109
163
|
) -> RidsPayload:
|
|
110
164
|
"""See protocol.api_models.FetchRids for available kwargs."""
|
|
111
165
|
request = req or FetchRids.model_validate(kwargs)
|
|
112
|
-
resp = self.make_request(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
116
|
-
logger.info(f"Fetched {len(resp.rids)} RID(s) from {node or url!r}")
|
|
166
|
+
resp = self.make_request(node, FETCH_RIDS_PATH, request)
|
|
167
|
+
if type(resp) != ErrorResponse:
|
|
168
|
+
logger.info(f"Fetched {len(resp.rids)} RID(s) from {node!r}")
|
|
117
169
|
return resp
|
|
118
170
|
|
|
119
171
|
def fetch_manifests(
|
|
120
172
|
self,
|
|
121
|
-
node: RID
|
|
122
|
-
url: str = None,
|
|
173
|
+
node: RID,
|
|
123
174
|
req: FetchManifests | None = None,
|
|
124
175
|
**kwargs
|
|
125
176
|
) -> ManifestsPayload:
|
|
126
177
|
"""See protocol.api_models.FetchManifests for available kwargs."""
|
|
127
178
|
request = req or FetchManifests.model_validate(kwargs)
|
|
128
|
-
resp = self.make_request(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
)
|
|
132
|
-
logger.info(f"Fetched {len(resp.manifests)} manifest(s) from {node or url!r}")
|
|
179
|
+
resp = self.make_request(node, FETCH_MANIFESTS_PATH, request)
|
|
180
|
+
if type(resp) != ErrorResponse:
|
|
181
|
+
logger.info(f"Fetched {len(resp.manifests)} manifest(s) from {node!r}")
|
|
133
182
|
return resp
|
|
134
183
|
|
|
135
184
|
def fetch_bundles(
|
|
136
185
|
self,
|
|
137
|
-
node: RID
|
|
138
|
-
url: str = None,
|
|
186
|
+
node: RID,
|
|
139
187
|
req: FetchBundles | None = None,
|
|
140
188
|
**kwargs
|
|
141
189
|
) -> BundlesPayload:
|
|
142
190
|
"""See protocol.api_models.FetchBundles for available kwargs."""
|
|
143
191
|
request = req or FetchBundles.model_validate(kwargs)
|
|
144
|
-
resp = self.make_request(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
)
|
|
148
|
-
logger.info(f"Fetched {len(resp.bundles)} bundle(s) from {node or url!r}")
|
|
192
|
+
resp = self.make_request(node, FETCH_BUNDLES_PATH, request)
|
|
193
|
+
if type(resp) != ErrorResponse:
|
|
194
|
+
logger.info(f"Fetched {len(resp.bundles)} bundle(s) from {node!r}")
|
|
149
195
|
return resp
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import httpx
|
|
3
|
+
from rid_lib import RID
|
|
4
|
+
from rid_lib.core import RIDType
|
|
5
|
+
from rid_lib.ext import Cache, Bundle
|
|
6
|
+
from rid_lib.types import KoiNetNode
|
|
7
|
+
|
|
8
|
+
from .graph import NetworkGraph
|
|
9
|
+
from .request_handler import RequestHandler
|
|
10
|
+
from ..protocol.node import NodeProfile, NodeType
|
|
11
|
+
from ..protocol.event import Event
|
|
12
|
+
from ..protocol.api_models import ErrorResponse
|
|
13
|
+
from ..identity import NodeIdentity
|
|
14
|
+
from ..config import NodeConfig
|
|
15
|
+
from ..effector import Effector
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NetworkResolver:
|
|
21
|
+
"""A collection of functions and classes to interact with the KOI network."""
|
|
22
|
+
|
|
23
|
+
config: NodeConfig
|
|
24
|
+
identity: NodeIdentity
|
|
25
|
+
effector: Effector
|
|
26
|
+
cache: Cache
|
|
27
|
+
graph: NetworkGraph
|
|
28
|
+
request_handler: RequestHandler
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
config: NodeConfig,
|
|
33
|
+
cache: Cache,
|
|
34
|
+
identity: NodeIdentity,
|
|
35
|
+
effector: Effector,
|
|
36
|
+
graph: NetworkGraph,
|
|
37
|
+
request_handler: RequestHandler,
|
|
38
|
+
):
|
|
39
|
+
self.config = config
|
|
40
|
+
self.identity = identity
|
|
41
|
+
self.cache = cache
|
|
42
|
+
self.graph = graph
|
|
43
|
+
self.request_handler = request_handler
|
|
44
|
+
self.effector = effector
|
|
45
|
+
|
|
46
|
+
self.poll_event_queue = dict()
|
|
47
|
+
self.webhook_event_queue = dict()
|
|
48
|
+
|
|
49
|
+
def get_state_providers(self, rid_type: RIDType) -> list[KoiNetNode]:
|
|
50
|
+
"""Returns list of node RIDs which provide state for the specified RID type."""
|
|
51
|
+
|
|
52
|
+
logger.debug(f"Looking for state providers of '{rid_type}'")
|
|
53
|
+
provider_nodes = []
|
|
54
|
+
for node_rid in self.cache.list_rids(rid_types=[KoiNetNode]):
|
|
55
|
+
if node_rid == self.identity.rid:
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
node_bundle = self.cache.read(node_rid)
|
|
59
|
+
|
|
60
|
+
node_profile = node_bundle.validate_contents(NodeProfile)
|
|
61
|
+
|
|
62
|
+
if (node_profile.node_type == NodeType.FULL) and (rid_type in node_profile.provides.state):
|
|
63
|
+
logger.debug(f"Found provider {node_rid!r}")
|
|
64
|
+
provider_nodes.append(node_rid)
|
|
65
|
+
|
|
66
|
+
if not provider_nodes:
|
|
67
|
+
logger.debug("Failed to find providers")
|
|
68
|
+
return provider_nodes
|
|
69
|
+
|
|
70
|
+
def fetch_remote_bundle(self, rid: RID) -> tuple[Bundle | None, KoiNetNode | None]:
|
|
71
|
+
"""Attempts to fetch a bundle by RID from known peer nodes."""
|
|
72
|
+
|
|
73
|
+
logger.debug(f"Fetching remote bundle {rid!r}")
|
|
74
|
+
remote_bundle, node_rid = None, None
|
|
75
|
+
for node_rid in self.get_state_providers(type(rid)):
|
|
76
|
+
payload = self.request_handler.fetch_bundles(
|
|
77
|
+
node=node_rid, rids=[rid])
|
|
78
|
+
|
|
79
|
+
if payload.bundles:
|
|
80
|
+
remote_bundle = payload.bundles[0]
|
|
81
|
+
logger.debug(f"Got bundle from {node_rid!r}")
|
|
82
|
+
break
|
|
83
|
+
|
|
84
|
+
if not remote_bundle:
|
|
85
|
+
logger.warning("Failed to fetch remote bundle")
|
|
86
|
+
|
|
87
|
+
return remote_bundle, node_rid
|
|
88
|
+
|
|
89
|
+
def fetch_remote_manifest(self, rid: RID) -> tuple[Bundle | None, KoiNetNode | None]:
|
|
90
|
+
"""Attempts to fetch a manifest by RID from known peer nodes."""
|
|
91
|
+
|
|
92
|
+
logger.debug(f"Fetching remote manifest {rid!r}")
|
|
93
|
+
remote_manifest, node_rid = None, None
|
|
94
|
+
for node_rid in self.get_state_providers(type(rid)):
|
|
95
|
+
payload = self.request_handler.fetch_manifests(
|
|
96
|
+
node=node_rid, rids=[rid])
|
|
97
|
+
|
|
98
|
+
if payload.manifests:
|
|
99
|
+
remote_manifest = payload.manifests[0]
|
|
100
|
+
logger.debug(f"Got bundle from {node_rid!r}")
|
|
101
|
+
break
|
|
102
|
+
|
|
103
|
+
if not remote_manifest:
|
|
104
|
+
logger.warning("Failed to fetch remote bundle")
|
|
105
|
+
|
|
106
|
+
return remote_manifest, node_rid
|
|
107
|
+
|
|
108
|
+
def poll_neighbors(self) -> dict[KoiNetNode, list[Event]]:
|
|
109
|
+
"""Polls all neighboring nodes and returns compiled list of events.
|
|
110
|
+
|
|
111
|
+
If this node has no neighbors, it will instead attempt to poll the provided first contact URL.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
graph_neighbors = self.graph.get_neighbors()
|
|
115
|
+
neighbors = []
|
|
116
|
+
|
|
117
|
+
if graph_neighbors:
|
|
118
|
+
for node_rid in graph_neighbors:
|
|
119
|
+
node_bundle = self.cache.read(node_rid)
|
|
120
|
+
if not node_bundle:
|
|
121
|
+
continue
|
|
122
|
+
node_profile = node_bundle.validate_contents(NodeProfile)
|
|
123
|
+
if node_profile.node_type != NodeType.FULL:
|
|
124
|
+
continue
|
|
125
|
+
neighbors.append(node_rid)
|
|
126
|
+
|
|
127
|
+
elif self.config.koi_net.first_contact.rid:
|
|
128
|
+
neighbors.append(self.config.koi_net.first_contact.rid)
|
|
129
|
+
|
|
130
|
+
event_dict = dict()
|
|
131
|
+
for node_rid in neighbors:
|
|
132
|
+
try:
|
|
133
|
+
payload = self.request_handler.poll_events(
|
|
134
|
+
node=node_rid,
|
|
135
|
+
rid=self.identity.rid
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if type(payload) == ErrorResponse:
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
if payload.events:
|
|
142
|
+
logger.debug(f"Received {len(payload.events)} events from {node_rid!r}")
|
|
143
|
+
|
|
144
|
+
event_dict[node_rid] = payload.events
|
|
145
|
+
|
|
146
|
+
except httpx.ConnectError:
|
|
147
|
+
logger.debug(f"Failed to reach node {node_rid!r}")
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
return event_dict
|
|
@@ -2,6 +2,7 @@ import logging
|
|
|
2
2
|
from rid_lib import RID
|
|
3
3
|
from rid_lib.ext import Manifest, Cache
|
|
4
4
|
from rid_lib.ext.bundle import Bundle
|
|
5
|
+
|
|
5
6
|
from ..protocol.api_models import (
|
|
6
7
|
RidsPayload,
|
|
7
8
|
ManifestsPayload,
|
|
@@ -10,6 +11,7 @@ from ..protocol.api_models import (
|
|
|
10
11
|
FetchManifests,
|
|
11
12
|
FetchBundles,
|
|
12
13
|
)
|
|
14
|
+
from ..effector import Effector
|
|
13
15
|
|
|
14
16
|
logger = logging.getLogger(__name__)
|
|
15
17
|
|
|
@@ -18,9 +20,15 @@ class ResponseHandler:
|
|
|
18
20
|
"""Handles generating responses to requests from other KOI nodes."""
|
|
19
21
|
|
|
20
22
|
cache: Cache
|
|
23
|
+
effector: Effector
|
|
21
24
|
|
|
22
|
-
def __init__(
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
cache: Cache,
|
|
28
|
+
effector: Effector,
|
|
29
|
+
):
|
|
23
30
|
self.cache = cache
|
|
31
|
+
self.effector = effector
|
|
24
32
|
|
|
25
33
|
def fetch_rids(self, req: FetchRids) -> RidsPayload:
|
|
26
34
|
logger.info(f"Request to fetch rids, allowed types {req.rid_types}")
|
|
@@ -35,7 +43,7 @@ class ResponseHandler:
|
|
|
35
43
|
not_found: list[RID] = []
|
|
36
44
|
|
|
37
45
|
for rid in (req.rids or self.cache.list_rids(req.rid_types)):
|
|
38
|
-
bundle = self.
|
|
46
|
+
bundle = self.effector.deref(rid)
|
|
39
47
|
if bundle:
|
|
40
48
|
manifests.append(bundle.manifest)
|
|
41
49
|
else:
|
|
@@ -50,7 +58,7 @@ class ResponseHandler:
|
|
|
50
58
|
not_found: list[RID] = []
|
|
51
59
|
|
|
52
60
|
for rid in req.rids:
|
|
53
|
-
bundle = self.
|
|
61
|
+
bundle = self.effector.deref(rid)
|
|
54
62
|
if bundle:
|
|
55
63
|
bundles.append(bundle)
|
|
56
64
|
else:
|
koi_net/poller.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
|
|
2
|
+
import time
|
|
3
|
+
import logging
|
|
4
|
+
from .processor.interface import ProcessorInterface
|
|
5
|
+
from .lifecycle import NodeLifecycle
|
|
6
|
+
from .network.resolver import NetworkResolver
|
|
7
|
+
from .config import NodeConfig
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NodePoller:
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
processor: ProcessorInterface,
|
|
16
|
+
lifecycle: NodeLifecycle,
|
|
17
|
+
resolver: NetworkResolver,
|
|
18
|
+
config: NodeConfig
|
|
19
|
+
):
|
|
20
|
+
self.processor = processor
|
|
21
|
+
self.lifecycle = lifecycle
|
|
22
|
+
self.resolver = resolver
|
|
23
|
+
self.config = config
|
|
24
|
+
|
|
25
|
+
def run(self):
|
|
26
|
+
try:
|
|
27
|
+
self.lifecycle.start()
|
|
28
|
+
while True:
|
|
29
|
+
start_time = time.time()
|
|
30
|
+
neighbors = self.resolver.poll_neighbors()
|
|
31
|
+
for node_rid in neighbors:
|
|
32
|
+
for event in neighbors[node_rid]:
|
|
33
|
+
self.processor.handle(event=event, source=node_rid)
|
|
34
|
+
self.processor.flush_kobj_queue()
|
|
35
|
+
|
|
36
|
+
elapsed = time.time() - start_time
|
|
37
|
+
sleep_time = self.config.koi_net.polling_interval - elapsed
|
|
38
|
+
if sleep_time > 0:
|
|
39
|
+
time.sleep(sleep_time)
|
|
40
|
+
|
|
41
|
+
except KeyboardInterrupt:
|
|
42
|
+
logger.info("Polling interrupted by user.")
|
|
43
|
+
|
|
44
|
+
finally:
|
|
45
|
+
self.lifecycle.stop()
|
koi_net/processor/__init__.py
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .interface import ProcessorInterface
|