|
42 | 42 | except ImportError: |
43 | 43 | HAVE_PYDANTIC = False |
44 | 44 |
|
| 45 | +import temporalio.exceptions |
45 | 46 | import temporalio.workflow |
46 | 47 |
|
47 | 48 | logger = logging.getLogger(__name__) |
@@ -82,6 +83,21 @@ def default_message(qualified_name: str) -> str: |
82 | 83 | ) |
83 | 84 |
|
84 | 85 |
|
| 86 | +class UnintentionalPassthroughError(temporalio.exceptions.TemporalError): |
| 87 | + """Error that occurs when a workflow unintentionally passes an import to the sandbox when |
| 88 | + the import notification policy includes :py:attr:`temporalio.workflow.SandboxImportNotificationPolicy.RAISE_ON_NON_PASSTHROUGH`. |
| 89 | +
|
| 90 | + Attributes: |
| 91 | + qualified_name: Fully qualified name of what was passed through to the sandbox. |
| 92 | + """ |
| 93 | + |
| 94 | + def __init__(self, qualified_name: str) -> None: |
| 95 | + """Create an unintentional passthrough error.""" |
| 96 | + super().__init__( |
| 97 | + f"Module {qualified_name} was not intentionally passed through to the sandbox." |
| 98 | + ) |
| 99 | + |
| 100 | + |
85 | 101 | @dataclass(frozen=True) |
86 | 102 | class SandboxRestrictions: |
87 | 103 | """Set of restrictions that can be applied to a sandbox.""" |
@@ -110,6 +126,13 @@ class methods (including __init__, etc). The check compares the against the |
110 | 126 | fully qualified path to the item. |
111 | 127 | """ |
112 | 128 |
|
| 129 | + import_notification_policy: temporalio.workflow.SandboxImportNotificationPolicy = ( |
| 130 | + temporalio.workflow.SandboxImportNotificationPolicy.WARN_ON_DYNAMIC_IMPORT |
| 131 | + ) |
| 132 | + """ |
| 133 | + The import notification policy to use when an import is triggered during workflow loading or execution. See :py:class:`temporalio.workflow.SandboxImportNotificationPolicy` for options. |
| 134 | + """ |
| 135 | + |
113 | 136 | passthrough_all_modules: bool = False |
114 | 137 | """ |
115 | 138 | Pass through all modules, do not sandbox any modules. This is the equivalent |
@@ -170,6 +193,12 @@ def with_passthrough_all_modules(self) -> SandboxRestrictions: |
170 | 193 | """ |
171 | 194 | return dataclasses.replace(self, passthrough_all_modules=True) |
172 | 195 |
|
| 196 | + def with_import_notification_policy( |
| 197 | + self, policy: temporalio.workflow.SandboxImportNotificationPolicy |
| 198 | + ) -> SandboxRestrictions: |
| 199 | + """Create a new restriction set with the given import notification policy as the :py:attr:`import_policy`.""" |
| 200 | + return dataclasses.replace(self, import_notification_policy=policy) |
| 201 | + |
173 | 202 |
|
174 | 203 | # We intentionally use specific fields instead of generic "matcher" callbacks |
175 | 204 | # for optimization reasons. |
@@ -305,10 +334,12 @@ def access_matcher( |
305 | 334 | if not child_matcher: |
306 | 335 | return None |
307 | 336 | matcher = child_matcher |
| 337 | + |
308 | 338 | if not context.is_runtime and matcher.only_runtime: |
309 | 339 | return None |
310 | 340 | if not matcher.match_self: |
311 | 341 | return None |
| 342 | + |
312 | 343 | return matcher |
313 | 344 |
|
314 | 345 | def match_access( |
@@ -819,6 +850,7 @@ def unwrap_if_proxied(v: Any) -> Any: |
819 | 850 | def __init__(self) -> None: |
820 | 851 | """Create a restriction context.""" |
821 | 852 | self.is_runtime = False |
| 853 | + self.in_activation = False |
822 | 854 |
|
823 | 855 |
|
824 | 856 | @dataclass |
|
0 commit comments