Coverage for /usr/local/lib/python3.12/site-packages/prefect/__init__.py: 50%

60 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-12-05 13:38 +0000

1# isort: skip_file 

2 

3# Setup version and path constants 

4 

5import sys 1a

6from . import _build_info 1a

7import importlib 1a

8import pathlib 1a

9from typing import TYPE_CHECKING, Any, Optional, TypedDict, cast 1a

10 

11if TYPE_CHECKING: 11 ↛ 12line 11 didn't jump to line 12 because the condition on line 11 was never true1a

12 from importlib.machinery import ModuleSpec 

13 from .main import ( 

14 allow_failure, 

15 flow, 

16 Flow, 

17 get_client, 

18 get_run_logger, 

19 State, 

20 tags, 

21 task, 

22 Task, 

23 Transaction, 

24 unmapped, 

25 serve, 

26 aserve, 

27 pause_flow_run, 

28 resume_flow_run, 

29 suspend_flow_run, 

30 ) 

31 from prefect.deployments.runner import deploy 

32 

33 __spec__: ModuleSpec 

34 

35 class VersionInfo(TypedDict("_FullRevisionId", {"full-revisionid": str})): 

36 version: str 

37 dirty: Optional[bool] 

38 error: Optional[str] 

39 date: Optional[str] 

40 

41 

42__version__ = _build_info.__version__ 1a

43__version_info__: "VersionInfo" = cast( 1a

44 "VersionInfo", 

45 { 

46 "version": __version__, 

47 "date": _build_info.__build_date__, 

48 "full-revisionid": _build_info.__git_commit__, 

49 "error": None, 

50 "dirty": _build_info.__dirty__, 

51 }, 

52) 

53 

54# The absolute path to this module 

55__module_path__: pathlib.Path = pathlib.Path(__file__).parent 1a

56# The absolute path to the root of the repository, only valid for use during development 

57__development_base_path__: pathlib.Path = __module_path__.parents[1] 1a

58 

59# The absolute path to the built UI within the Python module, used by 

60# `prefect server start` to serve a dynamic build of the UI 

61__ui_static_subpath__: pathlib.Path = __module_path__ / "server" / "ui_build" 1a

62 

63# The absolute path to the built UI within the Python module 

64__ui_static_path__: pathlib.Path = __module_path__ / "server" / "ui" 1a

65 

66del _build_info, pathlib 1a

67 

68 

69def _initialize_plugins() -> None: 1a

70 """ 

71 Initialize the experimental plugin system if enabled. 

72 

73 This runs automatically when Prefect is imported and plugins are enabled 

74 via experiments.plugins.enabled setting. Errors are logged but don't prevent 

75 Prefect from loading. 

76 """ 

77 try: 1a

78 # Import here to avoid circular imports and defer cost until needed 

79 from prefect.settings import get_current_settings 1a

80 

81 if not get_current_settings().experiments.plugins.enabled: 81 ↛ 84line 81 didn't jump to line 84 because the condition on line 81 was always true1a

82 return 1a

83 

84 import anyio 

85 

86 from prefect._experimental.plugins import run_startup_hooks 

87 from prefect._experimental.plugins.spec import HookContext 

88 from prefect.logging import get_logger 

89 from prefect.settings import get_current_settings 

90 

91 ctx = HookContext( 

92 prefect_version=__version__, 

93 api_url=get_current_settings().api.url, 

94 logger_factory=get_logger, 

95 ) 

96 

97 # Run plugin hooks synchronously during import 

98 anyio.run(run_startup_hooks, ctx) 

99 except SystemExit: 

100 # Re-raise SystemExit from strict mode 

101 raise 

102 except Exception as e: 

103 # Log but don't crash on plugin errors 

104 try: 

105 from prefect.logging import get_logger 

106 

107 logger = get_logger("prefect.plugins") 

108 logger.exception("Failed to initialize plugins: %s", e) 

109 except Exception: 

110 # If even logging fails, print to stderr and continue 

111 import sys 

112 

113 print(f"Failed to initialize plugins: {e}", file=sys.stderr) 

114 

115 

116# Initialize plugins on import if enabled 

117_initialize_plugins() 1a

118 

119_public_api: dict[str, tuple[Optional[str], str]] = { 1a

120 "allow_failure": (__spec__.parent, ".main"), 

121 "aserve": (__spec__.parent, ".main"), 

122 "deploy": (__spec__.parent, ".deployments.runner"), 

123 "flow": (__spec__.parent, ".main"), 

124 "Flow": (__spec__.parent, ".main"), 

125 "get_client": (__spec__.parent, ".main"), 

126 "get_run_logger": (__spec__.parent, ".main"), 

127 "pause_flow_run": (__spec__.parent, ".main"), 

128 "resume_flow_run": (__spec__.parent, ".main"), 

129 "serve": (__spec__.parent, ".main"), 

130 "State": (__spec__.parent, ".main"), 

131 "suspend_flow_run": (__spec__.parent, ".main"), 

132 "tags": (__spec__.parent, ".main"), 

133 "task": (__spec__.parent, ".main"), 

134 "Task": (__spec__.parent, ".main"), 

135 "Transaction": (__spec__.parent, ".main"), 

136 "unmapped": (__spec__.parent, ".main"), 

137} 

138 

139# Declare API for type-checkers 

140__all__ = [ 1a

141 "__version__", 

142 "allow_failure", 

143 "aserve", 

144 "deploy", 

145 "flow", 

146 "Flow", 

147 "get_client", 

148 "get_run_logger", 

149 "pause_flow_run", 

150 "resume_flow_run", 

151 "serve", 

152 "State", 

153 "suspend_flow_run", 

154 "tags", 

155 "task", 

156 "Task", 

157 "Transaction", 

158 "unmapped", 

159] 

160 

161 

162def __getattr__(attr_name: str) -> Any: 1a

163 try: 1ab

164 if (dynamic_attr := _public_api.get(attr_name)) is None: 1ab

165 return importlib.import_module(f".{attr_name}", package=__name__) 1a

166 

167 package, mname = dynamic_attr 1ab

168 if mname == "__module__": 168 ↛ 169line 168 didn't jump to line 169 because the condition on line 168 was never true1ab

169 return importlib.import_module(f".{attr_name}", package=package) 

170 else: 

171 module = importlib.import_module(mname, package=package) 1ab

172 return getattr(module, attr_name) 1ab

173 except ModuleNotFoundError as ex: 

174 mname, _, attr = (ex.name or "").rpartition(".") 

175 ctx = {"name": mname, "obj": attr} if sys.version_info >= (3, 10) else {} 

176 raise AttributeError(f"module {mname} has no attribute {attr}", **ctx) from ex