koi-net 1.2.0b1__py3-none-any.whl → 1.2.0b3__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.

Files changed (52) hide show
  1. koi_net/__init__.py +1 -1
  2. koi_net/assembler.py +95 -0
  3. koi_net/cli/models.py +2 -2
  4. koi_net/config/core.py +71 -0
  5. koi_net/config/full_node.py +31 -0
  6. koi_net/config/loader.py +46 -0
  7. koi_net/config/partial_node.py +18 -0
  8. koi_net/core.py +43 -206
  9. koi_net/default_actions.py +1 -2
  10. koi_net/effector.py +27 -15
  11. koi_net/entrypoints/__init__.py +2 -0
  12. koi_net/entrypoints/base.py +5 -0
  13. koi_net/{poller.py → entrypoints/poller.py} +14 -12
  14. koi_net/entrypoints/server.py +94 -0
  15. koi_net/handshaker.py +5 -5
  16. koi_net/identity.py +3 -4
  17. koi_net/lifecycle.py +42 -34
  18. koi_net/logger.py +176 -0
  19. koi_net/network/error_handler.py +7 -7
  20. koi_net/network/event_queue.py +9 -7
  21. koi_net/network/graph.py +8 -8
  22. koi_net/network/poll_event_buffer.py +26 -0
  23. koi_net/network/request_handler.py +23 -28
  24. koi_net/network/resolver.py +14 -14
  25. koi_net/network/response_handler.py +74 -9
  26. koi_net/{context.py → processor/context.py} +11 -19
  27. koi_net/processor/handler.py +4 -1
  28. koi_net/processor/{default_handlers.py → knowledge_handlers.py} +32 -31
  29. koi_net/processor/knowledge_object.py +2 -3
  30. koi_net/processor/kobj_queue.py +4 -4
  31. koi_net/processor/{knowledge_pipeline.py → pipeline.py} +25 -28
  32. koi_net/protocol/api_models.py +5 -2
  33. koi_net/protocol/envelope.py +5 -6
  34. koi_net/protocol/model_map.py +61 -0
  35. koi_net/protocol/node.py +3 -3
  36. koi_net/protocol/secure.py +14 -8
  37. koi_net/secure.py +6 -7
  38. koi_net/workers/__init__.py +2 -0
  39. koi_net/{worker.py → workers/base.py} +7 -0
  40. koi_net/{processor → workers}/event_worker.py +19 -23
  41. koi_net/{kobj_worker.py → workers/kobj_worker.py} +12 -13
  42. {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b3.dist-info}/METADATA +2 -1
  43. koi_net-1.2.0b3.dist-info/RECORD +56 -0
  44. koi_net/behaviors.py +0 -51
  45. koi_net/config.py +0 -161
  46. koi_net/models.py +0 -14
  47. koi_net/poll_event_buffer.py +0 -17
  48. koi_net/server.py +0 -145
  49. koi_net-1.2.0b1.dist-info/RECORD +0 -49
  50. {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b3.dist-info}/WHEEL +0 -0
  51. {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b3.dist-info}/entry_points.txt +0 -0
  52. {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b3.dist-info}/licenses/LICENSE +0 -0
@@ -1,27 +1,29 @@
1
1
 
2
2
  import time
3
- import logging
4
- from .processor.kobj_queue import KobjQueue
5
- from .lifecycle import NodeLifecycle
6
- from .network.resolver import NetworkResolver
7
- from .config import NodeConfig
3
+ import structlog
8
4
 
9
- logger = logging.getLogger(__name__)
5
+ from .base import EntryPoint
6
+ from ..processor.kobj_queue import KobjQueue
7
+ from ..lifecycle import NodeLifecycle
8
+ from ..network.resolver import NetworkResolver
9
+ from ..config.partial_node import PartialNodeConfig
10
10
 
11
+ log = structlog.stdlib.get_logger()
11
12
 
12
- class NodePoller:
13
+
14
+ class NodePoller(EntryPoint):
13
15
  """Manages polling based event loop for partial nodes."""
14
16
  kobj_queue: KobjQueue
15
17
  lifecycle: NodeLifecycle
16
18
  resolver: NetworkResolver
17
- config: NodeConfig
19
+ config: PartialNodeConfig
18
20
 
19
21
  def __init__(
20
22
  self,
21
- kobj_queue: KobjQueue,
23
+ config: PartialNodeConfig,
22
24
  lifecycle: NodeLifecycle,
25
+ kobj_queue: KobjQueue,
23
26
  resolver: NetworkResolver,
24
- config: NodeConfig
25
27
  ):
26
28
  self.kobj_queue = kobj_queue
27
29
  self.lifecycle = lifecycle
@@ -33,7 +35,7 @@ class NodePoller:
33
35
  neighbors = self.resolver.poll_neighbors()
34
36
  for node_rid in neighbors:
35
37
  for event in neighbors[node_rid]:
36
- self.kobj_queue.put_kobj(event=event, source=node_rid)
38
+ self.kobj_queue.push(event=event, source=node_rid)
37
39
 
38
40
  def run(self):
39
41
  """Runs polling event loop."""
@@ -42,6 +44,6 @@ class NodePoller:
42
44
  start_time = time.time()
43
45
  self.poll()
44
46
  elapsed = time.time() - start_time
45
- sleep_time = self.config.koi_net.polling_interval - elapsed
47
+ sleep_time = self.config.poller.polling_interval - elapsed
46
48
  if sleep_time > 0:
47
49
  time.sleep(sleep_time)
@@ -0,0 +1,94 @@
1
+ import structlog
2
+ import uvicorn
3
+ from contextlib import asynccontextmanager
4
+ from fastapi import FastAPI, APIRouter
5
+ from fastapi.responses import JSONResponse
6
+
7
+ from .base import EntryPoint
8
+ from ..network.response_handler import ResponseHandler
9
+ from ..protocol.model_map import API_MODEL_MAP
10
+ from ..protocol.api_models import ErrorResponse
11
+ from ..protocol.errors import ProtocolError
12
+ from ..lifecycle import NodeLifecycle
13
+ from ..config.full_node import FullNodeConfig
14
+
15
+ log = structlog.stdlib.get_logger()
16
+
17
+
18
+ class NodeServer(EntryPoint):
19
+ """Manages FastAPI server and event handling for full nodes."""
20
+ config: FullNodeConfig
21
+ lifecycle: NodeLifecycle
22
+ response_handler: ResponseHandler
23
+ app: FastAPI
24
+ router: APIRouter
25
+
26
+ def __init__(
27
+ self,
28
+ config: FullNodeConfig,
29
+ lifecycle: NodeLifecycle,
30
+ response_handler: ResponseHandler,
31
+ ):
32
+ self.config = config
33
+ self.lifecycle = lifecycle
34
+ self.response_handler = response_handler
35
+ self._build_app()
36
+
37
+ def _build_app(self):
38
+ """Builds FastAPI app and adds endpoints."""
39
+ @asynccontextmanager
40
+ async def lifespan(*args, **kwargs):
41
+ async with self.lifecycle.async_run():
42
+ yield
43
+
44
+ self.app = FastAPI(
45
+ lifespan=lifespan,
46
+ title="KOI-net Protocol API",
47
+ version="1.0.0"
48
+ )
49
+
50
+ self.app.add_exception_handler(ProtocolError, self.protocol_error_handler)
51
+
52
+ self.router = APIRouter(prefix="/koi-net")
53
+
54
+ for path, models in API_MODEL_MAP.items():
55
+ def create_endpoint(path: str):
56
+ async def endpoint(req):
57
+ return self.response_handler.handle_response(path, req)
58
+
59
+ # programmatically setting type hint annotations for FastAPI's model validation
60
+ endpoint.__annotations__ = {
61
+ "req": models.request_envelope,
62
+ "return": models.response_envelope
63
+ }
64
+
65
+ return endpoint
66
+
67
+ self.router.add_api_route(
68
+ path=path,
69
+ endpoint=create_endpoint(path),
70
+ methods=["POST"],
71
+ response_model_exclude_none=True
72
+ )
73
+
74
+ self.app.include_router(self.router)
75
+
76
+ def protocol_error_handler(self, request, exc: ProtocolError):
77
+ """Catches `ProtocolError` and returns as `ErrorResponse`."""
78
+ log.info(f"caught protocol error: {exc}")
79
+ resp = ErrorResponse(error=exc.error_type)
80
+ log.info(f"returning error response: {resp}")
81
+ return JSONResponse(
82
+ status_code=400,
83
+ content=resp.model_dump(mode="json")
84
+ )
85
+
86
+ def run(self):
87
+ """Starts FastAPI server and event handler."""
88
+ uvicorn.run(
89
+ app=self.app,
90
+ host=self.config.server.host,
91
+ port=self.config.server.port,
92
+ log_config=None,
93
+ access_log=False
94
+ )
koi_net/handshaker.py CHANGED
@@ -1,11 +1,11 @@
1
- from logging import getLogger
1
+ import structlog
2
2
  from rid_lib.ext import Cache
3
3
  from rid_lib.types import KoiNetNode
4
4
  from koi_net.identity import NodeIdentity
5
5
  from koi_net.network.event_queue import EventQueue
6
6
  from .protocol.event import Event, EventType
7
7
 
8
- logger = getLogger(__name__)
8
+ log = structlog.stdlib.get_logger()
9
9
 
10
10
 
11
11
  class Handshaker:
@@ -24,14 +24,14 @@ class Handshaker:
24
24
  Pushes successive `FORGET` and `NEW` events to target node to
25
25
  reset the target's cache in case it already knew this node.
26
26
  """
27
- logger.debug(f"Initiating handshake with {target}")
28
- self.event_queue.push_event_to(
27
+ log.debug(f"Initiating handshake with {target}")
28
+ self.event_queue.push(
29
29
  Event.from_rid(
30
30
  event_type=EventType.FORGET,
31
31
  rid=self.identity.rid),
32
32
  target=target
33
33
  )
34
- self.event_queue.push_event_to(
34
+ self.event_queue.push(
35
35
  event=Event.from_bundle(
36
36
  event_type=EventType.NEW,
37
37
  bundle=self.cache.read(self.identity.rid)),
koi_net/identity.py CHANGED
@@ -1,10 +1,9 @@
1
- import logging
1
+ import structlog
2
2
  from rid_lib.types.koi_net_node import KoiNetNode
3
- from .config import NodeConfig
3
+ from .config.core import NodeConfig
4
4
  from .protocol.node import NodeProfile
5
5
 
6
-
7
- logger = logging.getLogger(__name__)
6
+ log = structlog.stdlib.get_logger()
8
7
 
9
8
 
10
9
  class NodeIdentity:
koi_net/lifecycle.py CHANGED
@@ -1,22 +1,22 @@
1
- import logging
1
+ import structlog
2
2
  from contextlib import contextmanager, asynccontextmanager
3
3
 
4
4
  from rid_lib.ext import Bundle, Cache
5
5
  from rid_lib.types import KoiNetNode
6
6
 
7
- from koi_net.behaviors import Behaviors
8
- from koi_net.handshaker import Handshaker
9
- from koi_net.kobj_worker import KnowledgeProcessingWorker
10
- from koi_net.models import END
11
- from koi_net.network.event_queue import EventQueue
12
- from koi_net.processor.event_worker import EventProcessingWorker
13
-
14
- from .config import NodeConfig
7
+ from .handshaker import Handshaker
8
+ from .network.request_handler import RequestHandler
9
+ from .workers.kobj_worker import KnowledgeProcessingWorker
10
+ from .network.event_queue import EventQueue
11
+ from .workers import EventProcessingWorker
12
+ from .protocol.api_models import ErrorResponse
13
+ from .workers.base import STOP_WORKER
14
+ from .config.core import NodeConfig
15
15
  from .processor.kobj_queue import KobjQueue
16
16
  from .network.graph import NetworkGraph
17
17
  from .identity import NodeIdentity
18
18
 
19
- logger = logging.getLogger(__name__)
19
+ log = structlog.stdlib.get_logger()
20
20
 
21
21
 
22
22
  class NodeLifecycle:
@@ -31,7 +31,7 @@ class NodeLifecycle:
31
31
  event_worker: EventProcessingWorker
32
32
  cache: Cache
33
33
  handshaker: Handshaker
34
- behaviors: Behaviors
34
+ request_handler: RequestHandler
35
35
 
36
36
  def __init__(
37
37
  self,
@@ -44,7 +44,7 @@ class NodeLifecycle:
44
44
  event_worker: EventProcessingWorker,
45
45
  cache: Cache,
46
46
  handshaker: Handshaker,
47
- behaviors: Behaviors
47
+ request_handler: RequestHandler
48
48
  ):
49
49
  self.config = config
50
50
  self.identity = identity
@@ -55,32 +55,32 @@ class NodeLifecycle:
55
55
  self.event_worker = event_worker
56
56
  self.cache = cache
57
57
  self.handshaker = handshaker
58
- self.behaviors = behaviors
58
+ self.request_handler = request_handler
59
59
 
60
60
  @contextmanager
61
61
  def run(self):
62
62
  """Synchronous context manager for node startup and shutdown."""
63
63
  try:
64
- logger.info("Starting node lifecycle...")
64
+ log.info("Starting node lifecycle...")
65
65
  self.start()
66
66
  yield
67
67
  except KeyboardInterrupt:
68
- logger.info("Keyboard interrupt!")
68
+ log.info("Keyboard interrupt!")
69
69
  finally:
70
- logger.info("Stopping node lifecycle...")
70
+ log.info("Stopping node lifecycle...")
71
71
  self.stop()
72
72
 
73
73
  @asynccontextmanager
74
74
  async def async_run(self):
75
75
  """Asynchronous context manager for node startup and shutdown."""
76
76
  try:
77
- logger.info("Starting async node lifecycle...")
77
+ log.info("Starting async node lifecycle...")
78
78
  self.start()
79
79
  yield
80
80
  except KeyboardInterrupt:
81
- logger.info("Keyboard interrupt!")
81
+ log.info("Keyboard interrupt!")
82
82
  finally:
83
- logger.info("Stopping async node lifecycle...")
83
+ log.info("Stopping async node lifecycle...")
84
84
  self.stop()
85
85
 
86
86
  def start(self):
@@ -91,7 +91,7 @@ class NodeLifecycle:
91
91
  of node bundle. Initiates handshake with first contact if node
92
92
  doesn't have any neighbors. Catches up with coordinator state.
93
93
  """
94
- logger.info("Starting processor worker thread")
94
+ log.info("Starting processor worker thread")
95
95
 
96
96
  self.kobj_worker.thread.start()
97
97
  self.event_worker.thread.start()
@@ -99,35 +99,43 @@ class NodeLifecycle:
99
99
 
100
100
  # refresh to reflect changes (if any) in config.yaml
101
101
 
102
- self.kobj_queue.put_kobj(bundle=Bundle.generate(
102
+ self.kobj_queue.push(bundle=Bundle.generate(
103
103
  rid=self.identity.rid,
104
104
  contents=self.identity.profile.model_dump()
105
105
  ))
106
106
 
107
- logger.debug("Waiting for kobj queue to empty")
107
+ log.debug("Waiting for kobj queue to empty")
108
108
  self.kobj_queue.q.join()
109
109
 
110
- # TODO: FACTOR OUT BEHAVIOR
111
-
112
110
  coordinators = self.graph.get_neighbors(direction="in", allowed_type=KoiNetNode)
113
111
 
114
- if len(coordinators) == 0 and self.config.koi_net.first_contact.rid:
115
- logger.debug(f"I don't have any edges with coordinators, reaching out to first contact {self.config.koi_net.first_contact.rid!r}")
112
+ if len(coordinators) > 0:
113
+ for coordinator in coordinators:
114
+ payload = self.request_handler.fetch_manifests(
115
+ node=coordinator,
116
+ rid_types=[KoiNetNode]
117
+ )
118
+ if type(payload) is ErrorResponse:
119
+ continue
120
+
121
+ for manifest in payload.manifests:
122
+ self.kobj_queue.push(
123
+ manifest=manifest,
124
+ source=coordinator
125
+ )
126
+
127
+ elif self.config.koi_net.first_contact.rid:
128
+ log.debug(f"I don't have any edges with coordinators, reaching out to first contact {self.config.koi_net.first_contact.rid!r}")
116
129
 
117
130
  self.handshaker.handshake_with(self.config.koi_net.first_contact.rid)
118
131
 
119
-
120
-
121
- for coordinator in self.behaviors.identify_coordinators():
122
- self.behaviors.catch_up_with(coordinator, rid_types=[KoiNetNode])
123
-
124
132
 
125
133
  def stop(self):
126
134
  """Stops a node.
127
135
 
128
136
  Finishes processing knowledge object queue.
129
137
  """
130
- logger.info(f"Waiting for kobj queue to empty ({self.kobj_queue.q.unfinished_tasks} tasks remaining)")
138
+ log.info(f"Waiting for kobj queue to empty ({self.kobj_queue.q.unfinished_tasks} tasks remaining)")
131
139
 
132
- self.kobj_queue.q.put(END)
133
- self.event_queue.q.put(END)
140
+ self.kobj_queue.q.put(STOP_WORKER)
141
+ self.event_queue.q.put(STOP_WORKER)
koi_net/logger.py ADDED
@@ -0,0 +1,176 @@
1
+ from datetime import datetime
2
+ import logging
3
+ from logging.handlers import RotatingFileHandler
4
+ import colorama
5
+ import structlog
6
+ import sys
7
+ # from sentry_sdk import logger as sentry_logger
8
+
9
+
10
+ def my_processor(_, __, event: dict):
11
+ # print(_, __, event)
12
+ event["path"] = event["module"] + "." + event["func_name"]
13
+ return event
14
+
15
+ # def sentry_processor(_, method, event: dict):
16
+ # print(event)
17
+ # if method == "critical":
18
+ # sentry_logger.fatal(
19
+ # event["event"],
20
+ # attributes=event
21
+ # )
22
+ # elif method == "info":
23
+ # sentry_logger.info(
24
+ # event["event"],
25
+ # attributes=event
26
+ # )
27
+ # elif method == "debug":
28
+ # sentry_logger.debug(
29
+ # event["event"],
30
+ # attributes=event
31
+ # )
32
+ # return event
33
+
34
+ console_renderer = structlog.dev.ConsoleRenderer(
35
+ columns=[
36
+ # Render the timestamp without the key name in yellow.
37
+ structlog.dev.Column(
38
+ "timestamp",
39
+ structlog.dev.KeyValueColumnFormatter(
40
+ key_style=None,
41
+ value_style=colorama.Style.DIM,
42
+ reset_style=colorama.Style.RESET_ALL,
43
+ value_repr=lambda t: datetime.fromisoformat(t).strftime("%Y-%m-%d %H:%M:%S"),
44
+ ),
45
+ ),
46
+ structlog.dev.Column(
47
+ "level",
48
+ structlog.dev.LogLevelColumnFormatter(
49
+ level_styles={
50
+ level: colorama.Style.BRIGHT + color
51
+ for level, color in {
52
+ "critical": colorama.Fore.RED,
53
+ "exception": colorama.Fore.RED,
54
+ "error": colorama.Fore.RED,
55
+ "warn": colorama.Fore.YELLOW,
56
+ "warning": colorama.Fore.YELLOW,
57
+ "info": colorama.Fore.GREEN,
58
+ "debug": colorama.Fore.GREEN,
59
+ "notset": colorama.Back.RED,
60
+ }.items()
61
+ },
62
+ reset_style=colorama.Style.RESET_ALL,
63
+ width=9
64
+ )
65
+ ),
66
+ # Render the event without the key name in bright magenta.
67
+
68
+ # Default formatter for all keys not explicitly mentioned. The key is
69
+ # cyan, the value is green.
70
+ structlog.dev.Column(
71
+ "path",
72
+ structlog.dev.KeyValueColumnFormatter(
73
+ key_style=None,
74
+ value_style=colorama.Fore.MAGENTA,
75
+ reset_style=colorama.Style.RESET_ALL,
76
+ value_repr=str,
77
+ width=30
78
+ ),
79
+ ),
80
+ # structlog.dev.Column(
81
+ # "func_name",
82
+ # structlog.dev.KeyValueColumnFormatter(
83
+ # key_style=None,
84
+ # value_style=colorama.Fore.MAGENTA,
85
+ # reset_style=colorama.Style.RESET_ALL,
86
+ # value_repr=str,
87
+ # prefix="(",
88
+ # postfix=")",
89
+ # width=15
90
+ # ),
91
+ # ),
92
+ structlog.dev.Column(
93
+ "event",
94
+ structlog.dev.KeyValueColumnFormatter(
95
+ key_style=None,
96
+ value_style=colorama.Fore.WHITE,
97
+ reset_style=colorama.Style.RESET_ALL,
98
+ value_repr=str,
99
+ width=30
100
+ ),
101
+ ),
102
+ structlog.dev.Column(
103
+ "",
104
+ structlog.dev.KeyValueColumnFormatter(
105
+ key_style=colorama.Fore.BLUE,
106
+ value_style=colorama.Fore.GREEN,
107
+ reset_style=colorama.Style.RESET_ALL,
108
+ value_repr=str,
109
+ ),
110
+ )
111
+ ]
112
+ )
113
+
114
+ structlog.configure(
115
+ processors=[
116
+ # If log level is too low, abort pipeline and throw away log entry.
117
+ structlog.stdlib.filter_by_level,
118
+ # Add the name of the logger to event dict.
119
+ structlog.stdlib.add_logger_name,
120
+ # Add log level to event dict.
121
+ structlog.stdlib.add_log_level,
122
+ # Perform %-style formatting.
123
+ structlog.stdlib.PositionalArgumentsFormatter(),
124
+ # Add a timestamp in ISO 8601 format.
125
+ structlog.processors.TimeStamper(fmt="iso"),
126
+ # If the "stack_info" key in the event dict is true, remove it and
127
+ # render the current stack trace in the "stack" key.
128
+ structlog.processors.StackInfoRenderer(),
129
+ # If the "exc_info" key in the event dict is either true or a
130
+ # sys.exc_info() tuple, remove "exc_info" and render the exception
131
+ # with traceback into the "exception" key.
132
+ # structlog.processors.format_exc_info,
133
+ # If some value is in bytes, decode it to a Unicode str.
134
+ structlog.processors.UnicodeDecoder(),
135
+ # Add callsite parameters.
136
+ structlog.processors.CallsiteParameterAdder(
137
+ {
138
+ structlog.processors.CallsiteParameter.MODULE,
139
+ structlog.processors.CallsiteParameter.FUNC_NAME,
140
+ # structlog.processors.CallsiteParameter.LINENO,
141
+ }
142
+ ),
143
+ my_processor,
144
+ # Render the final event dict as JSON.
145
+ # sentry_processor,
146
+ console_renderer
147
+ # structlog.processors.JSONRenderer()
148
+
149
+ ],
150
+ # `wrapper_class` is the bound logger that you get back from
151
+ # get_logger(). This one imitates the API of `logging.Logger`.
152
+ wrapper_class=structlog.stdlib.BoundLogger,
153
+ # `logger_factory` is used to create wrapped loggers that are used for
154
+ # OUTPUT. This one returns a `logging.Logger`. The final value (a JSON
155
+ # string) from the final processor (`JSONRenderer`) will be passed to
156
+ # the method of the same name as that you've called on the bound logger.
157
+ logger_factory=structlog.stdlib.LoggerFactory(),
158
+ # Effectively freeze configuration after creating the first bound
159
+ # logger.
160
+ cache_logger_on_first_use=True,
161
+ )
162
+
163
+ file_handler = RotatingFileHandler(
164
+ filename="app.log",
165
+ maxBytes=10 * 1024 * 1024,
166
+ backupCount=5,
167
+ encoding="utf-8"
168
+ )
169
+
170
+ logging.basicConfig(
171
+ format="%(message)s",
172
+ stream=sys.stdout,
173
+ level=logging.INFO,
174
+ )
175
+
176
+ # log = structlog.stdlib.get_logger()
@@ -1,11 +1,11 @@
1
- from logging import getLogger
1
+ import structlog
2
2
  from koi_net.handshaker import Handshaker
3
3
  from koi_net.protocol.errors import ErrorType
4
4
  from koi_net.protocol.event import EventType
5
5
  from rid_lib.types import KoiNetNode
6
6
  from ..processor.kobj_queue import KobjQueue
7
7
 
8
- logger = getLogger(__name__)
8
+ log = structlog.stdlib.get_logger()
9
9
 
10
10
 
11
11
  class ErrorHandler:
@@ -27,11 +27,11 @@ class ErrorHandler:
27
27
  self.timeout_counter.setdefault(node, 0)
28
28
  self.timeout_counter[node] += 1
29
29
 
30
- logger.debug(f"{node} has timed out {self.timeout_counter[node]} time(s)")
30
+ log.debug(f"{node} has timed out {self.timeout_counter[node]} time(s)")
31
31
 
32
32
  if self.timeout_counter[node] > 3:
33
- logger.debug(f"Exceeded time out limit, forgetting node")
34
- self.kobj_queue.put_kobj(rid=node, event_type=EventType.FORGET)
33
+ log.debug(f"Exceeded time out limit, forgetting node")
34
+ self.kobj_queue.push(rid=node, event_type=EventType.FORGET)
35
35
  # do something
36
36
 
37
37
 
@@ -41,10 +41,10 @@ class ErrorHandler:
41
41
  node: KoiNetNode
42
42
  ):
43
43
  """Attempts handshake when this node is unknown to target."""
44
- logger.info(f"Handling protocol error {error_type} for node {node!r}")
44
+ log.info(f"Handling protocol error {error_type} for node {node!r}")
45
45
  match error_type:
46
46
  case ErrorType.UnknownNode:
47
- logger.info("Peer doesn't know me, attempting handshake...")
47
+ log.info("Peer doesn't know me, attempting handshake...")
48
48
  self.handshaker.handshake_with(node)
49
49
 
50
50
  case ErrorType.InvalidKey: ...
@@ -1,14 +1,18 @@
1
- import logging
1
+ import structlog
2
2
  from queue import Queue
3
3
 
4
4
  from rid_lib.types import KoiNetNode
5
+ from pydantic import BaseModel
5
6
 
6
- from ..models import QueuedEvent
7
7
  from ..protocol.event import Event
8
8
 
9
- logger = logging.getLogger(__name__)
9
+ log = structlog.stdlib.get_logger()
10
10
 
11
11
 
12
+ class QueuedEvent(BaseModel):
13
+ event: Event
14
+ target: KoiNetNode
15
+
12
16
  class EventQueue:
13
17
  """Handles out going network event queues."""
14
18
  q: Queue[QueuedEvent]
@@ -16,13 +20,11 @@ class EventQueue:
16
20
  def __init__(self):
17
21
  self.q = Queue()
18
22
 
19
- def push_event_to(self, event: Event, target: KoiNetNode):
23
+ def push(self, event: Event, target: KoiNetNode):
20
24
  """Pushes event to queue of specified node.
21
25
 
22
26
  Event will be sent to webhook or poll queue depending on the
23
- node type and edge type of the specified node. If `flush` is set
24
- to `True`, the webhook queued will be flushed after pushing the
25
- event.
27
+ node type and edge type of the specified node.
26
28
  """
27
29
 
28
30
  self.q.put(QueuedEvent(target=target, event=event))
koi_net/network/graph.py CHANGED
@@ -1,4 +1,4 @@
1
- import logging
1
+ import structlog
2
2
  from typing import Literal
3
3
  import networkx as nx
4
4
  from rid_lib import RIDType
@@ -7,7 +7,7 @@ from rid_lib.types import KoiNetEdge, KoiNetNode
7
7
  from ..identity import NodeIdentity
8
8
  from ..protocol.edge import EdgeProfile, EdgeStatus
9
9
 
10
- logger = logging.getLogger(__name__)
10
+ log = structlog.stdlib.get_logger()
11
11
 
12
12
 
13
13
  class NetworkGraph:
@@ -24,22 +24,22 @@ class NetworkGraph:
24
24
 
25
25
  def generate(self):
26
26
  """Generates directed graph from cached KOI nodes and edges."""
27
- logger.debug("Generating network graph")
27
+ log.debug("Generating network graph")
28
28
  self.dg.clear()
29
29
  for rid in self.cache.list_rids():
30
30
  if type(rid) == KoiNetNode:
31
31
  self.dg.add_node(rid)
32
- logger.debug(f"Added node {rid!r}")
32
+ log.debug(f"Added node {rid!r}")
33
33
 
34
34
  elif type(rid) == KoiNetEdge:
35
35
  edge_bundle = self.cache.read(rid)
36
36
  if not edge_bundle:
37
- logger.warning(f"Failed to load {rid!r}")
37
+ log.warning(f"Failed to load {rid!r}")
38
38
  continue
39
39
  edge_profile = edge_bundle.validate_contents(EdgeProfile)
40
40
  self.dg.add_edge(edge_profile.source, edge_profile.target, rid=rid)
41
- logger.debug(f"Added edge {rid!r} ({edge_profile.source} -> {edge_profile.target})")
42
- logger.debug("Done")
41
+ log.debug(f"Added edge {rid!r} ({edge_profile.source} -> {edge_profile.target})")
42
+ log.debug("Done")
43
43
 
44
44
  def get_edge(self, source: KoiNetNode, target: KoiNetNode,) -> KoiNetEdge | None:
45
45
  """Returns edge RID given the RIDs of a source and target node."""
@@ -97,7 +97,7 @@ class NetworkGraph:
97
97
  edge_bundle = self.cache.read(edge_rid)
98
98
 
99
99
  if not edge_bundle:
100
- logger.warning(f"Failed to find edge {edge_rid!r} in cache")
100
+ log.warning(f"Failed to find edge {edge_rid!r} in cache")
101
101
  continue
102
102
 
103
103
  edge_profile = edge_bundle.validate_contents(EdgeProfile)