iris-pex-embedded-python 4.0.0b2__tar.gz → 4.0.0b4__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.
- {iris_pex_embedded_python-4.0.0b2/src/iris_pex_embedded_python.egg-info → iris_pex_embedded_python-4.0.0b4}/PKG-INFO +1 -1
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/pyproject.toml +1 -1
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/__init__.py +8 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cli/main.py +19 -3
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cli/parser.py +5 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cli/types.py +1 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Service/Remote/Rest/v1.cls +2 -1
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Utils.cls +3 -1
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/messages/serialization.py +13 -4
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/migration/plans.py +25 -2
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/migration/utils.py +51 -7
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/__init__.py +8 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/common.py +19 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/diff.py +9 -2
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/import_.py +16 -1
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/model.py +56 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/reconstruction.py +14 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/rendering.py +37 -1
- iris_pex_embedded_python-4.0.0b4/src/iop/production/validation.py +453 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/runtime/director.py +15 -5
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/runtime/local.py +11 -2
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/runtime/protocol.py +5 -1
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/runtime/remote/director.py +10 -2
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/runtime/remote/migration.py +7 -1
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4/src/iris_pex_embedded_python.egg-info}/PKG-INFO +1 -1
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iris_pex_embedded_python.egg-info/SOURCES.txt +1 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/LICENSE +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/README.md +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/setup.cfg +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/__main__.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cli/__init__.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cli/formatting.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/BusinessOperation.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/BusinessProcess.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/BusinessService.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Common.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Director.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Duplex/Operation.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Duplex/Process.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Duplex/Service.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Generator/Message/Ack.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Generator/Message/Poll.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Generator/Message/Start.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Generator/Message/StartPickle.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Generator/Message/Stop.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/InboundAdapter.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Message/JSONSchema.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Message.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/OutboundAdapter.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/PickleMessage.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/PrivateSession/Duplex.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/PrivateSession/Message/Ack.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/PrivateSession/Message/Poll.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/PrivateSession/Message/Start.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/PrivateSession/Message/Stop.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Projection.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Service/Remote/Handler.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Test.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Wrapper.cls +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/__init__.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/async_request.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/business_host.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/business_operation.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/business_process.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/business_service.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/common.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/debugpy.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/generator_request.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/inbound_adapter.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/log_manager.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/outbound_adapter.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/polling_business_service.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/private_session_duplex.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/private_session_process.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/components/settings.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/messages/__init__.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/messages/base.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/messages/decorators.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/messages/dispatch.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/messages/persistent.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/messages/validation.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/migration/__init__.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/migration/io.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/actions.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/component.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/inspection.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/runtime.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/types.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/runtime/__init__.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/runtime/environment.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/runtime/iris.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/runtime/remote/__init__.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/runtime/remote/client.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/runtime/remote/settings.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/runtime/remote/setup.py +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iris_pex_embedded_python.egg-info/dependency_links.txt +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iris_pex_embedded_python.egg-info/entry_points.txt +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iris_pex_embedded_python.egg-info/requires.txt +0 -0
- {iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iris_pex_embedded_python.egg-info/top_level.txt +0 -0
|
@@ -31,6 +31,10 @@ from iop.production import Production as Production
|
|
|
31
31
|
from iop.production import ProductionDiff as ProductionDiff
|
|
32
32
|
from iop.production import ProductionDiffEntry as ProductionDiffEntry
|
|
33
33
|
from iop.production import ProductionGraph as ProductionGraph
|
|
34
|
+
from iop.production import ProductionValidationError as ProductionValidationError
|
|
35
|
+
from iop.production import ProductionValidationIssue as ProductionValidationIssue
|
|
36
|
+
from iop.production import ProductionValidationReport as ProductionValidationReport
|
|
37
|
+
from iop.production import ProductionValidationWarning as ProductionValidationWarning
|
|
34
38
|
from iop.production import target as target
|
|
35
39
|
from iop.runtime.director import _Director
|
|
36
40
|
from iop.runtime.protocol import DirectorProtocol as DirectorProtocol
|
|
@@ -59,6 +63,10 @@ __all__ = [
|
|
|
59
63
|
"ProductionDiff",
|
|
60
64
|
"ProductionDiffEntry",
|
|
61
65
|
"ProductionGraph",
|
|
66
|
+
"ProductionValidationError",
|
|
67
|
+
"ProductionValidationIssue",
|
|
68
|
+
"ProductionValidationReport",
|
|
69
|
+
"ProductionValidationWarning",
|
|
62
70
|
"PydanticMessage",
|
|
63
71
|
"PydanticPickleMessage",
|
|
64
72
|
"Setting",
|
|
@@ -244,17 +244,33 @@ class Command:
|
|
|
244
244
|
if self.args.migration_plan:
|
|
245
245
|
print(
|
|
246
246
|
migration_utils.explain_migration(
|
|
247
|
-
migrate_path,
|
|
247
|
+
migrate_path,
|
|
248
|
+
mode=mode,
|
|
249
|
+
namespace=self.director.namespace,
|
|
250
|
+
strict_production_validation=(
|
|
251
|
+
self.args.strict_production_validation
|
|
252
|
+
),
|
|
248
253
|
)
|
|
249
254
|
)
|
|
250
255
|
return
|
|
251
256
|
if self._is_remote:
|
|
252
257
|
print(
|
|
253
258
|
migration_utils.explain_migration(
|
|
254
|
-
migrate_path,
|
|
259
|
+
migrate_path,
|
|
260
|
+
mode=mode,
|
|
261
|
+
namespace=self.director.namespace,
|
|
262
|
+
strict_production_validation=(
|
|
263
|
+
self.args.strict_production_validation
|
|
264
|
+
),
|
|
255
265
|
)
|
|
256
266
|
)
|
|
257
|
-
self.
|
|
267
|
+
if self.args.strict_production_validation:
|
|
268
|
+
self.director.migrate(
|
|
269
|
+
migrate_path,
|
|
270
|
+
strict_production_validation=True,
|
|
271
|
+
)
|
|
272
|
+
else:
|
|
273
|
+
self.director.migrate(migrate_path)
|
|
258
274
|
if self._is_remote:
|
|
259
275
|
print(
|
|
260
276
|
migration_utils.format_migration_success(
|
|
@@ -95,6 +95,11 @@ def create_parser() -> argparse.ArgumentParser:
|
|
|
95
95
|
help="show the migration plan and validation messages without writing to IRIS",
|
|
96
96
|
action="store_true",
|
|
97
97
|
)
|
|
98
|
+
migrate.add_argument(
|
|
99
|
+
"--strict-production-validation",
|
|
100
|
+
help="fail migration when production validation reports issues",
|
|
101
|
+
action="store_true",
|
|
102
|
+
)
|
|
98
103
|
|
|
99
104
|
export = main_parser.add_argument_group("export arguments")
|
|
100
105
|
export.add_argument(
|
|
@@ -499,6 +499,7 @@ ClassMethod PutMigrate() As %DynamicObject
|
|
|
499
499
|
set targetDirectory = dyna.%Get("remote_folder")
|
|
500
500
|
set packageName = dyna.%Get("package")
|
|
501
501
|
set settingsFile = dyna.%Get("settings_file")
|
|
502
|
+
set strictProductionValidation = dyna.%Get("strict_production_validation")
|
|
502
503
|
If settingsFile = "" { Set settingsFile = "settings.py" }
|
|
503
504
|
// check for namespace existence and user permissions against namespace
|
|
504
505
|
If '..NamespaceCheck(namespace) {
|
|
@@ -551,7 +552,7 @@ ClassMethod PutMigrate() As %DynamicObject
|
|
|
551
552
|
set iopUtils = ##class(IOP.Wrapper).Import("iop.migration.utils")
|
|
552
553
|
set builtins = ##class(%SYS.Python).Import("builtins")
|
|
553
554
|
If builtins.hasattr(iopUtils, "migrate") {
|
|
554
|
-
do builtins.getattr(iopUtils, "migrate")."__call__"(##class(%Library.File).NormalizeFilename(settingsFile, packagePath))
|
|
555
|
+
do builtins.getattr(iopUtils, "migrate")."__call__"(##class(%Library.File).NormalizeFilename(settingsFile, packagePath),"REMOTE",namespace,strictProductionValidation)
|
|
555
556
|
} Else {
|
|
556
557
|
do iopUtils."_Utils".migrate(##class(%Library.File).NormalizeFilename(settingsFile, packagePath))
|
|
557
558
|
}
|
{iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/cls/IOP/Utils.cls
RENAMED
|
@@ -719,8 +719,10 @@ ClassMethod InsertSetting(
|
|
|
719
719
|
pSettingData) As %Status [ Internal, Private ]
|
|
720
720
|
{
|
|
721
721
|
#dim tSetting As Ens.Config.Setting
|
|
722
|
+
#dim tDefaultTarget As %String
|
|
723
|
+
Set tDefaultTarget = $Select(pOwner.%IsA("Ens.Config.Production"):"",1:"Host")
|
|
722
724
|
Set tSetting = ##class(Ens.Config.Setting).%New()
|
|
723
|
-
Set tSetting.Target = ..DynamicGet(pSettingData,"@Target",
|
|
725
|
+
Set tSetting.Target = ..DynamicGet(pSettingData,"@Target",tDefaultTarget)
|
|
724
726
|
Set tSetting.Name = ..DynamicGet(pSettingData,"@Name","")
|
|
725
727
|
Set tSetting.Value = ..DynamicGet(pSettingData,"#text","")
|
|
726
728
|
Do pOwner.Settings.Insert(tSetting)
|
|
@@ -22,6 +22,12 @@ class SerializationError(Exception):
|
|
|
22
22
|
pass
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
class MessageClassImportError(SerializationError, ImportError):
|
|
26
|
+
"""Raised when a JSON message's Python class cannot be imported."""
|
|
27
|
+
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
25
31
|
class TempPydanticModel(BaseModel):
|
|
26
32
|
model_config = {"arbitrary_types_allowed": True, "extra": "allow"}
|
|
27
33
|
|
|
@@ -93,8 +99,8 @@ class MessageSerializer:
|
|
|
93
99
|
)
|
|
94
100
|
module = importlib.import_module(module_name)
|
|
95
101
|
msg_class = getattr(module, class_name)
|
|
96
|
-
except
|
|
97
|
-
raise
|
|
102
|
+
except (ModuleNotFoundError, AttributeError, ValueError) as e:
|
|
103
|
+
raise MessageClassImportError(
|
|
98
104
|
f"Failed to load class {serial.classname}: {str(e)}"
|
|
99
105
|
) from e
|
|
100
106
|
|
|
@@ -130,9 +136,12 @@ class MessageSerializer:
|
|
|
130
136
|
|
|
131
137
|
@staticmethod
|
|
132
138
|
def _parse_classname(classname: str) -> tuple[str, str]:
|
|
133
|
-
|
|
139
|
+
try:
|
|
140
|
+
j = classname.rindex(".")
|
|
141
|
+
except ValueError as e:
|
|
142
|
+
raise ValueError(f"Classname must include a module: {classname}") from e
|
|
134
143
|
if j <= 0:
|
|
135
|
-
raise
|
|
144
|
+
raise ValueError(f"Classname must include a module: {classname}")
|
|
136
145
|
return classname[:j], classname[j + 1 :]
|
|
137
146
|
|
|
138
147
|
@staticmethod
|
{iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/migration/plans.py
RENAMED
|
@@ -4,6 +4,8 @@ import inspect
|
|
|
4
4
|
import os
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
+
from ..production.validation import validate_production_entry
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
def format_migration_success(filename: str, namespace: str | None = None) -> str:
|
|
9
11
|
suffix = f" in namespace {namespace}" if namespace else ""
|
|
@@ -21,6 +23,7 @@ def format_migration_plan(plan: dict[str, Any]) -> str:
|
|
|
21
23
|
lines.extend(format_plan_section("CLASSES", plan["classes"]))
|
|
22
24
|
lines.extend(format_plan_section("SCHEMAS", plan["schemas"]))
|
|
23
25
|
lines.extend(format_plan_section("PRODUCTIONS", plan["productions"]))
|
|
26
|
+
lines.extend(format_plan_section("VALIDATION", plan.get("validation", [])))
|
|
24
27
|
return "\n".join(lines)
|
|
25
28
|
|
|
26
29
|
|
|
@@ -47,6 +50,7 @@ class MigrationPlanner:
|
|
|
47
50
|
filename=None,
|
|
48
51
|
mode: str | None = None,
|
|
49
52
|
namespace: str | None = None,
|
|
53
|
+
strict_production_validation: bool = False,
|
|
50
54
|
) -> dict[str, Any]:
|
|
51
55
|
"""Build and validate a migration plan from a settings module."""
|
|
52
56
|
if not path:
|
|
@@ -59,11 +63,16 @@ class MigrationPlanner:
|
|
|
59
63
|
"classes": [],
|
|
60
64
|
"schemas": [],
|
|
61
65
|
"productions": [],
|
|
66
|
+
"validation": [],
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
self._add_class_entries(plan, getattr(settings, "CLASSES", {}), path)
|
|
65
70
|
self._add_schema_entries(plan, getattr(settings, "SCHEMAS", None))
|
|
66
|
-
self._add_production_entries(
|
|
71
|
+
self._add_production_entries(
|
|
72
|
+
plan,
|
|
73
|
+
getattr(settings, "PRODUCTIONS", None),
|
|
74
|
+
strict_production_validation=strict_production_validation,
|
|
75
|
+
)
|
|
67
76
|
return plan
|
|
68
77
|
|
|
69
78
|
@staticmethod
|
|
@@ -102,7 +111,11 @@ class MigrationPlanner:
|
|
|
102
111
|
plan["schemas"].append(self._utils._python_classname(cls))
|
|
103
112
|
|
|
104
113
|
def _add_production_entries(
|
|
105
|
-
self,
|
|
114
|
+
self,
|
|
115
|
+
plan: dict[str, Any],
|
|
116
|
+
productions: list[Any] | None,
|
|
117
|
+
*,
|
|
118
|
+
strict_production_validation: bool = False,
|
|
106
119
|
) -> None:
|
|
107
120
|
if productions is None:
|
|
108
121
|
return
|
|
@@ -110,6 +123,16 @@ class MigrationPlanner:
|
|
|
110
123
|
raise ValueError("PRODUCTIONS must be a list.")
|
|
111
124
|
auto_class_entries = set()
|
|
112
125
|
for production in productions:
|
|
126
|
+
report = validate_production_entry(
|
|
127
|
+
production,
|
|
128
|
+
strict=strict_production_validation,
|
|
129
|
+
warn=False,
|
|
130
|
+
)
|
|
131
|
+
if report.has_issues:
|
|
132
|
+
plan["validation"].extend(
|
|
133
|
+
f"{report.production_name}: {issue.to_text()}"
|
|
134
|
+
for issue in report.issues
|
|
135
|
+
)
|
|
113
136
|
if self._utils._is_production_object(production):
|
|
114
137
|
plan["productions"].append(production.name)
|
|
115
138
|
self._add_production_component_entries(
|
{iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/migration/utils.py
RENAMED
|
@@ -18,6 +18,7 @@ from ..messages.persistent import (
|
|
|
18
18
|
is_persistent_message_class,
|
|
19
19
|
register_persistent_message_class,
|
|
20
20
|
)
|
|
21
|
+
from ..production.validation import validate_production_entry
|
|
21
22
|
from ..runtime import iris as _iris
|
|
22
23
|
from ..runtime.environment import remove_sys_path, temporary_sys_path
|
|
23
24
|
from .io import (
|
|
@@ -425,7 +426,12 @@ def filename_to_module(filename) -> str:
|
|
|
425
426
|
return module
|
|
426
427
|
|
|
427
428
|
|
|
428
|
-
def migrate(
|
|
429
|
+
def migrate(
|
|
430
|
+
filename=None,
|
|
431
|
+
mode: str | None = None,
|
|
432
|
+
namespace: str | None = None,
|
|
433
|
+
strict_production_validation: bool = False,
|
|
434
|
+
):
|
|
429
435
|
"""
|
|
430
436
|
Read the settings.py file and register all the components
|
|
431
437
|
settings.py file has two dictionaries:
|
|
@@ -443,10 +449,19 @@ def migrate(filename=None, mode: str | None = None, namespace: str | None = None
|
|
|
443
449
|
|
|
444
450
|
try:
|
|
445
451
|
plan = _build_migration_plan(
|
|
446
|
-
settings,
|
|
452
|
+
settings,
|
|
453
|
+
path,
|
|
454
|
+
filename,
|
|
455
|
+
mode=mode,
|
|
456
|
+
namespace=namespace,
|
|
457
|
+
strict_production_validation=strict_production_validation,
|
|
447
458
|
)
|
|
448
459
|
print(format_migration_plan(plan))
|
|
449
|
-
_register_settings_components(
|
|
460
|
+
_register_settings_components(
|
|
461
|
+
settings,
|
|
462
|
+
path,
|
|
463
|
+
strict_production_validation=strict_production_validation,
|
|
464
|
+
)
|
|
450
465
|
print(
|
|
451
466
|
format_migration_success(
|
|
452
467
|
filename or inspect.getfile(settings), namespace=namespace
|
|
@@ -457,13 +472,21 @@ def migrate(filename=None, mode: str | None = None, namespace: str | None = None
|
|
|
457
472
|
|
|
458
473
|
|
|
459
474
|
def explain_migration(
|
|
460
|
-
filename=None,
|
|
475
|
+
filename=None,
|
|
476
|
+
mode: str | None = None,
|
|
477
|
+
namespace: str | None = None,
|
|
478
|
+
strict_production_validation: bool = False,
|
|
461
479
|
):
|
|
462
480
|
"""Return a human-readable migration plan without writing to IRIS."""
|
|
463
481
|
settings, path = _load_settings(filename)
|
|
464
482
|
try:
|
|
465
483
|
plan = _build_migration_plan(
|
|
466
|
-
settings,
|
|
484
|
+
settings,
|
|
485
|
+
path,
|
|
486
|
+
filename,
|
|
487
|
+
mode=mode,
|
|
488
|
+
namespace=namespace,
|
|
489
|
+
strict_production_validation=strict_production_validation,
|
|
467
490
|
)
|
|
468
491
|
return _format_migration_plan(plan)
|
|
469
492
|
finally:
|
|
@@ -488,6 +511,7 @@ def _build_migration_plan(
|
|
|
488
511
|
filename=None,
|
|
489
512
|
mode: str | None = None,
|
|
490
513
|
namespace: str | None = None,
|
|
514
|
+
strict_production_validation: bool = False,
|
|
491
515
|
):
|
|
492
516
|
return MigrationPlanner(sys.modules[__name__]).build(
|
|
493
517
|
settings,
|
|
@@ -495,6 +519,7 @@ def _build_migration_plan(
|
|
|
495
519
|
filename=filename,
|
|
496
520
|
mode=mode,
|
|
497
521
|
namespace=namespace,
|
|
522
|
+
strict_production_validation=strict_production_validation,
|
|
498
523
|
)
|
|
499
524
|
|
|
500
525
|
|
|
@@ -589,7 +614,12 @@ def _validate_dtl_schema_class(cls, setting_name):
|
|
|
589
614
|
)
|
|
590
615
|
|
|
591
616
|
|
|
592
|
-
def _register_settings_components(
|
|
617
|
+
def _register_settings_components(
|
|
618
|
+
settings,
|
|
619
|
+
path,
|
|
620
|
+
*,
|
|
621
|
+
strict_production_validation: bool = False,
|
|
622
|
+
):
|
|
593
623
|
"""Register all components from settings (classes, productions, schemas).
|
|
594
624
|
|
|
595
625
|
Args:
|
|
@@ -616,6 +646,7 @@ def _register_settings_components(settings, path):
|
|
|
616
646
|
settings.PRODUCTIONS,
|
|
617
647
|
path,
|
|
618
648
|
persistent_registry=persistent_registry,
|
|
649
|
+
strict_production_validation=strict_production_validation,
|
|
619
650
|
)
|
|
620
651
|
except AttributeError:
|
|
621
652
|
pass
|
|
@@ -835,18 +866,25 @@ def set_productions_settings(
|
|
|
835
866
|
production_list,
|
|
836
867
|
root_path=None,
|
|
837
868
|
persistent_registry=None,
|
|
869
|
+
strict_production_validation: bool = False,
|
|
838
870
|
):
|
|
839
871
|
"""
|
|
840
872
|
It takes a list of dictionaries and registers the productions
|
|
841
873
|
"""
|
|
842
874
|
# for each production in the list
|
|
843
875
|
for production in production_list:
|
|
844
|
-
|
|
876
|
+
production_is_object = _is_production_object(production)
|
|
877
|
+
if production_is_object:
|
|
845
878
|
_register_production_object_messages(
|
|
846
879
|
production,
|
|
847
880
|
persistent_registry=persistent_registry,
|
|
848
881
|
)
|
|
849
882
|
_register_production_object_components(production, root_path)
|
|
883
|
+
validate_production_entry(
|
|
884
|
+
production,
|
|
885
|
+
strict=strict_production_validation,
|
|
886
|
+
warn=True,
|
|
887
|
+
)
|
|
850
888
|
production = production.to_dict()
|
|
851
889
|
else:
|
|
852
890
|
production = copy.deepcopy(production)
|
|
@@ -860,6 +898,12 @@ def set_productions_settings(
|
|
|
860
898
|
production["Production"] = production.pop(production_name)
|
|
861
899
|
# handle Items
|
|
862
900
|
production = handle_items(production, root_path)
|
|
901
|
+
if not production_is_object:
|
|
902
|
+
validate_production_entry(
|
|
903
|
+
production,
|
|
904
|
+
strict=strict_production_validation,
|
|
905
|
+
warn=True,
|
|
906
|
+
)
|
|
863
907
|
# register the production
|
|
864
908
|
register_production_definition(production_name, production)
|
|
865
909
|
|
{iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/__init__.py
RENAMED
|
@@ -12,6 +12,10 @@ from .types import ProductionDiffEntry as ProductionDiffEntry
|
|
|
12
12
|
from .types import ProductionGraph as ProductionGraph
|
|
13
13
|
from .types import TargetSetting as TargetSetting
|
|
14
14
|
from .types import target as target
|
|
15
|
+
from .validation import ProductionValidationError as ProductionValidationError
|
|
16
|
+
from .validation import ProductionValidationIssue as ProductionValidationIssue
|
|
17
|
+
from .validation import ProductionValidationReport as ProductionValidationReport
|
|
18
|
+
from .validation import ProductionValidationWarning as ProductionValidationWarning
|
|
15
19
|
|
|
16
20
|
__all__ = [
|
|
17
21
|
"ComponentRef",
|
|
@@ -23,6 +27,10 @@ __all__ = [
|
|
|
23
27
|
"ProductionDiff",
|
|
24
28
|
"ProductionDiffEntry",
|
|
25
29
|
"ProductionGraph",
|
|
30
|
+
"ProductionValidationError",
|
|
31
|
+
"ProductionValidationIssue",
|
|
32
|
+
"ProductionValidationReport",
|
|
33
|
+
"ProductionValidationWarning",
|
|
26
34
|
"TargetSetting",
|
|
27
35
|
"resolve_target",
|
|
28
36
|
"target",
|
{iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/common.py
RENAMED
|
@@ -3,6 +3,25 @@ from __future__ import annotations
|
|
|
3
3
|
import importlib
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
+
PRODUCTION_SETTING_FIELDS: dict[str, tuple[str, Any]] = {
|
|
7
|
+
"shutdown_timeout": ("ShutdownTimeout", 120),
|
|
8
|
+
"update_timeout": ("UpdateTimeout", 10),
|
|
9
|
+
"alert_notification_manager": ("AlertNotificationManager", ""),
|
|
10
|
+
"alert_notification_operation": ("AlertNotificationOperation", ""),
|
|
11
|
+
"alert_notification_recipients": ("AlertNotificationRecipients", ""),
|
|
12
|
+
"alert_action_window": ("AlertActionWindow", 60),
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
PRODUCTION_SETTING_NAMES: dict[str, str] = {
|
|
16
|
+
field_name: iris_name
|
|
17
|
+
for field_name, (iris_name, _default) in PRODUCTION_SETTING_FIELDS.items()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
PRODUCTION_SETTING_FIELDS_BY_IRIS: dict[str, str] = {
|
|
21
|
+
iris_name: field_name
|
|
22
|
+
for field_name, (iris_name, _default) in PRODUCTION_SETTING_FIELDS.items()
|
|
23
|
+
}
|
|
24
|
+
|
|
6
25
|
|
|
7
26
|
def _bool_text(value: bool | str) -> str:
|
|
8
27
|
if isinstance(value, bool):
|
{iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/diff.py
RENAMED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import json
|
|
4
4
|
from typing import TYPE_CHECKING, Any
|
|
5
5
|
|
|
6
|
-
from .common import _bool_text, _text_value
|
|
6
|
+
from .common import PRODUCTION_SETTING_FIELDS, _bool_text, _text_value
|
|
7
7
|
from .types import (
|
|
8
8
|
GraphEdge,
|
|
9
9
|
ProductionDiff,
|
|
@@ -66,12 +66,19 @@ def _diff_warnings(desired: Production, current: Production) -> list[str]:
|
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
def _production_signature(production: Production) -> dict[str, Any]:
|
|
69
|
-
|
|
69
|
+
signature = {
|
|
70
70
|
"testing_enabled": _bool_text(production.testing_enabled),
|
|
71
71
|
"log_general_trace_events": _bool_text(production.log_general_trace_events),
|
|
72
72
|
"actor_pool_size": _text_value(production.actor_pool_size),
|
|
73
73
|
"description": production.description,
|
|
74
74
|
}
|
|
75
|
+
signature.update(
|
|
76
|
+
{
|
|
77
|
+
field_name: _text_value(getattr(production, field_name))
|
|
78
|
+
for field_name in PRODUCTION_SETTING_FIELDS
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
return signature
|
|
75
82
|
|
|
76
83
|
|
|
77
84
|
def _item_signatures(production: Production) -> dict[str, dict[str, Any]]:
|
{iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/import_.py
RENAMED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
-
from .common import _text_value
|
|
5
|
+
from .common import PRODUCTION_SETTING_FIELDS_BY_IRIS, _text_value
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def _as_list(value: Any) -> list[Any]:
|
|
@@ -54,6 +54,21 @@ def _split_settings(
|
|
|
54
54
|
return host_settings, adapter_settings, other_settings
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
def _split_production_settings(settings: Any) -> dict[str, Any]:
|
|
58
|
+
values: dict[str, Any] = {}
|
|
59
|
+
for setting in _as_list(settings):
|
|
60
|
+
if not isinstance(setting, dict):
|
|
61
|
+
continue
|
|
62
|
+
target = setting.get("@Target", "")
|
|
63
|
+
if target not in ("", "Production"):
|
|
64
|
+
continue
|
|
65
|
+
iris_name = str(setting.get("@Name", ""))
|
|
66
|
+
field_name = PRODUCTION_SETTING_FIELDS_BY_IRIS.get(iris_name)
|
|
67
|
+
if field_name:
|
|
68
|
+
values[field_name] = setting.get("#text", "")
|
|
69
|
+
return values
|
|
70
|
+
|
|
71
|
+
|
|
57
72
|
def _normalize_connections(
|
|
58
73
|
connections: Any,
|
|
59
74
|
) -> tuple[dict[str, list[dict[str, Any]]], set[str], list[str]]:
|
{iris_pex_embedded_python-4.0.0b2 → iris_pex_embedded_python-4.0.0b4}/src/iop/production/model.py
RENAMED
|
@@ -5,6 +5,7 @@ from typing import Any
|
|
|
5
5
|
from ..runtime.protocol import DirectorProtocol as _DirectorProtocol
|
|
6
6
|
from . import actions as _actions
|
|
7
7
|
from .common import (
|
|
8
|
+
PRODUCTION_SETTING_FIELDS,
|
|
8
9
|
_adapter_type_from_component_class,
|
|
9
10
|
_apply_settings_update,
|
|
10
11
|
_auto_proxy_class_name,
|
|
@@ -46,6 +47,20 @@ class Production:
|
|
|
46
47
|
log_general_trace_events: bool | str = False,
|
|
47
48
|
actor_pool_size: int | str = 2,
|
|
48
49
|
description: str = "",
|
|
50
|
+
shutdown_timeout: int | str = PRODUCTION_SETTING_FIELDS["shutdown_timeout"][1],
|
|
51
|
+
update_timeout: int | str = PRODUCTION_SETTING_FIELDS["update_timeout"][1],
|
|
52
|
+
alert_notification_manager: str = PRODUCTION_SETTING_FIELDS[
|
|
53
|
+
"alert_notification_manager"
|
|
54
|
+
][1],
|
|
55
|
+
alert_notification_operation: str = PRODUCTION_SETTING_FIELDS[
|
|
56
|
+
"alert_notification_operation"
|
|
57
|
+
][1],
|
|
58
|
+
alert_notification_recipients: str = PRODUCTION_SETTING_FIELDS[
|
|
59
|
+
"alert_notification_recipients"
|
|
60
|
+
][1],
|
|
61
|
+
alert_action_window: int | str = PRODUCTION_SETTING_FIELDS[
|
|
62
|
+
"alert_action_window"
|
|
63
|
+
][1],
|
|
49
64
|
namespace: str | None = None,
|
|
50
65
|
director: _DirectorProtocol | None = None,
|
|
51
66
|
):
|
|
@@ -54,6 +69,12 @@ class Production:
|
|
|
54
69
|
self.log_general_trace_events = log_general_trace_events
|
|
55
70
|
self.actor_pool_size = actor_pool_size
|
|
56
71
|
self.description = description
|
|
72
|
+
self.shutdown_timeout = shutdown_timeout
|
|
73
|
+
self.update_timeout = update_timeout
|
|
74
|
+
self.alert_notification_manager = alert_notification_manager
|
|
75
|
+
self.alert_notification_operation = alert_notification_operation
|
|
76
|
+
self.alert_notification_recipients = alert_notification_recipients
|
|
77
|
+
self.alert_action_window = alert_action_window
|
|
57
78
|
self.namespace = namespace
|
|
58
79
|
self._director = director
|
|
59
80
|
self._items: list[ComponentRef] = []
|
|
@@ -80,6 +101,36 @@ class Production:
|
|
|
80
101
|
self.description = text
|
|
81
102
|
return self
|
|
82
103
|
|
|
104
|
+
def timeouts(
|
|
105
|
+
self,
|
|
106
|
+
*,
|
|
107
|
+
shutdown: int | str | None = None,
|
|
108
|
+
update: int | str | None = None,
|
|
109
|
+
) -> Production:
|
|
110
|
+
if shutdown is not None:
|
|
111
|
+
self.shutdown_timeout = shutdown
|
|
112
|
+
if update is not None:
|
|
113
|
+
self.update_timeout = update
|
|
114
|
+
return self
|
|
115
|
+
|
|
116
|
+
def alerting(
|
|
117
|
+
self,
|
|
118
|
+
*,
|
|
119
|
+
manager: str | None = None,
|
|
120
|
+
operation: str | None = None,
|
|
121
|
+
recipients: str | None = None,
|
|
122
|
+
action_window: int | str | None = None,
|
|
123
|
+
) -> Production:
|
|
124
|
+
if manager is not None:
|
|
125
|
+
self.alert_notification_manager = manager
|
|
126
|
+
if operation is not None:
|
|
127
|
+
self.alert_notification_operation = operation
|
|
128
|
+
if recipients is not None:
|
|
129
|
+
self.alert_notification_recipients = recipients
|
|
130
|
+
if action_window is not None:
|
|
131
|
+
self.alert_action_window = action_window
|
|
132
|
+
return self
|
|
133
|
+
|
|
83
134
|
def in_namespace(self, namespace: str | None) -> Production:
|
|
84
135
|
self.namespace = namespace
|
|
85
136
|
return self
|
|
@@ -555,6 +606,11 @@ class Production:
|
|
|
555
606
|
def message_registrations(self) -> tuple[PersistentMessageRegistration, ...]:
|
|
556
607
|
return tuple(self._messages)
|
|
557
608
|
|
|
609
|
+
def validate(self, *, strict: bool = False):
|
|
610
|
+
from .validation import validate_production
|
|
611
|
+
|
|
612
|
+
return validate_production(self, strict=strict, warn=not strict)
|
|
613
|
+
|
|
558
614
|
def start(self, detach: bool = True) -> None:
|
|
559
615
|
_actions.start(self, detach=detach)
|
|
560
616
|
|
|
@@ -12,6 +12,7 @@ from .import_ import (
|
|
|
12
12
|
_normalize_runtime_item_metadata,
|
|
13
13
|
_production_payload,
|
|
14
14
|
_setting_targets,
|
|
15
|
+
_split_production_settings,
|
|
15
16
|
_split_settings,
|
|
16
17
|
)
|
|
17
18
|
|
|
@@ -26,12 +27,25 @@ def production_from_dict(
|
|
|
26
27
|
director: Any = None,
|
|
27
28
|
):
|
|
28
29
|
production_name, production_data = _production_payload(data)
|
|
30
|
+
production_settings = _split_production_settings(production_data.get("Setting"))
|
|
29
31
|
production = production_cls(
|
|
30
32
|
production_name,
|
|
31
33
|
testing_enabled=production_data.get("@TestingEnabled", False),
|
|
32
34
|
log_general_trace_events=production_data.get("@LogGeneralTraceEvents", False),
|
|
33
35
|
actor_pool_size=production_data.get("ActorPoolSize", 2),
|
|
34
36
|
description=production_data.get("Description", ""),
|
|
37
|
+
shutdown_timeout=production_settings.get("shutdown_timeout", 120),
|
|
38
|
+
update_timeout=production_settings.get("update_timeout", 10),
|
|
39
|
+
alert_notification_manager=production_settings.get(
|
|
40
|
+
"alert_notification_manager", ""
|
|
41
|
+
),
|
|
42
|
+
alert_notification_operation=production_settings.get(
|
|
43
|
+
"alert_notification_operation", ""
|
|
44
|
+
),
|
|
45
|
+
alert_notification_recipients=production_settings.get(
|
|
46
|
+
"alert_notification_recipients", ""
|
|
47
|
+
),
|
|
48
|
+
alert_action_window=production_settings.get("alert_action_window", 60),
|
|
35
49
|
namespace=namespace,
|
|
36
50
|
director=director,
|
|
37
51
|
)
|