Skip to content

Commit f56b3bf

Browse files
fix(platform): restore common interrupt model compatibility
1 parent fb99222 commit f56b3bf

4 files changed

Lines changed: 357 additions & 0 deletions

File tree

packages/uipath-platform/pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ name = "uipath-platform layered architecture"
137137
type = "layers"
138138
containers = ["uipath.platform"]
139139
exhaustive = true
140+
# TODO: Remove this exception in the next breaking-change release.
141+
ignore_imports = [
142+
"uipath.platform.common.interrupt_models -> uipath.platform.resume_triggers.interrupt_models",
143+
]
144+
unmatched_ignore_imports_alerting = "error"
140145
layers = [
141146
"_uipath : resume_triggers",
142147
"agenthub | automation_ops | automation_tracker | connections | context_grounding | entities | external_applications | governance | guardrails | memory | pii_detection | portal | resource_catalog | semantic_proxy",

packages/uipath-platform/src/uipath/platform/common/__init__.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,41 @@
33
This module contains common models used across multiple services.
44
"""
55

6+
from typing import TYPE_CHECKING, Any
7+
68
from uipath.core.triggers import UiPathResumeMetadata
79

10+
# TODO: Remove the interrupt-model compatibility exports in the next breaking-change release.
11+
if TYPE_CHECKING:
12+
from .interrupt_models import (
13+
CreateBatchTransform,
14+
CreateDeepRag,
15+
CreateDeepRagRaw,
16+
CreateEphemeralIndex,
17+
CreateEphemeralIndexRaw,
18+
CreateEscalation,
19+
CreateTask,
20+
DocumentExtraction,
21+
DocumentExtractionValidation,
22+
InvokeProcess,
23+
InvokeProcessRaw,
24+
InvokeSystemAgent,
25+
WaitBatchTransform,
26+
WaitDeepRag,
27+
WaitDeepRagRaw,
28+
WaitDocumentExtraction,
29+
WaitDocumentExtractionValidation,
30+
WaitEphemeralIndex,
31+
WaitEphemeralIndexRaw,
32+
WaitEscalation,
33+
WaitIntegrationEvent,
34+
WaitJob,
35+
WaitJobRaw,
36+
WaitSystemAgent,
37+
WaitTask,
38+
WaitUntil,
39+
)
40+
841
from ._api_client import ApiClient
942
from ._base_service import BaseService, resolve_trace_id
1043
from ._bindings import (
@@ -58,7 +91,33 @@
5891
"FolderContext",
5992
"TokenData",
6093
"UiPathConfig",
94+
"CreateTask",
95+
"CreateEscalation",
96+
"WaitEscalation",
97+
"InvokeProcess",
98+
"InvokeProcessRaw",
99+
"WaitTask",
100+
"WaitJob",
101+
"WaitJobRaw",
61102
"PagedResult",
103+
"CreateDeepRag",
104+
"CreateDeepRagRaw",
105+
"WaitDeepRag",
106+
"WaitDeepRagRaw",
107+
"CreateBatchTransform",
108+
"WaitBatchTransform",
109+
"DocumentExtraction",
110+
"WaitDocumentExtraction",
111+
"InvokeSystemAgent",
112+
"WaitSystemAgent",
113+
"CreateEphemeralIndex",
114+
"CreateEphemeralIndexRaw",
115+
"WaitEphemeralIndex",
116+
"WaitEphemeralIndexRaw",
117+
"DocumentExtractionValidation",
118+
"WaitDocumentExtractionValidation",
119+
"WaitIntegrationEvent",
120+
"WaitUntil",
62121
"RequestSpec",
63122
"Endpoint",
64123
"UiPathUrl",
@@ -97,3 +156,49 @@
97156
]
98157

99158
from .validation import validate_pagination_params
159+
160+
_INTERRUPT_MODEL_NAMES = {
161+
"CreateBatchTransform",
162+
"CreateDeepRag",
163+
"CreateDeepRagRaw",
164+
"CreateEphemeralIndex",
165+
"CreateEphemeralIndexRaw",
166+
"CreateEscalation",
167+
"CreateTask",
168+
"DocumentExtraction",
169+
"DocumentExtractionValidation",
170+
"InvokeProcess",
171+
"InvokeProcessRaw",
172+
"InvokeSystemAgent",
173+
"WaitBatchTransform",
174+
"WaitDeepRag",
175+
"WaitDeepRagRaw",
176+
"WaitDocumentExtraction",
177+
"WaitDocumentExtractionValidation",
178+
"WaitEphemeralIndex",
179+
"WaitEphemeralIndexRaw",
180+
"WaitEscalation",
181+
"WaitIntegrationEvent",
182+
"WaitJob",
183+
"WaitJobRaw",
184+
"WaitSystemAgent",
185+
"WaitTask",
186+
"WaitUntil",
187+
}
188+
189+
190+
def __getattr__(name: str) -> Any:
191+
"""Resolve moved interrupt models on demand."""
192+
if name not in _INTERRUPT_MODEL_NAMES:
193+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
194+
195+
from . import interrupt_models
196+
197+
value = getattr(interrupt_models, name)
198+
globals()[name] = value
199+
return value
200+
201+
202+
def __dir__() -> list[str]:
203+
"""List common exports, including compatibility aliases."""
204+
return sorted(set(globals()) | set(__all__))
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"""Deprecated lazy aliases for resume trigger interrupt models."""
2+
3+
import warnings
4+
from typing import TYPE_CHECKING, Any
5+
6+
# TODO: Remove this compatibility module in the next breaking-change release.
7+
if TYPE_CHECKING:
8+
from uipath.platform.resume_triggers.interrupt_models import (
9+
CreateBatchTransform,
10+
CreateDeepRag,
11+
CreateDeepRagRaw,
12+
CreateEphemeralIndex,
13+
CreateEphemeralIndexRaw,
14+
CreateEscalation,
15+
CreateTask,
16+
DocumentExtraction,
17+
DocumentExtractionValidation,
18+
InvokeProcess,
19+
InvokeProcessRaw,
20+
InvokeSystemAgent,
21+
WaitBatchTransform,
22+
WaitDeepRag,
23+
WaitDeepRagRaw,
24+
WaitDocumentExtraction,
25+
WaitDocumentExtractionValidation,
26+
WaitEphemeralIndex,
27+
WaitEphemeralIndexRaw,
28+
WaitEscalation,
29+
WaitIntegrationEvent,
30+
WaitJob,
31+
WaitJobRaw,
32+
WaitSystemAgent,
33+
WaitTask,
34+
WaitUntil,
35+
)
36+
37+
__all__ = [
38+
"CreateBatchTransform",
39+
"CreateDeepRag",
40+
"CreateDeepRagRaw",
41+
"CreateEphemeralIndex",
42+
"CreateEphemeralIndexRaw",
43+
"CreateEscalation",
44+
"CreateTask",
45+
"DocumentExtraction",
46+
"DocumentExtractionValidation",
47+
"InvokeProcess",
48+
"InvokeProcessRaw",
49+
"InvokeSystemAgent",
50+
"WaitBatchTransform",
51+
"WaitDeepRag",
52+
"WaitDeepRagRaw",
53+
"WaitDocumentExtraction",
54+
"WaitDocumentExtractionValidation",
55+
"WaitEphemeralIndex",
56+
"WaitEphemeralIndexRaw",
57+
"WaitEscalation",
58+
"WaitIntegrationEvent",
59+
"WaitJob",
60+
"WaitJobRaw",
61+
"WaitSystemAgent",
62+
"WaitTask",
63+
"WaitUntil",
64+
]
65+
66+
_warning_emitted = False
67+
68+
69+
def __getattr__(name: str) -> Any:
70+
"""Resolve deprecated interrupt model aliases on demand."""
71+
if name not in __all__:
72+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
73+
74+
import uipath.platform.resume_triggers.interrupt_models as canonical_models
75+
76+
value = getattr(canonical_models, name)
77+
global _warning_emitted
78+
if not _warning_emitted:
79+
warnings.warn(
80+
"Interrupt models from uipath.platform.common are deprecated and will "
81+
"be removed in a future release; import from "
82+
"uipath.platform.resume_triggers instead.",
83+
FutureWarning,
84+
stacklevel=2,
85+
)
86+
_warning_emitted = True
87+
globals()[name] = value
88+
return value
89+
90+
91+
def __dir__() -> list[str]:
92+
"""List compatibility exports."""
93+
return sorted(set(globals()) | set(__all__))
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import importlib
2+
import subprocess
3+
import sys
4+
import textwrap
5+
import warnings
6+
7+
import pytest
8+
9+
# TODO: Remove these tests with the compatibility bridge in the next breaking-change release.
10+
INTERRUPT_MODEL_NAMES = (
11+
"CreateBatchTransform",
12+
"CreateDeepRag",
13+
"CreateDeepRagRaw",
14+
"CreateEphemeralIndex",
15+
"CreateEphemeralIndexRaw",
16+
"CreateEscalation",
17+
"CreateTask",
18+
"DocumentExtraction",
19+
"DocumentExtractionValidation",
20+
"InvokeProcess",
21+
"InvokeProcessRaw",
22+
"InvokeSystemAgent",
23+
"WaitBatchTransform",
24+
"WaitDeepRag",
25+
"WaitDeepRagRaw",
26+
"WaitDocumentExtraction",
27+
"WaitDocumentExtractionValidation",
28+
"WaitEphemeralIndex",
29+
"WaitEphemeralIndexRaw",
30+
"WaitEscalation",
31+
"WaitIntegrationEvent",
32+
"WaitJob",
33+
"WaitJobRaw",
34+
"WaitSystemAgent",
35+
"WaitTask",
36+
"WaitUntil",
37+
)
38+
39+
40+
def _run_python(source: str) -> None:
41+
result = subprocess.run(
42+
[sys.executable, "-c", source],
43+
capture_output=True,
44+
check=False,
45+
text=True,
46+
)
47+
assert result.returncode == 0, result.stderr
48+
49+
50+
def test_compatibility_exports_are_canonical_objects():
51+
canonical = importlib.import_module(
52+
"uipath.platform.resume_triggers.interrupt_models"
53+
)
54+
common = importlib.import_module("uipath.platform.common")
55+
legacy = importlib.import_module("uipath.platform.common.interrupt_models")
56+
57+
with warnings.catch_warnings(record=True) as caught:
58+
warnings.simplefilter("always")
59+
for name in INTERRUPT_MODEL_NAMES:
60+
canonical_model = getattr(canonical, name)
61+
assert getattr(legacy, name) is canonical_model
62+
assert getattr(common, name) is canonical_model
63+
64+
compatibility_warnings = [
65+
warning
66+
for warning in caught
67+
if issubclass(warning.category, FutureWarning)
68+
and "uipath.platform.resume_triggers" in str(warning.message)
69+
]
70+
assert len(compatibility_warnings) == 1
71+
assert set(INTERRUPT_MODEL_NAMES) == set(legacy.__all__)
72+
assert set(INTERRUPT_MODEL_NAMES) <= set(common.__all__)
73+
assert set(INTERRUPT_MODEL_NAMES) <= set(dir(legacy))
74+
assert set(INTERRUPT_MODEL_NAMES) <= set(dir(common))
75+
76+
77+
def test_importing_common_does_not_load_resume_triggers_or_warn():
78+
_run_python(
79+
"""
80+
import sys
81+
import warnings
82+
83+
with warnings.catch_warnings(record=True) as caught:
84+
warnings.simplefilter("always")
85+
import uipath.platform.common
86+
87+
assert "uipath.platform.resume_triggers" not in sys.modules
88+
assert "uipath.platform.resume_triggers.interrupt_models" not in sys.modules
89+
assert not [item for item in caught if issubclass(item.category, FutureWarning)]
90+
"""
91+
)
92+
93+
94+
@pytest.mark.parametrize(
95+
"imports",
96+
[
97+
"""
98+
from uipath.platform.resume_triggers import WaitUntil as canonical
99+
from uipath.platform.common import WaitUntil as legacy_root
100+
from uipath.platform.common.interrupt_models import WaitUntil as legacy_direct
101+
""",
102+
"""
103+
from uipath.platform.common.interrupt_models import WaitUntil as legacy_direct
104+
from uipath.platform.common import WaitUntil as legacy_root
105+
from uipath.platform.resume_triggers import WaitUntil as canonical
106+
""",
107+
],
108+
)
109+
def test_import_orders_are_cycle_safe(imports: str):
110+
indented_imports = textwrap.indent(imports.strip(), " ")
111+
_run_python(
112+
f"""
113+
import warnings
114+
115+
with warnings.catch_warnings(record=True) as caught:
116+
warnings.simplefilter("always")
117+
{indented_imports}
118+
119+
assert canonical is legacy_root is legacy_direct
120+
compatibility_warnings = [
121+
item
122+
for item in caught
123+
if issubclass(item.category, FutureWarning)
124+
and "uipath.platform.resume_triggers" in str(item.message)
125+
]
126+
assert len(compatibility_warnings) == 1
127+
"""
128+
)
129+
130+
131+
def test_only_first_legacy_model_resolution_warns():
132+
_run_python(
133+
"""
134+
import importlib
135+
import warnings
136+
137+
common = importlib.import_module("uipath.platform.common")
138+
legacy = importlib.import_module("uipath.platform.common.interrupt_models")
139+
140+
with warnings.catch_warnings(record=True) as caught:
141+
warnings.simplefilter("always")
142+
legacy.WaitUntil
143+
legacy.WaitTask
144+
common.WaitJob
145+
146+
compatibility_warnings = [
147+
item
148+
for item in caught
149+
if issubclass(item.category, FutureWarning)
150+
and "uipath.platform.resume_triggers" in str(item.message)
151+
]
152+
assert len(compatibility_warnings) == 1
153+
"""
154+
)

0 commit comments

Comments
 (0)