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.
Files changed (73) hide show
  1. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/PKG-INFO +11 -3
  2. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/README.md +8 -0
  3. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/pyproject.toml +46 -39
  4. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/brokers/amqp_client.py +1 -1
  5. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/brokers/mqtt_client.py +1 -1
  6. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/data_plane/data_plane_manager.py +1 -1
  7. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/event_metadata.py +1 -1
  8. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/interfaces.py +21 -7
  9. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/messages/userspace.py +3 -3
  10. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/schema.py +3 -3
  11. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/app_lifecycle.py +1 -1
  12. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/capability/base.py +31 -1
  13. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/client.py +5 -5
  14. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/config/shared.py +1 -1
  15. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/schema.py +5 -5
  16. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/service.py +121 -19
  17. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/version.py +1 -1
  18. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/e2e/test_examples.py +7 -0
  19. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/fixtures/example_schema.json +1 -1
  20. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/fixtures/example_schema.py +12 -11
  21. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/integration/test_return_type_mismatch.py +0 -1
  22. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/integration/test_service.py +0 -1
  23. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_annotations.py +3 -2
  24. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_base_capability_implementation.py +14 -0
  25. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_config.py +2 -1
  26. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_invalid_schema_runtime.py +1 -0
  27. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_lifecycle_message.py +2 -1
  28. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_schema_invalids.py +8 -7
  29. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_schema_valid.py +46 -3
  30. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_userspace_message.py +2 -1
  31. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/unit/test_version_resolver.py +1 -0
  32. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/LICENSE +0 -0
  33. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/__init__.py +17 -17
  34. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/__init__.py +0 -0
  35. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/constants.py +0 -0
  36. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/__init__.py +0 -0
  37. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/brokers/__init__.py +0 -0
  38. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/brokers/broker_client.py +0 -0
  39. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/control_plane_manager.py +0 -0
  40. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/discovery_service.py +0 -0
  41. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/control_plane/topic_handler.py +0 -0
  42. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/data_plane/__init__.py +0 -0
  43. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/data_plane/minio_utils.py +0 -0
  44. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/exceptions.py +0 -0
  45. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/function_metadata.py +0 -0
  46. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/logger.py +0 -0
  47. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/messages/__init__.py +0 -0
  48. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/messages/event.py +0 -0
  49. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/messages/lifecycle.py +0 -0
  50. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/multi_flag_thread_event.py +0 -0
  51. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/pydantic_schema_generator.py +0 -0
  52. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/stoppable_thread.py +0 -0
  53. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/utils.py +0 -0
  54. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/version.py +0 -0
  55. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/_internal/version_resolver.py +0 -0
  56. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/capability/__init__.py +0 -0
  57. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/client_callback_definitions.py +0 -0
  58. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/config/__init__.py +0 -0
  59. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/config/client.py +0 -0
  60. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/config/service.py +0 -0
  61. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/constants.py +0 -0
  62. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/core_definitions.py +0 -0
  63. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/py.typed +0 -0
  64. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/service_callback_definitions.py +0 -0
  65. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/service_definitions.py +0 -0
  66. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/src/intersect_sdk/shared_callback_definitions.py +0 -0
  67. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/__init__.py +0 -0
  68. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/conftest.py +0 -0
  69. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/e2e/__init__.py +0 -0
  70. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/fixtures/__init__.py +0 -0
  71. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/fixtures/return_type_mismatch.py +0 -0
  72. {intersect_sdk-0.8.0 → intersect_sdk-0.8.2}/tests/integration/__init__.py +0 -0
  73. {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.0
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
+ [![Static Badge](https://img.shields.io/badge/DOI-10.11578%2Fdc.20240927.1-blue)](https://doi.org/10.11578/dc.20240927.1)
32
+ [![CI](https://github.com/INTERSECT-SDK/python-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/INTERSECT-SDK/python-sdk/actions/workflows/ci.yml)
33
+ [![Release to PyPi](https://github.com/INTERSECT-SDK/python-sdk/actions/workflows/publish.yml/badge.svg)](https://github.com/INTERSECT-SDK/python-sdk/actions/workflows/publish.yml)
34
+ [![PyPI version](https://badge.fury.io/py/intersect-sdk.svg)](https://badge.fury.io/py/intersect-sdk)
35
+ [![ReadTheDocs](https://readthedocs.org/projects/intersect-python-sdk/badge/?version=latest)](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
+ [![Static Badge](https://img.shields.io/badge/DOI-10.11578%2Fdc.20240927.1-blue)](https://doi.org/10.11578/dc.20240927.1)
2
+ [![CI](https://github.com/INTERSECT-SDK/python-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/INTERSECT-SDK/python-sdk/actions/workflows/ci.yml)
3
+ [![Release to PyPi](https://github.com/INTERSECT-SDK/python-sdk/actions/workflows/publish.yml/badge.svg)](https://github.com/INTERSECT-SDK/python-sdk/actions/workflows/publish.yml)
4
+ [![PyPI version](https://badge.fury.io/py/intersect-sdk.svg)](https://badge.fury.io/py/intersect-sdk)
5
+ [![ReadTheDocs](https://readthedocs.org/projects/intersect-python-sdk/badge/?version=latest)](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.0"
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.warn('Connection closed, reopening in 5 seconds: %s', reason)
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 immediatedly (False)
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.warn('WARNING: This service cannot support any MINIO instances')
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 = defintion key
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 abc import ABC, abstractmethod
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(ABC):
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 (capabilitiy) tells observer (i.e. service) to send an external request.
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 IntersectClientMessageParams object
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
+ ...
@@ -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 # noqa: TCH001 (this is runtime checked)
29
- from ...core_definitions import ( # noqa: TCH001 (this is runtime checked)
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: TCH001 (this is runtime checked)
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 capabilitiy may have an @intersect_status function')
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 %d' % 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 IntersectClientMessageParams object
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"{self._hierarchy.hierarchy_string('/')}/response",
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"{service.replace('.', '/')}/events",
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"{add_event.replace('.', '/')}/events",
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"{remove_event.replace('.', '/')}/events"
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"{params.destination.replace('.', '/')}/request"
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 registring this service in a system-of-system architecture."""
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
- - 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.
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.