vention-communication 0.3.0__py3-none-any.whl → 0.3.4__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.
communication/codegen.py CHANGED
@@ -1,17 +1,30 @@
1
1
  from __future__ import annotations
2
- from typing import Any, Dict, List, Optional, Type, Union, get_args, get_origin
2
+ import sys
3
+ from typing import Any, Dict, List, Optional, Type, Union, get_args, get_origin, Annotated
3
4
 
4
5
  from .decorators import collect_bundle
5
6
  from .typing_utils import is_pydantic_model
6
7
  from .entries import RpcBundle, StreamEntry
7
8
 
9
+
10
+ # Handle Python 3.10+ types.UnionType
11
+ if sys.version_info >= (3, 10):
12
+ from types import UnionType
13
+
14
+ _UNION_TYPES = (Union, UnionType)
15
+ else:
16
+ _UNION_TYPES = (Union,)
17
+
8
18
  _SCALAR_MAP = {
9
19
  int: "int32",
10
20
  float: "double",
11
21
  str: "string",
12
22
  bool: "bool",
23
+ bytes: "bytes",
13
24
  }
14
25
 
26
+ MAX_NESTING_DEPTH = 10
27
+
15
28
  HEADER = """syntax = "proto3";
16
29
  package vention.app.v1;
17
30
 
@@ -20,22 +33,45 @@ import "google/protobuf/empty.proto";
20
33
  """
21
34
 
22
35
 
36
+ def _unwrap_annotated(type_annotation: Any) -> Any:
37
+ """Unwrap Annotated[T, ...] to get the base type T."""
38
+ origin = get_origin(type_annotation)
39
+ if origin is Annotated:
40
+ args = get_args(type_annotation)
41
+ if args:
42
+ return args[0]
43
+ return type_annotation
44
+
45
+
46
+ def _filter_non_none_types(args: tuple[Any, ...]) -> list[Any]:
47
+ """Filter out NoneType from union type arguments."""
48
+ return [arg for arg in args if arg is not type(None)]
49
+
50
+
23
51
  def _unwrap_optional(type_annotation: Any) -> tuple[Any, bool]:
52
+ type_annotation = _unwrap_annotated(type_annotation)
53
+
24
54
  origin = get_origin(type_annotation)
25
- if origin is Union:
55
+ # Handle both typing.Union and types.UnionType (Python 3.10+)
56
+ if origin in _UNION_TYPES:
26
57
  args = get_args(type_annotation)
27
- non_none_args = [arg for arg in args if arg is not type(None)]
58
+ non_none_args = _filter_non_none_types(args)
28
59
  if len(non_none_args) == 1:
29
- return (non_none_args[0], True)
60
+ # Recursively unwrap in case the inner type is also Annotated
61
+ return (_unwrap_annotated(non_none_args[0]), True)
30
62
  return (type_annotation, False)
31
63
 
32
64
 
33
65
  def _unwrap_list(type_annotation: Any) -> tuple[Any, bool]:
66
+ # First unwrap Annotated if present
67
+ type_annotation = _unwrap_annotated(type_annotation)
68
+
34
69
  origin = get_origin(type_annotation)
35
70
  if origin in (list, List):
36
71
  args = get_args(type_annotation)
37
72
  if args:
38
- return (args[0], True)
73
+ # Unwrap Annotated from the inner type as well
74
+ return (_unwrap_annotated(args[0]), True)
39
75
  return (type_annotation, False)
40
76
 
41
77
 
@@ -47,16 +83,20 @@ def _determine_proto_type_for_field(
47
83
  inner_type: Type[Any],
48
84
  seen_models: set[str],
49
85
  lines: list[str],
86
+ depth: int = 0,
50
87
  ) -> str:
88
+ if depth > MAX_NESTING_DEPTH:
89
+ raise ValueError(f"Maximum nesting depth ({MAX_NESTING_DEPTH}) exceeded in proto generation")
90
+
51
91
  if inner_type in _SCALAR_MAP:
52
92
  return _SCALAR_MAP[inner_type]
53
93
 
54
94
  if is_pydantic_model(inner_type):
55
95
  model_name = inner_type.__name__
56
- # Recursively register nested model if not seen before
96
+
57
97
  if model_name not in seen_models:
58
98
  seen_models.add(model_name)
59
- lines.extend(_generate_pydantic_message(inner_type, seen_models, lines))
99
+ lines.extend(_generate_pydantic_message(inner_type, seen_models, lines, depth + 1))
60
100
  return model_name
61
101
 
62
102
  # Fallback to string for unknown types
@@ -69,14 +109,18 @@ def _process_pydantic_field(
69
109
  field_index: int,
70
110
  seen_models: set[str],
71
111
  lines: list[str],
112
+ depth: int = 0,
72
113
  ) -> str:
73
- inner_type, _ = _unwrap_optional(field_type)
74
- list_inner_type, is_list = _unwrap_list(inner_type)
114
+ unwrapped_type = _unwrap_annotated(field_type)
115
+ unwrapped_type, _ = _unwrap_optional(unwrapped_type)
116
+ list_inner_type, is_list = _unwrap_list(unwrapped_type)
117
+ # One more unwrap in case list contains Optional[Annotated[...]]
118
+ final_inner_type = _unwrap_annotated(list_inner_type)
75
119
 
76
- proto_type = _determine_proto_type_for_field(list_inner_type, seen_models, lines)
120
+ proto_type = _determine_proto_type_for_field(final_inner_type, seen_models, lines, depth)
77
121
 
78
122
  if is_list:
79
- proto_type = f"repeated {proto_type}"
123
+ return f" repeated {proto_type} {field_name} = {field_index};"
80
124
 
81
125
  return f" {proto_type} {field_name} = {field_index};"
82
126
 
@@ -85,6 +129,7 @@ def _generate_pydantic_message(
85
129
  type_annotation: Type[Any],
86
130
  seen_models: set[str],
87
131
  lines: list[str],
132
+ depth: int = 0,
88
133
  ) -> list[str]:
89
134
  model_name = type_annotation.__name__
90
135
  fields = []
@@ -97,6 +142,7 @@ def _generate_pydantic_message(
97
142
  field_index,
98
143
  seen_models,
99
144
  lines,
145
+ depth,
100
146
  )
101
147
  fields.append(field_line)
102
148
  field_index += 1
@@ -107,9 +153,7 @@ def _generate_pydantic_message(
107
153
  return lines_result
108
154
 
109
155
 
110
- def _generate_scalar_wrapper_message(
111
- stream_name: str, payload_type: Type[Any]
112
- ) -> list[str]:
156
+ def _generate_scalar_wrapper_message(stream_name: str, payload_type: Type[Any]) -> list[str]:
113
157
  wrapper_name = _msg_name_for_scalar_stream(stream_name)
114
158
  lines = [
115
159
  f"message {wrapper_name} {{",
@@ -150,17 +194,15 @@ def _register_pydantic_model(
150
194
  if type_annotation is None:
151
195
  return
152
196
 
153
- inner_type, _ = _unwrap_optional(type_annotation)
197
+ unwrapped_type, _ = _unwrap_optional(type_annotation)
154
198
 
155
- list_inner_type, _ = _unwrap_list(inner_type)
199
+ list_inner_type, _ = _unwrap_list(unwrapped_type)
156
200
 
157
201
  if is_pydantic_model(list_inner_type):
158
202
  model_name = list_inner_type.__name__
159
203
  if model_name not in seen_models:
160
204
  seen_models.add(model_name)
161
- lines.extend(
162
- _generate_pydantic_message(list_inner_type, seen_models, lines)
163
- )
205
+ lines.extend(_generate_pydantic_message(list_inner_type, seen_models, lines))
164
206
 
165
207
 
166
208
  def _process_stream_payload(
@@ -169,17 +211,15 @@ def _process_stream_payload(
169
211
  lines: list[str],
170
212
  scalar_wrappers: Dict[str, str],
171
213
  ) -> None:
172
- inner_type, _ = _unwrap_optional(stream_entry.payload_type)
173
- list_inner_type, _ = _unwrap_list(inner_type)
214
+ unwrapped_type, _ = _unwrap_optional(stream_entry.payload_type)
215
+ list_inner_type, _ = _unwrap_list(unwrapped_type)
174
216
 
175
217
  if is_pydantic_model(list_inner_type):
176
218
  _register_pydantic_model(list_inner_type, seen_models, lines)
177
219
  elif list_inner_type in _SCALAR_MAP:
178
220
  wrapper_name = _msg_name_for_scalar_stream(stream_entry.name)
179
221
  scalar_wrappers[stream_entry.name] = wrapper_name
180
- lines.extend(
181
- _generate_scalar_wrapper_message(stream_entry.name, list_inner_type)
182
- )
222
+ lines.extend(_generate_scalar_wrapper_message(stream_entry.name, list_inner_type))
183
223
 
184
224
 
185
225
  def _collect_message_types(
@@ -196,25 +236,17 @@ def _collect_message_types(
196
236
  _process_stream_payload(stream_entry, seen_models, lines, scalar_wrappers)
197
237
 
198
238
 
199
- def _generate_service_rpcs(
200
- bundle: RpcBundle, lines: list[str], scalar_wrappers: Dict[str, str]
201
- ) -> None:
239
+ def _generate_service_rpcs(bundle: RpcBundle, lines: list[str], scalar_wrappers: Dict[str, str]) -> None:
202
240
  rpc_prefix = " rpc"
203
241
 
204
242
  for action_entry in bundle.actions:
205
243
  input_type = _proto_type_name(action_entry.input_type, scalar_wrappers)
206
244
  output_type = _proto_type_name(action_entry.output_type, scalar_wrappers)
207
- lines.append(
208
- f"{rpc_prefix} {action_entry.name} ({input_type}) returns ({output_type});"
209
- )
245
+ lines.append(f"{rpc_prefix} {action_entry.name} ({input_type}) returns ({output_type});")
210
246
 
211
247
  for stream_entry in bundle.streams:
212
- output_type = _proto_type_name(
213
- stream_entry.payload_type, scalar_wrappers, stream_entry.name
214
- )
215
- lines.append(
216
- f"{rpc_prefix} {stream_entry.name} (google.protobuf.Empty) returns (stream {output_type});"
217
- )
248
+ output_type = _proto_type_name(stream_entry.payload_type, scalar_wrappers, stream_entry.name)
249
+ lines.append(f"{rpc_prefix} {stream_entry.name} (google.protobuf.Empty) returns (stream {output_type});")
218
250
 
219
251
 
220
252
  def generate_proto(app_name: str, *, bundle: Optional[RpcBundle] = None) -> str:
@@ -18,9 +18,7 @@ CONTENT_TYPE = "application/connect+json"
18
18
 
19
19
 
20
20
  def _frame(payload: Dict[str, Any], *, trailer: bool = False) -> bytes:
21
- body = json.dumps(payload, separators=(",", ":"), ensure_ascii=False).encode(
22
- "utf-8"
23
- )
21
+ body = json.dumps(payload, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
24
22
  flag = 0x80 if trailer else 0x00
25
23
  header = bytes([flag]) + len(body).to_bytes(4, byteorder="big", signed=False)
26
24
  return header + body
@@ -121,9 +119,7 @@ class StreamManager:
121
119
  """
122
120
  topic = self._topics[stream_name]
123
121
  entry: StreamEntry = topic["entry"]
124
- subscriber_queue: asyncio.Queue[Any] = asyncio.Queue(
125
- maxsize=entry.queue_maxsize
126
- )
122
+ subscriber_queue: asyncio.Queue[Any] = asyncio.Queue(maxsize=entry.queue_maxsize)
127
123
  subscriber = _Subscriber(queue=subscriber_queue)
128
124
  topic["subscribers"].add(subscriber)
129
125
  self.start_distributor_if_needed(stream_name)
@@ -217,7 +213,7 @@ class ConnectRouter:
217
213
  result = await _maybe_await(entry.func(validated_arg))
218
214
 
219
215
  if hasattr(result, "model_dump"):
220
- result = result.model_dump(by_alias=True)
216
+ result = result.model_dump(mode="json", by_alias=True)
221
217
  return JSONResponse(result or {})
222
218
  except Exception as exc:
223
219
  return JSONResponse(error_envelope(exc))
@@ -283,7 +279,7 @@ class ConnectRouter:
283
279
 
284
280
  def _serialize_stream_item(item: Any) -> Dict[str, Any]:
285
281
  if hasattr(item, "model_dump"):
286
- dumped = item.model_dump(by_alias=True)
282
+ dumped = item.model_dump(mode="json", by_alias=True)
287
283
  if isinstance(dumped, dict):
288
284
  return dumped
289
285
  return {"value": dumped}
@@ -38,9 +38,7 @@ def action(
38
38
  def decorator(function: Callable[..., Any]) -> Callable[..., Any]:
39
39
  input_type = infer_input_type(function)
40
40
  output_type = infer_output_type(function)
41
- entry = ActionEntry(
42
- name or function.__name__, function, input_type, output_type
43
- )
41
+ entry = ActionEntry(name or function.__name__, function, input_type, output_type)
44
42
  _actions.append(entry)
45
43
  return function
46
44
 
@@ -71,9 +69,7 @@ def stream(
71
69
  Decorator function that registers the stream
72
70
  """
73
71
  if not (is_pydantic_model(payload) or payload in (int, float, str, bool, dict)):
74
- raise ValueError(
75
- "payload must be a pydantic BaseModel or a JSON-serializable scalar/dict"
76
- )
72
+ raise ValueError("payload must be a pydantic BaseModel or a JSON-serializable scalar/dict")
77
73
 
78
74
  def decorator(function: Callable[..., Any]) -> Callable[..., Any]:
79
75
  entry = StreamEntry(
communication/errors.py CHANGED
@@ -5,9 +5,7 @@ from typing import Any, Dict, List, Optional
5
5
  class ConnectError(Exception):
6
6
  """Application-level error to send over Connect transport."""
7
7
 
8
- def __init__(
9
- self, code: str, message: str, *, details: Optional[List[Any]] = None
10
- ) -> None:
8
+ def __init__(self, code: str, message: str, *, details: Optional[List[Any]] = None) -> None:
11
9
  super().__init__(message)
12
10
  self.code = code
13
11
  self.message = message
@@ -39,9 +39,7 @@ class RpcRegistry:
39
39
  return merged
40
40
 
41
41
  # ------------- Model normalization / aliasing -------------
42
- def _normalize_model(
43
- self, model: Optional[Type[BaseModel]], seen: Set[Type[BaseModel]]
44
- ) -> None:
42
+ def _normalize_model(self, model: Optional[Type[BaseModel]], seen: Set[Type[BaseModel]]) -> None:
45
43
  """Recursively normalize a model and all its nested models."""
46
44
  if model is None:
47
45
  return
@@ -94,9 +94,7 @@ def is_pydantic_model(type_annotation: Any) -> bool:
94
94
  True if the type is a Pydantic BaseModel subclass, False otherwise
95
95
  """
96
96
  try:
97
- return isinstance(type_annotation, type) and issubclass(
98
- type_annotation, BaseModel
99
- )
97
+ return isinstance(type_annotation, type) and issubclass(type_annotation, BaseModel)
100
98
  except Exception:
101
99
  return False
102
100
 
@@ -120,9 +118,7 @@ def apply_aliases(model_cls: Type[BaseModel]) -> None:
120
118
  if existing_config is None:
121
119
  existing_dict: dict[str, Any] = {}
122
120
  else:
123
- existing_dict = (
124
- dict(existing_config) if isinstance(existing_config, dict) else {}
125
- )
121
+ existing_dict = dict(existing_config) if isinstance(existing_config, dict) else {}
126
122
 
127
123
  merged_config: dict[str, Any] = {
128
124
  "populate_by_name": True,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vention-communication
3
- Version: 0.3.0
3
+ Version: 0.3.4
4
4
  Summary: A framework for communication between machine apps and other services.
5
5
  License: Proprietary
6
6
  Author: VentionCo
@@ -8,8 +8,22 @@ Requires-Python: >=3.10,<3.11
8
8
  Classifier: License :: Other/Proprietary License
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: Programming Language :: Python :: 3.10
11
- Requires-Dist: fastapi (==0.121.1)
12
- Requires-Dist: uvicorn (>=0.35.0,<0.36.0)
11
+ Requires-Dist: annotated-doc (==0.0.4) ; python_version == "3.10"
12
+ Requires-Dist: annotated-types (==0.7.0) ; python_version == "3.10"
13
+ Requires-Dist: anyio (==4.11.0) ; python_version == "3.10"
14
+ Requires-Dist: click (==8.1.8) ; python_version == "3.10"
15
+ Requires-Dist: colorama (==0.4.6) ; python_version == "3.10" and platform_system == "Windows"
16
+ Requires-Dist: exceptiongroup (==1.3.0) ; python_version == "3.10"
17
+ Requires-Dist: fastapi (==0.121.1) ; python_version == "3.10"
18
+ Requires-Dist: h11 (==0.16.0) ; python_version == "3.10"
19
+ Requires-Dist: idna (==3.11) ; python_version == "3.10"
20
+ Requires-Dist: pydantic (==2.12.3) ; python_version == "3.10"
21
+ Requires-Dist: pydantic-core (==2.41.4) ; python_version == "3.10"
22
+ Requires-Dist: sniffio (==1.3.1) ; python_version == "3.10"
23
+ Requires-Dist: starlette (==0.48.0) ; python_version == "3.10"
24
+ Requires-Dist: typing-extensions (==4.15.0) ; python_version == "3.10"
25
+ Requires-Dist: typing-inspection (==0.4.2) ; python_version == "3.10"
26
+ Requires-Dist: uvicorn (==0.35.0) ; python_version == "3.10"
13
27
  Description-Content-Type: text/markdown
14
28
 
15
29
  # Vention Communication
@@ -88,8 +102,10 @@ A complete "hello world" in three steps.
88
102
 
89
103
  ```python
90
104
  from pydantic import BaseModel
91
- from vention_communication import VentionApp, action, stream
92
- import time, random
105
+ from communication.app import VentionApp
106
+ from communication.decorators import action, stream
107
+ import time
108
+ import random
93
109
 
94
110
  class PingRequest(BaseModel):
95
111
  message: str
@@ -114,6 +130,15 @@ async def heartbeat():
114
130
 
115
131
  app.finalize()
116
132
 
133
+ # Emit heartbeat every second
134
+ @app.on_event("startup")
135
+ async def startup():
136
+ asyncio.create_task(loop())
137
+
138
+ async def loop():
139
+ while True:
140
+ asyncio.create_task(heartbeat())
141
+ await asyncio.sleep(1)
117
142
  ```
118
143
 
119
144
  **Run:**
@@ -128,21 +153,59 @@ Endpoints are automatically registered under `/rpc/vention.app.v1.DemoAppService
128
153
 
129
154
  After startup, `proto/app.proto` is emitted automatically.
130
155
 
131
- You can now use Buf or protoc to generate client SDKs:
156
+ You can now use Buf or protoc to generate client SDKs, based on each client stack you desire.
157
+
158
+ The next section will provide an example for Typescript applications, but for any other environment, please refer to the official documentation on how to install and quickstart code generation:
159
+
160
+ https://buf.build/docs/cli/installation/
161
+
162
+ ### 3. Example TypeScript Client
163
+
164
+ Make sure you have Node 24+ installed. Use [NVM](https://github.com/nvm-sh/nvm) to easily install and manage different Node versions.
165
+
166
+ 1. Create a folder called `client` and `cd` into it.
167
+
168
+ #### Protobuf Javascript/Typescript libraries
169
+
170
+ 2. Install the runtime library, code generator, and the Buf CLI:
132
171
 
133
172
  ```bash
134
- buf generate --template buf.gen.ts.yaml
135
- buf generate --template buf.gen.python.yaml
173
+ npm install @bufbuild/protobuf
174
+ npm install --save-dev @bufbuild/protoc-gen-es @bufbuild/buf
136
175
  ```
137
176
 
138
- SDK generation is external to vention-communication allowing you to control versions and plugins.
177
+ 3. Create a buf.gen.yaml file that looks like this:
139
178
 
140
- ### 3. Example TypeScript Client
179
+ ```yaml
180
+ version: v2
181
+ inputs:
182
+ - directory: proto
183
+ plugins:
184
+ - local: protoc-gen-es
185
+ opt: target=ts
186
+ out: src/gen
187
+ ```
188
+
189
+ 4. Generate the client code, pointing the path to the newly generated *proto* folder:
190
+
191
+ ```bash
192
+ npx buf generate ../proto
193
+ ```
194
+
195
+ #### Client Application
196
+
197
+ 1. Install the client RPC libraries:
198
+
199
+ ```bash
200
+ npm i @connectrpc/connect @connectrpc/connect-web
201
+ ```
202
+
203
+ 2. Then create an `index.ts` file in the `client/src` folder, and paste the following code in it:
141
204
 
142
205
  ```typescript
143
206
  import { createClient } from "@connectrpc/connect";
144
207
  import { createConnectTransport } from "@connectrpc/connect-web";
145
- import { DemoAppService } from "./gen/connect/proto/app_connect";
208
+ import { DemoAppService } from "./gen/connect/app_pb.ts";
146
209
 
147
210
  const transport = createConnectTransport({
148
211
  baseUrl: "http://localhost:8000/rpc",
@@ -164,8 +227,11 @@ for await (const hb of client.heartbeat({})) {
164
227
  ### Add a new request-response endpoint
165
228
 
166
229
  ```python
230
+ class StatusResponse(BaseModel):
231
+ ok: bool
232
+
167
233
  @action()
168
- async def get_status() -> dict:
234
+ async def get_status() -> StatusResponse:
169
235
  return {"ok": True}
170
236
  ```
171
237
 
@@ -173,7 +239,7 @@ async def get_status() -> dict:
173
239
 
174
240
  ```python
175
241
  @stream(name="Status", payload=dict)
176
- async def publish_status() -> dict:
242
+ async def publish_status() -> StatusResponse:
177
243
  return {"ok": True}
178
244
  ```
179
245
 
@@ -0,0 +1,13 @@
1
+ communication/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ communication/app.py,sha256=dxbm7oG8-uQSu_dY4S8exHsBXND1N1nC2GdsMthqZE0,3423
3
+ communication/codegen.py,sha256=uY-IJ1QggjNPxiGCGfBPhTAL8p_nzYhxSDshkDqm_EQ,8464
4
+ communication/connect_router.py,sha256=fWn2KA8FAKYx1J_6RgtQSqS5hzAdPGEBn4Uk3UlqfqM,10517
5
+ communication/decorators.py,sha256=a16UUi-J2zi9fB3CxMtOygL74VOlCZTrL3hYkyBHvGo,3375
6
+ communication/entries.py,sha256=vdZc8GAQztRWEiav6R2wM4l35GE-EiEdRH0ZJR4GShM,1065
7
+ communication/errors.py,sha256=pwFcb-uce-aPkSXIrM7Ov7XQQXJrNQIgKgRU9QEWrqY,1759
8
+ communication/registry.py,sha256=acbhwU0z1iRHqzOahXG47GfJS5VQHjf69UiruoXrr7g,4517
9
+ communication/rpc_registry.py,sha256=WLu5kRZdZsMr-TCGEXzsAIMQJFk4rPctB4mL53NU70U,2972
10
+ communication/typing_utils.py,sha256=I3FdTTlvcPfiyf7t4nw4--eXf82opwjdGXfVYtprqoU,4513
11
+ vention_communication-0.3.4.dist-info/METADATA,sha256=2cwpJSwgqcpDC7KjGv4HSZbVYkG7lSUEKG4tVhMdGhw,13546
12
+ vention_communication-0.3.4.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
13
+ vention_communication-0.3.4.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- communication/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- communication/app.py,sha256=dxbm7oG8-uQSu_dY4S8exHsBXND1N1nC2GdsMthqZE0,3423
3
- communication/codegen.py,sha256=_1LYma-MXedwRHn_P3CqPs1xIMEZrZgiUAsGSj9yff4,7089
4
- communication/connect_router.py,sha256=tjNl9dRukjLecZH1y-GYIQXH-0xzz9JESbikSH5XhFo,10527
5
- communication/decorators.py,sha256=3pVlXUSX4KXSKlweskF0RfD8pST2zisTuFGJQHHIvcI,3419
6
- communication/entries.py,sha256=vdZc8GAQztRWEiav6R2wM4l35GE-EiEdRH0ZJR4GShM,1065
7
- communication/errors.py,sha256=hdJBB9jPJNWx8hbxIxwLBNKt2JVpmhZ1YF8q9VKk-dI,1773
8
- communication/registry.py,sha256=acbhwU0z1iRHqzOahXG47GfJS5VQHjf69UiruoXrr7g,4517
9
- communication/rpc_registry.py,sha256=90r4YYHKveVGBO-pOD91qAs9GSB3651WPclC6oRnrZo,2986
10
- communication/typing_utils.py,sha256=6S6LvtjFBZKo-gWPc8fbh4F1rlPWFgrH_mX8esZbzWM,4559
11
- vention_communication-0.3.0.dist-info/METADATA,sha256=e6h67l5DGs9LSAL_eBan_spH60FDQfBVcmeyoNWrlvg,11255
12
- vention_communication-0.3.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
13
- vention_communication-0.3.0.dist-info/RECORD,,