Coverage for /usr/local/lib/python3.12/site-packages/prefect/_internal/compatibility/migration.py: 55%
37 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-12-05 11:21 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-12-05 11:21 +0000
1"""
2This module provides a function to handle imports for moved or removed objects in Prefect 3.0 upgrade.
4The `getattr_migration` function is used to handle imports for moved or removed objects in Prefect 3.0 upgrade.
5It is used in the `__getattr__` attribute of modules that have moved or removed objects.
7Usage:
9Moved objects:
101. Add the old and new path to the `MOVED_IN_V3` dictionary, e.g. `MOVED_IN_V3 = {"old_path": "new_path"}`
112. In the module where the object was moved from, add the following lines:
12 ```python
13 # at top
14 from prefect._internal.compatibility.migration import getattr_migration
16 # at bottom
17 __getattr__ = getattr_migration(__name__)
18 ```
20 Example at src/prefect/engine.py
22Removed objects:
231. Add the old path and error message to the `REMOVED_IN_V3` dictionary, e.g. `REMOVED_IN_V3 = {"old_path": "error_message"}`
242. In the module where the object was removed, add the following lines:
25 ```python
26 # at top
27 from prefect._internal.compatibility.migration import getattr_migration
29 # at bottom
30 __getattr__ = getattr_migration(__name__)
32 ```
33 If the entire old module was removed, add a stub for the module with the following lines:
34 ```python
35 # at top
36 from prefect._internal.compatibility.migration import getattr_migration
38 # at bottom
39 __getattr__ = getattr_migration(__name__)
40 ```
42 Example at src/prefect/infrastructure/base.py
43"""
45import sys 1a
46from typing import Any, Callable 1a
48from pydantic_core import PydanticCustomError 1a
50from prefect.exceptions import PrefectImportError 1a
52MOVED_IN_V3 = { 1a
53 "prefect.deployments.deployments:load_flow_from_flow_run": "prefect.flows:load_flow_from_flow_run",
54 "prefect.deployments:load_flow_from_flow_run": "prefect.flows:load_flow_from_flow_run",
55 "prefect.variables:get": "prefect.variables:Variable.get",
56 "prefect.engine:pause_flow_run": "prefect.flow_runs:pause_flow_run",
57 "prefect.engine:resume_flow_run": "prefect.flow_runs:resume_flow_run",
58 "prefect.engine:suspend_flow_run": "prefect.flow_runs:suspend_flow_run",
59 "prefect.engine:_in_process_pause": "prefect.flow_runs:_in_process_pause",
60 "prefect.client:get_client": "prefect.client.orchestration:get_client",
61}
63upgrade_guide_msg = "Refer to the upgrade guide for more information: https://docs.prefect.io/v3/how-to-guides/migrate/upgrade-agents-to-workers." 1a
65REMOVED_IN_V3 = { 1a
66 "prefect.client.schemas.objects:MinimalDeploymentSchedule": "Use `prefect.client.schemas.actions.DeploymentScheduleCreate` instead.",
67 "prefect.context:PrefectObjectRegistry": upgrade_guide_msg,
68 "prefect.deployments.deployments:Deployment": "Use `flow.serve()`, `flow.deploy()`, or `prefect deploy` instead.",
69 "prefect.deployments:Deployment": "Use `flow.serve()`, `flow.deploy()`, or `prefect deploy` instead.",
70 "prefect.filesystems:GCS": "Use `prefect_gcp.GcsBucket` instead.",
71 "prefect.filesystems:Azure": "Use `prefect_azure.AzureBlobStorageContainer` instead.",
72 "prefect.filesystems:S3": "Use `prefect_aws.S3Bucket` instead.",
73 "prefect.filesystems:GitHub": "Use `prefect_github.GitHubRepository` instead.",
74 "prefect.engine:_out_of_process_pause": "Use `prefect.flow_runs.pause_flow_run` instead.",
75 "prefect.agent:PrefectAgent": "Use workers instead. " + upgrade_guide_msg,
76 "prefect.infrastructure:KubernetesJob": "Use workers instead. " + upgrade_guide_msg,
77 "prefect.infrastructure.base:Infrastructure": "Use the `BaseWorker` class to create custom infrastructure integrations instead. "
78 + upgrade_guide_msg,
79 "prefect.workers.block:BlockWorkerJobConfiguration": upgrade_guide_msg,
80 "prefect.workers.cloud:BlockWorker": upgrade_guide_msg,
81}
83# IMPORTANT FOR USAGE: When adding new modules to MOVED_IN_V3 or REMOVED_IN_V3, include the following lines at the bottom of that module:
84# from prefect._internal.compatibility.migration import getattr_migration
85# __getattr__ = getattr_migration(__name__)
86# See src/prefect/filesystems.py for an example
89def import_string_class_method(new_location: str) -> Callable[..., Any]: 1a
90 """
91 Handle moved class methods.
93 `import_string` does not account for moved class methods. This function handles cases where a method has been
94 moved to a class. For example, if `new_location` is 'prefect.variables:Variable.get', `import_string(new_location)`
95 will raise an error because it does not handle class methods. This function will import the class and get the
96 method from the class.
98 Args:
99 new_location (str): The new location of the method.
101 Returns:
102 method: The resolved method from the class.
104 Raises:
105 PrefectImportError: If the method is not found in the class.
106 """
107 from pydantic._internal._validators import import_string
109 class_name, method_name = new_location.rsplit(".", 1)
111 cls = import_string(class_name)
112 method = getattr(cls, method_name, None)
114 if method is not None and callable(method):
115 return method
117 raise PrefectImportError(f"Unable to import {new_location!r}")
120def getattr_migration(module_name: str) -> Callable[[str], Any]: 1a
121 """
122 Handle imports for moved or removed objects in Prefect 3.0 upgrade
124 Args:
125 module_name (str): The name of the module to handle imports for.
126 """
128 def wrapper(name: str) -> object: 1a
129 """
130 Raise a PrefectImportError if the object is not found, moved, or removed.
131 """
133 if name == "__path__": 1abcde
134 raise AttributeError(f"{module_name!r} object has no attribute {name!r}") 1abcde
135 import warnings 1a
137 from pydantic._internal._validators import import_string 1a
139 import_path = f"{module_name}:{name}" 1a
141 # Check if the attribute name corresponds to a moved or removed class or module
142 if import_path in MOVED_IN_V3.keys(): 142 ↛ 143line 142 didn't jump to line 143 because the condition on line 142 was never true1a
143 new_location = MOVED_IN_V3[import_path]
144 warnings.warn(
145 f"{import_path!r} has been moved to {new_location!r}. Importing from {new_location!r} instead. This warning will raise an error in a future release.",
146 DeprecationWarning,
147 stacklevel=2,
148 )
149 try:
150 return import_string(new_location)
151 except PydanticCustomError:
152 return import_string_class_method(new_location)
154 if import_path in REMOVED_IN_V3.keys(): 154 ↛ 155line 154 didn't jump to line 155 because the condition on line 154 was never true1a
155 error_message = REMOVED_IN_V3[import_path]
156 raise PrefectImportError(
157 f"`{import_path}` has been removed. {error_message}"
158 )
160 globals: dict[str, Any] = sys.modules[module_name].__dict__ 1a
161 if name in globals: 161 ↛ 162line 161 didn't jump to line 162 because the condition on line 161 was never true1a
162 return globals[name]
164 raise AttributeError(f"module {module_name!r} has no attribute {name!r}") 1a
166 return wrapper 1a