snowflake-cli-labs 2.8.1__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 +11 -7
  27. snowflake/cli/{plugins → _plugins}/git/manager.py +55 -9
  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 +2 -2
  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 +3 -2
  59. snowflake/cli/{plugins → _plugins}/notebook/manager.py +5 -5
  60. snowflake/cli/{plugins/nativeapp → _plugins/notebook}/plugin_spec.py +1 -1
  61. snowflake/cli/{plugins → _plugins}/object/command_aliases.py +4 -4
  62. snowflake/cli/{plugins → _plugins}/object/commands.py +4 -5
  63. snowflake/cli/{plugins → _plugins}/object/manager.py +36 -15
  64. snowflake/cli/{plugins → _plugins}/object/plugin_spec.py +1 -1
  65. snowflake/cli/_plugins/snowpark/commands.py +450 -0
  66. snowflake/cli/_plugins/snowpark/common.py +268 -0
  67. snowflake/cli/{plugins → _plugins}/snowpark/models.py +0 -7
  68. snowflake/cli/{plugins → _plugins}/snowpark/package/anaconda_packages.py +2 -36
  69. snowflake/cli/{plugins → _plugins}/snowpark/package/commands.py +13 -74
  70. snowflake/cli/{plugins → _plugins}/snowpark/package/manager.py +2 -2
  71. snowflake/cli/{plugins → _plugins}/snowpark/package_utils.py +5 -5
  72. snowflake/cli/_plugins/snowpark/plugin_spec.py +30 -0
  73. snowflake/cli/_plugins/snowpark/snowpark_entity.py +29 -0
  74. snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +173 -0
  75. snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +109 -0
  76. snowflake/cli/{plugins → _plugins}/snowpark/snowpark_shared.py +0 -36
  77. snowflake/cli/{plugins → _plugins}/snowpark/zipper.py +16 -8
  78. snowflake/cli/{plugins → _plugins}/spcs/__init__.py +5 -7
  79. snowflake/cli/{plugins → _plugins}/spcs/compute_pool/commands.py +8 -8
  80. snowflake/cli/{plugins → _plugins}/spcs/compute_pool/manager.py +3 -3
  81. snowflake/cli/{plugins → _plugins}/spcs/image_registry/commands.py +3 -3
  82. snowflake/cli/{plugins → _plugins}/spcs/image_repository/commands.py +6 -6
  83. snowflake/cli/{plugins → _plugins}/spcs/image_repository/manager.py +1 -1
  84. snowflake/cli/{plugins → _plugins}/spcs/plugin_spec.py +1 -1
  85. snowflake/cli/{plugins → _plugins}/spcs/services/commands.py +44 -11
  86. snowflake/cli/{plugins → _plugins}/spcs/services/manager.py +43 -5
  87. snowflake/cli/{plugins → _plugins}/sql/commands.py +20 -17
  88. snowflake/cli/{plugins → _plugins}/sql/manager.py +1 -1
  89. snowflake/cli/{plugins → _plugins}/sql/plugin_spec.py +1 -1
  90. snowflake/cli/{plugins → _plugins}/stage/commands.py +15 -14
  91. snowflake/cli/{plugins → _plugins}/stage/diff.py +1 -47
  92. snowflake/cli/{plugins → _plugins}/stage/manager.py +12 -7
  93. snowflake/cli/{plugins → _plugins}/stage/plugin_spec.py +1 -1
  94. snowflake/cli/_plugins/stage/utils.py +54 -0
  95. snowflake/cli/{plugins → _plugins}/streamlit/commands.py +64 -48
  96. snowflake/cli/{plugins → _plugins}/streamlit/manager.py +67 -69
  97. snowflake/cli/_plugins/streamlit/plugin_spec.py +30 -0
  98. snowflake/cli/_plugins/streamlit/streamlit_entity.py +12 -0
  99. snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +66 -0
  100. snowflake/cli/_plugins/workspace/action_context.py +18 -0
  101. snowflake/cli/_plugins/workspace/commands.py +306 -0
  102. snowflake/cli/_plugins/workspace/manager.py +74 -0
  103. snowflake/cli/_plugins/workspace/plugin_spec.py +30 -0
  104. snowflake/cli/api/cli_global_context.py +152 -295
  105. snowflake/cli/api/commands/common.py +25 -0
  106. snowflake/cli/api/commands/decorators.py +19 -4
  107. snowflake/cli/api/commands/experimental_behaviour.py +2 -3
  108. snowflake/cli/api/commands/flags.py +127 -228
  109. snowflake/cli/api/commands/overrideable_parameter.py +143 -0
  110. snowflake/cli/api/commands/snow_typer.py +21 -11
  111. snowflake/cli/api/commands/utils.py +18 -0
  112. snowflake/cli/api/config.py +44 -12
  113. snowflake/cli/api/connections.py +216 -0
  114. snowflake/cli/api/console/abc.py +8 -3
  115. snowflake/cli/api/constants.py +11 -0
  116. snowflake/cli/api/entities/common.py +56 -0
  117. snowflake/cli/api/entities/utils.py +370 -0
  118. snowflake/cli/api/errno.py +1 -0
  119. snowflake/cli/api/exceptions.py +31 -5
  120. snowflake/cli/api/feature_flags.py +0 -1
  121. snowflake/cli/api/identifiers.py +28 -5
  122. snowflake/cli/api/metrics.py +92 -0
  123. snowflake/cli/api/project/definition.py +48 -6
  124. snowflake/cli/api/project/definition_conversion.py +400 -0
  125. snowflake/cli/api/project/definition_manager.py +16 -5
  126. snowflake/cli/api/project/project_verification.py +3 -3
  127. snowflake/cli/api/project/schemas/entities/common.py +91 -16
  128. snowflake/cli/api/project/schemas/entities/entities.py +37 -6
  129. snowflake/cli/api/project/schemas/project_definition.py +180 -49
  130. snowflake/cli/api/project/schemas/updatable_model.py +11 -3
  131. snowflake/cli/api/project/schemas/v1/__init__.py +0 -0
  132. snowflake/cli/api/project/schemas/{identifier_model.py → v1/identifier_model.py} +3 -1
  133. snowflake/cli/api/project/schemas/v1/native_app/__init__.py +0 -0
  134. snowflake/cli/api/project/schemas/{native_app → v1/native_app}/application.py +8 -9
  135. snowflake/cli/api/project/schemas/{native_app → v1/native_app}/native_app.py +4 -4
  136. snowflake/cli/api/project/schemas/{native_app → v1/native_app}/package.py +7 -1
  137. snowflake/cli/api/project/schemas/v1/snowpark/__init__.py +0 -0
  138. snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/callable.py +2 -2
  139. snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/snowpark.py +2 -2
  140. snowflake/cli/api/project/schemas/v1/streamlit/__init__.py +0 -0
  141. snowflake/cli/api/project/schemas/{streamlit → v1/streamlit}/streamlit.py +2 -1
  142. snowflake/cli/api/project/util.py +23 -6
  143. snowflake/cli/api/rendering/jinja.py +14 -8
  144. snowflake/cli/api/rendering/project_definition_templates.py +5 -1
  145. snowflake/cli/api/rendering/sql_templates.py +56 -11
  146. snowflake/cli/api/rest_api.py +11 -5
  147. snowflake/cli/api/secure_path.py +16 -18
  148. snowflake/cli/api/secure_utils.py +90 -1
  149. snowflake/cli/api/sql_execution.py +43 -23
  150. snowflake/cli/api/utils/definition_rendering.py +45 -13
  151. {snowflake_cli_labs-2.8.1.dist-info → snowflake_cli_labs-3.0.0.dist-info}/METADATA +20 -18
  152. snowflake_cli_labs-3.0.0.dist-info/RECORD +242 -0
  153. snowflake_cli_labs-3.0.0.dist-info/entry_points.txt +2 -0
  154. snowflake/cli/api/commands/project_initialisation.py +0 -65
  155. snowflake/cli/api/commands/typer_pre_execute.py +0 -26
  156. snowflake/cli/api/project/schemas/entities/application_entity.py +0 -44
  157. snowflake/cli/api/project/schemas/entities/application_package_entity.py +0 -66
  158. snowflake/cli/app/build_and_push.sh +0 -8
  159. snowflake/cli/plugins/nativeapp/codegen/setup/native_app_setup_processor.py +0 -172
  160. snowflake/cli/plugins/nativeapp/init.py +0 -345
  161. snowflake/cli/plugins/nativeapp/manager.py +0 -823
  162. snowflake/cli/plugins/nativeapp/run_processor.py +0 -389
  163. snowflake/cli/plugins/nativeapp/teardown_processor.py +0 -301
  164. snowflake/cli/plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +0 -135
  165. snowflake/cli/plugins/nativeapp/version/version_processor.py +0 -362
  166. snowflake/cli/plugins/object_stage_deprecated/__init__.py +0 -15
  167. snowflake/cli/plugins/object_stage_deprecated/commands.py +0 -122
  168. snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +0 -32
  169. snowflake/cli/plugins/snowpark/commands.py +0 -546
  170. snowflake/cli/plugins/snowpark/common.py +0 -307
  171. snowflake/cli/plugins/snowpark/manager.py +0 -109
  172. snowflake/cli/plugins/snowpark/plugin_spec.py +0 -30
  173. snowflake/cli/plugins/snowpark/snowpark_package_paths.py +0 -65
  174. snowflake/cli/plugins/spcs/jobs/commands.py +0 -78
  175. snowflake/cli/plugins/spcs/jobs/manager.py +0 -53
  176. snowflake/cli/plugins/streamlit/__init__.py +0 -13
  177. snowflake/cli/plugins/streamlit/plugin_spec.py +0 -30
  178. snowflake/cli/plugins/workspace/__init__.py +0 -13
  179. snowflake/cli/plugins/workspace/commands.py +0 -35
  180. snowflake/cli/plugins/workspace/plugin_spec.py +0 -30
  181. snowflake/cli/templates/default_snowpark/.gitignore +0 -4
  182. snowflake/cli/templates/default_snowpark/app/common.py +0 -2
  183. snowflake/cli/templates/default_snowpark/app/functions.py +0 -15
  184. snowflake/cli/templates/default_snowpark/app/procedures.py +0 -22
  185. snowflake/cli/templates/default_snowpark/requirements.txt +0 -1
  186. snowflake/cli/templates/default_snowpark/snowflake.yml +0 -23
  187. snowflake/cli/templates/default_streamlit/.gitignore +0 -4
  188. snowflake/cli/templates/default_streamlit/common/hello.py +0 -2
  189. snowflake/cli/templates/default_streamlit/environment.yml +0 -6
  190. snowflake/cli/templates/default_streamlit/pages/my_page.py +0 -3
  191. snowflake/cli/templates/default_streamlit/snowflake.yml +0 -10
  192. snowflake/cli/templates/default_streamlit/streamlit_app.py +0 -4
  193. snowflake_cli_labs-2.8.1.dist-info/RECORD +0 -240
  194. snowflake_cli_labs-2.8.1.dist-info/entry_points.txt +0 -2
  195. /snowflake/cli/{app → _app}/__init__.py +0 -0
  196. /snowflake/cli/{api/project/schemas/native_app → _app/api_impl}/__init__.py +0 -0
  197. /snowflake/cli/{api/project/schemas/snowpark → _app/api_impl/plugin}/__init__.py +0 -0
  198. /snowflake/cli/{app → _app}/api_impl/plugin/plugin_config_provider_impl.py +0 -0
  199. /snowflake/cli/{app → _app}/commands_registration/__init__.py +0 -0
  200. /snowflake/cli/{app → _app}/commands_registration/threadsafe.py +0 -0
  201. /snowflake/cli/{app → _app}/constants.py +0 -0
  202. /snowflake/cli/{api/project/schemas/streamlit → _app/dev}/__init__.py +0 -0
  203. /snowflake/cli/{app → _app}/dev/commands_structure.py +0 -0
  204. /snowflake/cli/{app/api_impl → _app/dev/docs}/__init__.py +0 -0
  205. /snowflake/cli/{app → _app}/dev/docs/project_definition_generate_json_schema.py +0 -0
  206. /snowflake/cli/{app → _app}/dev/docs/template_utils.py +0 -0
  207. /snowflake/cli/{app → _app}/dev/docs/templates/definition_description.rst.jinja2 +0 -0
  208. /snowflake/cli/{app → _app}/dev/docs/templates/overview.rst.jinja2 +0 -0
  209. /snowflake/cli/{app → _app}/dev/pycharm_remote_debug.py +0 -0
  210. /snowflake/cli/{app → _app}/loggers.py +0 -0
  211. /snowflake/cli/{app/api_impl/plugin → _plugins}/__init__.py +0 -0
  212. /snowflake/cli/{app/dev → _plugins/connection}/__init__.py +0 -0
  213. /snowflake/cli/{app/dev/docs → _plugins/cortex}/__init__.py +0 -0
  214. /snowflake/cli/{plugins → _plugins}/cortex/types.py +0 -0
  215. /snowflake/cli/{plugins → _plugins/git}/__init__.py +0 -0
  216. /snowflake/cli/{plugins/connection → _plugins/helpers}/__init__.py +0 -0
  217. /snowflake/cli/{plugins/cortex → _plugins/init}/__init__.py +0 -0
  218. /snowflake/cli/{plugins/git → _plugins/nativeapp}/__init__.py +0 -0
  219. /snowflake/cli/{plugins/init → _plugins/nativeapp/codegen}/__init__.py +0 -0
  220. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/sandbox.py +0 -0
  221. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -0
  222. /snowflake/cli/{plugins → _plugins}/nativeapp/constants.py +0 -0
  223. /snowflake/cli/{templates/default_snowpark/app → _plugins/nativeapp/entities}/__init__.py +0 -0
  224. /snowflake/cli/{plugins → _plugins}/nativeapp/feature_flags.py +0 -0
  225. /snowflake/cli/{plugins → _plugins}/nativeapp/utils.py +0 -0
  226. /snowflake/cli/{plugins/nativeapp → _plugins/nativeapp/version}/__init__.py +0 -0
  227. /snowflake/cli/{plugins/nativeapp/codegen → _plugins/notebook}/__init__.py +0 -0
  228. /snowflake/cli/{plugins → _plugins}/notebook/exceptions.py +0 -0
  229. /snowflake/cli/{plugins → _plugins}/notebook/types.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.1.dist-info → snowflake_cli_labs-3.0.0.dist-info}/WHEEL +0 -0
  251. {snowflake_cli_labs-2.8.1.dist-info → snowflake_cli_labs-3.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,143 @@
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
+ from abc import ABC, abstractmethod
18
+ from inspect import signature
19
+ from typing import Any, List, Optional, Tuple
20
+
21
+ import typer
22
+ from click import ClickException
23
+ from snowflake.cli.api.exceptions import IncompatibleParametersError
24
+
25
+
26
+ class _OverrideableParameter(ABC):
27
+ """
28
+ Class that allows you to generate instances of typer.models.OptionInfo with some default properties while allowing
29
+ specific values to be overridden.
30
+
31
+ Custom parameters:
32
+ - mutually_exclusive (Tuple[str]|List[str]): A list of parameter names that this Option is not compatible with. If this Option has
33
+ a truthy value and any of the other parameters in the mutually_exclusive list has a truthy value, a
34
+ ClickException will be thrown. Note that mutually_exclusive can contain an option's own name but does not require
35
+ it.
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ default: Any = ...,
41
+ *param_decls: str,
42
+ mutually_exclusive: Optional[List[str] | Tuple[str]] = None,
43
+ **kwargs,
44
+ ):
45
+ self.default = default
46
+ self.param_decls = param_decls
47
+ self.mutually_exclusive = mutually_exclusive
48
+ self.kwargs = kwargs
49
+
50
+ def __call__(self, **kwargs) -> typer.models.ParameterInfo:
51
+ """
52
+ Returns a typer.models.OptionInfo instance initialized with the specified default values along with any overrides
53
+ from kwargs. Note that if you are overriding param_decls, you must pass an iterable of strings, you cannot use
54
+ positional arguments like you can with typer.Option. Does not modify the original instance.
55
+ """
56
+ default = kwargs.get("default", self.default)
57
+ param_decls = kwargs.get("param_decls", self.param_decls)
58
+ mutually_exclusive = kwargs.get("mutually_exclusive", self.mutually_exclusive)
59
+ if not isinstance(param_decls, list) and not isinstance(param_decls, tuple):
60
+ raise TypeError("param_decls must be a list or tuple")
61
+ passed_kwargs = self.kwargs.copy()
62
+ passed_kwargs.update(kwargs)
63
+ if passed_kwargs.get("callback", None) or mutually_exclusive:
64
+ passed_kwargs["callback"] = self._callback_factory(
65
+ passed_kwargs.get("callback", None), mutually_exclusive
66
+ )
67
+ for non_kwarg in ["default", "param_decls", "mutually_exclusive"]:
68
+ passed_kwargs.pop(non_kwarg, None)
69
+
70
+ return self.get_parameter(default, *param_decls, **passed_kwargs)
71
+
72
+ @abstractmethod
73
+ def get_parameter(
74
+ self, default: Any = None, *param_decls: str, **kwargs
75
+ ) -> typer.models.ParameterInfo:
76
+ pass
77
+
78
+ class InvalidCallbackSignature(ClickException):
79
+ def __init__(self, callback):
80
+ super().__init__(
81
+ f"Signature {signature(callback)} is not valid for an OverrideableOption callback function. Must have "
82
+ f"at most one parameter with each of the following types: (typer.Context, typer.CallbackParam, "
83
+ f"Any Other Type)"
84
+ )
85
+
86
+ def _callback_factory(
87
+ self, callback, mutually_exclusive: Optional[List[str] | Tuple[str]]
88
+ ):
89
+ callback = callback if callback else lambda x: x
90
+
91
+ # inspect existing_callback to make sure signature is valid
92
+ existing_params = signature(callback).parameters
93
+ # at most one parameter with each type in [typer.Context, typer.CallbackParam, any other type]
94
+ limits = [
95
+ lambda x: x == typer.Context,
96
+ lambda x: x == typer.CallbackParam,
97
+ lambda x: x != typer.Context and x != typer.CallbackParam,
98
+ ]
99
+ for limit in limits:
100
+ if len([v for v in existing_params.values() if limit(v.annotation)]) > 1:
101
+ raise self.InvalidCallbackSignature(callback)
102
+
103
+ def generated_callback(ctx: typer.Context, param: typer.CallbackParam, value):
104
+ if mutually_exclusive:
105
+ for name in mutually_exclusive:
106
+ if value and ctx.params.get(
107
+ name, False
108
+ ): # if the current parameter is set to True and a previous parameter is also Truthy
109
+ curr_opt = param.opts[0]
110
+ other_opt = [x for x in ctx.command.params if x.name == name][
111
+ 0
112
+ ].opts[0]
113
+ raise IncompatibleParametersError([curr_opt, other_opt])
114
+
115
+ # pass args to existing callback based on its signature (this is how Typer infers callback args)
116
+ passed_params = {}
117
+ for existing_param in existing_params:
118
+ annotation = existing_params[existing_param].annotation
119
+ if annotation == typer.Context:
120
+ passed_params[existing_param] = ctx
121
+ elif annotation == typer.CallbackParam:
122
+ passed_params[existing_param] = param
123
+ else:
124
+ passed_params[existing_param] = value
125
+ return callback(**passed_params)
126
+
127
+ return generated_callback
128
+
129
+
130
+ class OverrideableArgument(_OverrideableParameter):
131
+ def get_parameter(
132
+ self, default: Any = ..., *param_decls: str, **kwargs
133
+ ) -> typer.models.ArgumentInfo:
134
+ return typer.Argument(default, *param_decls, **kwargs)
135
+
136
+
137
+ # OverrideableOption doesn't work with flags with type List[Any] and default None, because typer executes the callback
138
+ # function which converts the default value iterating over it, but None is not iterable.
139
+ class OverrideableOption(_OverrideableParameter):
140
+ def get_parameter(
141
+ self, default: Any = ..., *param_decls: str, **kwargs
142
+ ) -> typer.models.OptionInfo:
143
+ return typer.Option(default, *param_decls, **kwargs)
@@ -20,6 +20,7 @@ from functools import wraps
20
20
  from typing import Any, Callable, Dict, List, Optional, Tuple
21
21
 
22
22
  import typer
23
+ from click import ClickException
23
24
  from snowflake.cli.api.commands.decorators import (
24
25
  global_options,
25
26
  global_options_with_connection,
@@ -29,10 +30,11 @@ from snowflake.cli.api.commands.execution_metadata import (
29
30
  ExecutionStatus,
30
31
  )
31
32
  from snowflake.cli.api.commands.flags import DEFAULT_CONTEXT_SETTINGS
32
- from snowflake.cli.api.commands.typer_pre_execute import run_pre_execute_commands
33
33
  from snowflake.cli.api.exceptions import CommandReturnTypeError
34
34
  from snowflake.cli.api.output.types import CommandResult
35
35
  from snowflake.cli.api.sanitizers import sanitize_for_terminal
36
+ from snowflake.cli.api.sql_execution import SqlExecutionMixin
37
+ from snowflake.connector import DatabaseError
36
38
 
37
39
  log = logging.getLogger(__name__)
38
40
 
@@ -46,6 +48,7 @@ class SnowTyper(typer.Typer):
46
48
  pretty_exceptions_show_locals=False,
47
49
  no_args_is_help=True,
48
50
  add_completion=True,
51
+ rich_markup_mode="markdown",
49
52
  )
50
53
 
51
54
  @staticmethod
@@ -70,6 +73,7 @@ class SnowTyper(typer.Typer):
70
73
  requires_global_options: bool = True,
71
74
  requires_connection: bool = False,
72
75
  is_enabled: Callable[[], bool] | None = None,
76
+ require_warehouse: bool = False,
73
77
  **kwargs,
74
78
  ):
75
79
  """
@@ -96,15 +100,15 @@ class SnowTyper(typer.Typer):
96
100
  def command_callable_decorator(*args, **kw):
97
101
  """Wrapper around command callable. This is what happens at "runtime"."""
98
102
  execution = ExecutionMetadata()
99
- self.pre_execute(execution)
103
+ self.pre_execute(execution, require_warehouse=require_warehouse)
100
104
  try:
101
105
  result = command_callable(*args, **kw)
102
106
  self.process_result(result)
103
107
  execution.complete(ExecutionStatus.SUCCESS)
104
- except Exception as err:
108
+ except BaseException as err:
105
109
  execution.complete(ExecutionStatus.FAILURE)
106
- self.exception_handler(err, execution)
107
- raise
110
+ exception = self.exception_handler(err, execution)
111
+ raise exception
108
112
  finally:
109
113
  self.post_execute(execution)
110
114
 
@@ -115,22 +119,25 @@ class SnowTyper(typer.Typer):
115
119
  return custom_command
116
120
 
117
121
  @staticmethod
118
- def pre_execute(execution: ExecutionMetadata):
122
+ def pre_execute(execution: ExecutionMetadata, require_warehouse: bool = False):
119
123
  """
120
124
  Callback executed before running any command callable (after context execution).
121
125
  Pay attention to make this method safe to use if performed operations are not necessary
122
126
  for executing the command in proper way.
123
127
  """
124
- from snowflake.cli.app.telemetry import log_command_usage
128
+ from snowflake.cli._app.telemetry import log_command_usage
125
129
 
126
130
  log.debug("Executing command pre execution callback")
127
- run_pre_execute_commands()
128
131
  log_command_usage(execution)
132
+ if require_warehouse and not SqlExecutionMixin().session_has_warehouse():
133
+ raise ClickException(
134
+ "The command requires warehouse. No warehouse found in current connection."
135
+ )
129
136
 
130
137
  @staticmethod
131
138
  def process_result(result):
132
139
  """Command result processor"""
133
- from snowflake.cli.app.printing import print_result
140
+ from snowflake.cli._app.printing import print_result
134
141
 
135
142
  # Because we still have commands like "logs" that do not return anything.
136
143
  # We should improve it in future.
@@ -145,10 +152,13 @@ class SnowTyper(typer.Typer):
145
152
  """
146
153
  Callback executed on command execution error.
147
154
  """
148
- from snowflake.cli.app.telemetry import log_command_execution_error
155
+ from snowflake.cli._app.telemetry import log_command_execution_error
149
156
 
150
157
  log.debug("Executing command exception callback")
151
158
  log_command_execution_error(exception, execution)
159
+ if isinstance(exception, DatabaseError):
160
+ return ClickException(exception.msg)
161
+ return exception
152
162
 
153
163
  @staticmethod
154
164
  def post_execute(execution: ExecutionMetadata):
@@ -156,7 +166,7 @@ class SnowTyper(typer.Typer):
156
166
  Callback executed after running any command callable. Pay attention to make this method safe to
157
167
  use if performed operations are not necessary for executing the command in proper way.
158
168
  """
159
- from snowflake.cli.app.telemetry import flush_telemetry, log_command_result
169
+ from snowflake.cli._app.telemetry import flush_telemetry, log_command_result
160
170
 
161
171
  log.debug("Executing command post execution callback")
162
172
  log_command_result(execution)
@@ -0,0 +1,18 @@
1
+ from typing import List, Optional
2
+
3
+ from click import ClickException
4
+ from snowflake.cli.api.commands.common import Variable
5
+
6
+
7
+ def parse_key_value_variables(variables: Optional[List[str]]) -> List[Variable]:
8
+ """Util for parsing key=value input. Useful for commands accepting multiple input options."""
9
+ result: List[Variable] = []
10
+ if not variables:
11
+ return result
12
+ for p in variables:
13
+ if "=" not in p:
14
+ raise ClickException(f"Invalid variable: '{p}'")
15
+
16
+ key, value = p.split("=", 1)
17
+ result.append(Variable(key.strip(), value.strip()))
18
+ return result
@@ -30,7 +30,10 @@ from snowflake.cli.api.exceptions import (
30
30
  UnsupportedConfigSectionTypeError,
31
31
  )
32
32
  from snowflake.cli.api.secure_path import SecurePath
33
- from snowflake.cli.api.secure_utils import file_permissions_are_strict
33
+ from snowflake.cli.api.secure_utils import (
34
+ file_permissions_are_strict,
35
+ windows_get_not_whitelisted_users_with_access,
36
+ )
34
37
  from snowflake.cli.api.utils.types import try_cast_to_bool
35
38
  from snowflake.connector.compat import IS_WINDOWS
36
39
  from snowflake.connector.config_manager import CONFIG_MANAGER
@@ -77,7 +80,7 @@ class ConnectionConfig:
77
80
  warehouse: Optional[str] = None
78
81
  role: Optional[str] = None
79
82
  authenticator: Optional[str] = None
80
- private_key_path: Optional[str] = None
83
+ private_key_file: Optional[str] = None
81
84
  token_file_path: Optional[str] = None
82
85
 
83
86
  _other_settings: dict = field(default_factory=lambda: {})
@@ -115,7 +118,7 @@ def config_init(config_file: Optional[Path]):
115
118
  Initializes the app configuration. Config provided via cli flag takes precedence.
116
119
  If config file does not exist we create an empty one.
117
120
  """
118
- from snowflake.cli.app.loggers import create_initial_loggers
121
+ from snowflake.cli._app.loggers import create_initial_loggers
119
122
 
120
123
  if config_file:
121
124
  CONFIG_MANAGER.file_path = config_file
@@ -127,12 +130,21 @@ def config_init(config_file: Optional[Path]):
127
130
  create_initial_loggers()
128
131
 
129
132
 
130
- def add_connection(name: str, connection_config: ConnectionConfig):
131
- set_config_value(
132
- section=CONNECTIONS_SECTION,
133
- key=name,
134
- value=connection_config.to_dict_of_all_non_empty_values(),
135
- )
133
+ def add_connection_to_proper_file(name: str, connection_config: ConnectionConfig):
134
+ if CONNECTIONS_FILE.exists():
135
+ existing_connections = _read_connections_toml()
136
+ existing_connections.update(
137
+ {name: connection_config.to_dict_of_all_non_empty_values()}
138
+ )
139
+ _update_connections_toml(existing_connections)
140
+ return CONNECTIONS_FILE
141
+ else:
142
+ set_config_value(
143
+ section=CONNECTIONS_SECTION,
144
+ key=name,
145
+ value=connection_config.to_dict_of_all_non_empty_values(),
146
+ )
147
+ return CONFIG_MANAGER.file_path
136
148
 
137
149
 
138
150
  _DEFAULT_LOGS_CONFIG = {
@@ -160,6 +172,18 @@ def _read_config_file():
160
172
  message="Bad owner or permissions.*",
161
173
  module="snowflake.connector.config_manager",
162
174
  )
175
+
176
+ if not file_permissions_are_strict(CONFIG_MANAGER.file_path):
177
+ users = ", ".join(
178
+ windows_get_not_whitelisted_users_with_access(
179
+ CONFIG_MANAGER.file_path
180
+ )
181
+ )
182
+ warnings.warn(
183
+ f"Unauthorized users ({users}) have access to configuration file {CONFIG_MANAGER.file_path}.\n"
184
+ f'Run `icacls "{CONFIG_MANAGER.file_path}" /deny <USER_ID>:F` on those users to restrict permissions.'
185
+ )
186
+
163
187
  try:
164
188
  CONFIG_MANAGER.read_config()
165
189
  except ConfigSourceError as exception:
@@ -281,7 +305,7 @@ def _initialise_config(config_file: Path) -> None:
281
305
 
282
306
 
283
307
  def get_env_variable_name(*path, key: str) -> str:
284
- return "SNOWFLAKE_" + "_".join(p.upper() for p in path) + f"_{key.upper()}"
308
+ return ("_".join(["snowflake", *path, key])).upper()
285
309
 
286
310
 
287
311
  def get_env_value(*path, key: str) -> str | None:
@@ -322,8 +346,6 @@ def _dump_config(conf_file_cache: Dict):
322
346
 
323
347
 
324
348
  def _check_default_config_files_permissions() -> None:
325
- if IS_WINDOWS:
326
- return
327
349
  if CONNECTIONS_FILE.exists() and not file_permissions_are_strict(CONNECTIONS_FILE):
328
350
  raise ConfigFileTooWidePermissionsError(CONNECTIONS_FILE)
329
351
  if CONFIG_FILE.exists() and not file_permissions_are_strict(CONFIG_FILE):
@@ -346,3 +368,13 @@ def get_feature_flags_section() -> Dict[str, bool | Literal["UNKNOWN"]]:
346
368
  return "UNKNOWN"
347
369
 
348
370
  return {k: _bool_or_unknown(v) for k, v in flags.items()}
371
+
372
+
373
+ def _read_connections_toml() -> dict:
374
+ with open(CONNECTIONS_FILE, "r") as f:
375
+ return tomlkit.loads(f.read()).unwrap()
376
+
377
+
378
+ def _update_connections_toml(connections: dict):
379
+ with open(CONNECTIONS_FILE, "w") as f:
380
+ f.write(tomlkit.dumps(connections))
@@ -0,0 +1,216 @@
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 asyncio
18
+ import logging
19
+ import re
20
+ import warnings
21
+ from dataclasses import asdict, dataclass, field, fields, replace
22
+ from pathlib import Path
23
+ from typing import Optional
24
+
25
+ from snowflake.cli.api.config import get_default_connection_name
26
+ from snowflake.cli.api.exceptions import InvalidSchemaError
27
+ from snowflake.connector import SnowflakeConnection
28
+ from snowflake.connector.compat import IS_WINDOWS
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+ schema_pattern = re.compile(r".+\..+")
33
+
34
+
35
+ @dataclass
36
+ class ConnectionContext:
37
+ # FIXME: can reduce duplication using config.ConnectionConfig
38
+ connection_name: Optional[str] = None
39
+ host: Optional[str] = None
40
+ port: Optional[int] = None
41
+ account: Optional[str] = None
42
+ database: Optional[str] = None
43
+ role: Optional[str] = None
44
+ schema: Optional[str] = None
45
+ user: Optional[str] = None
46
+ password: Optional[str] = field(default=None, repr=False)
47
+ authenticator: Optional[str] = None
48
+ private_key_file: Optional[str] = None
49
+ warehouse: Optional[str] = None
50
+ mfa_passcode: Optional[str] = None
51
+ enable_diag: Optional[bool] = False
52
+ diag_log_path: Optional[Path] = None
53
+ diag_allowlist_path: Optional[Path] = None
54
+ temporary_connection: bool = False
55
+ session_token: Optional[str] = None
56
+ master_token: Optional[str] = None
57
+ token_file_path: Optional[Path] = None
58
+
59
+ VALIDATED_FIELD_NAMES = ["schema"]
60
+
61
+ def present_values_as_dict(self) -> dict:
62
+ """Dictionary representation of this ConnectionContext for values that are not None"""
63
+ return {k: v for (k, v) in asdict(self).items() if v is not None}
64
+
65
+ def clone(self) -> ConnectionContext:
66
+ return replace(self)
67
+
68
+ def update(self, **updates):
69
+ """
70
+ Given a dictionary of property (key, value) mappings, update properties
71
+ of this context object with equivalent names to the keys.
72
+
73
+ Raises KeyError if a non-property is specified as a key.
74
+ """
75
+ field_map = {field.name: field for field in fields(self)}
76
+ for key, value in updates.items():
77
+ # ensure key represents a property
78
+ if key not in field_map:
79
+ raise KeyError(f"{key} is not a field of {self.__class__.__name__}")
80
+ setattr(self, key, value)
81
+
82
+ def __repr__(self) -> str:
83
+ """Minimal repr where None values have their keys omitted."""
84
+ items = [f"{k}={repr(v)}" for (k, v) in self.present_values_as_dict().items()]
85
+ return f"{self.__class__.__name__}({', '.join(items)})"
86
+
87
+ def __setattr__(self, prop, val):
88
+ """Runs registered validators before setting fields."""
89
+ if prop in self.VALIDATED_FIELD_NAMES:
90
+ validate = getattr(self, f"validate_{prop}")
91
+ validate(val)
92
+ super().__setattr__(prop, val)
93
+
94
+ def validate_schema(self, value: Optional[str]):
95
+ if (
96
+ value
97
+ and not (value.startswith('"') and value.endswith('"'))
98
+ # if schema is fully qualified name (db.schema)
99
+ and schema_pattern.match(value)
100
+ ):
101
+ raise InvalidSchemaError(value)
102
+
103
+ def validate_and_complete(self):
104
+ """
105
+ Ensure we can create a connection from this context.
106
+ """
107
+ if not self.temporary_connection and not self.connection_name:
108
+ self.connection_name = get_default_connection_name()
109
+
110
+ def build_connection(self):
111
+ from snowflake.cli._app.snow_connector import connect_to_snowflake
112
+
113
+ # Ignore warnings about bad owner or permissions on Windows
114
+ # Telemetry omit our warning filter from config.py
115
+ if IS_WINDOWS:
116
+ warnings.filterwarnings(
117
+ action="ignore",
118
+ message="Bad owner or permissions.*",
119
+ module="snowflake.connector.config_manager",
120
+ )
121
+
122
+ return connect_to_snowflake(**self.present_values_as_dict())
123
+
124
+
125
+ class OpenConnectionCache:
126
+ """
127
+ A connection cache that transparently manages SnowflakeConnection objects
128
+ and is keyed by ConnectionContext objects, e.g. cache[ctx].execute_string(...).
129
+ Connections are automatically closed after CONNECTION_CLEANUP_SEC, but
130
+ are guaranteed to be open (if config is valid) when returned by the cache.
131
+ """
132
+
133
+ connections: dict[str, SnowflakeConnection]
134
+ cleanup_futures: dict[str, asyncio.TimerHandle]
135
+
136
+ CONNECTION_CLEANUP_SEC: float = 10.0 * 60
137
+ """Connections are closed this many seconds after the last time they are accessed."""
138
+
139
+ def __init__(self):
140
+ self.connections = {}
141
+ self.cleanup_futures = {}
142
+
143
+ def __getitem__(self, ctx):
144
+ if not isinstance(ctx, ConnectionContext):
145
+ raise ValueError(
146
+ f"Expected key to be ConnectionContext but got {repr(ctx)}"
147
+ )
148
+ key = repr(ctx)
149
+ if not self._has_open_connection(key):
150
+ self._insert(key, ctx)
151
+ self._touch(key)
152
+ return self.connections[key]
153
+
154
+ def clear(self):
155
+ """Closes all connections and resets the cache to its initial state."""
156
+ connection_keys = list(self.connections.keys())
157
+ for key in connection_keys:
158
+ self._cleanup(key)
159
+
160
+ # if any orphaned futures still exist, clean them up too
161
+ for future in self.cleanup_futures.values():
162
+ future.cancel()
163
+ self.cleanup_futures.clear()
164
+
165
+ def _has_open_connection(self, key: str):
166
+ return key in self.connections
167
+
168
+ def _insert(self, key: str, ctx: ConnectionContext):
169
+ try:
170
+ # N.B. build_connection ultimately calls connect_to_snowflake, which
171
+ # interpolates in connection dicts (from config) and environment variables.
172
+ # This means that we could return a stale (incorrect) connection for the
173
+ # given ConnectionContext if get_env_value or get_connection_dict would
174
+ # have returned different values (i.e. env / config have changed).
175
+ self.connections[key] = ctx.build_connection()
176
+ except Exception:
177
+ logger.debug(
178
+ "ConnectionCache: failed to connect using %s; not caching.", key
179
+ )
180
+ raise
181
+
182
+ def _cancel_cleanup_future_if_exists(self, key: str):
183
+ if key in self.cleanup_futures:
184
+ self.cleanup_futures.pop(key).cancel()
185
+
186
+ def _touch(self, key: str):
187
+ """
188
+ Extend the lifetime of the cached connection at the given key.
189
+ """
190
+ loop = None
191
+ try:
192
+ loop = asyncio.get_event_loop()
193
+ except RuntimeError:
194
+ # Python 3.11+ will throw when no event loop;
195
+ # Python 3.10 will issue a DeprecationWarning and return None
196
+ pass
197
+
198
+ if not loop:
199
+ logger.debug(
200
+ "ConnectionCache: no event loop; connections will close at exit."
201
+ )
202
+ return
203
+
204
+ self._cancel_cleanup_future_if_exists(key)
205
+ self.cleanup_futures[key] = loop.call_later(
206
+ self.CONNECTION_CLEANUP_SEC, lambda: self._cleanup(key)
207
+ )
208
+
209
+ def _cleanup(self, key: str):
210
+ """Closes the cached connection at the given key."""
211
+ if key not in self.connections:
212
+ logger.debug("Cleaning up connection %s, but not found in cache!", key)
213
+
214
+ # doesn't cancel in-flight async queries
215
+ self._cancel_cleanup_future_if_exists(key)
216
+ self.connections.pop(key).close()
@@ -20,7 +20,10 @@ from typing import Callable, Iterator, Optional
20
20
 
21
21
  from rich import print as rich_print
22
22
  from rich.text import Text
23
- from snowflake.cli.api.cli_global_context import _CliGlobalContextAccess, cli_context
23
+ from snowflake.cli.api.cli_global_context import (
24
+ _CliGlobalContextAccess,
25
+ get_cli_context,
26
+ )
24
27
 
25
28
 
26
29
  class AbstractConsole(ABC):
@@ -34,14 +37,16 @@ class AbstractConsole(ABC):
34
37
  """
35
38
 
36
39
  _print_fn: Callable[[str], None]
37
- _cli_context: _CliGlobalContextAccess
38
40
  _in_phase: bool
39
41
 
40
42
  def __init__(self):
41
43
  super().__init__()
42
- self._cli_context = cli_context
43
44
  self._in_phase = False
44
45
 
46
+ @property
47
+ def _cli_context(self) -> _CliGlobalContextAccess:
48
+ return get_cli_context()
49
+
45
50
  @property
46
51
  def is_silent(self) -> bool:
47
52
  """Returns information whether intermediate output is muted."""
@@ -77,3 +77,14 @@ VALID_SCOPES = ["database", "schema", "compute-pool"]
77
77
  DEFAULT_SIZE_LIMIT_MB = 128
78
78
 
79
79
  SF_REST_API_URL_PREFIX = "/api/v2"
80
+
81
+ PROJECT_TEMPLATE_VARIABLE_OPENING = "<%"
82
+ PROJECT_TEMPLATE_VARIABLE_CLOSING = "%>"
83
+
84
+ INIT_TEMPLATE_VARIABLE_OPENING = "<!"
85
+ INIT_TEMPLATE_VARIABLE_CLOSING = "!>"
86
+
87
+ SNOWPARK_SHARED_MIXIN = "snowpark_shared"
88
+
89
+ DEFAULT_ENV_FILE = "environment.yml"
90
+ DEFAULT_PAGES_DIR = "pages"