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
@@ -21,12 +21,15 @@ from typing import Any, Dict, Union
21
21
 
22
22
  import click
23
23
  from snowflake.cli.__about__ import VERSION
24
- from snowflake.cli.api.cli_global_context import cli_context
24
+ from snowflake.cli._app.constants import PARAM_APPLICATION_NAME
25
+ from snowflake.cli.api.cli_global_context import (
26
+ _CliGlobalContextAccess,
27
+ get_cli_context,
28
+ )
25
29
  from snowflake.cli.api.commands.execution_metadata import ExecutionMetadata
26
30
  from snowflake.cli.api.config import get_feature_flags_section
27
31
  from snowflake.cli.api.output.formats import OutputFormat
28
32
  from snowflake.cli.api.utils.error_handling import ignore_exceptions
29
- from snowflake.cli.app.constants import PARAM_APPLICATION_NAME
30
33
  from snowflake.connector.telemetry import (
31
34
  TelemetryData,
32
35
  TelemetryField,
@@ -34,6 +37,12 @@ from snowflake.connector.telemetry import (
34
37
  from snowflake.connector.time_util import get_time_millis
35
38
 
36
39
 
40
+ @unique
41
+ class CLIInstallationSource(Enum):
42
+ BINARY = "binary"
43
+ PYPI = "pypi"
44
+
45
+
37
46
  @unique
38
47
  class CLITelemetryField(Enum):
39
48
  # Basic information
@@ -41,6 +50,7 @@ class CLITelemetryField(Enum):
41
50
  VERSION_CLI = "version_cli"
42
51
  VERSION_PYTHON = "version_python"
43
52
  VERSION_OS = "version_os"
53
+ INSTALLATION_SOURCE = "installation_source"
44
54
  # Command execution context
45
55
  COMMAND = "command"
46
56
  COMMAND_GROUP = "command_group"
@@ -51,6 +61,8 @@ class CLITelemetryField(Enum):
51
61
  COMMAND_EXECUTION_TIME = "command_execution_time"
52
62
  # Configuration
53
63
  CONFIG_FEATURE_FLAGS = "config_feature_flags"
64
+ # Metrics
65
+ COUNTERS = "counters"
54
66
  # Information
55
67
  EVENT = "event"
56
68
  ERROR_MSG = "error_msg"
@@ -69,6 +81,16 @@ class TelemetryEvent(Enum):
69
81
  TelemetryDict = Dict[Union[CLITelemetryField, TelemetryField], Any]
70
82
 
71
83
 
84
+ def _get_command_metrics() -> TelemetryDict:
85
+ cli_context = get_cli_context()
86
+
87
+ return {
88
+ CLITelemetryField.COUNTERS: {
89
+ **cli_context.metrics.counters,
90
+ }
91
+ }
92
+
93
+
72
94
  def _find_command_info() -> TelemetryDict:
73
95
  ctx = click.get_current_context()
74
96
  command_path = ctx.command_path.split(" ")[1:]
@@ -88,13 +110,18 @@ def _find_command_info() -> TelemetryDict:
88
110
 
89
111
 
90
112
  def _get_definition_version() -> str | None:
91
- from snowflake.cli.api.cli_global_context import cli_context
92
-
113
+ cli_context = get_cli_context()
93
114
  if cli_context.project_definition:
94
115
  return cli_context.project_definition.definition_version
95
116
  return None
96
117
 
97
118
 
119
+ def _get_installation_source() -> CLIInstallationSource:
120
+ if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
121
+ return CLIInstallationSource.BINARY
122
+ return CLIInstallationSource.PYPI
123
+
124
+
98
125
  def command_info() -> str:
99
126
  info = _find_command_info()
100
127
  command = ".".join(info[CLITelemetryField.COMMAND])
@@ -107,8 +134,9 @@ def python_version() -> str:
107
134
 
108
135
 
109
136
  class CLITelemetryClient:
110
- def __init__(self, ctx):
111
- self._ctx = ctx
137
+ @property
138
+ def _ctx(self) -> _CliGlobalContextAccess:
139
+ return get_cli_context()
112
140
 
113
141
  @staticmethod
114
142
  def generate_telemetry_data_dict(
@@ -116,6 +144,7 @@ class CLITelemetryClient:
116
144
  ) -> Dict[str, Any]:
117
145
  data = {
118
146
  CLITelemetryField.SOURCE: PARAM_APPLICATION_NAME,
147
+ CLITelemetryField.INSTALLATION_SOURCE: _get_installation_source().value,
119
148
  CLITelemetryField.VERSION_CLI: VERSION,
120
149
  CLITelemetryField.VERSION_OS: platform.platform(),
121
150
  CLITelemetryField.VERSION_PYTHON: python_version(),
@@ -144,7 +173,7 @@ class CLITelemetryClient:
144
173
  self._telemetry.send_batch()
145
174
 
146
175
 
147
- _telemetry = CLITelemetryClient(ctx=cli_context)
176
+ _telemetry = CLITelemetryClient()
148
177
 
149
178
 
150
179
  @ignore_exceptions()
@@ -165,6 +194,7 @@ def log_command_result(execution: ExecutionMetadata):
165
194
  CLITelemetryField.COMMAND_EXECUTION_ID: execution.execution_id,
166
195
  CLITelemetryField.COMMAND_RESULT_STATUS: execution.status.value,
167
196
  CLITelemetryField.COMMAND_EXECUTION_TIME: execution.get_duration(),
197
+ **_get_command_metrics(),
168
198
  }
169
199
  )
170
200
 
@@ -180,6 +210,7 @@ def log_command_execution_error(exception: Exception, execution: ExecutionMetada
180
210
  CLITelemetryField.ERROR_TYPE: exception_type,
181
211
  CLITelemetryField.IS_CLI_EXCEPTION: is_cli_exception,
182
212
  CLITelemetryField.COMMAND_EXECUTION_TIME: execution.get_duration(),
213
+ **_get_command_metrics(),
183
214
  }
184
215
  )
185
216
 
@@ -0,0 +1,74 @@
1
+ import json
2
+ import time
3
+
4
+ import requests
5
+ from packaging.version import Version
6
+ from snowflake.cli.__about__ import VERSION
7
+ from snowflake.cli.api.console import cli_console
8
+ from snowflake.cli.api.secure_path import SecurePath
9
+ from snowflake.connector.config_manager import CONFIG_MANAGER
10
+
11
+
12
+ def get_new_version_msg() -> str | None:
13
+ last = _VersionCache().get_last_version()
14
+ current = Version(VERSION)
15
+ if last and last > current:
16
+ return f"\nNew version of Snowflake CLI available. Newest: {last}, current: {VERSION}\n"
17
+ return None
18
+
19
+
20
+ def show_new_version_banner_callback(msg):
21
+ def _callback(*args, **kwargs):
22
+ if msg:
23
+ cli_console.message(msg)
24
+
25
+ return _callback
26
+
27
+
28
+ class _VersionCache:
29
+ _last_time = "last_time_check"
30
+ _version = "version"
31
+ _version_cache_file = SecurePath(
32
+ CONFIG_MANAGER.file_path.parent / ".cli_version.cache"
33
+ )
34
+
35
+ def __init__(self):
36
+ self._cache_file = _VersionCache._version_cache_file
37
+
38
+ def _save_latest_version(self, version: str):
39
+ data = {
40
+ _VersionCache._last_time: time.time(),
41
+ _VersionCache._version: str(version),
42
+ }
43
+ self._cache_file.write_text(json.dumps(data))
44
+
45
+ @staticmethod
46
+ def _get_version_from_pypi() -> str | None:
47
+ headers = {"Content-Type": "application/vnd.pypi.simple.v1+json"}
48
+ response = requests.get(
49
+ "https://pypi.org/pypi/snowflake-cli-labs/json", headers=headers, timeout=3
50
+ )
51
+ response.raise_for_status()
52
+ return response.json()["info"]["version"]
53
+
54
+ def _update_latest_version(self) -> Version | None:
55
+ version = self._get_version_from_pypi()
56
+ if version is None:
57
+ return None
58
+ self._save_latest_version(version)
59
+ return Version(version)
60
+
61
+ def _read_latest_version(self) -> Version | None:
62
+ if self._cache_file.exists():
63
+ data = json.loads(self._cache_file.read_text())
64
+ now = time.time()
65
+ if data[_VersionCache._last_time] > now - 60 * 60:
66
+ return Version(data[_VersionCache._version])
67
+
68
+ return self._update_latest_version()
69
+
70
+ def get_last_version(self) -> Version | None:
71
+ try:
72
+ return self._read_latest_version()
73
+ except: # anything, this it not crucial feature
74
+ return None
@@ -21,14 +21,19 @@ import typer
21
21
  from click import ClickException, Context, Parameter # type: ignore
22
22
  from click.core import ParameterSource # type: ignore
23
23
  from click.types import StringParamType
24
- from snowflake.cli.api.cli_global_context import cli_context
24
+ from snowflake.cli._plugins.connection.util import (
25
+ strip_and_check_if_exists,
26
+ strip_if_value_present,
27
+ )
28
+ from snowflake.cli._plugins.object.manager import ObjectManager
29
+ from snowflake.cli.api.cli_global_context import get_cli_context
25
30
  from snowflake.cli.api.commands.flags import (
26
31
  PLAIN_PASSWORD_MSG,
27
32
  )
28
33
  from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
29
34
  from snowflake.cli.api.config import (
30
35
  ConnectionConfig,
31
- add_connection,
36
+ add_connection_to_proper_file,
32
37
  connection_exists,
33
38
  get_all_connections,
34
39
  get_connection_dict,
@@ -43,9 +48,8 @@ from snowflake.cli.api.output.types import (
43
48
  MessageResult,
44
49
  ObjectResult,
45
50
  )
46
- from snowflake.cli.plugins.object.manager import ObjectManager
47
51
  from snowflake.connector import ProgrammingError
48
- from snowflake.connector.config_manager import CONFIG_MANAGER
52
+ from snowflake.connector.constants import CONNECTIONS_FILE
49
53
 
50
54
  app = SnowTyperFactory(
51
55
  name="connection",
@@ -87,6 +91,11 @@ def list_connections(**options) -> CommandResult:
87
91
  }
88
92
  for connection_name, connection_config in connections.items()
89
93
  )
94
+
95
+ if CONNECTIONS_FILE.exists():
96
+ cli_console.warning(
97
+ f"Reading connections from {CONNECTIONS_FILE}. Entries from config.toml are ignored."
98
+ )
90
99
  return CollectionResult(result)
91
100
 
92
101
 
@@ -94,8 +103,8 @@ def require_integer(field_name: str):
94
103
  def callback(value: str):
95
104
  if value is None:
96
105
  return None
97
- if value.isdigit():
98
- return value
106
+ if value.strip().isdigit():
107
+ return value.strip()
99
108
  raise ClickException(f"Value of {field_name} must be integer")
100
109
 
101
110
  return callback
@@ -117,6 +126,7 @@ def add(
117
126
  prompt="Name for this connection",
118
127
  help="Name of the new connection.",
119
128
  show_default=False,
129
+ callback=strip_if_value_present,
120
130
  ),
121
131
  account: str = typer.Option(
122
132
  None,
@@ -126,6 +136,7 @@ def add(
126
136
  prompt="Snowflake account name",
127
137
  help="Account name to use when authenticating with Snowflake.",
128
138
  show_default=False,
139
+ callback=strip_if_value_present,
129
140
  ),
130
141
  user: str = typer.Option(
131
142
  None,
@@ -135,6 +146,7 @@ def add(
135
146
  prompt="Snowflake username",
136
147
  show_default=False,
137
148
  help="Username to connect to Snowflake.",
149
+ callback=strip_if_value_present,
138
150
  ),
139
151
  password: str = typer.Option(
140
152
  EmptyInput(),
@@ -153,6 +165,7 @@ def add(
153
165
  click_type=OptionalPrompt(),
154
166
  prompt="Role for the connection",
155
167
  help="Role to use on Snowflake.",
168
+ callback=strip_if_value_present,
156
169
  ),
157
170
  warehouse: str = typer.Option(
158
171
  EmptyInput(),
@@ -161,6 +174,7 @@ def add(
161
174
  click_type=OptionalPrompt(),
162
175
  prompt="Warehouse for the connection",
163
176
  help="Warehouse to use on Snowflake.",
177
+ callback=strip_if_value_present,
164
178
  ),
165
179
  database: str = typer.Option(
166
180
  EmptyInput(),
@@ -169,6 +183,7 @@ def add(
169
183
  click_type=OptionalPrompt(),
170
184
  prompt="Database for the connection",
171
185
  help="Database to use on Snowflake.",
186
+ callback=strip_if_value_present,
172
187
  ),
173
188
  schema: str = typer.Option(
174
189
  EmptyInput(),
@@ -177,6 +192,7 @@ def add(
177
192
  click_type=OptionalPrompt(),
178
193
  prompt="Schema for the connection",
179
194
  help="Schema to use on Snowflake.",
195
+ callback=strip_if_value_present,
180
196
  ),
181
197
  host: str = typer.Option(
182
198
  EmptyInput(),
@@ -185,6 +201,7 @@ def add(
185
201
  click_type=OptionalPrompt(),
186
202
  prompt="Connection host",
187
203
  help="Host name the connection attempts to connect to Snowflake.",
204
+ callback=strip_if_value_present,
188
205
  ),
189
206
  port: int = typer.Option(
190
207
  EmptyInput(),
@@ -202,6 +219,7 @@ def add(
202
219
  click_type=OptionalPrompt(),
203
220
  prompt="Snowflake region",
204
221
  help="Region name if not the default Snowflake deployment.",
222
+ callback=strip_if_value_present,
205
223
  ),
206
224
  authenticator: str = typer.Option(
207
225
  EmptyInput(),
@@ -211,13 +229,15 @@ def add(
211
229
  prompt="Authentication method",
212
230
  help="Chosen authenticator, if other than password-based",
213
231
  ),
214
- private_key_path: str = typer.Option(
232
+ private_key_file: str = typer.Option(
215
233
  EmptyInput(),
216
234
  "--private-key",
235
+ "--private-key-path",
217
236
  "-k",
218
237
  click_type=OptionalPrompt(),
219
238
  prompt="Path to private key file",
220
239
  help="Path to file containing private key",
240
+ callback=strip_and_check_if_exists,
221
241
  ),
222
242
  token_file_path: str = typer.Option(
223
243
  EmptyInput(),
@@ -226,6 +246,7 @@ def add(
226
246
  click_type=OptionalPrompt(),
227
247
  prompt="Path to token file",
228
248
  help="Path to file with an OAuth token that should be used when connecting to Snowflake",
249
+ callback=strip_and_check_if_exists,
229
250
  ),
230
251
  set_as_default: bool = typer.Option(
231
252
  False,
@@ -239,7 +260,7 @@ def add(
239
260
  if connection_exists(connection_name):
240
261
  raise ClickException(f"Connection {connection_name} already exists")
241
262
 
242
- add_connection(
263
+ connections_file = add_connection_to_proper_file(
243
264
  connection_name,
244
265
  ConnectionConfig(
245
266
  account=account,
@@ -253,7 +274,7 @@ def add(
253
274
  warehouse=warehouse,
254
275
  role=role,
255
276
  authenticator=authenticator,
256
- private_key_path=private_key_path,
277
+ private_key_file=private_key_file,
257
278
  token_file_path=token_file_path,
258
279
  ),
259
280
  )
@@ -263,7 +284,7 @@ def add(
263
284
  )
264
285
 
265
286
  return MessageResult(
266
- f"Wrote new connection {connection_name} to {CONFIG_MANAGER.file_path}"
287
+ f"Wrote new connection {connection_name} to {connections_file}"
267
288
  )
268
289
 
269
290
 
@@ -276,6 +297,7 @@ def test(
276
297
  """
277
298
 
278
299
  # Test connection
300
+ cli_context = get_cli_context()
279
301
  conn = cli_context.connection
280
302
 
281
303
  # Test session attributes
@@ -320,7 +342,8 @@ def test(
320
342
  @app.command(requires_connection=False)
321
343
  def set_default(
322
344
  name: str = typer.Argument(
323
- help="Name of the connection, as defined in your `config.toml`"
345
+ help="Name of the connection, as defined in your `config.toml`",
346
+ show_default=False,
324
347
  ),
325
348
  **options,
326
349
  ):
@@ -0,0 +1,30 @@
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 snowflake.cli._plugins.connection import commands
16
+ from snowflake.cli.api.plugins.command import (
17
+ SNOWCLI_ROOT_COMMAND_PATH,
18
+ CommandSpec,
19
+ CommandType,
20
+ plugin_hook_impl,
21
+ )
22
+
23
+
24
+ @plugin_hook_impl
25
+ def command_spec():
26
+ return CommandSpec(
27
+ parent_command_path=SNOWCLI_ROOT_COMMAND_PATH,
28
+ command_type=CommandType.COMMAND_GROUP,
29
+ typer_instance=commands.app.create_instance(),
30
+ )
@@ -16,6 +16,8 @@ from __future__ import annotations
16
16
 
17
17
  import json
18
18
  import logging
19
+ import os
20
+ from typing import Optional
19
21
 
20
22
  from click.exceptions import ClickException
21
23
  from snowflake.connector import SnowflakeConnection
@@ -177,3 +179,17 @@ def make_snowsight_url(conn: SnowflakeConnection, path: str) -> str:
177
179
  account = get_account(conn)
178
180
  path_with_slash = path if path.startswith("/") else f"/{path}"
179
181
  return f"{snowsight_host}/{deployment}/{account}{path_with_slash}"
182
+
183
+
184
+ def strip_if_value_present(value: Optional[str]) -> Optional[str]:
185
+ return value.strip() if value else value
186
+
187
+
188
+ def ensure_that_path_exist(path: Optional[str]) -> Optional[str]:
189
+ if path and not os.path.exists(path):
190
+ raise ClickException(f"Path {path} does not exist.")
191
+ return path
192
+
193
+
194
+ def strip_and_check_if_exists(value: Optional[str]) -> Optional[str]:
195
+ return ensure_that_path_exist(strip_if_value_present(value))
@@ -21,8 +21,20 @@ from typing import List, Optional
21
21
  import click
22
22
  import typer
23
23
  from click import UsageError
24
- from snowflake.cli.api.cli_global_context import cli_context
25
- from snowflake.cli.api.commands.flags import readable_file_option
24
+ from snowflake.cli._plugins.cortex.constants import DEFAULT_MODEL
25
+ from snowflake.cli._plugins.cortex.manager import CortexManager
26
+ from snowflake.cli._plugins.cortex.types import (
27
+ Language,
28
+ Model,
29
+ Question,
30
+ SourceDocument,
31
+ Text,
32
+ )
33
+ from snowflake.cli.api.cli_global_context import get_cli_context
34
+ from snowflake.cli.api.commands.overrideable_parameter import (
35
+ OverrideableArgument,
36
+ OverrideableOption,
37
+ )
26
38
  from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
27
39
  from snowflake.cli.api.constants import PYTHON_3_12
28
40
  from snowflake.cli.api.output.types import (
@@ -31,15 +43,6 @@ from snowflake.cli.api.output.types import (
31
43
  MessageResult,
32
44
  )
33
45
  from snowflake.cli.api.secure_path import SecurePath
34
- from snowflake.cli.plugins.cortex.constants import DEFAULT_MODEL
35
- from snowflake.cli.plugins.cortex.manager import CortexManager
36
- from snowflake.cli.plugins.cortex.types import (
37
- Language,
38
- Model,
39
- Question,
40
- SourceDocument,
41
- Text,
42
- )
43
46
 
44
47
  app = SnowTyperFactory(
45
48
  name="cortex",
@@ -48,6 +51,25 @@ app = SnowTyperFactory(
48
51
 
49
52
  SEARCH_COMMAND_ENABLED = sys.version_info < PYTHON_3_12
50
53
 
54
+ SOURCE_EXCLUSIVE_OPTION_NAMES = ["text", "file", "source_document_text"]
55
+
56
+ # Creates a Typer option and verifies if the mutually exclusive options are set in the command.
57
+ ExclusiveReadableFileOption = OverrideableOption(
58
+ None,
59
+ "--file",
60
+ mutually_exclusive=SOURCE_EXCLUSIVE_OPTION_NAMES,
61
+ exists=True,
62
+ file_okay=True,
63
+ dir_okay=False,
64
+ readable=True,
65
+ show_default=False,
66
+ )
67
+
68
+ # Creates a Typer argument and verifies if the mutually exclusive options are set in the command.
69
+ ExclusiveTextSourceArgument = OverrideableArgument(
70
+ mutually_exclusive=SOURCE_EXCLUSIVE_OPTION_NAMES,
71
+ )
72
+
51
73
 
52
74
  @app.command(
53
75
  requires_connection=True,
@@ -79,7 +101,7 @@ def search(
79
101
  if not columns:
80
102
  columns = []
81
103
 
82
- conn = cli_context.connection
104
+ conn = get_cli_context().connection
83
105
 
84
106
  search_service = (
85
107
  Root(conn)
@@ -100,8 +122,8 @@ def search(
100
122
  requires_connection=True,
101
123
  )
102
124
  def complete(
103
- text: Optional[str] = typer.Argument(
104
- None,
125
+ text: Optional[str] = ExclusiveTextSourceArgument(
126
+ default=None,
105
127
  help="Prompt to be used to generate a completion. Cannot be combined with --file option.",
106
128
  show_default=False,
107
129
  ),
@@ -110,9 +132,8 @@ def complete(
110
132
  "--model",
111
133
  help="String specifying the model to be used.",
112
134
  ),
113
- file: Optional[Path] = readable_file_option(
114
- param_name="--file",
115
- help_str="JSON file containing conversation history to be used to generate a completion. Cannot be combined with TEXT argument.",
135
+ file: Optional[Path] = ExclusiveReadableFileOption(
136
+ help="JSON file containing conversation history to be used to generate a completion. Cannot be combined with TEXT argument.",
116
137
  ),
117
138
  **options,
118
139
  ) -> CommandResult:
@@ -124,8 +145,6 @@ def complete(
124
145
 
125
146
  manager = CortexManager()
126
147
 
127
- if text and file:
128
- raise UsageError("--file option cannot be used together with TEXT argument.")
129
148
  if text:
130
149
  result_text = manager.complete_for_prompt(
131
150
  text=Text(text),
@@ -152,14 +171,13 @@ def extract_answer(
152
171
  help="String containing the question to be answered.",
153
172
  show_default=False,
154
173
  ),
155
- source_document_text: Optional[str] = typer.Argument(
156
- None,
174
+ source_document_text: Optional[str] = ExclusiveTextSourceArgument(
175
+ default=None,
157
176
  help="String containing the plain-text or JSON document that contains the answer to the question. Cannot be combined with --file option.",
158
177
  show_default=False,
159
178
  ),
160
- file: Optional[Path] = readable_file_option(
161
- param_name="--file",
162
- help_str="File containing the plain-text or JSON document that contains the answer to the question. Cannot be combined with SOURCE_DOCUMENT_TEXT argument.",
179
+ file: Optional[Path] = ExclusiveReadableFileOption(
180
+ help="File containing the plain-text or JSON document that contains the answer to the question. Cannot be combined with SOURCE_DOCUMENT_TEXT argument.",
163
181
  ),
164
182
  **options,
165
183
  ) -> CommandResult:
@@ -170,10 +188,6 @@ def extract_answer(
170
188
 
171
189
  manager = CortexManager()
172
190
 
173
- if source_document_text and file:
174
- raise UsageError(
175
- "--file option cannot be used together with SOURCE_DOCUMENT_TEXT argument."
176
- )
177
191
  if source_document_text:
178
192
  result_text = manager.extract_answer_from_source_document(
179
193
  source_document=SourceDocument(source_document_text),
@@ -197,14 +211,13 @@ def extract_answer(
197
211
  requires_connection=True,
198
212
  )
199
213
  def sentiment(
200
- text: Optional[str] = typer.Argument(
201
- None,
214
+ text: Optional[str] = ExclusiveTextSourceArgument(
215
+ default=None,
202
216
  help="String containing the text for which a sentiment score should be calculated. Cannot be combined with --file option.",
203
217
  show_default=False,
204
218
  ),
205
- file: Optional[Path] = readable_file_option(
206
- param_name="--file",
207
- help_str="File containing the text for which a sentiment score should be calculated. Cannot be combined with TEXT argument.",
219
+ file: Optional[Path] = ExclusiveReadableFileOption(
220
+ help="File containing the text for which a sentiment score should be calculated. Cannot be combined with TEXT argument.",
208
221
  ),
209
222
  **options,
210
223
  ) -> CommandResult:
@@ -216,8 +229,6 @@ def sentiment(
216
229
 
217
230
  manager = CortexManager()
218
231
 
219
- if text and file:
220
- raise UsageError("--file option cannot be used together with TEXT argument.")
221
232
  if text:
222
233
  result_text = manager.calculate_sentiment_for_text(
223
234
  text=Text(text),
@@ -237,14 +248,13 @@ def sentiment(
237
248
  requires_connection=True,
238
249
  )
239
250
  def summarize(
240
- text: Optional[str] = typer.Argument(
241
- None,
251
+ text: Optional[str] = ExclusiveTextSourceArgument(
252
+ default=None,
242
253
  help="String containing the English text from which a summary should be generated. Cannot be combined with --file option.",
243
254
  show_default=False,
244
255
  ),
245
- file: Optional[Path] = readable_file_option(
246
- param_name="--file",
247
- help_str="File containing the English text from which a summary should be generated. Cannot be combined with TEXT argument.",
256
+ file: Optional[Path] = ExclusiveReadableFileOption(
257
+ help="File containing the English text from which a summary should be generated. Cannot be combined with TEXT argument.",
248
258
  ),
249
259
  **options,
250
260
  ) -> CommandResult:
@@ -254,8 +264,6 @@ def summarize(
254
264
 
255
265
  manager = CortexManager()
256
266
 
257
- if text and file:
258
- raise UsageError("--file option cannot be used together with TEXT argument.")
259
267
  if text:
260
268
  result_text = manager.summarize_text(
261
269
  text=Text(text),
@@ -275,8 +283,8 @@ def summarize(
275
283
  requires_connection=True,
276
284
  )
277
285
  def translate(
278
- text: Optional[str] = typer.Argument(
279
- None,
286
+ text: Optional[str] = ExclusiveTextSourceArgument(
287
+ default=None,
280
288
  help="String containing the text to be translated. Cannot be combined with --file option.",
281
289
  show_default=False,
282
290
  ),
@@ -292,9 +300,8 @@ def translate(
292
300
  help="String specifying the language code into which the text should be translated. See Snowflake Cortex documentation for a list of supported language codes.",
293
301
  show_default=False,
294
302
  ),
295
- file: Optional[Path] = readable_file_option(
296
- param_name="--file",
297
- help_str="File containing the text to be translated. Cannot be combined with TEXT argument.",
303
+ file: Optional[Path] = ExclusiveReadableFileOption(
304
+ help="File containing the text to be translated. Cannot be combined with TEXT argument.",
298
305
  ),
299
306
  **options,
300
307
  ) -> CommandResult:
@@ -307,8 +314,6 @@ def translate(
307
314
  source_language = None if from_language is None else Language(from_language)
308
315
  target_language = Language(to_language)
309
316
 
310
- if text and file:
311
- raise UsageError("--file option cannot be used together with TEXT argument.")
312
317
  if text:
313
318
  result_text = manager.translate_text(
314
319
  text=Text(text),
@@ -12,6 +12,6 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from snowflake.cli.plugins.cortex.types import Model
15
+ from snowflake.cli._plugins.cortex.types import Model
16
16
 
17
17
  DEFAULT_MODEL: Model = Model("snowflake-arctic")