koi-net 1.0.0b9__py3-none-any.whl → 1.0.0b10__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/core.py CHANGED
@@ -84,15 +84,15 @@ class NodeInterface:
84
84
  )
85
85
  )
86
86
 
87
- logger.info("Waiting for kobj queue to empty")
87
+ logger.debug("Waiting for kobj queue to empty")
88
88
  if self.use_kobj_processor_thread:
89
89
  self.processor.kobj_queue.join()
90
90
  else:
91
91
  self.processor.flush_kobj_queue()
92
- logger.info("Done")
92
+ logger.debug("Done")
93
93
 
94
94
  if not self.network.graph.get_neighbors() and self.first_contact:
95
- logger.info(f"I don't have any neighbors, reaching out to first contact {self.first_contact}")
95
+ logger.debug(f"I don't have any neighbors, reaching out to first contact {self.first_contact}")
96
96
 
97
97
  events = [
98
98
  Event.from_rid(EventType.FORGET, self.identity.rid),
@@ -106,7 +106,7 @@ class NodeInterface:
106
106
  )
107
107
 
108
108
  except httpx.ConnectError:
109
- logger.info("Failed to reach first contact")
109
+ logger.warning("Failed to reach first contact")
110
110
  return
111
111
 
112
112
 
@@ -118,7 +118,7 @@ class NodeInterface:
118
118
  logger.info("Stopping node...")
119
119
 
120
120
  if self.use_kobj_processor_thread:
121
- logger.info("Waiting for kobj queue to empty")
121
+ logger.info(f"Waiting for kobj queue to empty ({self.processor.kobj_queue.unfinished_tasks} tasks remaining)")
122
122
  self.processor.kobj_queue.join()
123
123
  else:
124
124
  self.processor.flush_kobj_queue()
koi_net/network/graph.py CHANGED
@@ -25,12 +25,12 @@ class NetworkGraph:
25
25
 
26
26
  def generate(self):
27
27
  """Generates directed graph from cached KOI nodes and edges."""
28
- logger.info("Generating network graph")
28
+ logger.debug("Generating network graph")
29
29
  self.dg.clear()
30
30
  for rid in self.cache.list_rids():
31
31
  if type(rid) == KoiNetNode:
32
32
  self.dg.add_node(rid)
33
- logger.info(f"Added node {rid}")
33
+ logger.debug(f"Added node {rid}")
34
34
 
35
35
  elif type(rid) == KoiNetEdge:
36
36
  edge_profile = self.get_edge_profile(rid)
@@ -38,8 +38,8 @@ class NetworkGraph:
38
38
  logger.warning(f"Failed to load {rid!r}")
39
39
  continue
40
40
  self.dg.add_edge(edge_profile.source, edge_profile.target, rid=rid)
41
- logger.info(f"Added edge {rid} ({edge_profile.source} -> {edge_profile.target})")
42
- logger.info("Done")
41
+ logger.debug(f"Added edge {rid} ({edge_profile.source} -> {edge_profile.target})")
42
+ logger.debug("Done")
43
43
 
44
44
  def get_node_profile(self, rid: KoiNetNode) -> NodeProfile | None:
45
45
  """Returns node profile given its RID."""
@@ -100,7 +100,7 @@ class NetworkInterface:
100
100
 
101
101
  Event will be sent to webhook or poll queue depending on the node type and edge type of the specified node. If `flush` is set to `True`, the webhook queued will be flushed after pushing the event.
102
102
  """
103
- logger.info(f"Pushing event {event.event_type} {event.rid} to {node}")
103
+ logger.debug(f"Pushing event {event.event_type} {event.rid} to {node}")
104
104
 
105
105
  node_profile = self.graph.get_node_profile(node)
106
106
  if not node_profile:
@@ -136,14 +136,14 @@ class NetworkInterface:
136
136
  if queue:
137
137
  while not queue.empty():
138
138
  event = queue.get()
139
- logger.info(f"Dequeued {event.event_type} '{event.rid}'")
139
+ logger.debug(f"Dequeued {event.event_type} '{event.rid}'")
140
140
  events.append(event)
141
141
 
142
142
  return events
143
143
 
144
144
  def flush_poll_queue(self, node: KoiNetNode) -> list[Event]:
145
145
  """Flushes a node's poll queue, returning list of events."""
146
- logger.info(f"Flushing poll queue for {node}")
146
+ logger.debug(f"Flushing poll queue for {node}")
147
147
  return self._flush_queue(self.poll_event_queue, node)
148
148
 
149
149
  def flush_webhook_queue(self, node: KoiNetNode):
@@ -152,7 +152,7 @@ class NetworkInterface:
152
152
  If node profile is unknown, or node type is not `FULL`, this operation will fail silently. If the remote node cannot be reached, all events will be requeued.
153
153
  """
154
154
 
155
- logger.info(f"Flushing webhook queue for {node}")
155
+ logger.debug(f"Flushing webhook queue for {node}")
156
156
 
157
157
  node_profile = self.graph.get_node_profile(node)
158
158
 
@@ -167,7 +167,7 @@ class NetworkInterface:
167
167
  events = self._flush_queue(self.webhook_event_queue, node)
168
168
  if not events: return
169
169
 
170
- logger.info(f"Broadcasting {len(events)} events")
170
+ logger.debug(f"Broadcasting {len(events)} events")
171
171
 
172
172
  try:
173
173
  self.request_handler.broadcast_events(node, events=events)
@@ -179,23 +179,23 @@ class NetworkInterface:
179
179
  def get_state_providers(self, rid_type: RIDType) -> list[KoiNetNode]:
180
180
  """Returns list of node RIDs which provide state for the specified RID type."""
181
181
 
182
- logger.info(f"Looking for state providers of '{rid_type}'")
182
+ logger.debug(f"Looking for state providers of '{rid_type}'")
183
183
  provider_nodes = []
184
184
  for node_rid in self.cache.list_rids(rid_types=[KoiNetNode]):
185
185
  node = self.graph.get_node_profile(node_rid)
186
186
 
187
187
  if node.node_type == NodeType.FULL and rid_type in node.provides.state:
188
- logger.info(f"Found provider '{node_rid}'")
188
+ logger.debug(f"Found provider '{node_rid}'")
189
189
  provider_nodes.append(node_rid)
190
190
 
191
191
  if not provider_nodes:
192
- logger.info("Failed to find providers")
192
+ logger.debug("Failed to find providers")
193
193
  return provider_nodes
194
194
 
195
195
  def fetch_remote_bundle(self, rid: RID):
196
196
  """Attempts to fetch a bundle by RID from known peer nodes."""
197
197
 
198
- logger.info(f"Fetching remote bundle '{rid}'")
198
+ logger.debug(f"Fetching remote bundle '{rid}'")
199
199
  remote_bundle = None
200
200
  for node_rid in self.get_state_providers(type(rid)):
201
201
  payload = self.request_handler.fetch_bundles(
@@ -203,7 +203,7 @@ class NetworkInterface:
203
203
 
204
204
  if payload.bundles:
205
205
  remote_bundle = payload.bundles[0]
206
- logger.info(f"Got bundle from '{node_rid}'")
206
+ logger.debug(f"Got bundle from '{node_rid}'")
207
207
  break
208
208
 
209
209
  if not remote_bundle:
@@ -214,7 +214,7 @@ class NetworkInterface:
214
214
  def fetch_remote_manifest(self, rid: RID):
215
215
  """Attempts to fetch a manifest by RID from known peer nodes."""
216
216
 
217
- logger.info(f"Fetching remote manifest '{rid}'")
217
+ logger.debug(f"Fetching remote manifest '{rid}'")
218
218
  remote_manifest = None
219
219
  for node_rid in self.get_state_providers(type(rid)):
220
220
  payload = self.request_handler.fetch_manifests(
@@ -222,7 +222,7 @@ class NetworkInterface:
222
222
 
223
223
  if payload.manifests:
224
224
  remote_manifest = payload.manifests[0]
225
- logger.info(f"Got bundle from '{node_rid}'")
225
+ logger.debug(f"Got bundle from '{node_rid}'")
226
226
  break
227
227
 
228
228
  if not remote_manifest:
@@ -239,17 +239,17 @@ class NetworkInterface:
239
239
  neighbors = self.graph.get_neighbors()
240
240
 
241
241
  if not neighbors and self.first_contact:
242
- logger.info("No neighbors found, polling first contact")
242
+ logger.debug("No neighbors found, polling first contact")
243
243
  try:
244
244
  payload = self.request_handler.poll_events(
245
245
  url=self.first_contact,
246
246
  rid=self.identity.rid
247
247
  )
248
248
  if payload.events:
249
- logger.info(f"Received {len(payload.events)} events from '{self.first_contact}'")
249
+ logger.debug(f"Received {len(payload.events)} events from '{self.first_contact}'")
250
250
  return payload.events
251
251
  except httpx.ConnectError:
252
- logger.info(f"Failed to reach first contact '{self.first_contact}'")
252
+ logger.debug(f"Failed to reach first contact '{self.first_contact}'")
253
253
 
254
254
  events = []
255
255
  for node_rid in neighbors:
@@ -263,10 +263,10 @@ class NetworkInterface:
263
263
  rid=self.identity.rid
264
264
  )
265
265
  if payload.events:
266
- logger.info(f"Received {len(payload.events)} events from {node_rid!r}")
266
+ logger.debug(f"Received {len(payload.events)} events from {node_rid!r}")
267
267
  events.extend(payload.events)
268
268
  except httpx.ConnectError:
269
- logger.info(f"Failed to reach node '{node_rid}'")
269
+ logger.debug(f"Failed to reach node '{node_rid}'")
270
270
  continue
271
271
 
272
272
  return events
@@ -1,6 +1,5 @@
1
1
  import logging
2
2
  import httpx
3
- from pydantic import BaseModel
4
3
  from rid_lib import RID
5
4
  from rid_lib.ext import Cache
6
5
  from rid_lib.types.koi_net_node import KoiNetNode
@@ -46,7 +45,7 @@ class RequestHandler:
46
45
  request: RequestModels,
47
46
  response_model: type[ResponseModels] | None = None
48
47
  ) -> ResponseModels | None:
49
- logger.info(f"Making request to {url}")
48
+ logger.debug(f"Making request to {url}")
50
49
  resp = httpx.post(
51
50
  url=url,
52
51
  data=request.model_dump_json()
@@ -66,7 +65,7 @@ class RequestHandler:
66
65
  raise Exception("Node not found")
67
66
  if node_profile.node_type != NodeType.FULL:
68
67
  raise Exception("Can't query partial node")
69
- logger.info(f"Resolved {node_rid!r} to {node_profile.base_url}")
68
+ logger.debug(f"Resolved {node_rid!r} to {node_profile.base_url}")
70
69
  return node_profile.base_url
71
70
  else:
72
71
  return url
@@ -79,10 +78,11 @@ class RequestHandler:
79
78
  **kwargs
80
79
  ) -> None:
81
80
  """See protocol.api_models.EventsPayload for available kwargs."""
81
+ request = req or EventsPayload.model_validate(kwargs)
82
82
  self.make_request(
83
- self.get_url(node, url) + BROADCAST_EVENTS_PATH,
84
- req or EventsPayload.model_validate(kwargs)
83
+ self.get_url(node, url) + BROADCAST_EVENTS_PATH, request
85
84
  )
85
+ logger.info(f"Broadcasted {len(request.events)} event(s) to {node or url!r}")
86
86
 
87
87
  def poll_events(
88
88
  self,
@@ -92,12 +92,14 @@ class RequestHandler:
92
92
  **kwargs
93
93
  ) -> EventsPayload:
94
94
  """See protocol.api_models.PollEvents for available kwargs."""
95
- return self.make_request(
96
- self.get_url(node, url) + POLL_EVENTS_PATH,
97
- req or PollEvents.model_validate(kwargs),
95
+ request = req or PollEvents.model_validate(kwargs)
96
+ resp = self.make_request(
97
+ self.get_url(node, url) + POLL_EVENTS_PATH, request,
98
98
  response_model=EventsPayload
99
99
  )
100
-
100
+ logger.info(f"Polled {len(resp.events)} events from {node or url!r}")
101
+ return resp
102
+
101
103
  def fetch_rids(
102
104
  self,
103
105
  node: RID = None,
@@ -106,11 +108,13 @@ class RequestHandler:
106
108
  **kwargs
107
109
  ) -> RidsPayload:
108
110
  """See protocol.api_models.FetchRids for available kwargs."""
109
- return self.make_request(
110
- self.get_url(node, url) + FETCH_RIDS_PATH,
111
- req or FetchRids.model_validate(kwargs),
111
+ request = req or FetchRids.model_validate(kwargs)
112
+ resp = self.make_request(
113
+ self.get_url(node, url) + FETCH_RIDS_PATH, request,
112
114
  response_model=RidsPayload
113
115
  )
116
+ logger.info(f"Fetched {len(resp.rids)} RID(s) from {node or url!r}")
117
+ return resp
114
118
 
115
119
  def fetch_manifests(
116
120
  self,
@@ -120,11 +124,13 @@ class RequestHandler:
120
124
  **kwargs
121
125
  ) -> ManifestsPayload:
122
126
  """See protocol.api_models.FetchManifests for available kwargs."""
123
- return self.make_request(
124
- self.get_url(node, url) + FETCH_MANIFESTS_PATH,
125
- req or FetchManifests.model_validate(kwargs),
127
+ request = req or FetchManifests.model_validate(kwargs)
128
+ resp = self.make_request(
129
+ self.get_url(node, url) + FETCH_MANIFESTS_PATH, request,
126
130
  response_model=ManifestsPayload
127
131
  )
132
+ logger.info(f"Fetched {len(resp.manifests)} manifest(s) from {node or url!r}")
133
+ return resp
128
134
 
129
135
  def fetch_bundles(
130
136
  self,
@@ -134,8 +140,10 @@ class RequestHandler:
134
140
  **kwargs
135
141
  ) -> BundlesPayload:
136
142
  """See protocol.api_models.FetchBundles for available kwargs."""
137
- return self.make_request(
138
- self.get_url(node, url) + FETCH_BUNDLES_PATH,
139
- req or FetchBundles.model_validate(kwargs),
143
+ request = req or FetchBundles.model_validate(kwargs)
144
+ resp = self.make_request(
145
+ self.get_url(node, url) + FETCH_BUNDLES_PATH, request,
140
146
  response_model=BundlesPayload
141
- )
147
+ )
148
+ logger.info(f"Fetched {len(resp.bundles)} bundle(s) from {node or url!r}")
149
+ return resp
@@ -22,7 +22,7 @@ def basic_rid_handler(processor: ProcessorInterface, kobj: KnowledgeObject):
22
22
  """
23
23
  if (kobj.rid == processor.identity.rid and
24
24
  kobj.source == KnowledgeSource.External):
25
- logger.info("Don't let anyone else tell me who I am!")
25
+ logger.debug("Don't let anyone else tell me who I am!")
26
26
  return STOP_CHAIN
27
27
 
28
28
  if kobj.event_type == EventType.FORGET:
@@ -41,17 +41,17 @@ def basic_manifest_handler(processor: ProcessorInterface, kobj: KnowledgeObject)
41
41
 
42
42
  if prev_bundle:
43
43
  if kobj.manifest.sha256_hash == prev_bundle.manifest.sha256_hash:
44
- logger.info("Hash of incoming manifest is same as existing knowledge, ignoring")
44
+ logger.debug("Hash of incoming manifest is same as existing knowledge, ignoring")
45
45
  return STOP_CHAIN
46
46
  if kobj.manifest.timestamp <= prev_bundle.manifest.timestamp:
47
- logger.info("Timestamp of incoming manifest is the same or older than existing knowledge, ignoring")
47
+ logger.debug("Timestamp of incoming manifest is the same or older than existing knowledge, ignoring")
48
48
  return STOP_CHAIN
49
49
 
50
- logger.info("RID previously known to me, labeling as 'UPDATE'")
50
+ logger.debug("RID previously known to me, labeling as 'UPDATE'")
51
51
  kobj.normalized_event_type = EventType.UPDATE
52
52
 
53
53
  else:
54
- logger.info("RID previously unknown to me, labeling as 'NEW'")
54
+ logger.debug("RID previously unknown to me, labeling as 'NEW'")
55
55
  kobj.normalized_event_type = EventType.NEW
56
56
 
57
57
  return kobj
@@ -77,7 +77,7 @@ def edge_negotiation_handler(processor: ProcessorInterface, kobj: KnowledgeObjec
77
77
  if edge_profile.status != EdgeStatus.PROPOSED:
78
78
  return
79
79
 
80
- logger.info("Handling edge negotiation")
80
+ logger.debug("Handling edge negotiation")
81
81
 
82
82
  peer_rid = edge_profile.target
83
83
  peer_profile = processor.network.graph.get_node_profile(peer_rid)
@@ -96,11 +96,11 @@ def edge_negotiation_handler(processor: ProcessorInterface, kobj: KnowledgeObjec
96
96
  abort = False
97
97
  if (edge_profile.edge_type == EdgeType.WEBHOOK and
98
98
  peer_profile.node_type == NodeType.PARTIAL):
99
- logger.info("Partial nodes cannot use webhooks")
99
+ logger.debug("Partial nodes cannot use webhooks")
100
100
  abort = True
101
101
 
102
102
  if not set(edge_profile.rid_types).issubset(provided_events):
103
- logger.info("Requested RID types not provided by this node")
103
+ logger.debug("Requested RID types not provided by this node")
104
104
  abort = True
105
105
 
106
106
  if abort:
@@ -110,16 +110,16 @@ def edge_negotiation_handler(processor: ProcessorInterface, kobj: KnowledgeObjec
110
110
 
111
111
  else:
112
112
  # approve edge profile
113
- logger.info("Approving proposed edge")
113
+ logger.debug("Approving proposed edge")
114
114
  edge_profile.status = EdgeStatus.APPROVED
115
115
  updated_bundle = Bundle.generate(kobj.rid, edge_profile.model_dump())
116
116
 
117
- processor.handle(bundle=updated_bundle)
117
+ processor.handle(bundle=updated_bundle, event_type=EventType.UPDATE)
118
118
  return
119
119
 
120
120
  elif edge_profile.target == processor.identity.rid:
121
121
  if edge_profile.status == EdgeStatus.APPROVED:
122
- logger.info("Edge approved by other node!")
122
+ logger.debug("Edge approved by other node!")
123
123
 
124
124
 
125
125
  # Network handlers
@@ -140,12 +140,12 @@ def basic_network_output_filter(processor: ProcessorInterface, kobj: KnowledgeOb
140
140
  edge_profile = kobj.bundle.validate_contents(EdgeProfile)
141
141
 
142
142
  if edge_profile.source == processor.identity.rid:
143
- logger.info(f"Adding edge target '{edge_profile.target!r}' to network targets")
143
+ logger.debug(f"Adding edge target '{edge_profile.target!r}' to network targets")
144
144
  kobj.network_targets.update([edge_profile.target])
145
145
  involves_me = True
146
146
 
147
147
  elif edge_profile.target == processor.identity.rid:
148
- logger.info(f"Adding edge source '{edge_profile.source!r}' to network targets")
148
+ logger.debug(f"Adding edge source '{edge_profile.source!r}' to network targets")
149
149
  kobj.network_targets.update([edge_profile.source])
150
150
  involves_me = True
151
151
 
@@ -156,7 +156,7 @@ def basic_network_output_filter(processor: ProcessorInterface, kobj: KnowledgeOb
156
156
  allowed_type=type(kobj.rid)
157
157
  )
158
158
 
159
- logger.info(f"Updating network targets with '{type(kobj.rid)}' subscribers: {subscribers}")
159
+ logger.debug(f"Updating network targets with '{type(kobj.rid)}' subscribers: {subscribers}")
160
160
  kobj.network_targets.update(subscribers)
161
161
 
162
162
  return kobj
@@ -101,12 +101,12 @@ class ProcessorInterface:
101
101
  if handler.event_types and kobj.event_type not in handler.event_types:
102
102
  continue
103
103
 
104
- logger.info(f"Calling {handler_type} handler '{handler.func.__name__}'")
104
+ logger.debug(f"Calling {handler_type} handler '{handler.func.__name__}'")
105
105
  resp = handler.func(self, kobj.model_copy())
106
106
 
107
107
  # stops handler chain execution
108
108
  if resp is STOP_CHAIN:
109
- logger.info(f"Handler chain stopped by {handler.func.__name__}")
109
+ logger.debug(f"Handler chain stopped by {handler.func.__name__}")
110
110
  return STOP_CHAIN
111
111
  # kobj unmodified
112
112
  elif resp is None:
@@ -114,7 +114,7 @@ class ProcessorInterface:
114
114
  # kobj modified by handler
115
115
  elif isinstance(resp, KnowledgeObject):
116
116
  kobj = resp
117
- logger.info(f"Knowledge object modified by {handler.func.__name__}")
117
+ logger.debug(f"Knowledge object modified by {handler.func.__name__}")
118
118
  else:
119
119
  raise ValueError(f"Handler {handler.func.__name__} returned invalid response '{resp}'")
120
120
 
@@ -134,34 +134,41 @@ class ProcessorInterface:
134
134
  The pipeline may be stopped by any point by a single handler returning the `STOP_CHAIN` sentinel. In that case, the process will exit immediately. Further handlers of that type and later handler chains will not be called.
135
135
  """
136
136
 
137
- logger.info(f"Handling {kobj!r}")
137
+ logger.debug(f"Handling {kobj!r}")
138
138
  kobj = self.call_handler_chain(HandlerType.RID, kobj)
139
139
  if kobj is STOP_CHAIN: return
140
140
 
141
141
  if kobj.event_type == EventType.FORGET:
142
142
  bundle = self.cache.read(kobj.rid)
143
143
  if not bundle:
144
- logger.info("Local bundle not found")
144
+ logger.debug("Local bundle not found")
145
145
  return
146
146
 
147
147
  # the bundle (to be deleted) attached to kobj for downstream analysis
148
- logger.info("Adding local bundle (to be deleted) to knowledge object")
148
+ logger.debug("Adding local bundle (to be deleted) to knowledge object")
149
149
  kobj.manifest = bundle.manifest
150
150
  kobj.contents = bundle.contents
151
151
 
152
152
  else:
153
153
  # attempt to retrieve manifest
154
154
  if not kobj.manifest:
155
+ logger.debug("Manifest not found")
155
156
  if kobj.source == KnowledgeSource.External:
156
- logger.info("Manifest not found, attempting to fetch remotely")
157
+ logger.debug("Attempting to fetch remote manifest")
157
158
  manifest = self.network.fetch_remote_manifest(kobj.rid)
158
- if not manifest: return
159
159
 
160
160
  elif kobj.source == KnowledgeSource.Internal:
161
- logger.info("Manifest not found, attempting to read cache")
161
+ logger.debug("Attempting to read manifest from cache")
162
162
  bundle = self.cache.read(kobj.rid)
163
- if not bundle: return
164
- manifest = bundle.manifest
163
+ if bundle:
164
+ manifest = bundle.manifest
165
+ else:
166
+ manifest = None
167
+ return
168
+
169
+ if not manifest:
170
+ logger.debug("Failed to find manifest")
171
+ return
165
172
 
166
173
  kobj.manifest = manifest
167
174
 
@@ -170,19 +177,22 @@ class ProcessorInterface:
170
177
 
171
178
  # attempt to retrieve bundle
172
179
  if not kobj.bundle:
180
+ logger.debug("Bundle not found")
173
181
  if kobj.source == KnowledgeSource.External:
174
- logger.info("Bundle not found, attempting to fetch")
182
+ logger.debug("Attempting to fetch remote bundle")
175
183
  bundle = self.network.fetch_remote_bundle(kobj.rid)
176
- # TODO: WARNING MANIFEST MAY BE DIFFERENT
177
184
 
178
185
  elif kobj.source == KnowledgeSource.Internal:
179
- logger.info("Bundle not found, attempting to read cache")
186
+ logger.debug("Attempting to read bundle from cache")
180
187
  bundle = self.cache.read(kobj.rid)
181
188
 
182
189
  if kobj.manifest != bundle.manifest:
183
190
  logger.warning("Retrieved bundle contains a different manifest")
184
191
 
185
- if not bundle: return
192
+ if not bundle:
193
+ logger.debug("Failed to find bundle")
194
+ return
195
+
186
196
  kobj.manifest = bundle.manifest
187
197
  kobj.contents = bundle.contents
188
198
 
@@ -190,28 +200,28 @@ class ProcessorInterface:
190
200
  if kobj is STOP_CHAIN: return
191
201
 
192
202
  if kobj.normalized_event_type in (EventType.UPDATE, EventType.NEW):
193
- logger.info(f"Writing {kobj!r} to cache")
203
+ logger.info(f"Writing to cache: {kobj!r}")
194
204
  self.cache.write(kobj.bundle)
195
205
 
196
206
  elif kobj.normalized_event_type == EventType.FORGET:
197
- logger.info(f"Deleting {kobj!r} from cache")
207
+ logger.info(f"Deleting from cache: {kobj!r}")
198
208
  self.cache.delete(kobj.rid)
199
209
 
200
210
  else:
201
- logger.info("Normalized event type was never set, no cache or network operations will occur")
211
+ logger.debug("Normalized event type was never set, no cache or network operations will occur")
202
212
  return
203
213
 
204
214
  if type(kobj.rid) in (KoiNetNode, KoiNetEdge):
205
- logger.info("Change to node or edge, regenerating network graph")
215
+ logger.debug("Change to node or edge, regenerating network graph")
206
216
  self.network.graph.generate()
207
217
 
208
218
  kobj = self.call_handler_chain(HandlerType.Network, kobj)
209
219
  if kobj is STOP_CHAIN: return
210
220
 
211
221
  if kobj.network_targets:
212
- logger.info(f"Broadcasting event to {len(kobj.network_targets)} network target(s)")
222
+ logger.debug(f"Broadcasting event to {len(kobj.network_targets)} network target(s)")
213
223
  else:
214
- logger.info("No network targets set")
224
+ logger.debug("No network targets set")
215
225
 
216
226
  for node in kobj.network_targets:
217
227
  self.network.push_event_to(kobj.normalized_event, node, flush=True)
@@ -228,18 +238,25 @@ class ProcessorInterface:
228
238
 
229
239
  while not self.kobj_queue.empty():
230
240
  kobj = self.kobj_queue.get()
231
- logger.info(f"Dequeued {kobj!r}")
232
- self.process_kobj(kobj)
233
- self.kobj_queue.task_done()
234
- logger.info("Done handling")
241
+ logger.debug(f"Dequeued {kobj!r}")
242
+
243
+ try:
244
+ self.process_kobj(kobj)
245
+ finally:
246
+ self.kobj_queue.task_done()
247
+ logger.debug("Done")
235
248
 
236
249
  def kobj_processor_worker(self, timeout=0.1):
237
250
  while True:
238
251
  try:
239
252
  kobj = self.kobj_queue.get(timeout=timeout)
240
- logger.info(f"Dequeued {kobj!r}")
241
- self.process_kobj(kobj)
242
- self.kobj_queue.task_done()
253
+ logger.debug(f"Dequeued {kobj!r}")
254
+
255
+ try:
256
+ self.process_kobj(kobj)
257
+ finally:
258
+ self.kobj_queue.task_done()
259
+ logger.debug("Done")
243
260
 
244
261
  except queue.Empty:
245
262
  pass
@@ -275,4 +292,4 @@ class ProcessorInterface:
275
292
  raise ValueError("One of 'rid', 'manifest', 'bundle', 'event', or 'kobj' must be provided")
276
293
 
277
294
  self.kobj_queue.put(_kobj)
278
- logger.info(f"Queued {_kobj!r}")
295
+ logger.debug(f"Queued {_kobj!r}")
@@ -35,7 +35,7 @@ class KnowledgeObject(BaseModel):
35
35
  network_targets: set[KoiNetNode] = set()
36
36
 
37
37
  def __repr__(self):
38
- return f"<Knowledge Object '{self.rid}' ({self.event_type}) -> ({self.normalized_event_type})>"
38
+ return f"<KObj '{self.rid}' event type: '{self.event_type}' -> '{self.normalized_event_type}', source: '{self.source}'>"
39
39
 
40
40
  @classmethod
41
41
  def from_rid(
koi_net/protocol/event.py CHANGED
@@ -15,6 +15,9 @@ class Event(BaseModel):
15
15
  manifest: Manifest | None = None
16
16
  contents: dict | None = None
17
17
 
18
+ def __repr__(self):
19
+ return f"<Event '{self.rid}' event type: '{self.event_type}'>"
20
+
18
21
  @classmethod
19
22
  def from_bundle(cls, event_type: EventType, bundle: Bundle):
20
23
  return cls(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: koi-net
3
- Version: 1.0.0b9
3
+ Version: 1.0.0b10
4
4
  Summary: Implementation of KOI-net protocol in Python
5
5
  Project-URL: Homepage, https://github.com/BlockScience/koi-net/
6
6
  Author-email: Luke Miller <luke@block.science>
@@ -44,6 +44,37 @@ Description-Content-Type: text/markdown
44
44
 
45
45
  *This specification is the result of several iterations of KOI research, [read more here](https://github.com/BlockScience/koi).*
46
46
 
47
+ ### Jump to Sections:
48
+ - [Protocol](#protocol)
49
+ - [Introduction](#introduction)
50
+ - [Communication Methods](#communication-methods)
51
+ - [Quickstart](#quickstart)
52
+ - [Setup](#setup)
53
+ - [Creating a Node](#creating-a-node)
54
+ - [Knowledge Processing](#knowledge-processing)
55
+ - [Try It Out!](#try-it-out)
56
+ - [Advanced](#advanced)
57
+ - [Knowledge Processing Pipeline](#knowledge-processing-pipeline)
58
+ - [Knowledge Handlers](#knowledge-handlers)
59
+ - [RID Handler](#rid-handler)
60
+ - [Manifest Handler](#manifest-handler)
61
+ - [Bundle Handler](#bundle-handler)
62
+ - [Network Handler](#network-handler)
63
+ - [Final Handler](#final-handler)
64
+ - [Registering Handlers](#registering-handlers)
65
+ - [Default Behavior](#default-behavior)
66
+ - [Implementation Reference](#implementation-reference)
67
+ - [Node Interface](#node-interface)
68
+ - [Node Identity](#node-identity)
69
+ - [Network Interface](#network-interface)
70
+ - [Network Graph](#network-graph)
71
+ - [Request Handler](#request-handler)
72
+ - [Response Handler](#response-handler)
73
+ - [Processor Interface](#processor-interface)
74
+ - [Development](#development)
75
+ - [Setup](#setup-1)
76
+ - [Distribution](#distribution)
77
+
47
78
  # Protocol
48
79
  ## Introduction
49
80
 
@@ -653,5 +684,5 @@ python -m build
653
684
  ```
654
685
  Push new package build to PyPI:
655
686
  ```shell
656
- python -m twine upload dist/*
687
+ python -m twine upload --skip-existing dist/*
657
688
  ```
@@ -0,0 +1,24 @@
1
+ koi_net/__init__.py,sha256=b0Ze0pZmJAuygpWUFHM6Kvqo3DkU_uzmkptv1EpAArw,31
2
+ koi_net/core.py,sha256=9Paw7GTf3JeE8X73fGgi9K2uoT2A20Ft4m_gA9xAepI,4683
3
+ koi_net/identity.py,sha256=PBgmAx5f3zzQmHASB1TJW2g19n9TLfmSJMXg2eQFg0A,2386
4
+ koi_net/network/__init__.py,sha256=r_RN-q_mDYC-2RAkN-lJoMUX76TXyfEUc_MVKW87z0g,39
5
+ koi_net/network/graph.py,sha256=KyS4Vv94DhHQk12u6rmE8nOIuTxeoJjrnoYEwVMNM74,4837
6
+ koi_net/network/interface.py,sha256=8zO2qBYmyd-MyAX-8RAf1mjL_0fn08-IbVOKHCAwNf8,10660
7
+ koi_net/network/request_handler.py,sha256=dufyJRwKD5w578UM-wzFVT3EU78NMvrh_yNSiI7vgfw,4938
8
+ koi_net/network/response_handler.py,sha256=HaP8Fl0bp_lfMmevhdVY8s9o0Uf8CR1ZaW5g3jsX8gw,1888
9
+ koi_net/processor/__init__.py,sha256=x4fAY0hvQEDcpfdTB3POIzxBQjYAtn0qQazPo1Xm0m4,41
10
+ koi_net/processor/default_handlers.py,sha256=ebFJ_MqvGBAb0SxmreZ9Pvyi0tD7ygDvJKECglw8uAQ,6972
11
+ koi_net/processor/handler.py,sha256=a2wxG3kgux-07YP13HSj_PH5VH_PjbnTS4uZEHjxMw0,2582
12
+ koi_net/processor/interface.py,sha256=8DT7-FFWj2vh-DXtuI9NXicOTSozaMSDTziMC2RMwuQ,13010
13
+ koi_net/processor/knowledge_object.py,sha256=xI1K4lIGJrWbb7yStjpU4eqZGI7t4wFklv72V7TRfXY,4212
14
+ koi_net/protocol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ koi_net/protocol/api_models.py,sha256=RDwVHAahiWzwzUnlj5MIm9et5WVpQOaG-Uscv1B9coU,1116
16
+ koi_net/protocol/consts.py,sha256=zeWJvRpqcERrqJq39heyNHb6f_9QrvoBZJHd70yE914,249
17
+ koi_net/protocol/edge.py,sha256=G3D9Ie0vbTSMJdoTw9g_oBmFCqzJ1gO7U1PVrw7p3j8,447
18
+ koi_net/protocol/event.py,sha256=YHsLD9HPwib-HiCWv9ep0sPqL2VHQqx_egDmbriChXM,1378
19
+ koi_net/protocol/helpers.py,sha256=9E9PaoIuSNrTBATGCLJ_kSBMZ2z-KIMnLJzGOTqQDC0,719
20
+ koi_net/protocol/node.py,sha256=Ntrx01dbm39ViKGtr4gLmztcMwKpTIweS6rRL-zoU_Y,391
21
+ koi_net-1.0.0b10.dist-info/METADATA,sha256=eQtnFOOH0wpuPMaASpOJJieHhEUadPvVcZ7XdQw0Jh8,33783
22
+ koi_net-1.0.0b10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
+ koi_net-1.0.0b10.dist-info/licenses/LICENSE,sha256=XBcvl8yjCAezfuqN1jadQykrX7H2g4nr2WRDmHLW6ik,1090
24
+ koi_net-1.0.0b10.dist-info/RECORD,,
@@ -1,24 +0,0 @@
1
- koi_net/__init__.py,sha256=b0Ze0pZmJAuygpWUFHM6Kvqo3DkU_uzmkptv1EpAArw,31
2
- koi_net/core.py,sha256=dE4sE2qsoIRUU1zsnrjx7aqYtYdHyCx-Dv4cwbkRjy4,4613
3
- koi_net/identity.py,sha256=PBgmAx5f3zzQmHASB1TJW2g19n9TLfmSJMXg2eQFg0A,2386
4
- koi_net/network/__init__.py,sha256=r_RN-q_mDYC-2RAkN-lJoMUX76TXyfEUc_MVKW87z0g,39
5
- koi_net/network/graph.py,sha256=KMUCU3AweRvivwy7GuWgX2zX74FPgHeVMO5ydvhVyvA,4833
6
- koi_net/network/interface.py,sha256=4JTeg8Eah0z5YKhcVKJbCVZw_Ghl_6xfG8aa1I5PCWI,10643
7
- koi_net/network/request_handler.py,sha256=fhuCDsxI8fZ4p5TntcTZR4mnLrLQ61zDy7Oca3ooFCE,4402
8
- koi_net/network/response_handler.py,sha256=HaP8Fl0bp_lfMmevhdVY8s9o0Uf8CR1ZaW5g3jsX8gw,1888
9
- koi_net/processor/__init__.py,sha256=x4fAY0hvQEDcpfdTB3POIzxBQjYAtn0qQazPo1Xm0m4,41
10
- koi_net/processor/default_handlers.py,sha256=cdGDFb4z1vnDmAXQO5XeDjhbUHbB8TndC-E6AFJzp2M,6930
11
- koi_net/processor/handler.py,sha256=a2wxG3kgux-07YP13HSj_PH5VH_PjbnTS4uZEHjxMw0,2582
12
- koi_net/processor/interface.py,sha256=5zYxaD_ATjjp1xFunRomCrrzjyspi8zZzeY2Rca7bVU,12522
13
- koi_net/processor/knowledge_object.py,sha256=cGv33fwNZQMylkhlTaQTbk96FVIVbdOUaBsG06u0m4k,4187
14
- koi_net/protocol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- koi_net/protocol/api_models.py,sha256=RDwVHAahiWzwzUnlj5MIm9et5WVpQOaG-Uscv1B9coU,1116
16
- koi_net/protocol/consts.py,sha256=zeWJvRpqcERrqJq39heyNHb6f_9QrvoBZJHd70yE914,249
17
- koi_net/protocol/edge.py,sha256=G3D9Ie0vbTSMJdoTw9g_oBmFCqzJ1gO7U1PVrw7p3j8,447
18
- koi_net/protocol/event.py,sha256=dzJmcHbimo7p5NwH2drccF0vMcAj9oQRj3iZ9Bjf7kg,1275
19
- koi_net/protocol/helpers.py,sha256=9E9PaoIuSNrTBATGCLJ_kSBMZ2z-KIMnLJzGOTqQDC0,719
20
- koi_net/protocol/node.py,sha256=Ntrx01dbm39ViKGtr4gLmztcMwKpTIweS6rRL-zoU_Y,391
21
- koi_net-1.0.0b9.dist-info/METADATA,sha256=GZbTzNd_k5uVlvaik7_YXdYd17Ya6XtVTf6NXrxbRes,32539
22
- koi_net-1.0.0b9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
- koi_net-1.0.0b9.dist-info/licenses/LICENSE,sha256=XBcvl8yjCAezfuqN1jadQykrX7H2g4nr2WRDmHLW6ik,1090
24
- koi_net-1.0.0b9.dist-info/RECORD,,