intersect-sdk 0.9.0__tar.gz → 0.9.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 (85) hide show
  1. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/PKG-INFO +11 -13
  2. intersect_sdk-0.9.2/pyproject.toml +199 -0
  3. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/__init__.py +74 -42
  4. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/_internal/interfaces.py +2 -1
  5. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/_internal/pydantic_schema_generator.py +45 -12
  6. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/_internal/schema.py +11 -8
  7. intersect_sdk-0.9.2/src/intersect_sdk/_internal/service_queue_name.py +14 -0
  8. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/capability/universal_capability/status.py +0 -2
  9. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/client.py +21 -23
  10. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/config/client.py +3 -3
  11. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/config/service.py +7 -4
  12. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/exceptions.py +2 -2
  13. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/schema.py +1 -1
  14. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/service.py +92 -40
  15. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/service_definitions.py +1 -1
  16. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/shared_callback_definitions.py +5 -3
  17. intersect_sdk-0.9.2/src/intersect_sdk/version.py +9 -0
  18. intersect_sdk-0.9.0/LICENSE +0 -29
  19. intersect_sdk-0.9.0/pyproject.toml +0 -253
  20. intersect_sdk-0.9.0/src/intersect_sdk/_internal/control_plane/__init__.py +0 -0
  21. intersect_sdk-0.9.0/src/intersect_sdk/_internal/control_plane/brokers/__init__.py +0 -0
  22. intersect_sdk-0.9.0/src/intersect_sdk/_internal/control_plane/brokers/amqp_client.py +0 -627
  23. intersect_sdk-0.9.0/src/intersect_sdk/_internal/control_plane/brokers/broker_client.py +0 -76
  24. intersect_sdk-0.9.0/src/intersect_sdk/_internal/control_plane/brokers/mqtt_client.py +0 -288
  25. intersect_sdk-0.9.0/src/intersect_sdk/_internal/control_plane/control_plane_manager.py +0 -182
  26. intersect_sdk-0.9.0/src/intersect_sdk/_internal/control_plane/definitions.py +0 -10
  27. intersect_sdk-0.9.0/src/intersect_sdk/_internal/control_plane/topic_handler.py +0 -17
  28. intersect_sdk-0.9.0/src/intersect_sdk/_internal/data_plane/__init__.py +0 -0
  29. intersect_sdk-0.9.0/src/intersect_sdk/_internal/data_plane/data_plane_manager.py +0 -113
  30. intersect_sdk-0.9.0/src/intersect_sdk/_internal/data_plane/minio_utils.py +0 -154
  31. intersect_sdk-0.9.0/src/intersect_sdk/_internal/exceptions.py +0 -17
  32. intersect_sdk-0.9.0/src/intersect_sdk/_internal/messages/__init__.py +0 -0
  33. intersect_sdk-0.9.0/src/intersect_sdk/_internal/messages/event.py +0 -145
  34. intersect_sdk-0.9.0/src/intersect_sdk/_internal/messages/lifecycle.py +0 -107
  35. intersect_sdk-0.9.0/src/intersect_sdk/_internal/messages/userspace.py +0 -183
  36. intersect_sdk-0.9.0/src/intersect_sdk/_internal/multi_flag_thread_event.py +0 -77
  37. intersect_sdk-0.9.0/src/intersect_sdk/_internal/version.py +0 -18
  38. intersect_sdk-0.9.0/src/intersect_sdk/_internal/version_resolver.py +0 -59
  39. intersect_sdk-0.9.0/src/intersect_sdk/config/shared.py +0 -168
  40. intersect_sdk-0.9.0/src/intersect_sdk/constants.py +0 -28
  41. intersect_sdk-0.9.0/src/intersect_sdk/core_definitions.py +0 -47
  42. intersect_sdk-0.9.0/src/intersect_sdk/version.py +0 -19
  43. intersect_sdk-0.9.0/tests/__init__.py +0 -0
  44. intersect_sdk-0.9.0/tests/conftest.py +0 -0
  45. intersect_sdk-0.9.0/tests/e2e/__init__.py +0 -0
  46. intersect_sdk-0.9.0/tests/e2e/test_examples.py +0 -152
  47. intersect_sdk-0.9.0/tests/fixtures/__init__.py +0 -0
  48. intersect_sdk-0.9.0/tests/fixtures/example_schema.json +0 -1275
  49. intersect_sdk-0.9.0/tests/fixtures/example_schema.py +0 -622
  50. intersect_sdk-0.9.0/tests/fixtures/return_type_mismatch.py +0 -7
  51. intersect_sdk-0.9.0/tests/integration/__init__.py +0 -0
  52. intersect_sdk-0.9.0/tests/integration/test_return_type_mismatch.py +0 -126
  53. intersect_sdk-0.9.0/tests/integration/test_service.py +0 -510
  54. intersect_sdk-0.9.0/tests/unit/__init__.py +0 -0
  55. intersect_sdk-0.9.0/tests/unit/test_annotations.py +0 -103
  56. intersect_sdk-0.9.0/tests/unit/test_base_capability_implementation.py +0 -169
  57. intersect_sdk-0.9.0/tests/unit/test_config.py +0 -194
  58. intersect_sdk-0.9.0/tests/unit/test_event_message_headers.py +0 -119
  59. intersect_sdk-0.9.0/tests/unit/test_imports.py +0 -7
  60. intersect_sdk-0.9.0/tests/unit/test_invalid_schema_runtime.py +0 -48
  61. intersect_sdk-0.9.0/tests/unit/test_lifecycle_message_headers.py +0 -101
  62. intersect_sdk-0.9.0/tests/unit/test_schema_invalids.py +0 -929
  63. intersect_sdk-0.9.0/tests/unit/test_schema_ref_resolution.py +0 -55
  64. intersect_sdk-0.9.0/tests/unit/test_schema_valid.py +0 -157
  65. intersect_sdk-0.9.0/tests/unit/test_userspace_message_headers.py +0 -139
  66. intersect_sdk-0.9.0/tests/unit/test_version_resolver.py +0 -123
  67. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/README.md +0 -0
  68. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/_internal/__init__.py +0 -0
  69. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/_internal/constants.py +0 -0
  70. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/_internal/event_metadata.py +0 -0
  71. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/_internal/function_metadata.py +0 -0
  72. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/_internal/generic_serializer.py +0 -0
  73. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/_internal/logger.py +0 -0
  74. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/_internal/status_metadata.py +0 -0
  75. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/_internal/stoppable_thread.py +0 -0
  76. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/_internal/utils.py +0 -0
  77. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/app_lifecycle.py +0 -0
  78. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/capability/__init__.py +0 -0
  79. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/capability/base.py +0 -0
  80. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/capability/universal_capability/__init__.py +0 -0
  81. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/capability/universal_capability/universal_capability.py +0 -0
  82. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/client_callback_definitions.py +0 -0
  83. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/config/__init__.py +0 -0
  84. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/py.typed +0 -0
  85. {intersect_sdk-0.9.0 → intersect_sdk-0.9.2}/src/intersect_sdk/service_callback_definitions.py +0 -0
@@ -1,30 +1,28 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: intersect-sdk
3
- Version: 0.9.0
3
+ Version: 0.9.2
4
4
  Summary: Python SDK to interact with INTERSECT
5
5
  Keywords: intersect
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>
6
+ Author: Lance Drane, Marshall McDonnell, Seth Hitefield, Andrew Ayres, Gregory Cage, Jesse McGaha, Robert Smith, Gavin Wiggins, Michael Brim, Rick Archibald, Addi Malviya Thakur
7
+ 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
8
  License: BSD-3-Clause
8
9
  Classifier: Programming Language :: Python :: 3
9
10
  Classifier: Programming Language :: Python :: 3.10
10
11
  Classifier: Programming Language :: Python :: 3.11
11
12
  Classifier: Programming Language :: Python :: 3.12
12
13
  Classifier: Programming Language :: Python :: 3.13
14
+ Requires-Dist: pydantic>=2.7.0
15
+ Requires-Dist: intersect-sdk-common>=0.9.6,<0.10.0
16
+ Requires-Dist: jsonschema[format-nongpl]>=4.21.1
17
+ Requires-Dist: psutil>=7.0.0
18
+ Requires-Dist: sphinx>=5.3.0 ; extra == 'docs'
19
+ Requires-Dist: furo>=2023.3.27 ; extra == 'docs'
20
+ Requires-Python: >=3.10, <4.0
13
21
  Project-URL: Homepage, https://github.com/INTERSECT-SDK/python-sdk/
14
22
  Project-URL: Changelog, https://github.com/INTERSECT-SDK/python-sdk/blob/main/CHANGELOG.md
15
23
  Project-URL: Documentation, https://intersect-python-sdk.readthedocs.io/en/latest/
16
24
  Project-URL: Issues, https://github.com/INTERSECT-SDK/python-sdk/issues
17
- Requires-Python: <4.0,>=3.10
18
- Requires-Dist: pydantic>=2.7.0
19
- Requires-Dist: retrying<2.0.0,>=1.3.4
20
- Requires-Dist: paho-mqtt<3.0.0,>=2.1.0
21
- Requires-Dist: pika<2.0.0,>=1.3.2
22
- Requires-Dist: minio>=7.2.3
23
- Requires-Dist: jsonschema[format-nongpl]>=4.21.1
24
- Requires-Dist: psutil>=7.0.0
25
25
  Provides-Extra: docs
26
- Requires-Dist: sphinx>=5.3.0; extra == "docs"
27
- Requires-Dist: furo>=2023.3.27; extra == "docs"
28
26
  Description-Content-Type: text/markdown
29
27
 
30
28
  [![Static Badge](https://img.shields.io/badge/DOI-10.11578%2Fdc.20240927.1-blue)](https://doi.org/10.11578/dc.20240927.1)
@@ -0,0 +1,199 @@
1
+ [project]
2
+ name = "intersect-sdk"
3
+ description = "Python SDK to interact with INTERSECT"
4
+ authors = [
5
+ { name = "Lance Drane", email = "dranelt@ornl.gov" },
6
+ { name = "Marshall McDonnell", email = "mcdonnellmt@ornl.gov" },
7
+ { name = "Seth Hitefield", email = "hitefieldsd@ornl.gov" },
8
+ { name = "Andrew Ayres", email = "ayresaf@ornl.gov" },
9
+ { name = "Gregory Cage", email = "cagege@ornl.gov" },
10
+ { name = "Jesse McGaha", email = "mcgahajr@ornl.gov" },
11
+ { name = "Robert Smith", email = "smithrw@ornl.gov" },
12
+ { name = "Gavin Wiggins", email = "wigginsg@ornl.gov" },
13
+ { name = "Michael Brim", email = "brimmj@ornl.gov" },
14
+ { name = "Rick Archibald", email = "archibaldrk@ornl.gov" },
15
+ { name = "Addi Malviya Thakur", email = "malviyaa@ornl.gov" },
16
+ ]
17
+ version = "0.9.2"
18
+ readme = "README.md"
19
+ license = { text = "BSD-3-Clause" }
20
+ requires-python = ">=3.10,<4.0"
21
+ keywords = ["intersect"]
22
+ classifiers = [
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
+ ]
29
+ dependencies = [
30
+ "pydantic>=2.7.0",
31
+ "intersect-sdk-common>=0.9.6,<0.10.0",
32
+ "jsonschema[format-nongpl]>=4.21.1", # extras necessary for enforcing formats
33
+ #"brotli>=1.1.0", # TODO - add this dependency when we add compression
34
+ "psutil>=7.0.0",
35
+ ]
36
+
37
+ [project.urls]
38
+ Homepage = "https://github.com/INTERSECT-SDK/python-sdk/"
39
+ Changelog = "https://github.com/INTERSECT-SDK/python-sdk/blob/main/CHANGELOG.md"
40
+ Documentation = "https://intersect-python-sdk.readthedocs.io/en/latest/"
41
+ Issues = "https://github.com/INTERSECT-SDK/python-sdk/issues"
42
+
43
+ [project.optional-dependencies]
44
+ docs = ["sphinx>=5.3.0", "furo>=2023.3.27"]
45
+
46
+ [dependency-groups]
47
+ dev = [
48
+ "pre-commit>=3.3.1",
49
+ "ruff==0.12.7",
50
+ "mypy>=1.10.0",
51
+ "codespell>=2.3.0",
52
+ "pytest>=7.3.2",
53
+ "pytest-cov>=4.1.0",
54
+ "fastapi",
55
+ "uvicorn",
56
+ ]
57
+
58
+ [build-system]
59
+ requires = ["uv_build>=0.10.2,<0.11.0"]
60
+ build-backend = "uv_build"
61
+
62
+ [tool.ruff]
63
+ line-length = 100
64
+ format = { quote-style = 'single' }
65
+
66
+ [tool.ruff.lint]
67
+ isort = { known-first-party = ['src'] }
68
+ pydocstyle = { convention = 'google' }
69
+ flake8-quotes = { inline-quotes = 'single', multiline-quotes = 'double' }
70
+ mccabe = { max-complexity = 20 }
71
+ pylint = { max-args = 10, max-branches = 20, max-returns = 15, max-statements = 75 }
72
+ # pyflakes and the relevant pycodestyle rules are already configured
73
+ extend-select = [
74
+ 'C90', # mccabe complexity
75
+ 'I', # isort
76
+ 'N', # pep8-naming
77
+ 'D', # pydocstyle
78
+ 'UP', # pyupgrade
79
+ 'YTT', # flake8-2020
80
+ 'ANN', # flake8-annotations
81
+ 'ASYNC', # flake8-async
82
+ 'S', # flake8-bandit
83
+ 'BLE', # flake8-blind-except
84
+ 'B', # flake8-bugbear
85
+ 'A', # flake8-builtins
86
+ 'COM', # flake8-commas
87
+ 'C4', # flake8-comprehensions
88
+ 'DTZ', # flake8-datetimez
89
+ 'T10', # flake8-debugger
90
+ 'EM', # flake8-error-message
91
+ 'FA', # flake8-future-annotations
92
+ 'ISC', # flake8-implicit-string-concat
93
+ 'ICN', # flake8-import-conventions
94
+ 'G', # flake8-logging-format
95
+ 'INP', # flake8-no-pep420
96
+ 'PIE', # flake8-PIE
97
+ 'T20', # flake8-T20
98
+ 'PYI', # flake8-pyi
99
+ 'PT', # flake8-pytest-style
100
+ 'Q', # flake8-quotes
101
+ 'RSE', # flake8-raise
102
+ 'RET', # flake8-return
103
+ 'SLF', # flake8-self
104
+ 'SLOT', # flake8-slots
105
+ 'SIM', # flake8-simplify
106
+ 'TC', # flake8-type-checking
107
+ 'ARG', # flake8-unused-arguments
108
+ 'PTH', # flake8-use-pathlib
109
+ 'PGH', # pygrep-hooks
110
+ 'PL', # pylint
111
+ 'TRY', # tryceratops
112
+ 'FLY', # flynt
113
+ 'RUF', # RUFF additional rules
114
+ 'INT', # flake8-gettext
115
+ ]
116
+ # If you're seeking to disable a rule, first consider whether the rule is overbearing, or if it should only be turned off for your usecase.
117
+ ignore = [
118
+ 'COM812', # formatter, handled by Ruff format
119
+ 'ISC001', # formatter, handled by Ruff format
120
+ 'SIM105', # "with contextlib.suppress():" is slower than try-except-pass
121
+ 'ANN401', # allow explicit "Any" typing, use with care
122
+ 'PLR2004', # allow "magic numbers"
123
+ ]
124
+
125
+ [tool.ruff.lint.flake8-type-checking]
126
+ runtime-evaluated-base-classes = [
127
+ "pydantic.BaseModel",
128
+ "intersect_sdk.IntersectBaseCapabilityImplementation",
129
+ ]
130
+ runtime-evaluated-decorators = [
131
+ "pydantic.dataclasses.dataclass",
132
+ "pydantic.validate_call",
133
+ ]
134
+
135
+ [tool.ruff.lint.extend-per-file-ignores]
136
+ '__init__.py' = [
137
+ 'F401', # __init__.py commonly has unused imports
138
+ 'TC004', # do lazy imports when importing from the base module
139
+ ]
140
+ 'docs/*' = [
141
+ 'D', # the documentation folder does not need documentation
142
+ 'INP001', # docs are not a namespace package
143
+ ]
144
+ 'examples/*' = [
145
+ 'N999', # module names for examples are not standard
146
+ 'T20', # allow print/pprint statements in examples
147
+ 'S106', # don't care about credentials in examples
148
+ 'D100', # documenting modules in examples is unhelpful
149
+ 'D104', # documenting packages in examples is unhelpful
150
+ 'TRY002', # examples can raise their own exception
151
+ ]
152
+ 'tests/*' = [
153
+ 'S101', # allow assert statements in tests
154
+ 'S106', # don't care about credentials in tests
155
+ 'S311', # don't care about cryptographic security in tests
156
+ 'SLF001', # allow private member access in tests
157
+ 'ANN', # tests in general don't need types, unless they are runtime types.
158
+ 'ARG', # allow unused parameters in tests
159
+ 'D', # ignore documentation in tests
160
+ 'RUF012', # permit "mutable" class attributes to not be annotated with typing.ClassVar (these shouldn't be mutated anyways...)
161
+ ]
162
+
163
+ # see https://mypy.readthedocs.io/en/stable/config_file.html for a complete reference
164
+ [tool.mypy]
165
+ strict = true
166
+ ignore_missing_imports = true # don't require typing for library stubs if they don't exist
167
+ disallow_untyped_decorators = false # this is needed for library decorator compatibility, i.e. "retrying"
168
+ plugins = ["pydantic.mypy"]
169
+
170
+ [tool.pydantic-mypy]
171
+ init_forbid_extra = true
172
+ init_typed = true
173
+ warn_required_dynamic_aliases = true
174
+ warn_untyped_fields = true
175
+
176
+ [tool.pytest.ini_options]
177
+ log_cli = true
178
+ addopts = "-ra"
179
+
180
+ [tool.coverage.report]
181
+ omit = [
182
+ '*__init__*', # __init__ files should just re-export other classes and functions
183
+ '*/discovery_service.py', # currently unused
184
+ ]
185
+ exclude_also = [
186
+ 'pragma: no-cover', # standard
187
+ 'if (typing\\.)?TYPE_CHECKING:', # type checking blocks are not executed in coverage, but we don't care
188
+ '@(abc\\.)?abstractmethod', # don't try to cover abstract methods
189
+ "class .*\\bProtocol\\):", # don't cover protocol classes (similar to abstract classes)
190
+ 'raise NotImplementedError', # it's not implemented so shouldn't be covered
191
+ 'except.* ImportError', # these are usually used to throw a "friendlier" error and are not really worth testing
192
+ ]
193
+
194
+ [tool.codespell]
195
+ # Ref: https://github.com/codespell-project/codespell#using-a-config-file
196
+ skip = '.git*,*.lock,.venv,.*cache/*,./docs/_build/*'
197
+ check-hidden = true
198
+ # ignore-regex = ''
199
+ # ignore-words-list = ''
@@ -12,6 +12,18 @@ from typing import TYPE_CHECKING
12
12
 
13
13
  # import everything eagerly for IDEs/LSPs
14
14
  if TYPE_CHECKING:
15
+ from intersect_sdk_common import (
16
+ ControlPlaneConfig,
17
+ ControlProvider,
18
+ DataStoreConfig,
19
+ DataStoreConfigMap,
20
+ HierarchyConfig,
21
+ IntersectDataHandler,
22
+ IntersectMimeType,
23
+ intersect_sdk_version_info,
24
+ intersect_sdk_version_string,
25
+ )
26
+
15
27
  from .app_lifecycle import default_intersect_lifecycle_loop
16
28
  from .capability.base import IntersectBaseCapabilityImplementation
17
29
  from .client import IntersectClient
@@ -22,14 +34,6 @@ if TYPE_CHECKING:
22
34
  )
23
35
  from .config.client import IntersectClientConfig
24
36
  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
37
  from .exceptions import IntersectCapabilityError
34
38
  from .schema import get_schema_from_capability_implementations
35
39
  from .service import IntersectService
@@ -47,7 +51,7 @@ if TYPE_CHECKING:
47
51
  IntersectDirectMessageParams,
48
52
  IntersectEventMessageParams,
49
53
  )
50
- from .version import __version__, version_info, version_string
54
+ from .version import __version__
51
55
 
52
56
  __all__ = (
53
57
  'INTERSECT_CLIENT_EVENT_CALLBACK_TYPE',
@@ -76,50 +80,78 @@ __all__ = (
76
80
  'default_intersect_lifecycle_loop',
77
81
  'get_schema_from_capability_implementations',
78
82
  'intersect_message',
83
+ 'intersect_sdk_version_info',
84
+ 'intersect_sdk_version_string',
79
85
  'intersect_status',
80
- 'version_info',
81
- 'version_string',
82
86
  )
83
87
 
84
88
  # PEP 562 stuff: do lazy imports for people who just want to import from the top-level module
85
-
89
+ # [0] = package, [1] = path to module within package
86
90
  __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',
91
+ # COMMON: core types
92
+ 'IntersectDataHandler': ('intersect_sdk_common', '.'),
93
+ 'IntersectMimeType': ('intersect_sdk_common', '.'),
94
+ # COMMON: config types
95
+ 'ControlPlaneConfig': ('intersect_sdk_common', '.'),
96
+ 'ControlProvider': ('intersect_sdk_common', '.'),
97
+ 'DataStoreConfig': ('intersect_sdk_common', '.'),
98
+ 'DataStoreConfigMap': ('intersect_sdk_common', '.'),
99
+ 'HierarchyConfig': ('intersect_sdk_common', '.'),
100
+ # COMMON: version
101
+ 'intersect_sdk_version_info': ('intersect_sdk_common', '.'),
102
+ 'intersect_sdk_version_string': ('intersect_sdk_common', '.'),
103
+ # imports not in common
104
+ 'INTERSECT_CLIENT_EVENT_CALLBACK_TYPE': (
105
+ __spec__.parent,
106
+ '.client_callback_definitions',
107
+ ),
108
+ 'INTERSECT_CLIENT_RESPONSE_CALLBACK_TYPE': (
109
+ __spec__.parent,
110
+ '.client_callback_definitions',
111
+ ),
112
+ 'INTERSECT_JSON_VALUE': (__spec__.parent, '.shared_callback_definitions'),
113
+ 'INTERSECT_RESPONSE_VALUE': (
114
+ __spec__.parent,
115
+ '.shared_callback_definitions',
116
+ ),
117
+ 'INTERSECT_SERVICE_RESPONSE_CALLBACK_TYPE': (
118
+ __spec__.parent,
119
+ '.service_callback_definitions',
120
+ ),
121
+ 'IntersectBaseCapabilityImplementation': (
122
+ __spec__.parent,
123
+ '.capability.base',
124
+ ),
125
+ 'IntersectCapabilityError': (__spec__.parent, '.exceptions'),
126
+ 'IntersectClient': (__spec__.parent, '.client'),
127
+ 'IntersectClientCallback': (
128
+ __spec__.parent,
129
+ '.client_callback_definitions',
130
+ ),
131
+ 'IntersectClientConfig': (__spec__.parent, '.config.client'),
132
+ 'IntersectDirectMessageParams': (
133
+ __spec__.parent,
134
+ '.shared_callback_definitions',
135
+ ),
136
+ 'IntersectEventDefinition': (__spec__.parent, '.service_definitions'),
137
+ 'IntersectEventMessageParams': (
138
+ __spec__.parent,
139
+ '.shared_callback_definitions',
140
+ ),
141
+ 'IntersectService': (__spec__.parent, '.service'),
142
+ 'IntersectServiceConfig': (__spec__.parent, '.config.service'),
143
+ '__version__': (__spec__.parent, '.version'),
144
+ 'default_intersect_lifecycle_loop': (__spec__.parent, '.app_lifecycle'),
145
+ 'get_schema_from_capability_implementations': (__spec__.parent, '.schema'),
146
+ 'intersect_message': (__spec__.parent, '.service_definitions'),
147
+ 'intersect_status': (__spec__.parent, '.service_definitions'),
116
148
  }
117
149
 
118
150
 
119
151
  def __getattr__(attr_name: str) -> object:
120
152
  attr_module = __lazy_imports.get(attr_name)
121
153
  if attr_module:
122
- module = import_module(attr_module, package=__spec__.parent)
154
+ module = import_module(attr_module[1], package=attr_module[0])
123
155
  return getattr(module, attr_name)
124
156
 
125
157
  msg = f'module {__name__!r} has no attribute {attr_name!r}'
@@ -5,8 +5,9 @@ from typing import TYPE_CHECKING, Any, Protocol
5
5
  if TYPE_CHECKING:
6
6
  from uuid import UUID
7
7
 
8
+ from intersect_sdk_common import HierarchyConfig
9
+
8
10
  from ..client_callback_definitions import INTERSECT_CLIENT_EVENT_CALLBACK_TYPE
9
- from ..config.shared import HierarchyConfig
10
11
  from ..service_callback_definitions import (
11
12
  INTERSECT_SERVICE_RESPONSE_CALLBACK_TYPE,
12
13
  )
@@ -3,6 +3,7 @@
3
3
  See: https://docs.pydantic.dev/latest/api/json_schema/#pydantic.json_schema.GenerateJsonSchema
4
4
  """
5
5
 
6
+ import collections
6
7
  from typing import (
7
8
  Any,
8
9
  )
@@ -10,14 +11,23 @@ from typing import (
10
11
  from jsonschema import Draft202012Validator as SchemaValidator
11
12
  from jsonschema import ValidationError as SchemaValidationError
12
13
  from jsonschema.validators import validator_for
13
- from pydantic import PydanticInvalidForJsonSchema, PydanticSchemaGenerationError, TypeAdapter
14
+ from pydantic import (
15
+ PydanticInvalidForJsonSchema,
16
+ PydanticSchemaGenerationError,
17
+ TypeAdapter,
18
+ )
14
19
  from pydantic.json_schema import (
15
20
  GenerateJsonSchema,
16
21
  JsonSchemaMode,
17
22
  JsonSchemaValue,
18
23
  )
19
24
  from pydantic.type_adapter import _type_has_config
20
- from pydantic_core import CoreSchema, PydanticSerializationError, core_schema, to_jsonable_python
25
+ from pydantic_core import (
26
+ CoreSchema,
27
+ PydanticSerializationError,
28
+ core_schema,
29
+ to_jsonable_python,
30
+ )
21
31
 
22
32
 
23
33
  # build nested dictionary from list of keys. i.e. if keys = ['one', 'two', 'three']
@@ -73,7 +83,8 @@ def validate_schema(json_schema: Any) -> list[str]:
73
83
  # custom impl. of check_schema() , gather all errors instead of throwing on first error
74
84
  validator_cls = validator_for(SchemaValidator.META_SCHEMA, default=SchemaValidator)
75
85
  metavalidator: SchemaValidator = validator_cls(
76
- SchemaValidator.META_SCHEMA, format_checker=SchemaValidator.FORMAT_CHECKER
86
+ SchemaValidator.META_SCHEMA,
87
+ format_checker=SchemaValidator.FORMAT_CHECKER,
77
88
  )
78
89
  return [
79
90
  f'{error.json_path} : {error.message}' for error in metavalidator.iter_errors(json_schema)
@@ -120,7 +131,10 @@ class GenerateTypedJsonSchema(GenerateJsonSchema):
120
131
  ref_parts = self.ref_template.split('/')[1:-1]
121
132
  defs_schema = build_nested_dict(ref_parts, defs)
122
133
 
123
- for default, inner_schema in self.intersect_sdk_postgeneration_defaults:
134
+ for (
135
+ default,
136
+ inner_schema,
137
+ ) in self.intersect_sdk_postgeneration_defaults:
124
138
  final_schema = {**defs_schema, **inner_schema}
125
139
  try:
126
140
  validate_against_schema(final_schema, default)
@@ -146,7 +160,8 @@ class GenerateTypedJsonSchema(GenerateJsonSchema):
146
160
  The generated JSON schema.
147
161
  """
148
162
  return self.handle_invalid_for_json_schema(
149
- schema, 'Any or object : dynamic typing is not allowed for INTERSECT schemas'
163
+ schema,
164
+ 'Any or object : dynamic typing is not allowed for INTERSECT schemas',
150
165
  )
151
166
 
152
167
  def is_subclass_schema(self, schema: core_schema.IsSubclassSchema) -> JsonSchemaValue:
@@ -249,12 +264,14 @@ class GenerateTypedJsonSchema(GenerateJsonSchema):
249
264
  """
250
265
  if not schema.get('items_schema'):
251
266
  return self.handle_invalid_for_json_schema(
252
- schema, 'generator yield subtyping (first argument) may not be dynamic in INTERSECT'
267
+ schema,
268
+ 'generator yield subtyping (first argument) may not be dynamic in INTERSECT',
253
269
  )
254
270
  json_schema = super().generator_schema(schema)
255
271
  if not json_schema.get('items'):
256
272
  return self.handle_invalid_for_json_schema(
257
- schema, 'generator yield subtyping (first argument) may not be dynamic in INTERSECT'
273
+ schema,
274
+ 'generator yield subtyping (first argument) may not be dynamic in INTERSECT',
258
275
  )
259
276
  return json_schema
260
277
 
@@ -281,7 +298,8 @@ class GenerateTypedJsonSchema(GenerateJsonSchema):
281
298
  )
282
299
  if 'values_schema' not in schema:
283
300
  return self.handle_invalid_for_json_schema(
284
- schema, 'dict or mapping: value type cannot be Any/object for INTERSECT'
301
+ schema,
302
+ 'dict or mapping: value type cannot be Any/object for INTERSECT',
285
303
  )
286
304
  keys_schema = self.generate_inner(schema['keys_schema']).copy()
287
305
  values_schema = self.generate_inner(schema['values_schema']).copy()
@@ -343,6 +361,19 @@ class GenerateTypedJsonSchema(GenerateJsonSchema):
343
361
  return json_schema
344
362
  default = schema['default']
345
363
 
364
+ # Sort set/frozenset defaults to ensure deterministic JSON schema generation
365
+ # We only sort if len > 1 because sets of size 0 or 1 are already deterministic
366
+ if isinstance(default, collections.abc.Set) and len(default) > 1:
367
+ try:
368
+ default = sorted(default)
369
+ except TypeError:
370
+ # If items aren't comparable (e.g. mixed types), we can't sort them.
371
+ # Unlike Pydantic, we will actually raise an error here, as we want to force deterministic schema generation
372
+ err_msg = (
373
+ f'Do not use "{default}" as a default value: sets must contain the same types'
374
+ )
375
+ raise PydanticInvalidForJsonSchema(err_msg) # noqa: B904 (TypeError provides indirection)
376
+
346
377
  try:
347
378
  encoded_default = self.encode_default(default)
348
379
  except PydanticSerializationError:
@@ -352,9 +383,9 @@ class GenerateTypedJsonSchema(GenerateJsonSchema):
352
383
  f"Default value '{default}' is not JSON serializable; use pydantic.Field's 'default_factory' argument for this.",
353
384
  )
354
385
 
355
- if '$ref' in json_schema:
356
- # TODO - not best way to handle this
357
- # validate this default later, when all $refs have been resolved
386
+ # check if the schema has $ref in it directly, or if it has a boolean schema composition keyword
387
+ # if it does, we must validate this during $ref construction
388
+ if '$ref' in json_schema or 'oneOf' in json_schema or 'anyOf' in json_schema:
358
389
  # IMPORTANT!!! You must send the "json_schema" object itself, it will be modified later internally by Pydantic
359
390
  self.intersect_sdk_postgeneration_defaults.append((encoded_default, json_schema))
360
391
  # Since reference schemas do not support child keys, we wrap the reference schema in a single-case allOf:
@@ -479,7 +510,9 @@ class GenerateTypedJsonSchema(GenerateJsonSchema):
479
510
  return super().kw_arguments_schema(arguments, var_kwargs_schema)
480
511
 
481
512
  def p_arguments_schema(
482
- self, arguments: list[core_schema.ArgumentsParameter], var_args_schema: CoreSchema | None
513
+ self,
514
+ arguments: list[core_schema.ArgumentsParameter],
515
+ var_args_schema: CoreSchema | None,
483
516
  ) -> JsonSchemaValue:
484
517
  """Generates a JSON schema that matches a schema that defines a function's positional arguments.
485
518
 
@@ -14,12 +14,18 @@ from typing import (
14
14
  get_origin,
15
15
  )
16
16
 
17
+ from intersect_sdk_common import intersect_sdk_version_string
18
+ from intersect_sdk_common.constants import CAPABILITY_REGEX
19
+ from intersect_sdk_common.control_plane.messages.event import (
20
+ EventMessageHeaders,
21
+ )
22
+ from intersect_sdk_common.control_plane.messages.userspace import (
23
+ UserspaceMessageHeaders,
24
+ )
17
25
  from pydantic import Field, PydanticUserError, TypeAdapter
18
26
  from typing_extensions import TypeAliasType
19
27
 
20
- from ..constants import CAPABILITY_REGEX
21
28
  from ..service_definitions import IntersectEventDefinition
22
- from ..version import version_string
23
29
  from .constants import (
24
30
  BASE_RESPONSE_ATTR,
25
31
  BASE_STATUS_ATTR,
@@ -32,18 +38,15 @@ from .constants import (
32
38
  from .event_metadata import EventMetadata, definition_metadata_differences
33
39
  from .function_metadata import FunctionMetadata
34
40
  from .logger import logger
35
- from .messages.event import EventMessageHeaders
36
- from .messages.userspace import UserspaceMessageHeaders
37
41
  from .pydantic_schema_generator import GenerateTypedJsonSchema
38
42
  from .status_metadata import StatusMetadata
39
43
  from .utils import die
40
44
 
41
45
  if TYPE_CHECKING:
46
+ from intersect_sdk_common import HierarchyConfig, IntersectDataHandler
42
47
  from pydantic.json_schema import JsonSchemaMode
43
48
 
44
49
  from ..capability.base import IntersectBaseCapabilityImplementation
45
- from ..config.shared import HierarchyConfig
46
- from ..core_definitions import IntersectDataHandler
47
50
 
48
51
  ASYNCAPI_VERSION = '2.6.0'
49
52
 
@@ -619,7 +622,7 @@ def get_schema_and_functions_from_capability_implementations(
619
622
  or not re.fullmatch(CAPABILITY_REGEX, cap_name)
620
623
  ):
621
624
  die(
622
- f'Invalid intersect_sdk_capability_name on capability {capability_type.__name__} - must be a non-empty string with only alphanumeric characters and hyphens (you must explicitly set this, and do so on the class and not an instance).'
625
+ f'Invalid intersect_sdk_capability_name on capability {capability_type.__name__} - must be a non-empty string with only alphanumeric characters and underscores (you must explicitly set this, and do so on the class and not an instance).'
623
626
  )
624
627
  if cap_name in capability_schemas:
625
628
  die(
@@ -659,7 +662,7 @@ def get_schema_and_functions_from_capability_implementations(
659
662
 
660
663
  asyncapi_spec = {
661
664
  'asyncapi': ASYNCAPI_VERSION,
662
- 'x-intersect-version': version_string,
665
+ 'x-intersect-version': intersect_sdk_version_string,
663
666
  'info': {
664
667
  'title': service_name.hierarchy_string('.'),
665
668
  'description': 'INTERSECT schema',
@@ -0,0 +1,14 @@
1
+ from hashlib import sha384
2
+
3
+
4
+ def get_service_queue_name(routing_key: str) -> str:
5
+ """Generate a valid queue name from the routing key.
6
+
7
+ We want to always be able to generate the same queue name from the routing key every time,
8
+ so we don't use UUIDs or want the broker to generate a key name.
9
+
10
+ We must also keep the length under 128 characters.
11
+
12
+ See https://www.rabbitmq.com/docs/queues#names for a complete reference.
13
+ """
14
+ return sha384(routing_key.encode()).hexdigest()
@@ -1,7 +1,5 @@
1
1
  """Definitions supporting the 'core status' functionality of the core capability."""
2
2
 
3
- from __future__ import annotations
4
-
5
3
  import datetime
6
4
  from typing import Annotated
7
5