intersect-sdk 0.8.0__tar.gz → 0.8.2__tar.gz
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.
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/PKG-INFO +11 -3
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/README.md +8 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/pyproject.toml +46 -39
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/brokers/amqp_client.py +1 -1
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/brokers/mqtt_client.py +1 -1
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/data_plane/data_plane_manager.py +1 -1
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/event_metadata.py +1 -1
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/interfaces.py +21 -7
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/messages/userspace.py +3 -3
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/schema.py +3 -3
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/app_lifecycle.py +1 -1
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/capability/base.py +31 -1
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/client.py +5 -5
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/config/shared.py +1 -1
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/schema.py +5 -5
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/service.py +121 -19
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/version.py +1 -1
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/e2e/test_examples.py +7 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/fixtures/example_schema.json +1 -1
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/fixtures/example_schema.py +12 -11
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/integration/test_return_type_mismatch.py +0 -1
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/integration/test_service.py +0 -1
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_annotations.py +3 -2
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_base_capability_implementation.py +14 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_config.py +2 -1
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_invalid_schema_runtime.py +1 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_lifecycle_message.py +2 -1
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_schema_invalids.py +8 -7
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_schema_valid.py +46 -3
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_userspace_message.py +2 -1
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_version_resolver.py +1 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/LICENSE +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/__init__.py +17 -17
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/__init__.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/constants.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/__init__.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/brokers/__init__.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/brokers/broker_client.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/control_plane_manager.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/discovery_service.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/topic_handler.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/data_plane/__init__.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/data_plane/minio_utils.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/exceptions.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/function_metadata.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/logger.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/messages/__init__.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/messages/event.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/messages/lifecycle.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/multi_flag_thread_event.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/pydantic_schema_generator.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/stoppable_thread.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/utils.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/version.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/version_resolver.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/capability/__init__.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/client_callback_definitions.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/config/__init__.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/config/client.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/config/service.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/constants.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/core_definitions.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/py.typed +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/service_callback_definitions.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/service_definitions.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/shared_callback_definitions.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/__init__.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/conftest.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/e2e/__init__.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/fixtures/__init__.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/fixtures/return_type_mismatch.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/integration/__init__.py +0 -0
- {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: intersect-sdk
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.2
|
|
4
4
|
Summary: Python SDK to interact with INTERSECT
|
|
5
5
|
Keywords: intersect
|
|
6
6
|
Author-Email: Lance Drane <dranelt@ornl.gov>, Marshall McDonnell <mcdonnellmt@ornl.gov>, Seth Hitefield <hitefieldsd@ornl.gov>, Andrew Ayres <ayresaf@ornl.gov>, Gregory Cage <cagege@ornl.gov>, Jesse McGaha <mcgahajr@ornl.gov>, Robert Smith <smithrw@ornl.gov>, Gavin Wiggins <wigginsg@ornl.gov>, Michael Brim <brimmj@ornl.gov>, Rick Archibald <archibaldrk@ornl.gov>, Addi Malviya Thakur <malviyaa@ornl.gov>
|
|
@@ -21,13 +21,19 @@ Requires-Dist: paho-mqtt<2.0.0,>=1.6.1
|
|
|
21
21
|
Requires-Dist: minio>=7.2.3
|
|
22
22
|
Requires-Dist: jsonschema[format-nongpl]>=4.21.1
|
|
23
23
|
Requires-Dist: eval-type-backport>=0.1.3; python_version < "3.10"
|
|
24
|
+
Provides-Extra: amqp
|
|
24
25
|
Requires-Dist: pika<2.0.0,>=1.3.2; extra == "amqp"
|
|
26
|
+
Provides-Extra: docs
|
|
25
27
|
Requires-Dist: sphinx>=5.3.0; extra == "docs"
|
|
26
28
|
Requires-Dist: furo>=2023.3.27; extra == "docs"
|
|
27
|
-
Provides-Extra: amqp
|
|
28
|
-
Provides-Extra: docs
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
|
|
31
|
+
[](https://doi.org/10.11578/dc.20240927.1)
|
|
32
|
+
[](https://github.com/INTERSECT-SDK/python-sdk/actions/workflows/ci.yml)
|
|
33
|
+
[](https://github.com/INTERSECT-SDK/python-sdk/actions/workflows/publish.yml)
|
|
34
|
+
[](https://badge.fury.io/py/intersect-sdk)
|
|
35
|
+
[](https://intersect-python-sdk.readthedocs.io/en/latest/)
|
|
36
|
+
|
|
31
37
|
# INTERSECT-SDK
|
|
32
38
|
|
|
33
39
|
The INTERSECT-SDK is a framework for microservices to integrate themselves into the wider Interconnected Science Ecosystem (INTERSECT).
|
|
@@ -37,6 +43,8 @@ Please note that this README is currently a work in progress.
|
|
|
37
43
|
## What is INTERSECT?
|
|
38
44
|
[INTERSECT](https://www.ornl.gov/intersect) was designed as a specific usecase - as an open federated hardware/software architecture for the laboratory of the future, which connects scientific instruments, robot-controlled laboratories and edge/center computing/data resources to enable autonomous experiments, self-driving laboratories, smart manufacturing, and AI-driven design, discovery and evaluation.
|
|
39
45
|
|
|
46
|
+
For a high-level overview, please see [the architecture website.](https://intersect-architecture.readthedocs.io/en/latest/)
|
|
47
|
+
|
|
40
48
|
## What are the core design philosophies of the SDK?
|
|
41
49
|
|
|
42
50
|
- Event-driven architecture
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
[](https://doi.org/10.11578/dc.20240927.1)
|
|
2
|
+
[](https://github.com/INTERSECT-SDK/python-sdk/actions/workflows/ci.yml)
|
|
3
|
+
[](https://github.com/INTERSECT-SDK/python-sdk/actions/workflows/publish.yml)
|
|
4
|
+
[](https://badge.fury.io/py/intersect-sdk)
|
|
5
|
+
[](https://intersect-python-sdk.readthedocs.io/en/latest/)
|
|
6
|
+
|
|
1
7
|
# INTERSECT-SDK
|
|
2
8
|
|
|
3
9
|
The INTERSECT-SDK is a framework for microservices to integrate themselves into the wider Interconnected Science Ecosystem (INTERSECT).
|
|
@@ -7,6 +13,8 @@ Please note that this README is currently a work in progress.
|
|
|
7
13
|
## What is INTERSECT?
|
|
8
14
|
[INTERSECT](https://www.ornl.gov/intersect) was designed as a specific usecase - as an open federated hardware/software architecture for the laboratory of the future, which connects scientific instruments, robot-controlled laboratories and edge/center computing/data resources to enable autonomous experiments, self-driving laboratories, smart manufacturing, and AI-driven design, discovery and evaluation.
|
|
9
15
|
|
|
16
|
+
For a high-level overview, please see [the architecture website.](https://intersect-architecture.readthedocs.io/en/latest/)
|
|
17
|
+
|
|
10
18
|
## What are the core design philosophies of the SDK?
|
|
11
19
|
|
|
12
20
|
- Event-driven architecture
|
|
@@ -35,7 +35,7 @@ dependencies = [
|
|
|
35
35
|
"jsonschema[format-nongpl]>=4.21.1",
|
|
36
36
|
"eval-type-backport>=0.1.3;python_version<'3.10'",
|
|
37
37
|
]
|
|
38
|
-
version = "0.8.
|
|
38
|
+
version = "0.8.2"
|
|
39
39
|
|
|
40
40
|
[project.license]
|
|
41
41
|
text = "BSD-3-Clause"
|
|
@@ -55,42 +55,6 @@ docs = [
|
|
|
55
55
|
"furo>=2023.3.27",
|
|
56
56
|
]
|
|
57
57
|
|
|
58
|
-
[tool.pdm.dev-dependencies]
|
|
59
|
-
lint = [
|
|
60
|
-
"pre-commit>=3.3.1",
|
|
61
|
-
"ruff==0.5.7",
|
|
62
|
-
"mypy>=1.10.0",
|
|
63
|
-
"types-paho-mqtt>=1.6.0.20240106",
|
|
64
|
-
]
|
|
65
|
-
test = [
|
|
66
|
-
"pytest>=7.3.2",
|
|
67
|
-
"pytest-cov>=4.1.0",
|
|
68
|
-
"httpretty>=1.1.4",
|
|
69
|
-
]
|
|
70
|
-
|
|
71
|
-
[tool.pdm.scripts]
|
|
72
|
-
test-all = "pytest tests/ --cov=src/intersect_sdk/ --cov-fail-under=80 --cov-report=html:reports/htmlcov/ --cov-report=xml:reports/coverage_report.xml --junitxml=reports/junit.xml"
|
|
73
|
-
test-all-debug = "pytest tests/ --cov=src/intersect_sdk/ --cov-fail-under=80 --cov-report=html:reports/htmlcov/ --cov-report=xml:reports/coverage_report.xml --junitxml=reports/junit.xml -s"
|
|
74
|
-
test-unit = "pytest tests/unit --cov=src/intersect_sdk/"
|
|
75
|
-
test-e2e = "pytest tests/e2e --cov=src/intersect_sdk/"
|
|
76
|
-
lint-format = "ruff format"
|
|
77
|
-
lint-ruff = "ruff check --fix"
|
|
78
|
-
lint-mypy = "mypy src/intersect_sdk/"
|
|
79
|
-
|
|
80
|
-
[tool.pdm.scripts.lint]
|
|
81
|
-
composite = [
|
|
82
|
-
"lint-format",
|
|
83
|
-
"lint-ruff",
|
|
84
|
-
"lint-mypy",
|
|
85
|
-
]
|
|
86
|
-
|
|
87
|
-
[tool.pdm.build]
|
|
88
|
-
package-dir = "src"
|
|
89
|
-
|
|
90
|
-
[tool.pdm.version]
|
|
91
|
-
source = "file"
|
|
92
|
-
path = "src/intersect_sdk/version.py"
|
|
93
|
-
|
|
94
58
|
[tool.ruff]
|
|
95
59
|
line-length = 100
|
|
96
60
|
|
|
@@ -144,8 +108,6 @@ ignore = [
|
|
|
144
108
|
"COM812",
|
|
145
109
|
"ISC001",
|
|
146
110
|
"SIM105",
|
|
147
|
-
"ANN101",
|
|
148
|
-
"ANN102",
|
|
149
111
|
"ANN401",
|
|
150
112
|
"PLR2004",
|
|
151
113
|
]
|
|
@@ -231,6 +193,51 @@ exclude_also = [
|
|
|
231
193
|
"except.* ImportError",
|
|
232
194
|
]
|
|
233
195
|
|
|
196
|
+
[tool.pdm.dev-dependencies]
|
|
197
|
+
lint = [
|
|
198
|
+
"pre-commit>=3.3.1",
|
|
199
|
+
"ruff==0.9.4",
|
|
200
|
+
"mypy>=1.10.0",
|
|
201
|
+
"types-paho-mqtt>=1.6.0.20240106",
|
|
202
|
+
"codespell>=2.3.0",
|
|
203
|
+
]
|
|
204
|
+
test = [
|
|
205
|
+
"pytest>=7.3.2",
|
|
206
|
+
"pytest-cov>=4.1.0",
|
|
207
|
+
"httpretty>=1.1.4",
|
|
208
|
+
]
|
|
209
|
+
|
|
210
|
+
[tool.pdm.scripts]
|
|
211
|
+
test-all = "pytest tests/ --cov=src/intersect_sdk/ --cov-fail-under=80 --cov-report=html:reports/htmlcov/ --cov-report=xml:reports/coverage_report.xml --junitxml=reports/junit.xml"
|
|
212
|
+
test-all-debug = "pytest tests/ --cov=src/intersect_sdk/ --cov-fail-under=80 --cov-report=html:reports/htmlcov/ --cov-report=xml:reports/coverage_report.xml --junitxml=reports/junit.xml -s"
|
|
213
|
+
test-unit = "pytest tests/unit --cov=src/intersect_sdk/"
|
|
214
|
+
test-e2e = "pytest tests/e2e --cov=src/intersect_sdk/"
|
|
215
|
+
lint-docs = "sphinx-build -W --keep-going docs docs/_build"
|
|
216
|
+
lint-format = "ruff format"
|
|
217
|
+
lint-ruff = "ruff check --fix"
|
|
218
|
+
lint-mypy = "mypy src/intersect_sdk/"
|
|
219
|
+
lint-spelling = "codespell -w docs examples src tests *.md -S docs/_build"
|
|
220
|
+
|
|
221
|
+
[tool.pdm.scripts.lint]
|
|
222
|
+
composite = [
|
|
223
|
+
"lint-format",
|
|
224
|
+
"lint-ruff",
|
|
225
|
+
"lint-mypy",
|
|
226
|
+
"lint-spelling",
|
|
227
|
+
"lint-docs",
|
|
228
|
+
]
|
|
229
|
+
|
|
230
|
+
[tool.pdm.build]
|
|
231
|
+
package-dir = "src"
|
|
232
|
+
|
|
233
|
+
[tool.pdm.version]
|
|
234
|
+
source = "file"
|
|
235
|
+
path = "src/intersect_sdk/version.py"
|
|
236
|
+
|
|
237
|
+
[tool.codespell]
|
|
238
|
+
skip = ".git*,*.lock,.venv,.*cache/*,./docs/_build/*"
|
|
239
|
+
check-hidden = true
|
|
240
|
+
|
|
234
241
|
[build-system]
|
|
235
242
|
requires = [
|
|
236
243
|
"pdm-backend",
|
|
@@ -254,7 +254,7 @@ class AMQPClient(BrokerClient):
|
|
|
254
254
|
if self._should_disconnect:
|
|
255
255
|
connection.ioloop.stop()
|
|
256
256
|
else:
|
|
257
|
-
logger.
|
|
257
|
+
logger.warning('Connection closed, reopening in 5 seconds: %s', reason)
|
|
258
258
|
connection.ioloop.call_later(5, connection.ioloop.stop)
|
|
259
259
|
self._channel_flags.unset_all()
|
|
260
260
|
self._channel_out = None
|
|
@@ -113,7 +113,7 @@ class MQTTClient(BrokerClient):
|
|
|
113
113
|
topic: The topic on which to publish the message as a string.
|
|
114
114
|
payload: The message to publish, as raw bytes.
|
|
115
115
|
persist: Determine if the message should live until queue consumers or available (True), or
|
|
116
|
-
if it should be removed
|
|
116
|
+
if it should be removed immediately (False)
|
|
117
117
|
"""
|
|
118
118
|
# NOTE: RabbitMQ only works with QOS of 1 and 0, and seems to convert QOS2 to QOS1
|
|
119
119
|
self._connection.publish(topic, payload, qos=2 if persist else 0)
|
|
@@ -32,7 +32,7 @@ class DataPlaneManager:
|
|
|
32
32
|
|
|
33
33
|
# warn users about missing data plane
|
|
34
34
|
if not self._minio_providers:
|
|
35
|
-
logger.
|
|
35
|
+
logger.warning('WARNING: This service cannot support any MINIO instances')
|
|
36
36
|
|
|
37
37
|
def incoming_message_data_handler(self, message: UserspaceMessage | EventMessage) -> bytes:
|
|
38
38
|
"""Get data from the request data provider.
|
|
@@ -42,7 +42,7 @@ def definition_metadata_differences(
|
|
|
42
42
|
) -> list[tuple[str, str, str]]:
|
|
43
43
|
"""Return a list of differences between 'definition' and 'metadata'.
|
|
44
44
|
|
|
45
|
-
First tuple value =
|
|
45
|
+
First tuple value = definition key
|
|
46
46
|
Second tuple value = second value
|
|
47
47
|
Third tuple value = already cached value
|
|
48
48
|
"""
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from typing import TYPE_CHECKING, Any
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Protocol
|
|
5
4
|
|
|
6
5
|
if TYPE_CHECKING:
|
|
7
6
|
from uuid import UUID
|
|
8
7
|
|
|
8
|
+
from ..client_callback_definitions import INTERSECT_CLIENT_EVENT_CALLBACK_TYPE
|
|
9
|
+
from ..config.shared import HierarchyConfig
|
|
9
10
|
from ..service_callback_definitions import (
|
|
10
11
|
INTERSECT_SERVICE_RESPONSE_CALLBACK_TYPE,
|
|
11
12
|
)
|
|
@@ -14,13 +15,12 @@ if TYPE_CHECKING:
|
|
|
14
15
|
)
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
class IntersectEventObserver(
|
|
18
|
+
class IntersectEventObserver(Protocol):
|
|
18
19
|
"""Abstract definition of an entity which observes an INTERSECT event (i.e. IntersectService).
|
|
19
20
|
|
|
20
21
|
Used as the common interface for event emitters (i.e. CapabilityImplementations).
|
|
21
22
|
"""
|
|
22
23
|
|
|
23
|
-
@abstractmethod
|
|
24
24
|
def _on_observe_event(self, event_name: str, event_value: Any, operation: str) -> None:
|
|
25
25
|
"""How to react to an event being fired.
|
|
26
26
|
|
|
@@ -31,17 +31,16 @@ class IntersectEventObserver(ABC):
|
|
|
31
31
|
"""
|
|
32
32
|
...
|
|
33
33
|
|
|
34
|
-
@abstractmethod
|
|
35
34
|
def create_external_request(
|
|
36
35
|
self,
|
|
37
36
|
request: IntersectDirectMessageParams,
|
|
38
37
|
response_handler: INTERSECT_SERVICE_RESPONSE_CALLBACK_TYPE | None = None,
|
|
39
38
|
timeout: float = 300.0,
|
|
40
39
|
) -> UUID:
|
|
41
|
-
"""Observed entity (
|
|
40
|
+
"""Observed entity (capability) tells observer (i.e. service) to send an external request.
|
|
42
41
|
|
|
43
42
|
Params:
|
|
44
|
-
- request: the request we want to send out, encapsulated as an
|
|
43
|
+
- request: the request we want to send out, encapsulated as an IntersectDirectMessageParams object
|
|
45
44
|
- response_handler: optional callback for how we want to handle the response from this request.
|
|
46
45
|
- timeout: optional value for how long we should wait on the request, in seconds (default: 300 seconds)
|
|
47
46
|
|
|
@@ -49,3 +48,18 @@ class IntersectEventObserver(ABC):
|
|
|
49
48
|
- generated RequestID associated with your request
|
|
50
49
|
"""
|
|
51
50
|
...
|
|
51
|
+
|
|
52
|
+
def register_event(
|
|
53
|
+
self,
|
|
54
|
+
service: HierarchyConfig,
|
|
55
|
+
event_name: str,
|
|
56
|
+
response_handler: INTERSECT_CLIENT_EVENT_CALLBACK_TYPE,
|
|
57
|
+
) -> None:
|
|
58
|
+
"""Observed entity (capability) tells observer (i.e. service) to subscribe to a specific event.
|
|
59
|
+
|
|
60
|
+
Params:
|
|
61
|
+
- service: HierarchyConfig of the service we want to talk to
|
|
62
|
+
- event_name: name of event to subscribe to
|
|
63
|
+
- response_handler: callback for how to handle the reception of an event
|
|
64
|
+
"""
|
|
65
|
+
...
|
{intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/messages/userspace.py
RENAMED
|
@@ -25,13 +25,13 @@ from typing import Any, Union
|
|
|
25
25
|
from pydantic import AwareDatetime, Field, TypeAdapter
|
|
26
26
|
from typing_extensions import Annotated, TypedDict
|
|
27
27
|
|
|
28
|
-
from ...constants import SYSTEM_OF_SYSTEM_REGEX
|
|
29
|
-
from ...core_definitions import (
|
|
28
|
+
from ...constants import SYSTEM_OF_SYSTEM_REGEX
|
|
29
|
+
from ...core_definitions import (
|
|
30
30
|
IntersectDataHandler,
|
|
31
31
|
IntersectMimeType,
|
|
32
32
|
)
|
|
33
33
|
from ...version import version_string
|
|
34
|
-
from ..data_plane.minio_utils import MinioPayload # noqa:
|
|
34
|
+
from ..data_plane.minio_utils import MinioPayload # noqa: TC001 (this is runtime checked)
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class UserspaceMessageHeader(TypedDict):
|
|
@@ -143,9 +143,9 @@ def _get_functions(
|
|
|
143
143
|
logger.warning(
|
|
144
144
|
f"Class '{capability.__name__}' has no function annotated with the @intersect_status() decorator. No status information will be provided when sending status messages."
|
|
145
145
|
)
|
|
146
|
-
if not intersect_messages and not intersect_events:
|
|
146
|
+
if not intersect_messages and not intersect_events and not intersect_status:
|
|
147
147
|
die(
|
|
148
|
-
f"No intersect annotations detected on class '{capability.__name__}'. Please annotate at least one entrypoint with '@intersect_message()', or one event-emitting function with '@intersect_event()' ."
|
|
148
|
+
f"No intersect annotations detected on class '{capability.__name__}'. Please annotate at least one entrypoint with '@intersect_message()', or one event-emitting function with '@intersect_event()', or create an '@intersect_status' function."
|
|
149
149
|
)
|
|
150
150
|
return intersect_status, intersect_messages, intersect_events
|
|
151
151
|
|
|
@@ -557,7 +557,7 @@ def get_schema_and_functions_from_capability_implementations(
|
|
|
557
557
|
if cap_status_fn_name and cap_status_schema and cap_status_type_adapter:
|
|
558
558
|
if status_function_name is not None:
|
|
559
559
|
# TODO may want to change this later
|
|
560
|
-
die('Only one
|
|
560
|
+
die('Only one capability may have an @intersect_status function')
|
|
561
561
|
status_function_cap = capability_type
|
|
562
562
|
status_function_name = cap_status_fn_name
|
|
563
563
|
status_function_schema = cap_status_schema
|
|
@@ -82,7 +82,7 @@ class SignalHandler:
|
|
|
82
82
|
----------
|
|
83
83
|
signal (int): signal code.
|
|
84
84
|
"""
|
|
85
|
-
logger.warning('shutting down and handling signal %
|
|
85
|
+
logger.warning('shutting down and handling signal %s', signal)
|
|
86
86
|
if self._cleanup_callback:
|
|
87
87
|
self._cleanup_callback(signal)
|
|
88
88
|
self._exit.set()
|
|
@@ -14,6 +14,8 @@ if TYPE_CHECKING:
|
|
|
14
14
|
from uuid import UUID
|
|
15
15
|
|
|
16
16
|
from .._internal.interfaces import IntersectEventObserver
|
|
17
|
+
from ..client_callback_definitions import INTERSECT_CLIENT_EVENT_CALLBACK_TYPE
|
|
18
|
+
from ..config.shared import HierarchyConfig
|
|
17
19
|
from ..service_callback_definitions import (
|
|
18
20
|
INTERSECT_SERVICE_RESPONSE_CALLBACK_TYPE,
|
|
19
21
|
)
|
|
@@ -66,6 +68,8 @@ class IntersectBaseCapabilityImplementation:
|
|
|
66
68
|
is not IntersectBaseCapabilityImplementation.intersect_sdk_emit_event
|
|
67
69
|
or cls.intersect_sdk_call_service
|
|
68
70
|
is not IntersectBaseCapabilityImplementation.intersect_sdk_call_service
|
|
71
|
+
or cls.intersect_sdk_listen_for_service_event
|
|
72
|
+
is not IntersectBaseCapabilityImplementation.intersect_sdk_listen_for_service_event
|
|
69
73
|
):
|
|
70
74
|
msg = f"{cls.__name__}: Attempted to override a reserved INTERSECT-SDK function (don't start your function names with '_intersect_sdk_' or 'intersect_sdk_')"
|
|
71
75
|
raise RuntimeError(msg)
|
|
@@ -137,8 +141,10 @@ class IntersectBaseCapabilityImplementation:
|
|
|
137
141
|
) -> list[UUID]:
|
|
138
142
|
"""Create an external request that we'll send to a different Service.
|
|
139
143
|
|
|
144
|
+
Note: You should generally NOT call this function until after you have initialized the IntersectService class.
|
|
145
|
+
|
|
140
146
|
Params:
|
|
141
|
-
- request: the request we want to send out, encapsulated as an
|
|
147
|
+
- request: the request we want to send out, encapsulated as an IntersectDirectMessageParams object
|
|
142
148
|
- response_handler: optional callback for how we want to handle the response from this request.
|
|
143
149
|
- timeout: optional value for how long we should wait on the request, in seconds (default: 300 seconds)
|
|
144
150
|
|
|
@@ -153,3 +159,27 @@ class IntersectBaseCapabilityImplementation:
|
|
|
153
159
|
observer.create_external_request(request, response_handler, timeout)
|
|
154
160
|
for observer in self.__intersect_sdk_observers__
|
|
155
161
|
]
|
|
162
|
+
|
|
163
|
+
@final
|
|
164
|
+
def intersect_sdk_listen_for_service_event(
|
|
165
|
+
self,
|
|
166
|
+
service: HierarchyConfig,
|
|
167
|
+
event_name: str,
|
|
168
|
+
response_handler: INTERSECT_CLIENT_EVENT_CALLBACK_TYPE,
|
|
169
|
+
) -> None:
|
|
170
|
+
"""Start listening to events from a specific Service.
|
|
171
|
+
|
|
172
|
+
Note: You should generally NOT call this function until after you have initialized the IntersectService class.
|
|
173
|
+
|
|
174
|
+
Params:
|
|
175
|
+
- service: The system-of-system hierarchy which points to the specific service
|
|
176
|
+
- event_name: The name of the event we want to listen for
|
|
177
|
+
- response_handler: callback for how to handle the reception of an event
|
|
178
|
+
The callback submits these parameters:
|
|
179
|
+
1) message source
|
|
180
|
+
2) name of operation
|
|
181
|
+
3) name of event
|
|
182
|
+
4) payload
|
|
183
|
+
"""
|
|
184
|
+
for observer in self.__intersect_sdk_observers__:
|
|
185
|
+
observer.register_event(service, event_name, response_handler)
|
|
@@ -129,7 +129,7 @@ class IntersectClient:
|
|
|
129
129
|
if user_callback:
|
|
130
130
|
# Do not persist, as we use the temporary client information to build this.
|
|
131
131
|
self._control_plane_manager.add_subscription_channel(
|
|
132
|
-
f
|
|
132
|
+
f'{self._hierarchy.hierarchy_string("/")}/response',
|
|
133
133
|
{self._handle_userspace_message_raw},
|
|
134
134
|
persist=False,
|
|
135
135
|
)
|
|
@@ -140,7 +140,7 @@ class IntersectClient:
|
|
|
140
140
|
service
|
|
141
141
|
) in config.initial_message_event_config.services_to_start_listening_for_events:
|
|
142
142
|
self._control_plane_manager.add_subscription_channel(
|
|
143
|
-
f
|
|
143
|
+
f'{service.replace(".", "/")}/events',
|
|
144
144
|
{self._handle_event_message_raw},
|
|
145
145
|
persist=False,
|
|
146
146
|
)
|
|
@@ -390,13 +390,13 @@ class IntersectClient:
|
|
|
390
390
|
if self._event_callback:
|
|
391
391
|
for add_event in validated_result.services_to_start_listening_for_events:
|
|
392
392
|
self._control_plane_manager.add_subscription_channel(
|
|
393
|
-
f
|
|
393
|
+
f'{add_event.replace(".", "/")}/events',
|
|
394
394
|
{self._handle_event_message_raw},
|
|
395
395
|
persist=False,
|
|
396
396
|
)
|
|
397
397
|
for remove_event in validated_result.services_to_stop_listening_for_events:
|
|
398
398
|
self._control_plane_manager.remove_subscription_channel(
|
|
399
|
-
f
|
|
399
|
+
f'{remove_event.replace(".", "/")}/events'
|
|
400
400
|
)
|
|
401
401
|
|
|
402
402
|
# sending userspace messages without the callback is okay, we just won't get the response
|
|
@@ -431,7 +431,7 @@ class IntersectClient:
|
|
|
431
431
|
payload=out_payload,
|
|
432
432
|
)
|
|
433
433
|
logger.debug(f'Send userspace message:\n{msg}')
|
|
434
|
-
channel = f
|
|
434
|
+
channel = f'{params.destination.replace(".", "/")}/request'
|
|
435
435
|
# WARNING: If both the Service and the Client drop, the Service will execute the command
|
|
436
436
|
# but cannot communicate the response to the Client.
|
|
437
437
|
# in experiment controllers or production, you'll want to set persist to True
|
|
@@ -28,7 +28,7 @@ ControlProvider = Literal['mqtt3.1.1', 'amqp0.9.1']
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
class HierarchyConfig(BaseModel):
|
|
31
|
-
"""Configuration for
|
|
31
|
+
"""Configuration for registering this service in a system-of-system architecture."""
|
|
32
32
|
|
|
33
33
|
service: Annotated[str, Field(pattern=HIERARCHY_REGEX)]
|
|
34
34
|
"""
|
|
@@ -48,11 +48,11 @@ def get_schema_from_capability_implementations(
|
|
|
48
48
|
|
|
49
49
|
The generated schema is currently not a complete replica of the AsyncAPI spec. See https://www.asyncapi.com/docs/reference/specification/v2.6.0 for the complete specification.
|
|
50
50
|
Some key differences:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
- We utilize three custom fields: "capabilities", "events", and "status".
|
|
52
|
+
- "capabilities" contains a dictionary: the keys of this dictionary are capability names. The values are dictionaries with the "description" property being a string which describes the capability,
|
|
53
|
+
and a "channels" property which more closely follows the AsyncAPI specification of the top-level value "channels".
|
|
54
|
+
- "events" is a key-value dictionary: the keys represent the event name, the values represent the associated schema of the event type. Events are currently shared across all capabilities.
|
|
55
|
+
- "status" will have a value of the status schema - if no status has been defined, a null schema is used.
|
|
56
56
|
|
|
57
57
|
Params:
|
|
58
58
|
- capability_types - list of classes (not objects) of capabilities. We do not require capabilities to be instantiated here, in case the instantiation of a capability has external dependencies.
|