snowflake-cli-labs 2.8.0rc1__py3-none-any.whl → 3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/{app → _app}/__main__.py +1 -1
  3. snowflake/cli/{app → _app}/cli_app.py +22 -13
  4. snowflake/cli/{app → _app}/commands_registration/builtin_plugins.py +15 -19
  5. snowflake/cli/{app → _app}/commands_registration/command_plugins_loader.py +9 -9
  6. snowflake/cli/{app → _app}/commands_registration/commands_registration_with_callbacks.py +4 -4
  7. snowflake/cli/{app → _app}/commands_registration/exception_logging.py +2 -2
  8. snowflake/cli/{app → _app}/commands_registration/typer_registration.py +2 -2
  9. snowflake/cli/{app → _app}/dev/docs/commands_docs_generator.py +30 -12
  10. snowflake/cli/{app → _app}/dev/docs/generator.py +3 -3
  11. snowflake/cli/{app → _app}/dev/docs/project_definition_docs_generator.py +4 -4
  12. snowflake/cli/{app → _app}/dev/docs/templates/usage.rst.jinja2 +14 -4
  13. snowflake/cli/{app → _app}/main_typer.py +2 -2
  14. snowflake/cli/{app → _app}/printing.py +2 -2
  15. snowflake/cli/_app/secret.py +9 -0
  16. snowflake/cli/{app → _app}/snow_connector.py +127 -61
  17. snowflake/cli/{app → _app}/telemetry.py +38 -7
  18. snowflake/cli/_app/version_check.py +74 -0
  19. snowflake/cli/{plugins → _plugins}/connection/commands.py +34 -11
  20. snowflake/cli/_plugins/connection/plugin_spec.py +30 -0
  21. snowflake/cli/{plugins → _plugins}/connection/util.py +16 -0
  22. snowflake/cli/{plugins → _plugins}/cortex/commands.py +54 -49
  23. snowflake/cli/{plugins → _plugins}/cortex/constants.py +1 -1
  24. snowflake/cli/{plugins → _plugins}/cortex/manager.py +5 -5
  25. snowflake/cli/{plugins → _plugins}/cortex/plugin_spec.py +1 -1
  26. snowflake/cli/{plugins → _plugins}/git/commands.py +79 -26
  27. snowflake/cli/{plugins → _plugins}/git/manager.py +72 -17
  28. snowflake/cli/{plugins → _plugins}/git/plugin_spec.py +1 -1
  29. snowflake/cli/_plugins/helpers/commands.py +90 -0
  30. snowflake/cli/{plugins/notebook → _plugins/helpers}/plugin_spec.py +1 -1
  31. snowflake/cli/{plugins → _plugins}/init/commands.py +10 -6
  32. snowflake/cli/{plugins → _plugins}/init/plugin_spec.py +1 -1
  33. snowflake/cli/{plugins → _plugins}/nativeapp/artifacts.py +24 -9
  34. snowflake/cli/_plugins/nativeapp/bundle_context.py +31 -0
  35. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/artifact_processor.py +4 -4
  36. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/compiler.py +37 -18
  37. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +249 -0
  38. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/setup/setup_driver.py.source +5 -2
  39. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/extension_function_utils.py +5 -5
  40. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/models.py +1 -1
  41. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/python_processor.py +29 -34
  42. snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +114 -0
  43. snowflake/cli/{plugins → _plugins}/nativeapp/commands.py +252 -132
  44. snowflake/cli/{plugins → _plugins}/nativeapp/common_flags.py +1 -1
  45. snowflake/cli/_plugins/nativeapp/entities/application.py +878 -0
  46. snowflake/cli/_plugins/nativeapp/entities/application_package.py +1392 -0
  47. snowflake/cli/{plugins → _plugins}/nativeapp/exceptions.py +3 -12
  48. snowflake/cli/_plugins/nativeapp/manager.py +415 -0
  49. snowflake/cli/{plugins/connection → _plugins/nativeapp}/plugin_spec.py +1 -1
  50. snowflake/cli/{plugins → _plugins}/nativeapp/policy.py +3 -0
  51. snowflake/cli/{plugins → _plugins}/nativeapp/project_model.py +36 -20
  52. snowflake/cli/_plugins/nativeapp/run_processor.py +184 -0
  53. snowflake/cli/_plugins/nativeapp/same_account_install_method.py +70 -0
  54. snowflake/cli/_plugins/nativeapp/teardown_processor.py +70 -0
  55. snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +262 -0
  56. snowflake/cli/{plugins → _plugins}/nativeapp/version/commands.py +20 -49
  57. snowflake/cli/_plugins/nativeapp/version/version_processor.py +98 -0
  58. snowflake/cli/{plugins → _plugins}/notebook/commands.py +8 -6
  59. snowflake/cli/{plugins → _plugins}/notebook/manager.py +14 -14
  60. snowflake/cli/{plugins/nativeapp → _plugins/notebook}/plugin_spec.py +1 -1
  61. snowflake/cli/{plugins → _plugins}/notebook/types.py +0 -1
  62. snowflake/cli/{plugins → _plugins}/object/command_aliases.py +6 -5
  63. snowflake/cli/{plugins → _plugins}/object/commands.py +16 -10
  64. snowflake/cli/{plugins → _plugins}/object/manager.py +43 -21
  65. snowflake/cli/{plugins → _plugins}/object/plugin_spec.py +1 -1
  66. snowflake/cli/_plugins/snowpark/commands.py +450 -0
  67. snowflake/cli/_plugins/snowpark/common.py +268 -0
  68. snowflake/cli/{plugins → _plugins}/snowpark/models.py +2 -8
  69. snowflake/cli/{plugins → _plugins}/snowpark/package/anaconda_packages.py +2 -36
  70. snowflake/cli/{plugins → _plugins}/snowpark/package/commands.py +13 -74
  71. snowflake/cli/{plugins → _plugins}/snowpark/package/manager.py +4 -3
  72. snowflake/cli/{plugins → _plugins}/snowpark/package_utils.py +5 -5
  73. snowflake/cli/_plugins/snowpark/plugin_spec.py +30 -0
  74. snowflake/cli/_plugins/snowpark/snowpark_entity.py +29 -0
  75. snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +173 -0
  76. snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +109 -0
  77. snowflake/cli/{plugins → _plugins}/snowpark/snowpark_shared.py +0 -36
  78. snowflake/cli/{plugins → _plugins}/snowpark/zipper.py +16 -8
  79. snowflake/cli/{plugins → _plugins}/spcs/__init__.py +5 -7
  80. snowflake/cli/{plugins → _plugins}/spcs/compute_pool/commands.py +29 -28
  81. snowflake/cli/{plugins → _plugins}/spcs/compute_pool/manager.py +3 -3
  82. snowflake/cli/{plugins → _plugins}/spcs/image_registry/commands.py +3 -3
  83. snowflake/cli/{plugins → _plugins}/spcs/image_repository/commands.py +25 -19
  84. snowflake/cli/{plugins → _plugins}/spcs/image_repository/manager.py +1 -1
  85. snowflake/cli/{plugins → _plugins}/spcs/plugin_spec.py +1 -1
  86. snowflake/cli/{plugins → _plugins}/spcs/services/commands.py +66 -32
  87. snowflake/cli/{plugins → _plugins}/spcs/services/manager.py +43 -5
  88. snowflake/cli/{plugins → _plugins}/sql/commands.py +20 -17
  89. snowflake/cli/{plugins → _plugins}/sql/manager.py +1 -1
  90. snowflake/cli/{plugins → _plugins}/sql/plugin_spec.py +1 -1
  91. snowflake/cli/{plugins → _plugins}/stage/commands.py +20 -17
  92. snowflake/cli/{plugins → _plugins}/stage/diff.py +1 -47
  93. snowflake/cli/{plugins → _plugins}/stage/manager.py +62 -24
  94. snowflake/cli/{plugins → _plugins}/stage/plugin_spec.py +1 -1
  95. snowflake/cli/_plugins/stage/utils.py +54 -0
  96. snowflake/cli/{plugins → _plugins}/streamlit/commands.py +71 -62
  97. snowflake/cli/{plugins → _plugins}/streamlit/manager.py +68 -70
  98. snowflake/cli/_plugins/streamlit/plugin_spec.py +30 -0
  99. snowflake/cli/_plugins/streamlit/streamlit_entity.py +12 -0
  100. snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +66 -0
  101. snowflake/cli/_plugins/workspace/action_context.py +18 -0
  102. snowflake/cli/_plugins/workspace/commands.py +306 -0
  103. snowflake/cli/_plugins/workspace/manager.py +74 -0
  104. snowflake/cli/_plugins/workspace/plugin_spec.py +30 -0
  105. snowflake/cli/api/cli_global_context.py +152 -295
  106. snowflake/cli/api/commands/common.py +25 -0
  107. snowflake/cli/api/commands/decorators.py +19 -4
  108. snowflake/cli/api/commands/experimental_behaviour.py +2 -3
  109. snowflake/cli/api/commands/flags.py +143 -222
  110. snowflake/cli/api/commands/overrideable_parameter.py +143 -0
  111. snowflake/cli/api/commands/snow_typer.py +21 -11
  112. snowflake/cli/api/commands/utils.py +18 -0
  113. snowflake/cli/api/config.py +44 -12
  114. snowflake/cli/api/connections.py +216 -0
  115. snowflake/cli/api/console/abc.py +8 -3
  116. snowflake/cli/api/constants.py +11 -0
  117. snowflake/cli/api/entities/common.py +56 -0
  118. snowflake/cli/api/entities/utils.py +370 -0
  119. snowflake/cli/api/errno.py +1 -0
  120. snowflake/cli/api/exceptions.py +31 -5
  121. snowflake/cli/api/feature_flags.py +0 -1
  122. snowflake/cli/api/identifiers.py +45 -9
  123. snowflake/cli/api/metrics.py +92 -0
  124. snowflake/cli/api/project/definition.py +48 -6
  125. snowflake/cli/api/project/definition_conversion.py +400 -0
  126. snowflake/cli/api/project/definition_manager.py +16 -5
  127. snowflake/cli/api/project/project_verification.py +3 -3
  128. snowflake/cli/api/project/schemas/entities/common.py +91 -16
  129. snowflake/cli/api/project/schemas/entities/entities.py +37 -6
  130. snowflake/cli/api/project/schemas/project_definition.py +180 -49
  131. snowflake/cli/api/project/schemas/updatable_model.py +11 -3
  132. snowflake/cli/api/project/schemas/v1/__init__.py +0 -0
  133. snowflake/cli/api/project/schemas/{identifier_model.py → v1/identifier_model.py} +3 -1
  134. snowflake/cli/api/project/schemas/v1/native_app/__init__.py +0 -0
  135. snowflake/cli/api/project/schemas/{native_app → v1/native_app}/application.py +8 -9
  136. snowflake/cli/api/project/schemas/{native_app → v1/native_app}/native_app.py +4 -4
  137. snowflake/cli/api/project/schemas/{native_app → v1/native_app}/package.py +7 -1
  138. snowflake/cli/api/project/schemas/v1/snowpark/__init__.py +0 -0
  139. snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/callable.py +2 -2
  140. snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/snowpark.py +2 -2
  141. snowflake/cli/api/project/schemas/v1/streamlit/__init__.py +0 -0
  142. snowflake/cli/api/project/schemas/{streamlit → v1/streamlit}/streamlit.py +2 -1
  143. snowflake/cli/api/project/util.py +23 -6
  144. snowflake/cli/api/rendering/jinja.py +14 -8
  145. snowflake/cli/api/rendering/project_definition_templates.py +5 -1
  146. snowflake/cli/api/rendering/sql_templates.py +56 -11
  147. snowflake/cli/api/rest_api.py +11 -5
  148. snowflake/cli/api/secure_path.py +16 -18
  149. snowflake/cli/api/secure_utils.py +90 -1
  150. snowflake/cli/api/sql_execution.py +47 -27
  151. snowflake/cli/api/utils/definition_rendering.py +45 -13
  152. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0.dist-info}/METADATA +20 -18
  153. snowflake_cli_labs-3.0.0.dist-info/RECORD +242 -0
  154. snowflake_cli_labs-3.0.0.dist-info/entry_points.txt +2 -0
  155. snowflake/cli/api/commands/project_initialisation.py +0 -65
  156. snowflake/cli/api/commands/typer_pre_execute.py +0 -26
  157. snowflake/cli/api/project/schemas/entities/application_entity.py +0 -44
  158. snowflake/cli/api/project/schemas/entities/application_package_entity.py +0 -66
  159. snowflake/cli/app/build_and_push.sh +0 -8
  160. snowflake/cli/plugins/nativeapp/codegen/setup/native_app_setup_processor.py +0 -172
  161. snowflake/cli/plugins/nativeapp/init.py +0 -345
  162. snowflake/cli/plugins/nativeapp/manager.py +0 -823
  163. snowflake/cli/plugins/nativeapp/run_processor.py +0 -389
  164. snowflake/cli/plugins/nativeapp/teardown_processor.py +0 -301
  165. snowflake/cli/plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +0 -135
  166. snowflake/cli/plugins/nativeapp/version/version_processor.py +0 -362
  167. snowflake/cli/plugins/object_stage_deprecated/__init__.py +0 -15
  168. snowflake/cli/plugins/object_stage_deprecated/commands.py +0 -122
  169. snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +0 -32
  170. snowflake/cli/plugins/snowpark/commands.py +0 -548
  171. snowflake/cli/plugins/snowpark/common.py +0 -307
  172. snowflake/cli/plugins/snowpark/manager.py +0 -109
  173. snowflake/cli/plugins/snowpark/plugin_spec.py +0 -30
  174. snowflake/cli/plugins/snowpark/snowpark_package_paths.py +0 -65
  175. snowflake/cli/plugins/spcs/jobs/commands.py +0 -78
  176. snowflake/cli/plugins/spcs/jobs/manager.py +0 -53
  177. snowflake/cli/plugins/streamlit/__init__.py +0 -13
  178. snowflake/cli/plugins/streamlit/plugin_spec.py +0 -30
  179. snowflake/cli/plugins/workspace/__init__.py +0 -13
  180. snowflake/cli/plugins/workspace/commands.py +0 -35
  181. snowflake/cli/plugins/workspace/plugin_spec.py +0 -30
  182. snowflake/cli/templates/default_snowpark/.gitignore +0 -4
  183. snowflake/cli/templates/default_snowpark/app/common.py +0 -2
  184. snowflake/cli/templates/default_snowpark/app/functions.py +0 -15
  185. snowflake/cli/templates/default_snowpark/app/procedures.py +0 -22
  186. snowflake/cli/templates/default_snowpark/requirements.txt +0 -1
  187. snowflake/cli/templates/default_snowpark/snowflake.yml +0 -23
  188. snowflake/cli/templates/default_streamlit/.gitignore +0 -4
  189. snowflake/cli/templates/default_streamlit/common/hello.py +0 -2
  190. snowflake/cli/templates/default_streamlit/environment.yml +0 -6
  191. snowflake/cli/templates/default_streamlit/pages/my_page.py +0 -3
  192. snowflake/cli/templates/default_streamlit/snowflake.yml +0 -10
  193. snowflake/cli/templates/default_streamlit/streamlit_app.py +0 -4
  194. snowflake_cli_labs-2.8.0rc1.dist-info/RECORD +0 -240
  195. snowflake_cli_labs-2.8.0rc1.dist-info/entry_points.txt +0 -2
  196. /snowflake/cli/{app → _app}/__init__.py +0 -0
  197. /snowflake/cli/{api/project/schemas/native_app → _app/api_impl}/__init__.py +0 -0
  198. /snowflake/cli/{api/project/schemas/snowpark → _app/api_impl/plugin}/__init__.py +0 -0
  199. /snowflake/cli/{app → _app}/api_impl/plugin/plugin_config_provider_impl.py +0 -0
  200. /snowflake/cli/{app → _app}/commands_registration/__init__.py +0 -0
  201. /snowflake/cli/{app → _app}/commands_registration/threadsafe.py +0 -0
  202. /snowflake/cli/{app → _app}/constants.py +0 -0
  203. /snowflake/cli/{api/project/schemas/streamlit → _app/dev}/__init__.py +0 -0
  204. /snowflake/cli/{app → _app}/dev/commands_structure.py +0 -0
  205. /snowflake/cli/{app/api_impl → _app/dev/docs}/__init__.py +0 -0
  206. /snowflake/cli/{app → _app}/dev/docs/project_definition_generate_json_schema.py +0 -0
  207. /snowflake/cli/{app → _app}/dev/docs/template_utils.py +0 -0
  208. /snowflake/cli/{app → _app}/dev/docs/templates/definition_description.rst.jinja2 +0 -0
  209. /snowflake/cli/{app → _app}/dev/docs/templates/overview.rst.jinja2 +0 -0
  210. /snowflake/cli/{app → _app}/dev/pycharm_remote_debug.py +0 -0
  211. /snowflake/cli/{app → _app}/loggers.py +0 -0
  212. /snowflake/cli/{app/api_impl/plugin → _plugins}/__init__.py +0 -0
  213. /snowflake/cli/{app/dev → _plugins/connection}/__init__.py +0 -0
  214. /snowflake/cli/{app/dev/docs → _plugins/cortex}/__init__.py +0 -0
  215. /snowflake/cli/{plugins → _plugins}/cortex/types.py +0 -0
  216. /snowflake/cli/{plugins → _plugins/git}/__init__.py +0 -0
  217. /snowflake/cli/{plugins/connection → _plugins/helpers}/__init__.py +0 -0
  218. /snowflake/cli/{plugins/cortex → _plugins/init}/__init__.py +0 -0
  219. /snowflake/cli/{plugins/git → _plugins/nativeapp}/__init__.py +0 -0
  220. /snowflake/cli/{plugins/init → _plugins/nativeapp/codegen}/__init__.py +0 -0
  221. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/sandbox.py +0 -0
  222. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -0
  223. /snowflake/cli/{plugins → _plugins}/nativeapp/constants.py +0 -0
  224. /snowflake/cli/{templates/default_snowpark/app → _plugins/nativeapp/entities}/__init__.py +0 -0
  225. /snowflake/cli/{plugins → _plugins}/nativeapp/feature_flags.py +0 -0
  226. /snowflake/cli/{plugins → _plugins}/nativeapp/utils.py +0 -0
  227. /snowflake/cli/{plugins/nativeapp → _plugins/nativeapp/version}/__init__.py +0 -0
  228. /snowflake/cli/{plugins/nativeapp/codegen → _plugins/notebook}/__init__.py +0 -0
  229. /snowflake/cli/{plugins → _plugins}/notebook/exceptions.py +0 -0
  230. /snowflake/cli/{plugins/nativeapp/version → _plugins/object}/__init__.py +0 -0
  231. /snowflake/cli/{plugins → _plugins}/object/common.py +0 -0
  232. /snowflake/cli/{plugins/notebook → _plugins/snowpark}/__init__.py +0 -0
  233. /snowflake/cli/{plugins/object → _plugins/snowpark/package}/__init__.py +0 -0
  234. /snowflake/cli/{plugins → _plugins}/snowpark/package/utils.py +0 -0
  235. /snowflake/cli/{plugins → _plugins}/spcs/common.py +0 -0
  236. /snowflake/cli/{plugins/snowpark → _plugins/spcs/compute_pool}/__init__.py +0 -0
  237. /snowflake/cli/{plugins/snowpark/package → _plugins/spcs/image_registry}/__init__.py +0 -0
  238. /snowflake/cli/{plugins → _plugins}/spcs/image_registry/manager.py +0 -0
  239. /snowflake/cli/{plugins/spcs/compute_pool → _plugins/spcs/image_repository}/__init__.py +0 -0
  240. /snowflake/cli/{plugins/spcs/image_registry → _plugins/spcs/services}/__init__.py +0 -0
  241. /snowflake/cli/{plugins/spcs/image_repository → _plugins/sql}/__init__.py +0 -0
  242. /snowflake/cli/{plugins → _plugins}/sql/snowsql_templating.py +0 -0
  243. /snowflake/cli/{plugins/spcs/jobs → _plugins/stage}/__init__.py +0 -0
  244. /snowflake/cli/{plugins → _plugins}/stage/md5.py +0 -0
  245. /snowflake/cli/{plugins/spcs/services → _plugins/streamlit}/__init__.py +0 -0
  246. /snowflake/cli/{plugins/sql → _plugins/workspace}/__init__.py +0 -0
  247. /snowflake/cli/{plugins/stage → api/project/schemas/entities}/__init__.py +0 -0
  248. /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/path_mapping.py +0 -0
  249. /snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/argument.py +0 -0
  250. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0.dist-info}/WHEEL +0 -0
  251. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,31 @@
1
+ # Copyright (c) 2024 Snowflake Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from dataclasses import dataclass
16
+ from pathlib import Path
17
+ from typing import (
18
+ List,
19
+ )
20
+
21
+ from snowflake.cli.api.project.schemas.v1.native_app.path_mapping import PathMapping
22
+
23
+
24
+ @dataclass
25
+ class BundleContext:
26
+ package_name: str
27
+ artifacts: List[PathMapping]
28
+ project_root: Path
29
+ bundle_root: Path
30
+ deploy_root: Path
31
+ generated_root: Path
@@ -19,11 +19,11 @@ from pathlib import Path
19
19
  from typing import Optional
20
20
 
21
21
  from click import ClickException
22
- from snowflake.cli.api.project.schemas.native_app.path_mapping import (
22
+ from snowflake.cli._plugins.nativeapp.bundle_context import BundleContext
23
+ from snowflake.cli.api.project.schemas.v1.native_app.path_mapping import (
23
24
  PathMapping,
24
25
  ProcessorMapping,
25
26
  )
26
- from snowflake.cli.plugins.nativeapp.project_model import NativeAppProjectModel
27
27
 
28
28
 
29
29
  class UnsupportedArtifactProcessorError(ClickException):
@@ -74,9 +74,9 @@ class ProjectFileContextManager:
74
74
  class ArtifactProcessor(ABC):
75
75
  def __init__(
76
76
  self,
77
- na_project: NativeAppProjectModel,
77
+ bundle_ctx: BundleContext,
78
78
  ) -> None:
79
- self._na_project = na_project
79
+ self._bundle_ctx = bundle_ctx
80
80
 
81
81
  @abstractmethod
82
82
  def process(
@@ -14,31 +14,41 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
+ import copy
18
+ import re
17
19
  from typing import Dict, Optional
18
20
 
19
- from snowflake.cli.api.console import cli_console as cc
20
- from snowflake.cli.api.project.schemas.native_app.path_mapping import (
21
- ProcessorMapping,
22
- )
23
- from snowflake.cli.plugins.nativeapp.codegen.artifact_processor import (
21
+ from click import ClickException
22
+ from snowflake.cli._plugins.nativeapp.bundle_context import BundleContext
23
+ from snowflake.cli._plugins.nativeapp.codegen.artifact_processor import (
24
24
  ArtifactProcessor,
25
25
  UnsupportedArtifactProcessorError,
26
26
  )
27
- from snowflake.cli.plugins.nativeapp.codegen.setup.native_app_setup_processor import (
27
+ from snowflake.cli._plugins.nativeapp.codegen.setup.native_app_setup_processor import (
28
28
  NativeAppSetupProcessor,
29
29
  )
30
- from snowflake.cli.plugins.nativeapp.codegen.snowpark.python_processor import (
30
+ from snowflake.cli._plugins.nativeapp.codegen.snowpark.python_processor import (
31
31
  SnowparkAnnotationProcessor,
32
32
  )
33
- from snowflake.cli.plugins.nativeapp.feature_flags import FeatureFlag
34
- from snowflake.cli.plugins.nativeapp.project_model import NativeAppProjectModel
33
+ from snowflake.cli._plugins.nativeapp.codegen.templates.templates_processor import (
34
+ TemplatesProcessor,
35
+ )
36
+ from snowflake.cli._plugins.nativeapp.feature_flags import FeatureFlag
37
+ from snowflake.cli.api.cli_global_context import get_cli_context
38
+ from snowflake.cli.api.console import cli_console as cc
39
+ from snowflake.cli.api.metrics import CLICounterField
40
+ from snowflake.cli.api.project.schemas.v1.native_app.path_mapping import (
41
+ ProcessorMapping,
42
+ )
35
43
 
36
44
  SNOWPARK_PROCESSOR = "snowpark"
37
- NA_SETUP_PROCESSOR = "native-app-setup"
45
+ NA_SETUP_PROCESSOR = "native app setup"
46
+ TEMPLATES_PROCESSOR = "templates"
38
47
 
39
48
  _REGISTERED_PROCESSORS_BY_NAME = {
40
49
  SNOWPARK_PROCESSOR: SnowparkAnnotationProcessor,
41
50
  NA_SETUP_PROCESSOR: NativeAppSetupProcessor,
51
+ TEMPLATES_PROCESSOR: TemplatesProcessor,
42
52
  }
43
53
 
44
54
 
@@ -53,9 +63,9 @@ class NativeAppCompiler:
53
63
 
54
64
  def __init__(
55
65
  self,
56
- na_project: NativeAppProjectModel,
66
+ bundle_ctx: BundleContext,
57
67
  ):
58
- self._na_project = na_project
68
+ self._bundle_ctx = bundle_ctx
59
69
  # dictionary of all processors created and shared between different artifact objects.
60
70
  self.cached_processors: Dict[str, ArtifactProcessor] = {}
61
71
 
@@ -64,17 +74,20 @@ class NativeAppCompiler:
64
74
  Go through every artifact object in the project definition of a native app, and execute processors in order of specification for each of the artifact object.
65
75
  May have side-effects on the filesystem by either directly editing source files or the deploy root.
66
76
  """
77
+ metrics = get_cli_context().metrics
78
+ metrics.set_counter_default(CLICounterField.TEMPLATES_PROCESSOR, 0)
79
+ metrics.set_counter_default(CLICounterField.SNOWPARK_PROCESSOR, 0)
67
80
 
68
81
  if not self._should_invoke_processors():
69
82
  return
70
83
 
71
84
  with cc.phase("Invoking artifact processors"):
72
- if self._na_project.generated_root.exists():
85
+ if self._bundle_ctx.generated_root.exists():
73
86
  raise ClickException(
74
- f"Path {self._na_project.generated_root} already exists. Please choose a different name for your generated directory in the project definition file."
87
+ f"Path {self._bundle_ctx.generated_root} already exists. Please choose a different name for your generated directory in the project definition file."
75
88
  )
76
89
 
77
- for artifact in self._na_project.artifacts:
90
+ for artifact in self._bundle_ctx.artifacts:
78
91
  for processor in artifact.processors:
79
92
  if self._is_enabled(processor):
80
93
  artifact_processor = self._try_create_processor(
@@ -110,15 +123,21 @@ class NativeAppCompiler:
110
123
  # No registered processor with the specified name
111
124
  return None
112
125
 
113
- current_processor = processor_factory(
114
- na_project=self._na_project,
126
+ processor_ctx = copy.copy(self._bundle_ctx)
127
+ processor_subdirectory = re.sub(r"[^a-zA-Z0-9_$]", "_", processor_name)
128
+ processor_ctx.bundle_root = (
129
+ self._bundle_ctx.bundle_root / processor_subdirectory
130
+ )
131
+ processor_ctx.generated_root = (
132
+ self._bundle_ctx.generated_root / processor_subdirectory
115
133
  )
134
+ current_processor = processor_factory(processor_ctx)
116
135
  self.cached_processors[processor_name] = current_processor
117
136
 
118
137
  return current_processor
119
138
 
120
139
  def _should_invoke_processors(self):
121
- for artifact in self._na_project.artifacts:
140
+ for artifact in self._bundle_ctx.artifacts:
122
141
  for processor in artifact.processors:
123
142
  if self._is_enabled(processor):
124
143
  return True
@@ -0,0 +1,249 @@
1
+ # Copyright (c) 2024 Snowflake Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ import logging
19
+ import os.path
20
+ from pathlib import Path
21
+ from typing import List, Optional
22
+
23
+ import yaml
24
+ from click import ClickException
25
+ from snowflake.cli._plugins.nativeapp.artifacts import (
26
+ BundleMap,
27
+ find_manifest_file,
28
+ find_setup_script_file,
29
+ )
30
+ from snowflake.cli._plugins.nativeapp.codegen.artifact_processor import (
31
+ ArtifactProcessor,
32
+ is_python_file_artifact,
33
+ )
34
+ from snowflake.cli._plugins.nativeapp.codegen.sandbox import (
35
+ ExecutionEnvironmentType,
36
+ SandboxEnvBuilder,
37
+ execute_script_in_sandbox,
38
+ )
39
+ from snowflake.cli._plugins.stage.diff import to_stage_path
40
+ from snowflake.cli.api.console import cli_console as cc
41
+ from snowflake.cli.api.project.schemas.v1.native_app.path_mapping import (
42
+ PathMapping,
43
+ ProcessorMapping,
44
+ )
45
+
46
+ DEFAULT_TIMEOUT = 30
47
+ DRIVER_PATH = Path(__file__).parent / "setup_driver.py.source"
48
+
49
+ log = logging.getLogger(__name__)
50
+
51
+
52
+ def safe_set(d: dict, *keys: str, **kwargs) -> None:
53
+ """
54
+ Sets a value in a nested dictionary structure, creating intermediate dictionaries as needed.
55
+ Sample usage:
56
+
57
+ d = {}
58
+ safe_set(d, "a", "b", "c", value=42)
59
+
60
+ d is now:
61
+ {
62
+ "a": {
63
+ "b": {
64
+ "c": 42
65
+ }
66
+ }
67
+ }
68
+ """
69
+ curr = d
70
+ for k in keys[:-1]:
71
+ curr = curr.setdefault(k, {})
72
+
73
+ curr[keys[-1]] = kwargs.get("value")
74
+
75
+
76
+ class NativeAppSetupProcessor(ArtifactProcessor):
77
+ def __init__(self, *args, **kwargs):
78
+ super().__init__(*args, **kwargs)
79
+
80
+ def process(
81
+ self,
82
+ artifact_to_process: PathMapping,
83
+ processor_mapping: Optional[ProcessorMapping],
84
+ **kwargs,
85
+ ) -> None:
86
+ """
87
+ Processes a Python setup script and generates the corresponding SQL commands.
88
+ """
89
+ bundle_map = BundleMap(
90
+ project_root=self._bundle_ctx.project_root,
91
+ deploy_root=self._bundle_ctx.deploy_root,
92
+ )
93
+ bundle_map.add(artifact_to_process)
94
+
95
+ self._create_or_update_sandbox()
96
+
97
+ cc.step("Processing Python setup files")
98
+
99
+ files_to_process = []
100
+ for src_file, dest_file in bundle_map.all_mappings(
101
+ absolute=True, expand_directories=True, predicate=is_python_file_artifact
102
+ ):
103
+ cc.message(
104
+ f"Found Python setup file: {src_file.relative_to(self._bundle_ctx.project_root)}"
105
+ )
106
+ files_to_process.append(src_file)
107
+
108
+ result = self._execute_in_sandbox(files_to_process)
109
+ if not result:
110
+ return # nothing to do
111
+
112
+ logs = result.get("logs", [])
113
+ for msg in logs:
114
+ log.debug(msg)
115
+
116
+ warnings = result.get("warnings", [])
117
+ for msg in warnings:
118
+ cc.warning(msg)
119
+
120
+ schema_version = result.get("schema_version")
121
+ if schema_version != "1":
122
+ raise ClickException(
123
+ f"Unsupported schema version returned from snowflake-app-python library: {schema_version}"
124
+ )
125
+
126
+ setup_script_mods = [
127
+ mod
128
+ for mod in result.get("modifications", [])
129
+ if mod.get("target") == "native_app:setup_script"
130
+ ]
131
+ if setup_script_mods:
132
+ self._edit_setup_sql(setup_script_mods)
133
+
134
+ manifest_mods = [
135
+ mod
136
+ for mod in result.get("modifications", [])
137
+ if mod.get("target") == "native_app:manifest"
138
+ ]
139
+ if manifest_mods:
140
+ self._edit_manifest(manifest_mods)
141
+
142
+ def _execute_in_sandbox(self, py_files: List[Path]) -> dict:
143
+ file_count = len(py_files)
144
+ cc.step(f"Processing {file_count} setup file{'s' if file_count > 1 else ''}")
145
+
146
+ manifest_path = find_manifest_file(deploy_root=self._bundle_ctx.deploy_root)
147
+
148
+ generated_root = self._bundle_ctx.generated_root
149
+ generated_root.mkdir(exist_ok=True, parents=True)
150
+
151
+ env_vars = {
152
+ "_SNOWFLAKE_CLI_PROJECT_PATH": str(self._bundle_ctx.project_root),
153
+ "_SNOWFLAKE_CLI_SETUP_FILES": os.pathsep.join(map(str, py_files)),
154
+ "_SNOWFLAKE_CLI_APP_NAME": str(self._bundle_ctx.package_name),
155
+ "_SNOWFLAKE_CLI_SQL_DEST_DIR": str(generated_root),
156
+ "_SNOWFLAKE_CLI_MANIFEST_PATH": str(manifest_path),
157
+ }
158
+
159
+ try:
160
+ result = execute_script_in_sandbox(
161
+ script_source=DRIVER_PATH.read_text(),
162
+ env_type=ExecutionEnvironmentType.VENV,
163
+ cwd=self._bundle_ctx.bundle_root,
164
+ timeout=DEFAULT_TIMEOUT,
165
+ path=self.sandbox_root,
166
+ env_vars=env_vars,
167
+ )
168
+ except Exception as e:
169
+ raise ClickException(
170
+ f"Exception while executing python setup script logic: {e}"
171
+ )
172
+
173
+ if result.returncode == 0:
174
+ return json.loads(result.stdout)
175
+ else:
176
+ raise ClickException(
177
+ f"Failed to execute python setup script logic: {result.stderr}"
178
+ )
179
+
180
+ def _edit_setup_sql(self, modifications: List[dict]) -> None:
181
+ cc.step("Patching setup script")
182
+ setup_file_path = find_setup_script_file(
183
+ deploy_root=self._bundle_ctx.deploy_root
184
+ )
185
+
186
+ with self.edit_file(setup_file_path) as f:
187
+ prepended = []
188
+ appended = []
189
+
190
+ for mod in modifications:
191
+ for inst in mod.get("instructions", []):
192
+ if inst.get("type") == "insert":
193
+ default_loc = inst.get("default_location")
194
+ if default_loc == "end":
195
+ appended.append(self._setup_mod_instruction_to_sql(inst))
196
+ elif default_loc == "start":
197
+ prepended.append(self._setup_mod_instruction_to_sql(inst))
198
+
199
+ if prepended or appended:
200
+ f.edited_contents = "\n".join(prepended + [f.contents] + appended)
201
+
202
+ def _edit_manifest(self, modifications: List[dict]) -> None:
203
+ cc.step("Patching manifest")
204
+ manifest_path = find_manifest_file(deploy_root=self._bundle_ctx.deploy_root)
205
+
206
+ with self.edit_file(manifest_path) as f:
207
+ manifest = yaml.safe_load(f.contents)
208
+
209
+ for mod in modifications:
210
+ for inst in mod.get("instructions", []):
211
+ if inst.get("type") == "set":
212
+ payload = inst.get("payload")
213
+ if payload:
214
+ key = payload.get("key")
215
+ value = payload.get("value")
216
+ safe_set(manifest, *key.split("."), value=value)
217
+ f.edited_contents = yaml.safe_dump(manifest, sort_keys=False)
218
+
219
+ def _setup_mod_instruction_to_sql(self, mod_inst: dict) -> str:
220
+ payload = mod_inst.get("payload")
221
+ if not payload:
222
+ raise ClickException("Unsupported instruction received: no payload found")
223
+
224
+ payload_type = payload.get("type")
225
+ if payload_type == "execute immediate":
226
+ file_path = payload.get("file_path")
227
+ if file_path:
228
+ sql_file_path = self._bundle_ctx.generated_root / file_path
229
+ return f"EXECUTE IMMEDIATE FROM '/{to_stage_path(sql_file_path.relative_to(self._bundle_ctx.deploy_root))}';"
230
+
231
+ raise ClickException(f"Unsupported instruction type received: {payload_type}")
232
+
233
+ @property
234
+ def sandbox_root(self):
235
+ return self._bundle_ctx.bundle_root / "venv"
236
+
237
+ def _create_or_update_sandbox(self):
238
+ sandbox_root = self.sandbox_root
239
+ env_builder = SandboxEnvBuilder(sandbox_root, with_pip=True)
240
+ if sandbox_root.exists():
241
+ cc.step("Virtual environment found")
242
+ else:
243
+ cc.step(
244
+ f"Creating virtual environment in {sandbox_root.relative_to(self._bundle_ctx.project_root)}"
245
+ )
246
+ env_builder.ensure_created()
247
+
248
+ # Temporarily fetch the library from a location specified via env vars
249
+ env_builder.pip_install(os.environ["SNOWFLAKE_APP_PYTHON_LOC"])
@@ -20,8 +20,11 @@ from pathlib import Path
20
20
  import snowflake.app.context as ctx
21
21
  from snowflake.app.sql import SQLGenerator
22
22
 
23
- ctx._project_path = os.environ["_SNOWFLAKE_CLI_PROJECT_PATH"]
24
- ctx._current_app_name = os.environ["_SNOWFLAKE_CLI_APP_NAME"]
23
+ ctx.configure("project_path", os.environ.get("_SNOWFLAKE_CLI_PROJECT_PATH", None))
24
+ ctx.configure("manifest_path", os.environ.get("_SNOWFLAKE_CLI_MANIFEST_PATH", None))
25
+ ctx.configure("current_app_name", os.environ.get("_SNOWFLAKE_CLI_APP_NAME", None))
26
+ ctx.configure("enable_sql_generation", True)
27
+
25
28
  __snowflake_internal_py_files = os.environ["_SNOWFLAKE_CLI_SETUP_FILES"].split(
26
29
  os.pathsep
27
30
  )
@@ -23,17 +23,17 @@ from typing import (
23
23
  )
24
24
 
25
25
  from click.exceptions import ClickException
26
- from snowflake.cli.api.project.schemas.snowpark.argument import Argument
26
+ from snowflake.cli._plugins.nativeapp.codegen.snowpark.models import (
27
+ ExtensionFunctionTypeEnum,
28
+ NativeAppExtensionFunction,
29
+ )
30
+ from snowflake.cli.api.project.schemas.v1.snowpark.argument import Argument
27
31
  from snowflake.cli.api.project.util import (
28
32
  is_valid_identifier,
29
33
  is_valid_string_literal,
30
34
  to_identifier,
31
35
  to_string_literal,
32
36
  )
33
- from snowflake.cli.plugins.nativeapp.codegen.snowpark.models import (
34
- ExtensionFunctionTypeEnum,
35
- NativeAppExtensionFunction,
36
- )
37
37
 
38
38
  ASTDefNode = Union[ast.FunctionDef, ast.ClassDef]
39
39
 
@@ -18,8 +18,8 @@ from enum import Enum
18
18
  from typing import List, Optional
19
19
 
20
20
  from pydantic import Field
21
- from snowflake.cli.api.project.schemas.snowpark.callable import _CallableBase
22
21
  from snowflake.cli.api.project.schemas.updatable_model import IdentifierField
22
+ from snowflake.cli.api.project.schemas.v1.snowpark.callable import _CallableBase
23
23
 
24
24
 
25
25
  class ExtensionFunctionTypeEnum(str, Enum):
@@ -21,26 +21,20 @@ from textwrap import dedent
21
21
  from typing import Any, Dict, List, Optional, Set
22
22
 
23
23
  from pydantic import ValidationError
24
- from snowflake.cli.api.console import cli_console as cc
25
- from snowflake.cli.api.project.schemas.native_app.path_mapping import (
26
- PathMapping,
27
- ProcessorMapping,
28
- )
29
- from snowflake.cli.api.rendering.jinja import jinja_render_from_file
30
- from snowflake.cli.plugins.nativeapp.artifacts import (
24
+ from snowflake.cli._plugins.nativeapp.artifacts import (
31
25
  BundleMap,
32
26
  find_setup_script_file,
33
27
  )
34
- from snowflake.cli.plugins.nativeapp.codegen.artifact_processor import (
28
+ from snowflake.cli._plugins.nativeapp.codegen.artifact_processor import (
35
29
  ArtifactProcessor,
36
30
  is_python_file_artifact,
37
31
  )
38
- from snowflake.cli.plugins.nativeapp.codegen.sandbox import (
32
+ from snowflake.cli._plugins.nativeapp.codegen.sandbox import (
39
33
  ExecutionEnvironmentType,
40
34
  SandboxExecutionError,
41
35
  execute_script_in_sandbox,
42
36
  )
43
- from snowflake.cli.plugins.nativeapp.codegen.snowpark.extension_function_utils import (
37
+ from snowflake.cli._plugins.nativeapp.codegen.snowpark.extension_function_utils import (
44
38
  deannotate_module_source,
45
39
  ensure_all_string_literals,
46
40
  ensure_string_literal,
@@ -49,12 +43,19 @@ from snowflake.cli.plugins.nativeapp.codegen.snowpark.extension_function_utils i
49
43
  get_sql_argument_signature,
50
44
  get_sql_object_type,
51
45
  )
52
- from snowflake.cli.plugins.nativeapp.codegen.snowpark.models import (
46
+ from snowflake.cli._plugins.nativeapp.codegen.snowpark.models import (
53
47
  ExtensionFunctionTypeEnum,
54
48
  NativeAppExtensionFunction,
55
49
  )
56
- from snowflake.cli.plugins.nativeapp.project_model import NativeAppProjectModel
57
- from snowflake.cli.plugins.stage.diff import to_stage_path
50
+ from snowflake.cli._plugins.stage.diff import to_stage_path
51
+ from snowflake.cli.api.cli_global_context import get_cli_context
52
+ from snowflake.cli.api.console import cli_console as cc
53
+ from snowflake.cli.api.metrics import CLICounterField
54
+ from snowflake.cli.api.project.schemas.v1.native_app.path_mapping import (
55
+ PathMapping,
56
+ ProcessorMapping,
57
+ )
58
+ from snowflake.cli.api.rendering.jinja import jinja_render_from_file
58
59
 
59
60
  DEFAULT_TIMEOUT = 30
60
61
  TEMPLATE_PATH = Path(__file__).parent / "callback_source.py.jinja"
@@ -163,11 +164,8 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
163
164
  and generate SQL code for creation of extension functions based on those discovered objects.
164
165
  """
165
166
 
166
- def __init__(
167
- self,
168
- na_project: NativeAppProjectModel,
169
- ):
170
- super().__init__(na_project=na_project)
167
+ def __init__(self, *args, **kwargs):
168
+ super().__init__(*args, **kwargs)
171
169
 
172
170
  def process(
173
171
  self,
@@ -180,9 +178,11 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
180
178
  setup script with generated SQL that registers these functions.
181
179
  """
182
180
 
181
+ get_cli_context().metrics.set_counter(CLICounterField.SNOWPARK_PROCESSOR, 1)
182
+
183
183
  bundle_map = BundleMap(
184
- project_root=self._na_project.project_root,
185
- deploy_root=self._na_project.deploy_root,
184
+ project_root=self._bundle_ctx.project_root,
185
+ deploy_root=self._bundle_ctx.deploy_root,
186
186
  )
187
187
  bundle_map.add(artifact_to_process)
188
188
 
@@ -230,13 +230,9 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
230
230
  edit_setup_script_with_exec_imm_sql(
231
231
  collected_sql_files=collected_sql_files,
232
232
  deploy_root=bundle_map.deploy_root(),
233
- generated_root=self._generated_root,
233
+ generated_root=self._bundle_ctx.generated_root,
234
234
  )
235
235
 
236
- @property
237
- def _generated_root(self):
238
- return self._na_project.generated_root / "snowpark"
239
-
240
236
  def _normalize_imports(
241
237
  self,
242
238
  extension_fn: NativeAppExtensionFunction,
@@ -315,7 +311,7 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
315
311
  self, bundle_map: BundleMap, processor_mapping: Optional[ProcessorMapping]
316
312
  ) -> Dict[Path, List[NativeAppExtensionFunction]]:
317
313
  kwargs = (
318
- _determine_virtual_env(self._na_project.project_root, processor_mapping)
314
+ _determine_virtual_env(self._bundle_ctx.project_root, processor_mapping)
319
315
  if processor_mapping is not None
320
316
  else {}
321
317
  )
@@ -331,14 +327,11 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
331
327
  predicate=is_python_file_artifact,
332
328
  )
333
329
  ):
334
- cc.step(
335
- "Processing Snowpark annotations from {}".format(
336
- dest_file.relative_to(bundle_map.deploy_root())
337
- )
338
- )
330
+ src_file_name = src_file.relative_to(self._bundle_ctx.project_root)
331
+ cc.step(f"Processing Snowpark annotations from {src_file_name}")
339
332
  collected_extension_function_json = _execute_in_sandbox(
340
333
  py_file=str(dest_file.resolve()),
341
- deploy_root=self._na_project.deploy_root,
334
+ deploy_root=self._bundle_ctx.deploy_root,
342
335
  kwargs=kwargs,
343
336
  )
344
337
 
@@ -369,8 +362,10 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
369
362
  """
370
363
  Generates a SQL filename for the generated root from the python file, and creates its parent directories.
371
364
  """
372
- relative_py_file = py_file.relative_to(self._na_project.deploy_root)
373
- sql_file = Path(self._generated_root, relative_py_file.with_suffix(".sql"))
365
+ relative_py_file = py_file.relative_to(self._bundle_ctx.deploy_root)
366
+ sql_file = Path(
367
+ self._bundle_ctx.generated_root, relative_py_file.with_suffix(".sql")
368
+ )
374
369
  if sql_file.exists():
375
370
  cc.warning(
376
371
  f"""\