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

1""" 

2This module provides a function to handle imports for moved or removed objects in Prefect 3.0 upgrade. 

3 

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. 

6 

7Usage: 

8 

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 

15 

16 # at bottom 

17 __getattr__ = getattr_migration(__name__) 

18 ``` 

19 

20 Example at src/prefect/engine.py 

21 

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 

28 

29 # at bottom 

30 __getattr__ = getattr_migration(__name__) 

31 

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 

37 

38 # at bottom 

39 __getattr__ = getattr_migration(__name__) 

40 ``` 

41 

42 Example at src/prefect/infrastructure/base.py 

43""" 

44 

45import sys 1a

46from typing import Any, Callable 1a

47 

48from pydantic_core import PydanticCustomError 1a

49 

50from prefect.exceptions import PrefectImportError 1a

51 

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} 

62 

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

64 

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} 

82 

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 

87 

88 

89def import_string_class_method(new_location: str) -> Callable[..., Any]: 1a

90 """ 

91 Handle moved class methods. 

92 

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. 

97 

98 Args: 

99 new_location (str): The new location of the method. 

100 

101 Returns: 

102 method: The resolved method from the class. 

103 

104 Raises: 

105 PrefectImportError: If the method is not found in the class. 

106 """ 

107 from pydantic._internal._validators import import_string 

108 

109 class_name, method_name = new_location.rsplit(".", 1) 

110 

111 cls = import_string(class_name) 

112 method = getattr(cls, method_name, None) 

113 

114 if method is not None and callable(method): 

115 return method 

116 

117 raise PrefectImportError(f"Unable to import {new_location!r}") 

118 

119 

120def getattr_migration(module_name: str) -> Callable[[str], Any]: 1a

121 """ 

122 Handle imports for moved or removed objects in Prefect 3.0 upgrade 

123 

124 Args: 

125 module_name (str): The name of the module to handle imports for. 

126 """ 

127 

128 def wrapper(name: str) -> object: 1a

129 """ 

130 Raise a PrefectImportError if the object is not found, moved, or removed. 

131 """ 

132 

133 if name == "__path__": 1abcde

134 raise AttributeError(f"{module_name!r} object has no attribute {name!r}") 1abcde

135 import warnings 1a

136 

137 from pydantic._internal._validators import import_string 1a

138 

139 import_path = f"{module_name}:{name}" 1a

140 

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) 

153 

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 ) 

159 

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] 

163 

164 raise AttributeError(f"module {module_name!r} has no attribute {name!r}") 1a

165 

166 return wrapper 1a