intersect-sdk 0.8.3__tar.gz → 0.9.0__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 (92) hide show
  1. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/PKG-INFO +9 -9
  2. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/README.md +2 -1
  3. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/pyproject.toml +23 -15
  4. intersect_sdk-0.9.0/src/intersect_sdk/__init__.py +130 -0
  5. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/constants.py +0 -2
  6. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/control_plane/brokers/amqp_client.py +38 -9
  7. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/control_plane/brokers/broker_client.py +5 -1
  8. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/control_plane/brokers/mqtt_client.py +109 -25
  9. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/control_plane/control_plane_manager.py +21 -19
  10. intersect_sdk-0.9.0/src/intersect_sdk/_internal/control_plane/definitions.py +10 -0
  11. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/control_plane/topic_handler.py +2 -4
  12. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/data_plane/data_plane_manager.py +20 -9
  13. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/data_plane/minio_utils.py +9 -4
  14. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/event_metadata.py +2 -6
  15. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/exceptions.py +1 -1
  16. intersect_sdk-0.9.0/src/intersect_sdk/_internal/function_metadata.py +52 -0
  17. intersect_sdk-0.9.0/src/intersect_sdk/_internal/generic_serializer.py +5 -0
  18. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/interfaces.py +4 -2
  19. intersect_sdk-0.9.0/src/intersect_sdk/_internal/messages/event.py +145 -0
  20. intersect_sdk-0.9.0/src/intersect_sdk/_internal/messages/lifecycle.py +107 -0
  21. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/messages/userspace.py +76 -78
  22. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/pydantic_schema_generator.py +1 -7
  23. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/schema.py +232 -154
  24. intersect_sdk-0.9.0/src/intersect_sdk/_internal/status_metadata.py +14 -0
  25. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/version.py +2 -2
  26. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/version_resolver.py +18 -17
  27. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/app_lifecycle.py +3 -1
  28. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/capability/__init__.py +0 -6
  29. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/capability/base.py +36 -49
  30. intersect_sdk-0.9.0/src/intersect_sdk/capability/universal_capability/__init__.py +4 -0
  31. intersect_sdk-0.9.0/src/intersect_sdk/capability/universal_capability/status.py +25 -0
  32. intersect_sdk-0.9.0/src/intersect_sdk/capability/universal_capability/universal_capability.py +60 -0
  33. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/client.py +61 -51
  34. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/client_callback_definitions.py +26 -19
  35. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/config/__init__.py +0 -11
  36. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/config/client.py +3 -3
  37. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/config/service.py +2 -3
  38. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/config/shared.py +7 -8
  39. intersect_sdk-0.9.0/src/intersect_sdk/constants.py +28 -0
  40. intersect_sdk-0.9.0/src/intersect_sdk/core_definitions.py +47 -0
  41. intersect_sdk-0.9.0/src/intersect_sdk/exceptions.py +40 -0
  42. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/schema.py +7 -6
  43. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/service.py +361 -240
  44. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/service_callback_definitions.py +7 -4
  45. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/service_definitions.py +25 -80
  46. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/shared_callback_definitions.py +34 -14
  47. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/version.py +1 -3
  48. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/fixtures/example_schema.json +384 -168
  49. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/fixtures/example_schema.py +83 -54
  50. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/integration/test_return_type_mismatch.py +17 -16
  51. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/integration/test_service.py +217 -93
  52. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/unit/test_annotations.py +19 -80
  53. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/unit/test_base_capability_implementation.py +10 -46
  54. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/unit/test_config.py +1 -1
  55. intersect_sdk-0.9.0/tests/unit/test_event_message_headers.py +119 -0
  56. intersect_sdk-0.9.0/tests/unit/test_imports.py +7 -0
  57. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/unit/test_invalid_schema_runtime.py +6 -3
  58. intersect_sdk-0.9.0/tests/unit/test_lifecycle_message_headers.py +101 -0
  59. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/unit/test_schema_invalids.py +71 -121
  60. intersect_sdk-0.9.0/tests/unit/test_schema_ref_resolution.py +55 -0
  61. intersect_sdk-0.9.0/tests/unit/test_schema_valid.py +157 -0
  62. intersect_sdk-0.9.0/tests/unit/test_userspace_message_headers.py +139 -0
  63. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/unit/test_version_resolver.py +39 -51
  64. intersect_sdk-0.8.3/src/intersect_sdk/__init__.py +0 -73
  65. intersect_sdk-0.8.3/src/intersect_sdk/_internal/control_plane/discovery_service.py +0 -40
  66. intersect_sdk-0.8.3/src/intersect_sdk/_internal/function_metadata.py +0 -31
  67. intersect_sdk-0.8.3/src/intersect_sdk/_internal/messages/event.py +0 -157
  68. intersect_sdk-0.8.3/src/intersect_sdk/_internal/messages/lifecycle.py +0 -174
  69. intersect_sdk-0.8.3/src/intersect_sdk/constants.py +0 -9
  70. intersect_sdk-0.8.3/src/intersect_sdk/core_definitions.py +0 -37
  71. intersect_sdk-0.8.3/tests/unit/test_lifecycle_message.py +0 -101
  72. intersect_sdk-0.8.3/tests/unit/test_schema_valid.py +0 -134
  73. intersect_sdk-0.8.3/tests/unit/test_userspace_message.py +0 -106
  74. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/LICENSE +0 -0
  75. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/__init__.py +0 -0
  76. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/control_plane/__init__.py +0 -0
  77. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/control_plane/brokers/__init__.py +0 -0
  78. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/data_plane/__init__.py +0 -0
  79. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/logger.py +0 -0
  80. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/messages/__init__.py +0 -0
  81. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/multi_flag_thread_event.py +0 -0
  82. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/stoppable_thread.py +0 -0
  83. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/_internal/utils.py +0 -0
  84. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/src/intersect_sdk/py.typed +0 -0
  85. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/__init__.py +0 -0
  86. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/conftest.py +0 -0
  87. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/e2e/__init__.py +0 -0
  88. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/e2e/test_examples.py +0 -0
  89. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/fixtures/__init__.py +0 -0
  90. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/fixtures/return_type_mismatch.py +0 -0
  91. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/integration/__init__.py +0 -0
  92. {intersect_sdk-0.8.3 → intersect_sdk-0.9.0}/tests/unit/__init__.py +0 -0
@@ -1,28 +1,27 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: intersect-sdk
3
- Version: 0.8.3
3
+ Version: 0.9.0
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>
7
7
  License: BSD-3-Clause
8
8
  Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.8
10
- Classifier: Programming Language :: Python :: 3.9
11
9
  Classifier: Programming Language :: Python :: 3.10
12
10
  Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
13
  Project-URL: Homepage, https://github.com/INTERSECT-SDK/python-sdk/
14
14
  Project-URL: Changelog, https://github.com/INTERSECT-SDK/python-sdk/blob/main/CHANGELOG.md
15
15
  Project-URL: Documentation, https://intersect-python-sdk.readthedocs.io/en/latest/
16
16
  Project-URL: Issues, https://github.com/INTERSECT-SDK/python-sdk/issues
17
- Requires-Python: <4.0,>=3.8.10
17
+ Requires-Python: <4.0,>=3.10
18
18
  Requires-Dist: pydantic>=2.7.0
19
19
  Requires-Dist: retrying<2.0.0,>=1.3.4
20
- Requires-Dist: paho-mqtt<2.0.0,>=1.6.1
20
+ Requires-Dist: paho-mqtt<3.0.0,>=2.1.0
21
+ Requires-Dist: pika<2.0.0,>=1.3.2
21
22
  Requires-Dist: minio>=7.2.3
22
23
  Requires-Dist: jsonschema[format-nongpl]>=4.21.1
23
- Requires-Dist: eval-type-backport>=0.1.3; python_version < "3.10"
24
- Provides-Extra: amqp
25
- Requires-Dist: pika<2.0.0,>=1.3.2; extra == "amqp"
24
+ Requires-Dist: psutil>=7.0.0
26
25
  Provides-Extra: docs
27
26
  Requires-Dist: sphinx>=5.3.0; extra == "docs"
28
27
  Requires-Dist: furo>=2023.3.27; extra == "docs"
@@ -49,7 +48,8 @@ For a high-level overview, please see [the architecture website.](https://inters
49
48
 
50
49
  - Event-driven architecture
51
50
  - Support core interaction types: request/response, events, commands, statuses
52
- - Borrows several concepts from [AsyncAPI](https://www.asyncapi.com/docs/reference/specification/latest), and intends to support multiple different protocols. Currently, we support MQTT 3.1.1 and AMQP 0.9.1, but other protocols will be supported as well.
51
+ - Borrows several concepts from [AsyncAPI](https://www.asyncapi.com/docs/reference/specification/latest), and intends to support multiple different protocols. Currently, we support MQTT 5.0 and AMQP 0.9.1, but other protocols will be supported as well.
52
+ - As a general rule, we will not support any protocols which do not support headers, do not allow for asynchronous messaging, or require the microservice itself to "keep alive" multiple connections.
53
53
  - Users automatically generate schema from code; schemas are part of the core contract of an INTERSECT microservice, and both external inputs and microservice outputs are required to uphold this contract.
54
54
 
55
55
  ## Authors
@@ -19,7 +19,8 @@ For a high-level overview, please see [the architecture website.](https://inters
19
19
 
20
20
  - Event-driven architecture
21
21
  - Support core interaction types: request/response, events, commands, statuses
22
- - Borrows several concepts from [AsyncAPI](https://www.asyncapi.com/docs/reference/specification/latest), and intends to support multiple different protocols. Currently, we support MQTT 3.1.1 and AMQP 0.9.1, but other protocols will be supported as well.
22
+ - Borrows several concepts from [AsyncAPI](https://www.asyncapi.com/docs/reference/specification/latest), and intends to support multiple different protocols. Currently, we support MQTT 5.0 and AMQP 0.9.1, but other protocols will be supported as well.
23
+ - As a general rule, we will not support any protocols which do not support headers, do not allow for asynchronous messaging, or require the microservice itself to "keep alive" multiple connections.
23
24
  - Users automatically generate schema from code; schemas are part of the core contract of an INTERSECT microservice, and both external inputs and microservice outputs are required to uphold this contract.
24
25
 
25
26
  ## Authors
@@ -15,27 +15,28 @@ authors = [
15
15
  { name = "Addi Malviya Thakur", email = "malviyaa@ornl.gov" },
16
16
  ]
17
17
  readme = "README.md"
18
- requires-python = ">=3.8.10,<4.0"
18
+ requires-python = ">=3.10,<4.0"
19
19
  keywords = [
20
20
  "intersect",
21
21
  ]
22
22
  dynamic = []
23
23
  classifiers = [
24
24
  "Programming Language :: Python :: 3",
25
- "Programming Language :: Python :: 3.8",
26
- "Programming Language :: Python :: 3.9",
27
25
  "Programming Language :: Python :: 3.10",
28
26
  "Programming Language :: Python :: 3.11",
27
+ "Programming Language :: Python :: 3.12",
28
+ "Programming Language :: Python :: 3.13",
29
29
  ]
30
30
  dependencies = [
31
31
  "pydantic>=2.7.0",
32
32
  "retrying>=1.3.4,<2.0.0",
33
- "paho-mqtt>=1.6.1,<2.0.0",
33
+ "paho-mqtt>=2.1.0,<3.0.0",
34
+ "pika>=1.3.2,<2.0.0",
34
35
  "minio>=7.2.3",
35
36
  "jsonschema[format-nongpl]>=4.21.1",
36
- "eval-type-backport>=0.1.3;python_version<'3.10'",
37
+ "psutil>=7.0.0",
37
38
  ]
38
- version = "0.8.3"
39
+ version = "0.9.0"
39
40
 
40
41
  [project.license]
41
42
  text = "BSD-3-Clause"
@@ -47,9 +48,6 @@ Documentation = "https://intersect-python-sdk.readthedocs.io/en/latest/"
47
48
  Issues = "https://github.com/INTERSECT-SDK/python-sdk/issues"
48
49
 
49
50
  [project.optional-dependencies]
50
- amqp = [
51
- "pika>=1.3.2,<2.0.0",
52
- ]
53
51
  docs = [
54
52
  "sphinx>=5.3.0",
55
53
  "furo>=2023.3.27",
@@ -95,7 +93,7 @@ extend-select = [
95
93
  "SLF",
96
94
  "SLOT",
97
95
  "SIM",
98
- "TCH",
96
+ "TC",
99
97
  "ARG",
100
98
  "PTH",
101
99
  "PGH",
@@ -103,6 +101,7 @@ extend-select = [
103
101
  "TRY",
104
102
  "FLY",
105
103
  "RUF",
104
+ "INT",
106
105
  ]
107
106
  ignore = [
108
107
  "COM812",
@@ -130,12 +129,23 @@ max-complexity = 20
130
129
  [tool.ruff.lint.pylint]
131
130
  max-args = 10
132
131
  max-branches = 20
133
- max-returns = 10
132
+ max-returns = 15
134
133
  max-statements = 75
135
134
 
135
+ [tool.ruff.lint.flake8-type-checking]
136
+ runtime-evaluated-base-classes = [
137
+ "pydantic.BaseModel",
138
+ "intersect_sdk.IntersectBaseCapabilityImplementation",
139
+ ]
140
+ runtime-evaluated-decorators = [
141
+ "pydantic.dataclasses.dataclass",
142
+ "pydantic.validate_call",
143
+ ]
144
+
136
145
  [tool.ruff.lint.extend-per-file-ignores]
137
146
  "__init__.py" = [
138
147
  "F401",
148
+ "TC004",
139
149
  ]
140
150
  "docs/*" = [
141
151
  "D",
@@ -148,7 +158,6 @@ max-statements = 75
148
158
  "D100",
149
159
  "D104",
150
160
  "TRY002",
151
- "FA100",
152
161
  ]
153
162
  "tests/*" = [
154
163
  "S101",
@@ -158,7 +167,7 @@ max-statements = 75
158
167
  "ANN",
159
168
  "ARG",
160
169
  "D",
161
- "FA100",
170
+ "RUF012",
162
171
  ]
163
172
 
164
173
  [tool.mypy]
@@ -196,9 +205,8 @@ exclude_also = [
196
205
  [tool.pdm.dev-dependencies]
197
206
  lint = [
198
207
  "pre-commit>=3.3.1",
199
- "ruff==0.9.4",
208
+ "ruff==0.12.7",
200
209
  "mypy>=1.10.0",
201
- "types-paho-mqtt>=1.6.0.20240106",
202
210
  "codespell>=2.3.0",
203
211
  ]
204
212
  test = [
@@ -0,0 +1,130 @@
1
+ """The root module contains the intended public API for users of the INTERSECT-SDK.
2
+
3
+ Users should not need to import anything outside of the root.
4
+
5
+ In general, most breaking changes on version updates will relate to:
6
+ - Configuration classes (both adding and removing new config models). These configuration classes are relevant to the next point.
7
+ - When a new data service is integrated into INTERSECT, ALL adapters will need to update to support this data service, which will include new dependencies.
8
+ """
9
+
10
+ from importlib import import_module
11
+ from typing import TYPE_CHECKING
12
+
13
+ # import everything eagerly for IDEs/LSPs
14
+ if TYPE_CHECKING:
15
+ from .app_lifecycle import default_intersect_lifecycle_loop
16
+ from .capability.base import IntersectBaseCapabilityImplementation
17
+ from .client import IntersectClient
18
+ from .client_callback_definitions import (
19
+ INTERSECT_CLIENT_EVENT_CALLBACK_TYPE,
20
+ INTERSECT_CLIENT_RESPONSE_CALLBACK_TYPE,
21
+ IntersectClientCallback,
22
+ )
23
+ from .config.client import IntersectClientConfig
24
+ from .config.service import IntersectServiceConfig
25
+ from .config.shared import (
26
+ ControlPlaneConfig,
27
+ ControlProvider,
28
+ DataStoreConfig,
29
+ DataStoreConfigMap,
30
+ HierarchyConfig,
31
+ )
32
+ from .core_definitions import IntersectDataHandler, IntersectMimeType
33
+ from .exceptions import IntersectCapabilityError
34
+ from .schema import get_schema_from_capability_implementations
35
+ from .service import IntersectService
36
+ from .service_callback_definitions import (
37
+ INTERSECT_SERVICE_RESPONSE_CALLBACK_TYPE,
38
+ )
39
+ from .service_definitions import (
40
+ IntersectEventDefinition,
41
+ intersect_message,
42
+ intersect_status,
43
+ )
44
+ from .shared_callback_definitions import (
45
+ INTERSECT_JSON_VALUE,
46
+ INTERSECT_RESPONSE_VALUE,
47
+ IntersectDirectMessageParams,
48
+ IntersectEventMessageParams,
49
+ )
50
+ from .version import __version__, version_info, version_string
51
+
52
+ __all__ = (
53
+ 'INTERSECT_CLIENT_EVENT_CALLBACK_TYPE',
54
+ 'INTERSECT_CLIENT_RESPONSE_CALLBACK_TYPE',
55
+ 'INTERSECT_JSON_VALUE',
56
+ 'INTERSECT_RESPONSE_VALUE',
57
+ 'INTERSECT_SERVICE_RESPONSE_CALLBACK_TYPE',
58
+ 'ControlPlaneConfig',
59
+ 'ControlProvider',
60
+ 'DataStoreConfig',
61
+ 'DataStoreConfigMap',
62
+ 'HierarchyConfig',
63
+ 'IntersectBaseCapabilityImplementation',
64
+ 'IntersectCapabilityError',
65
+ 'IntersectClient',
66
+ 'IntersectClientCallback',
67
+ 'IntersectClientConfig',
68
+ 'IntersectDataHandler',
69
+ 'IntersectDirectMessageParams',
70
+ 'IntersectEventDefinition',
71
+ 'IntersectEventMessageParams',
72
+ 'IntersectMimeType',
73
+ 'IntersectService',
74
+ 'IntersectServiceConfig',
75
+ '__version__',
76
+ 'default_intersect_lifecycle_loop',
77
+ 'get_schema_from_capability_implementations',
78
+ 'intersect_message',
79
+ 'intersect_status',
80
+ 'version_info',
81
+ 'version_string',
82
+ )
83
+
84
+ # PEP 562 stuff: do lazy imports for people who just want to import from the top-level module
85
+
86
+ __lazy_imports = {
87
+ 'INTERSECT_CLIENT_EVENT_CALLBACK_TYPE': '.client_callback_definitions',
88
+ 'INTERSECT_CLIENT_RESPONSE_CALLBACK_TYPE': '.client_callback_definitions',
89
+ 'INTERSECT_JSON_VALUE': '.shared_callback_definitions',
90
+ 'INTERSECT_RESPONSE_VALUE': '.shared_callback_definitions',
91
+ 'INTERSECT_SERVICE_RESPONSE_CALLBACK_TYPE': '.service_callback_definitions',
92
+ 'ControlPlaneConfig': '.config.shared',
93
+ 'ControlProvider': '.config.shared',
94
+ 'DataStoreConfig': '.config.shared',
95
+ 'DataStoreConfigMap': '.config.shared',
96
+ 'HierarchyConfig': '.config.shared',
97
+ 'IntersectBaseCapabilityImplementation': '.capability.base',
98
+ 'IntersectCapabilityError': '.exceptions',
99
+ 'IntersectClient': '.client',
100
+ 'IntersectClientCallback': '.client_callback_definitions',
101
+ 'IntersectClientConfig': '.config.client',
102
+ 'IntersectDataHandler': '.core_definitions',
103
+ 'IntersectDirectMessageParams': '.shared_callback_definitions',
104
+ 'IntersectEventDefinition': '.service_definitions',
105
+ 'IntersectEventMessageParams': '.shared_callback_definitions',
106
+ 'IntersectMimeType': '.core_definitions',
107
+ 'IntersectService': '.service',
108
+ 'IntersectServiceConfig': '.config.service',
109
+ '__version__': '.version',
110
+ 'default_intersect_lifecycle_loop': '.app_lifecycle',
111
+ 'get_schema_from_capability_implementations': '.schema',
112
+ 'intersect_message': '.service_definitions',
113
+ 'intersect_status': '.service_definitions',
114
+ 'version_info': '.version',
115
+ 'version_string': '.version',
116
+ }
117
+
118
+
119
+ def __getattr__(attr_name: str) -> object:
120
+ attr_module = __lazy_imports.get(attr_name)
121
+ if attr_module:
122
+ module = import_module(attr_module, package=__spec__.parent)
123
+ return getattr(module, attr_name)
124
+
125
+ msg = f'module {__name__!r} has no attribute {attr_name!r}'
126
+ raise AttributeError(msg)
127
+
128
+
129
+ def __dir__() -> list[str]:
130
+ return list(__all__)
@@ -1,10 +1,8 @@
1
1
  BASE_RESPONSE_ATTR = '__is_intersect_response__'
2
2
  BASE_STATUS_ATTR = '__is_intersect_status__'
3
- BASE_EVENT_ATTR = '__is_intersect_event__'
4
3
  # in theory, as long as the next attributes are unique, they can be any string
5
4
  REQUEST_CONTENT = '__request_content_type__'
6
5
  RESPONSE_CONTENT = '__response_content_type__'
7
6
  RESPONSE_DATA = '__response_data_transfer_handler__'
8
7
  STRICT_VALIDATION = '__strict_validation__'
9
8
  SHUTDOWN_KEYS = '__ignore_message__'
10
- EVENT_ATTR_KEY = '__intersect_sdk_events__'
@@ -12,7 +12,7 @@ from __future__ import annotations
12
12
  import functools
13
13
  import threading
14
14
  from hashlib import sha384
15
- from typing import TYPE_CHECKING, Callable
15
+ from typing import TYPE_CHECKING
16
16
 
17
17
  import pika
18
18
  import pika.delivery_mode
@@ -24,10 +24,13 @@ from ...multi_flag_thread_event import MultiFlagThreadEvent
24
24
  from .broker_client import BrokerClient
25
25
 
26
26
  if TYPE_CHECKING:
27
+ from collections.abc import Callable
28
+
27
29
  from pika.channel import Channel
28
30
  from pika.frame import Frame
29
31
  from pika.spec import Basic, BasicProperties
30
32
 
33
+ from ..definitions import MessageCallback
31
34
  from ..topic_handler import TopicHandler
32
35
 
33
36
 
@@ -191,7 +194,9 @@ class AMQPClient(BrokerClient):
191
194
  def considered_unrecoverable(self) -> bool:
192
195
  return self._unrecoverable
193
196
 
194
- def publish(self, topic: str, payload: bytes, persist: bool) -> None:
197
+ def publish(
198
+ self, topic: str, payload: bytes, content_type: str, headers: dict[str, str], persist: bool
199
+ ) -> None:
195
200
  """Publish the given message.
196
201
 
197
202
  Publish payload with the pre-existing connection (via connect()) on topic.
@@ -199,6 +204,8 @@ class AMQPClient(BrokerClient):
199
204
  Args:
200
205
  topic: The topic on which to publish the message as a string
201
206
  payload: The message to publish, as raw bytes.
207
+ content_type: The content type of the message (if the data plane used is the control plane itself), or the value to be retrieved from the data plane (if the message handler is MINIO/etc.)
208
+ headers: UTF-8 dictionary which can help parse information about the message
202
209
  persist: True if message should persist until consumers available, False if message should be removed immediately.
203
210
  """
204
211
  topic = _hierarchy_2_amqp(topic)
@@ -210,11 +217,11 @@ class AMQPClient(BrokerClient):
210
217
  routing_key=topic,
211
218
  body=payload,
212
219
  properties=pika.BasicProperties(
213
- content_type='text/plain',
220
+ content_type=content_type,
221
+ headers=headers,
214
222
  delivery_mode=pika.delivery_mode.DeliveryMode.Persistent
215
223
  if persist
216
224
  else pika.delivery_mode.DeliveryMode.Transient,
217
- # expiration=None if persist else '8640000',
218
225
  ),
219
226
  )
220
227
  else:
@@ -533,7 +540,7 @@ class AMQPClient(BrokerClient):
533
540
  self,
534
541
  channel: Channel,
535
542
  basic_deliver: Basic.Deliver,
536
- _properties: BasicProperties,
543
+ properties: BasicProperties,
537
544
  body: bytes,
538
545
  persist: bool,
539
546
  ) -> None:
@@ -545,7 +552,7 @@ class AMQPClient(BrokerClient):
545
552
  Args:
546
553
  channel: The AMQP channel the message was received on. Used to manually acknowledge messages.
547
554
  basic_deliver: Contains internal AMQP delivery information - i.e. the routing key.
548
- _properties: Object from the AMQP call. Ignored.
555
+ properties: Object from the AMQP call. Contains various metadata.
549
556
  body: the AMQP message to be handled.
550
557
  persist: Whether or not our queue should persist on either broker or application shutdown.
551
558
  """
@@ -557,6 +564,18 @@ class AMQPClient(BrokerClient):
557
564
  channel.basic_reject(basic_deliver.delivery_tag)
558
565
  return
559
566
 
567
+ # make sure that we have a content-type and headers, note that this does not publish a "reply" message if we fail here
568
+ content_type = properties.content_type
569
+ if not content_type:
570
+ logger.error('Missing message content type')
571
+ channel.basic_ack(basic_deliver.delivery_tag)
572
+ return
573
+ headers = properties.headers
574
+ if not headers:
575
+ logger.error('Missing message headers')
576
+ channel.basic_ack(basic_deliver.delivery_tag)
577
+ return
578
+
560
579
  tth_key = _amqp_2_hierarchy(basic_deliver.routing_key)
561
580
  topic_handler = self._topics_to_handlers().get(tth_key)
562
581
  if topic_handler:
@@ -571,7 +590,15 @@ class AMQPClient(BrokerClient):
571
590
  consumer_tag_info.wait(1.0)
572
591
  thrd = threading.Thread(
573
592
  target=self._consume_message_subthread,
574
- args=(channel, topic_handler.callbacks, body, basic_deliver.delivery_tag, persist),
593
+ args=(
594
+ channel,
595
+ topic_handler.callbacks,
596
+ body,
597
+ content_type,
598
+ headers,
599
+ basic_deliver.delivery_tag,
600
+ persist,
601
+ ),
575
602
  )
576
603
  self._consumer_tags_to_threads[consumer_tag_info.consumer_tag] = thrd
577
604
  thrd.start()
@@ -582,14 +609,16 @@ class AMQPClient(BrokerClient):
582
609
  def _consume_message_subthread(
583
610
  self,
584
611
  channel: Channel,
585
- callbacks: set[Callable[[bytes], None]],
612
+ callbacks: set[MessageCallback],
586
613
  body: bytes,
614
+ content_type: str,
615
+ headers: dict[str, str],
587
616
  delivery_tag: int,
588
617
  persist: bool,
589
618
  ) -> None:
590
619
  """This is a subthread which executes the consumer code without blocking the IO loop. Without using a subthread, the AMQP heartbeat checker will be blocked."""
591
620
  for cb in callbacks:
592
- cb(body)
621
+ cb(body, content_type, headers)
593
622
  # With persistent messages, we only acknowledge the message AFTER we are done processing
594
623
  # (this removes the message from the broker queue)
595
624
  # this allows us to retry a message if the broker OR this application goes down
@@ -32,7 +32,9 @@ class BrokerClient(Protocol):
32
32
  """
33
33
  ...
34
34
 
35
- def publish(self, topic: str, payload: bytes, persist: bool) -> None:
35
+ def publish(
36
+ self, topic: str, payload: bytes, content_type: str, headers: dict[str, str], persist: bool
37
+ ) -> None:
36
38
  """Publishes the given message.
37
39
 
38
40
  Publish payload with the pre-existing connection (via connect()) on topic.
@@ -40,6 +42,8 @@ class BrokerClient(Protocol):
40
42
  Args:
41
43
  topic: The topic on which to publish the message as a string.
42
44
  payload: The message to publish, as raw bytes.
45
+ content_type: The content type of the message (if the data plane used is the control plane itself), or the value to be retrieved from the data plane (if the message handler is MINIO/etc.)
46
+ headers: UTF-8 dictionary which can help parse information about the message
43
47
  persist:
44
48
  True = message will persist forever in associated queues until consumers are available (usually used for Userspace messages)
45
49
  False = remove message immediately if no consumers available (usually used for Event messages and Lifecycle messages)