madsci.node_module 0.7.0rc1__tar.gz → 0.8.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.
- madsci_node_module-0.7.0rc1/README.md → madsci_node_module-0.8.0/PKG-INFO +23 -2
- madsci_node_module-0.7.0rc1/PKG-INFO → madsci_node_module-0.8.0/README.md +10 -15
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/madsci/node_module/abstract_node_module.py +221 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/pyproject.toml +1 -1
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/tests/conftest.py +3 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/tests/test_argument_parsing.py +1 -1
- madsci_node_module-0.8.0/tests/test_intrinsic_location_handler.py +344 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/tests/test_location_argument_serialization.py +7 -7
- madsci_node_module-0.8.0/tests/test_node_registry.py +215 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/tests/test_rest_functionality.py +22 -21
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/tests/test_rest_node_client.py +133 -113
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/tests/test_rest_node_module.py +47 -0
- madsci_node_module-0.8.0/tests/test_template_handler.py +362 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/madsci/node_module/__init__.py +0 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/madsci/node_module/helpers.py +0 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/madsci/node_module/rest_node_module.py +0 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/madsci/node_module/type_analyzer.py +0 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/tests/test_action_parsing_integration.py +0 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/tests/test_helpers.py +0 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/tests/test_node.py +0 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/tests/test_node_infrastructure.py +0 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/tests/test_openapi_schemas.py +0 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/tests/test_rest_utils.py +0 -0
- {madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/tests/test_type_analyzer.py +0 -0
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: madsci.node_module
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: The Modular Autonomous Discovery for Science (MADSci) Node Module Helper Classes.
|
|
5
|
+
Author-Email: Tobias Ginsburg <tginsburg@anl.gov>, "Ryan D. Lewis" <ryan.lewis@anl.gov>, Casey Stone <cstone@anl.gov>, Doga Ozgulbas <dozgulbas@anl.gov>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/AD-SDL/MADSci
|
|
8
|
+
Requires-Python: >=3.10.0
|
|
9
|
+
Requires-Dist: madsci.common
|
|
10
|
+
Requires-Dist: madsci.client
|
|
11
|
+
Requires-Dist: regex
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
1
14
|
# MADSci Node Module
|
|
2
15
|
|
|
3
16
|
Framework for creating laboratory instrument nodes that integrate with MADSci workcells via REST APIs.
|
|
@@ -105,8 +118,8 @@ module_version: 1.0.0
|
|
|
105
118
|
# Run directly
|
|
106
119
|
python my_instrument_node.py
|
|
107
120
|
|
|
108
|
-
#
|
|
109
|
-
|
|
121
|
+
# Configuration is provided via environment variables (NODE_NAME, NODE_URL, etc.)
|
|
122
|
+
# or settings files (settings.yaml, .env)
|
|
110
123
|
|
|
111
124
|
# Node will be available at http://localhost:2000/docs
|
|
112
125
|
```
|
|
@@ -1266,3 +1279,11 @@ def get_multiple_files(self) -> list[Path]:
|
|
|
1266
1279
|
```
|
|
1267
1280
|
|
|
1268
1281
|
**Working examples**: See [example_lab/](../../examples/example_lab/) for a complete working laboratory with multiple integrated nodes.
|
|
1282
|
+
|
|
1283
|
+
|
|
1284
|
+
## Development Steps
|
|
1285
|
+
In order to ensure a MADSci module is up to standard and will run reliably, developers should:
|
|
1286
|
+
1. Document expected behavior for each action exposed by the Node, and then test it on the device to ensure it behaves as documented
|
|
1287
|
+
2. Ensure that the module folder contains no extraneous files unused by the code.
|
|
1288
|
+
3. Ensure that the code passes automated linting and code quality checks (we use [pre-commit](https://pre-commit.com/) and [ruff](https://astral.sh/ruff), for instance. See [our pre-commit config](../../.pre-commit-config.yaml) and [ruff config](../../ruff.toml).
|
|
1289
|
+
4. Where appropriate, write a Docker image for the module and ensure that it builds and runs.
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: madsci.node_module
|
|
3
|
-
Version: 0.7.0rc1
|
|
4
|
-
Summary: The Modular Autonomous Discovery for Science (MADSci) Node Module Helper Classes.
|
|
5
|
-
Author-Email: Tobias Ginsburg <tginsburg@anl.gov>, "Ryan D. Lewis" <ryan.lewis@anl.gov>, Casey Stone <cstone@anl.gov>, Doga Ozgulbas <dozgulbas@anl.gov>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/AD-SDL/MADSci
|
|
8
|
-
Requires-Python: >=3.10.0
|
|
9
|
-
Requires-Dist: madsci.common
|
|
10
|
-
Requires-Dist: madsci.client
|
|
11
|
-
Requires-Dist: regex
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
|
|
14
1
|
# MADSci Node Module
|
|
15
2
|
|
|
16
3
|
Framework for creating laboratory instrument nodes that integrate with MADSci workcells via REST APIs.
|
|
@@ -118,8 +105,8 @@ module_version: 1.0.0
|
|
|
118
105
|
# Run directly
|
|
119
106
|
python my_instrument_node.py
|
|
120
107
|
|
|
121
|
-
#
|
|
122
|
-
|
|
108
|
+
# Configuration is provided via environment variables (NODE_NAME, NODE_URL, etc.)
|
|
109
|
+
# or settings files (settings.yaml, .env)
|
|
123
110
|
|
|
124
111
|
# Node will be available at http://localhost:2000/docs
|
|
125
112
|
```
|
|
@@ -1279,3 +1266,11 @@ def get_multiple_files(self) -> list[Path]:
|
|
|
1279
1266
|
```
|
|
1280
1267
|
|
|
1281
1268
|
**Working examples**: See [example_lab/](../../examples/example_lab/) for a complete working laboratory with multiple integrated nodes.
|
|
1269
|
+
|
|
1270
|
+
|
|
1271
|
+
## Development Steps
|
|
1272
|
+
In order to ensure a MADSci module is up to standard and will run reliably, developers should:
|
|
1273
|
+
1. Document expected behavior for each action exposed by the Node, and then test it on the device to ensure it behaves as documented
|
|
1274
|
+
2. Ensure that the module folder contains no extraneous files unused by the code.
|
|
1275
|
+
3. Ensure that the code passes automated linting and code quality checks (we use [pre-commit](https://pre-commit.com/) and [ruff](https://astral.sh/ruff), for instance. See [our pre-commit config](../../.pre-commit-config.yaml) and [ruff config](../../ruff.toml).
|
|
1276
|
+
4. Where appropriate, write a Docker image for the module and ensure that it builds and runs.
|
{madsci_node_module-0.7.0rc1 → madsci_node_module-0.8.0}/madsci/node_module/abstract_node_module.py
RENAMED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"""Base Node Module helper classes."""
|
|
2
2
|
|
|
3
|
+
import atexit
|
|
3
4
|
import contextlib
|
|
4
5
|
import inspect
|
|
6
|
+
import logging
|
|
5
7
|
import threading
|
|
8
|
+
import weakref
|
|
6
9
|
from pathlib import Path
|
|
7
10
|
from typing import (
|
|
8
11
|
Annotated,
|
|
@@ -39,15 +42,20 @@ from madsci.common.types.action_types import (
|
|
|
39
42
|
LocationArgumentDefinition,
|
|
40
43
|
)
|
|
41
44
|
from madsci.common.types.admin_command_types import AdminCommandResponse
|
|
45
|
+
from madsci.common.types.auth_types import OwnershipInfo
|
|
42
46
|
from madsci.common.types.base_types import Error
|
|
43
47
|
from madsci.common.types.datapoint_types import DataPoint, FileDataPoint, ValueDataPoint
|
|
44
48
|
from madsci.common.types.event_types import Event, EventType
|
|
49
|
+
from madsci.common.types.location_types import LocationManagement
|
|
45
50
|
from madsci.common.types.node_types import (
|
|
46
51
|
AdminCommands,
|
|
47
52
|
NodeCapabilities,
|
|
48
53
|
NodeClientCapabilities,
|
|
49
54
|
NodeConfig,
|
|
50
55
|
NodeInfo,
|
|
56
|
+
NodeIntrinsicLocationDefinition,
|
|
57
|
+
NodeRepresentationTemplateDefinition,
|
|
58
|
+
NodeResourceTemplateDefinition,
|
|
51
59
|
NodeSetConfigResponse,
|
|
52
60
|
NodeStatus,
|
|
53
61
|
)
|
|
@@ -96,6 +104,17 @@ class AbstractNode(MadsciClientMixin):
|
|
|
96
104
|
_action_lock: ClassVar[threading.Lock] = threading.Lock()
|
|
97
105
|
"""Ensures only one blocking action can run at a time."""
|
|
98
106
|
|
|
107
|
+
resource_templates: ClassVar[list[NodeResourceTemplateDefinition]] = []
|
|
108
|
+
"""Declarative resource template definitions to register on startup."""
|
|
109
|
+
|
|
110
|
+
location_representation_templates: ClassVar[
|
|
111
|
+
list[NodeRepresentationTemplateDefinition]
|
|
112
|
+
] = []
|
|
113
|
+
"""Declarative location representation template definitions to register on startup."""
|
|
114
|
+
|
|
115
|
+
intrinsic_locations: ClassVar[list[NodeIntrinsicLocationDefinition]] = []
|
|
116
|
+
"""Intrinsic location definitions to register on startup."""
|
|
117
|
+
|
|
99
118
|
def __init__(
|
|
100
119
|
self,
|
|
101
120
|
node_config: Optional[NodeConfig] = None,
|
|
@@ -115,6 +134,18 @@ class AbstractNode(MadsciClientMixin):
|
|
|
115
134
|
module_name=module_name,
|
|
116
135
|
)
|
|
117
136
|
|
|
137
|
+
# * Populate intrinsic location definitions from class variables
|
|
138
|
+
self.node_info.intrinsic_locations = list(self.__class__.intrinsic_locations)
|
|
139
|
+
self.node_info.location_representation_templates = list(
|
|
140
|
+
self.__class__.location_representation_templates
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Resolve stable identity from registry if enabled
|
|
144
|
+
self._resolver = None
|
|
145
|
+
self._atexit_registered = False
|
|
146
|
+
if self.config.enable_registry_resolution:
|
|
147
|
+
self._resolve_identity_from_registry()
|
|
148
|
+
|
|
118
149
|
global_ownership_info.node_id = self.node_info.node_id
|
|
119
150
|
self._configure_clients()
|
|
120
151
|
|
|
@@ -136,6 +167,23 @@ class AbstractNode(MadsciClientMixin):
|
|
|
136
167
|
"""Node Lifecycle and Public Methods"""
|
|
137
168
|
"""------------------------------------------------------------------------------------------------"""
|
|
138
169
|
|
|
170
|
+
def close(self) -> None:
|
|
171
|
+
"""Release registry identity and clean up client resources.
|
|
172
|
+
|
|
173
|
+
This method is idempotent and safe to call multiple times.
|
|
174
|
+
Call this before reassigning a node variable to a new node
|
|
175
|
+
with the same name (e.g. in notebook cells) to avoid
|
|
176
|
+
``RegistryLockError`` from lingering heartbeat threads.
|
|
177
|
+
"""
|
|
178
|
+
self._release_registry_identity()
|
|
179
|
+
self._resolver = None
|
|
180
|
+
self.teardown_clients()
|
|
181
|
+
|
|
182
|
+
def __del__(self) -> None:
|
|
183
|
+
"""Best-effort cleanup when the node is garbage-collected."""
|
|
184
|
+
with contextlib.suppress(Exception):
|
|
185
|
+
self.close()
|
|
186
|
+
|
|
139
187
|
def start_node(self) -> None:
|
|
140
188
|
"""
|
|
141
189
|
Called once to start the node.
|
|
@@ -164,12 +212,183 @@ class AbstractNode(MadsciClientMixin):
|
|
|
164
212
|
def state_handler(self) -> None:
|
|
165
213
|
"""Called periodically to update the node state. Should set `self.node_state`"""
|
|
166
214
|
|
|
215
|
+
def template_handler(self) -> None:
|
|
216
|
+
"""Register declarative templates with the resource and location managers.
|
|
217
|
+
|
|
218
|
+
Iterates over ``resource_templates`` and ``location_representation_templates``
|
|
219
|
+
class members. Each template is registered via the appropriate client API.
|
|
220
|
+
Errors are caught and logged per-template (with the template name and type
|
|
221
|
+
clearly identified) so that a single failed registration does not prevent the
|
|
222
|
+
node from starting.
|
|
223
|
+
"""
|
|
224
|
+
# 1. Resource templates (via resource_client.init_template)
|
|
225
|
+
for defn in self.resource_templates:
|
|
226
|
+
try:
|
|
227
|
+
self.resource_client.init_template(
|
|
228
|
+
resource=defn.resource,
|
|
229
|
+
template_name=defn.template_name,
|
|
230
|
+
description=defn.description,
|
|
231
|
+
required_overrides=defn.required_overrides,
|
|
232
|
+
tags=defn.tags,
|
|
233
|
+
created_by=self.node_info.node_id,
|
|
234
|
+
version=defn.version,
|
|
235
|
+
)
|
|
236
|
+
self.logger.info(
|
|
237
|
+
"Registered resource template",
|
|
238
|
+
event_type=EventType.LOG_INFO,
|
|
239
|
+
template_name=defn.template_name,
|
|
240
|
+
template_type="resource",
|
|
241
|
+
)
|
|
242
|
+
except Exception as e:
|
|
243
|
+
self.logger.warning(
|
|
244
|
+
"Failed to register resource template",
|
|
245
|
+
event_type=EventType.LOG_WARNING,
|
|
246
|
+
template_name=defn.template_name,
|
|
247
|
+
template_type="resource",
|
|
248
|
+
error=str(e),
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# 2. Location representation templates (via location_client.init_representation_template)
|
|
252
|
+
for defn in self.location_representation_templates:
|
|
253
|
+
try:
|
|
254
|
+
self.location_client.init_representation_template(
|
|
255
|
+
template_name=defn.template_name,
|
|
256
|
+
default_values=defn.default_values,
|
|
257
|
+
schema_def=defn.schema_def,
|
|
258
|
+
required_overrides=defn.required_overrides,
|
|
259
|
+
tags=defn.tags,
|
|
260
|
+
created_by=self.node_info.node_id,
|
|
261
|
+
version=defn.version,
|
|
262
|
+
description=defn.description,
|
|
263
|
+
)
|
|
264
|
+
self.logger.info(
|
|
265
|
+
"Registered location representation template",
|
|
266
|
+
event_type=EventType.LOG_INFO,
|
|
267
|
+
template_name=defn.template_name,
|
|
268
|
+
template_type="location_representation",
|
|
269
|
+
)
|
|
270
|
+
except Exception as e:
|
|
271
|
+
self.logger.warning(
|
|
272
|
+
"Failed to register location representation template",
|
|
273
|
+
event_type=EventType.LOG_WARNING,
|
|
274
|
+
template_name=defn.template_name,
|
|
275
|
+
template_type="location_representation",
|
|
276
|
+
error=str(e),
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
def intrinsic_location_handler(self) -> None:
|
|
280
|
+
"""Register intrinsic locations with the Location Manager.
|
|
281
|
+
|
|
282
|
+
Iterates over ``intrinsic_locations`` class variable. Each location is
|
|
283
|
+
registered via location_client.init_location() with automatic
|
|
284
|
+
'{node_name}.' prefix. Errors are caught per-location so a single
|
|
285
|
+
failure does not prevent the node from starting.
|
|
286
|
+
"""
|
|
287
|
+
for defn in self.intrinsic_locations:
|
|
288
|
+
try:
|
|
289
|
+
location_name = f"{self.node_info.node_name}.{defn.location_name}"
|
|
290
|
+
representations = {
|
|
291
|
+
self.node_info.node_name: defn.representation_overrides
|
|
292
|
+
}
|
|
293
|
+
owner = OwnershipInfo(node_id=self.node_info.node_id)
|
|
294
|
+
|
|
295
|
+
self.location_client.init_location(
|
|
296
|
+
location_name=location_name,
|
|
297
|
+
representations=representations,
|
|
298
|
+
resource_template_name=defn.resource_template_name,
|
|
299
|
+
resource_template_overrides=defn.resource_template_overrides,
|
|
300
|
+
description=defn.description,
|
|
301
|
+
allow_transfers=defn.allow_transfers,
|
|
302
|
+
managed_by=LocationManagement.NODE,
|
|
303
|
+
owner=owner,
|
|
304
|
+
)
|
|
305
|
+
self.logger.info(
|
|
306
|
+
"Registered intrinsic location",
|
|
307
|
+
event_type=EventType.LOG_INFO,
|
|
308
|
+
location_name=location_name,
|
|
309
|
+
)
|
|
310
|
+
except Exception as e:
|
|
311
|
+
self.logger.warning(
|
|
312
|
+
"Failed to register intrinsic location",
|
|
313
|
+
event_type=EventType.LOG_WARNING,
|
|
314
|
+
location_name=defn.location_name,
|
|
315
|
+
error=str(e),
|
|
316
|
+
)
|
|
317
|
+
|
|
167
318
|
def startup_handler(self) -> None:
|
|
168
319
|
"""Called to (re)initialize the node. Should be used to open connections to devices or initialize any other resources."""
|
|
169
320
|
|
|
170
321
|
def shutdown_handler(self) -> None:
|
|
171
322
|
"""Called to shut down the node. Should be used to clean up any resources."""
|
|
172
323
|
|
|
324
|
+
def _resolve_identity_from_registry(self) -> None:
|
|
325
|
+
"""Resolve node identity from the ID Registry.
|
|
326
|
+
|
|
327
|
+
Uses IdentityResolver to look up or create a stable ID for this
|
|
328
|
+
node. The resolved ID is written back to ``self.node_info.node_id``.
|
|
329
|
+
|
|
330
|
+
A ``RegistryLockError`` (after retry exhaustion) is fatal — the
|
|
331
|
+
node cannot start without a stable identity. Other errors
|
|
332
|
+
(e.g. missing registry file) are non-fatal: a warning is logged
|
|
333
|
+
and the generated ID is kept.
|
|
334
|
+
"""
|
|
335
|
+
from madsci.common.registry.identity_resolver import ( # noqa: PLC0415
|
|
336
|
+
IdentityResolver,
|
|
337
|
+
)
|
|
338
|
+
from madsci.common.registry.lock_manager import ( # noqa: PLC0415
|
|
339
|
+
RegistryLockError,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
try:
|
|
343
|
+
self._resolver = IdentityResolver(lab_url=self.config.lab_url)
|
|
344
|
+
|
|
345
|
+
node_name = self.node_info.node_name
|
|
346
|
+
|
|
347
|
+
result = self._resolver.resolve_with_info(
|
|
348
|
+
name=node_name,
|
|
349
|
+
component_type="node",
|
|
350
|
+
metadata={"module_name": self.node_info.module_name},
|
|
351
|
+
retry_timeout=self.config.registry_lock_timeout,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
self.node_info.node_id = result.id
|
|
355
|
+
|
|
356
|
+
if not self._atexit_registered:
|
|
357
|
+
ref = weakref.ref(self)
|
|
358
|
+
|
|
359
|
+
def _release_on_exit(weak_ref: weakref.ref = ref) -> None:
|
|
360
|
+
obj = weak_ref()
|
|
361
|
+
if obj is not None:
|
|
362
|
+
obj._release_registry_identity()
|
|
363
|
+
|
|
364
|
+
atexit.register(_release_on_exit)
|
|
365
|
+
self._atexit_registered = True
|
|
366
|
+
|
|
367
|
+
except RegistryLockError:
|
|
368
|
+
raise
|
|
369
|
+
except Exception:
|
|
370
|
+
logging.getLogger(__name__).warning(
|
|
371
|
+
"Registry identity resolution failed; using generated ID",
|
|
372
|
+
exc_info=True,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
def _release_registry_identity(self) -> None:
|
|
376
|
+
"""Release the registry lock for this node's identity.
|
|
377
|
+
|
|
378
|
+
Called via ``atexit`` to allow other instances to acquire the
|
|
379
|
+
same name.
|
|
380
|
+
"""
|
|
381
|
+
if self._resolver is None:
|
|
382
|
+
return
|
|
383
|
+
|
|
384
|
+
try:
|
|
385
|
+
self._resolver.release(self.node_info.node_name)
|
|
386
|
+
except Exception:
|
|
387
|
+
logging.getLogger(__name__).warning(
|
|
388
|
+
"Failed to release registry identity",
|
|
389
|
+
exc_info=True,
|
|
390
|
+
)
|
|
391
|
+
|
|
173
392
|
"""------------------------------------------------------------------------------------------------"""
|
|
174
393
|
"""Interface Methods"""
|
|
175
394
|
"""------------------------------------------------------------------------------------------------"""
|
|
@@ -1254,6 +1473,8 @@ class AbstractNode(MadsciClientMixin):
|
|
|
1254
1473
|
self.node_status.locked = False
|
|
1255
1474
|
self.node_status.paused = False
|
|
1256
1475
|
self.node_status.stopped = False
|
|
1476
|
+
self.template_handler()
|
|
1477
|
+
self.intrinsic_location_handler()
|
|
1257
1478
|
self.startup_handler()
|
|
1258
1479
|
# * Start status and state update loops
|
|
1259
1480
|
repeat_on_interval(
|
|
@@ -117,10 +117,13 @@ def test_node_factory() -> Generator[Callable[..., TestNode], None, None]:
|
|
|
117
117
|
Configured TestNode instance
|
|
118
118
|
"""
|
|
119
119
|
# Create base config with identity fields
|
|
120
|
+
# Disable registry resolution by default in tests to prevent
|
|
121
|
+
# lock contention and side effects from shared registry files.
|
|
120
122
|
config_params = {
|
|
121
123
|
"test_required_param": 1,
|
|
122
124
|
"node_name": node_name,
|
|
123
125
|
"module_name": module_name,
|
|
126
|
+
"enable_registry_resolution": False,
|
|
124
127
|
**config_kwargs,
|
|
125
128
|
}
|
|
126
129
|
if config_overrides:
|
|
@@ -30,7 +30,7 @@ class TestArgumentParsingNode(RestNode):
|
|
|
30
30
|
"""Test node for argument parsing tests."""
|
|
31
31
|
|
|
32
32
|
__test__ = False
|
|
33
|
-
config: TestConfig = TestConfig()
|
|
33
|
+
config: TestConfig = TestConfig(enable_registry_resolution=False)
|
|
34
34
|
config_model = TestConfig
|
|
35
35
|
|
|
36
36
|
def startup_handler(self) -> None:
|