snowflake-cli 3.11.0__py3-none-any.whl → 3.13.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 (56) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/cli_app.py +43 -1
  3. snowflake/cli/_app/commands_registration/builtin_plugins.py +1 -1
  4. snowflake/cli/_app/commands_registration/command_plugins_loader.py +14 -1
  5. snowflake/cli/_app/printing.py +153 -19
  6. snowflake/cli/_app/telemetry.py +25 -10
  7. snowflake/cli/_plugins/auth/__init__.py +0 -2
  8. snowflake/cli/_plugins/connection/commands.py +1 -78
  9. snowflake/cli/_plugins/dbt/commands.py +44 -19
  10. snowflake/cli/_plugins/dbt/constants.py +1 -1
  11. snowflake/cli/_plugins/dbt/manager.py +252 -47
  12. snowflake/cli/_plugins/dcm/commands.py +65 -90
  13. snowflake/cli/_plugins/dcm/manager.py +137 -50
  14. snowflake/cli/_plugins/logs/commands.py +7 -0
  15. snowflake/cli/_plugins/logs/manager.py +21 -1
  16. snowflake/cli/_plugins/nativeapp/entities/application_package.py +4 -1
  17. snowflake/cli/_plugins/nativeapp/sf_sql_facade.py +3 -1
  18. snowflake/cli/_plugins/object/manager.py +1 -0
  19. snowflake/cli/_plugins/snowpark/common.py +1 -0
  20. snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +29 -5
  21. snowflake/cli/_plugins/snowpark/package_utils.py +44 -3
  22. snowflake/cli/_plugins/spcs/services/commands.py +19 -1
  23. snowflake/cli/_plugins/spcs/services/manager.py +17 -4
  24. snowflake/cli/_plugins/spcs/services/service_entity_model.py +5 -0
  25. snowflake/cli/_plugins/sql/lexer/types.py +1 -0
  26. snowflake/cli/_plugins/sql/repl.py +100 -26
  27. snowflake/cli/_plugins/sql/repl_commands.py +607 -0
  28. snowflake/cli/_plugins/sql/statement_reader.py +44 -20
  29. snowflake/cli/_plugins/streamlit/streamlit_entity.py +28 -2
  30. snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +24 -4
  31. snowflake/cli/api/artifacts/bundle_map.py +32 -2
  32. snowflake/cli/api/artifacts/regex_resolver.py +54 -0
  33. snowflake/cli/api/artifacts/upload.py +5 -1
  34. snowflake/cli/api/artifacts/utils.py +12 -1
  35. snowflake/cli/api/cli_global_context.py +7 -0
  36. snowflake/cli/api/commands/decorators.py +7 -0
  37. snowflake/cli/api/commands/flags.py +24 -1
  38. snowflake/cli/api/console/abc.py +13 -2
  39. snowflake/cli/api/console/console.py +20 -0
  40. snowflake/cli/api/constants.py +9 -0
  41. snowflake/cli/api/entities/utils.py +10 -6
  42. snowflake/cli/api/feature_flags.py +3 -2
  43. snowflake/cli/api/identifiers.py +18 -1
  44. snowflake/cli/api/project/schemas/entities/entities.py +0 -6
  45. snowflake/cli/api/rendering/sql_templates.py +2 -0
  46. {snowflake_cli-3.11.0.dist-info → snowflake_cli-3.13.0.dist-info}/METADATA +7 -7
  47. {snowflake_cli-3.11.0.dist-info → snowflake_cli-3.13.0.dist-info}/RECORD +51 -54
  48. snowflake/cli/_plugins/auth/keypair/__init__.py +0 -0
  49. snowflake/cli/_plugins/auth/keypair/commands.py +0 -153
  50. snowflake/cli/_plugins/auth/keypair/manager.py +0 -331
  51. snowflake/cli/_plugins/dcm/dcm_project_entity_model.py +0 -59
  52. snowflake/cli/_plugins/sql/snowsql_commands.py +0 -331
  53. /snowflake/cli/_plugins/auth/{keypair/plugin_spec.py → plugin_spec.py} +0 -0
  54. {snowflake_cli-3.11.0.dist-info → snowflake_cli-3.13.0.dist-info}/WHEEL +0 -0
  55. {snowflake_cli-3.11.0.dist-info → snowflake_cli-3.13.0.dist-info}/entry_points.txt +0 -0
  56. {snowflake_cli-3.11.0.dist-info → snowflake_cli-3.13.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,3 +1,4 @@
1
+ from contextlib import contextmanager
1
2
  from logging import getLogger
2
3
  from typing import Iterable
3
4
 
@@ -10,6 +11,7 @@ from prompt_toolkit.lexers import PygmentsLexer
10
11
  from snowflake.cli._app.printing import print_result
11
12
  from snowflake.cli._plugins.sql.lexer import CliLexer, cli_completer
12
13
  from snowflake.cli._plugins.sql.manager import SqlManager
14
+ from snowflake.cli._plugins.sql.repl_commands import detect_command
13
15
  from snowflake.cli.api.cli_global_context import get_cli_context_manager
14
16
  from snowflake.cli.api.console import cli_console
15
17
  from snowflake.cli.api.output.types import MultipleResults, QueryResult
@@ -28,6 +30,21 @@ EXIT_KEYWORDS = ("exit", "quit")
28
30
  log.debug("setting history file to: %s", HISTORY_FILE.as_posix())
29
31
 
30
32
 
33
+ @contextmanager
34
+ def repl_context(repl_instance):
35
+ """Context manager for REPL execution that handles CLI context registration."""
36
+ context_manager = get_cli_context_manager()
37
+ context_manager.is_repl = True
38
+ context_manager.repl_instance = repl_instance
39
+
40
+ try:
41
+ yield
42
+ finally:
43
+ # Clean up REPL context
44
+ context_manager.is_repl = False
45
+ context_manager.repl_instance = None
46
+
47
+
31
48
  class Repl:
32
49
  """Basic REPL implementation for the Snowflake CLI."""
33
50
 
@@ -45,7 +62,6 @@ class Repl:
45
62
  `retain_comments` how to handle comments in queries
46
63
  """
47
64
  super().__init__()
48
- setattr(get_cli_context_manager(), "is_repl", True)
49
65
  self._data = data or {}
50
66
  self._retain_comments = retain_comments
51
67
  self._template_syntax_config = template_syntax_config
@@ -56,6 +72,7 @@ class Repl:
56
72
  self._yes_no_keybindings = self._setup_yn_key_bindings()
57
73
  self._sql_manager = sql_manager
58
74
  self.session = PromptSession(history=self._history)
75
+ self._next_input: str | None = None
59
76
 
60
77
  def _setup_key_bindings(self) -> KeyBindings:
61
78
  """Key bindings for repl. Helps detecting ; at end of buffer."""
@@ -65,22 +82,52 @@ class Repl:
65
82
  def not_searching():
66
83
  return not is_searching()
67
84
 
85
+ @kb.add(Keys.BracketedPaste)
86
+ def _(event):
87
+ """Handle bracketed paste - normalize line endings and strip trailing whitespace."""
88
+ pasted_data = event.data
89
+ # Normalize line endings: \r\n -> \n, \r -> \n
90
+ normalized_data = pasted_data.replace("\r\n", "\n").replace("\r", "\n")
91
+ # Strip trailing whitespace
92
+ cleaned_data = normalized_data.rstrip()
93
+ buffer = event.app.current_buffer
94
+ buffer.insert_text(cleaned_data)
95
+ log.debug(
96
+ "handled paste operation, normalized line endings and stripped trailing whitespace"
97
+ )
98
+
68
99
  @kb.add(Keys.Enter, filter=not_searching)
69
100
  def _(event):
70
- """Handle Enter key press."""
101
+ """Handle Enter key press with intelligent execution logic.
102
+
103
+ Execution priority:
104
+ 1. Exit keywords (exit, quit) - execute immediately
105
+ 2. REPL commands (starting with !) - execute immediately
106
+ 3. SQL with trailing semicolon - execute immediately
107
+ 4. All other input - add new line for multi-line editing
108
+ """
71
109
  buffer = event.app.current_buffer
72
- stripped_buffer = buffer.text.strip()
110
+ buffer_text = buffer.text
111
+ stripped_text = buffer_text.strip()
73
112
 
74
- if stripped_buffer:
113
+ if stripped_text:
75
114
  log.debug("evaluating repl input")
76
115
  cursor_position = buffer.cursor_position
77
- ends_with_semicolon = buffer.text.endswith(";")
116
+ ends_with_semicolon = stripped_text.endswith(";")
117
+ is_command = detect_command(stripped_text) is not None
78
118
 
79
- if stripped_buffer.lower() in EXIT_KEYWORDS:
80
- log.debug("exit keyword detected %r", stripped_buffer)
119
+ meaningful_content_end = len(buffer_text.rstrip())
120
+ cursor_at_meaningful_end = cursor_position >= meaningful_content_end
121
+
122
+ if stripped_text.lower() in EXIT_KEYWORDS:
123
+ log.debug("exit keyword detected %r", stripped_text)
124
+ buffer.validate_and_handle()
125
+
126
+ elif is_command:
127
+ log.debug("command detected, submitting input")
81
128
  buffer.validate_and_handle()
82
129
 
83
- elif ends_with_semicolon and cursor_position >= len(stripped_buffer):
130
+ elif ends_with_semicolon and cursor_at_meaningful_end:
84
131
  log.debug("semicolon detected, submitting input")
85
132
  buffer.validate_and_handle()
86
133
 
@@ -118,16 +165,27 @@ class Repl:
118
165
 
119
166
  return kb
120
167
 
121
- def repl_propmpt(self, msg: str = " > ") -> str:
122
- """Regular repl prompt."""
123
- return self.session.prompt(
124
- msg,
125
- lexer=self._lexer,
126
- completer=self._completer,
127
- multiline=True,
128
- wrap_lines=True,
129
- key_bindings=self._repl_key_bindings,
130
- )
168
+ def repl_prompt(self, msg: str = " > ") -> str:
169
+ """Regular repl prompt with support for pre-filled input.
170
+
171
+ Checks for queued input from commands like !edit and uses it as
172
+ default text in the prompt. The queued input is cleared after use.
173
+ """
174
+ default_text = self._next_input
175
+
176
+ try:
177
+ return self.session.prompt(
178
+ msg,
179
+ lexer=self._lexer,
180
+ completer=self._completer,
181
+ multiline=True,
182
+ wrap_lines=True,
183
+ key_bindings=self._repl_key_bindings,
184
+ default=default_text or "",
185
+ )
186
+ finally:
187
+ if self._next_input == default_text:
188
+ self._next_input = None
131
189
 
132
190
  def yn_prompt(self, msg: str) -> str:
133
191
  """Yes/No prompt."""
@@ -142,7 +200,7 @@ class Repl:
142
200
 
143
201
  @property
144
202
  def _welcome_banner(self) -> str:
145
- return f"Welcome to Snowflake-CLI REPL\nType 'exit' or 'quit' to leave"
203
+ return "Welcome to Snowflake-CLI REPL\nType 'exit' or 'quit' to leave"
146
204
 
147
205
  def _initialize_connection(self):
148
206
  """Early connection for possible fast fail."""
@@ -163,12 +221,13 @@ class Repl:
163
221
  return cursors
164
222
 
165
223
  def run(self):
166
- try:
167
- cli_console.panel(self._welcome_banner)
168
- self._initialize_connection()
169
- self._repl_loop()
170
- except (KeyboardInterrupt, EOFError):
171
- cli_console.message("\n[bold orange_red1]Leaving REPL, bye ...")
224
+ with repl_context(self):
225
+ try:
226
+ cli_console.panel(self._welcome_banner)
227
+ self._initialize_connection()
228
+ self._repl_loop()
229
+ except (KeyboardInterrupt, EOFError):
230
+ cli_console.message("\n[bold orange_red1]Leaving REPL, bye ...")
172
231
 
173
232
  def _repl_loop(self):
174
233
  """Main REPL loop. Handles input and query execution.
@@ -178,7 +237,7 @@ class Repl:
178
237
  """
179
238
  while True:
180
239
  try:
181
- user_input = self.repl_propmpt().strip()
240
+ user_input = self.repl_prompt().strip()
182
241
 
183
242
  if not user_input:
184
243
  continue
@@ -210,6 +269,21 @@ class Repl:
210
269
  except Exception as e:
211
270
  cli_console.warning(f"\nError occurred: {e}")
212
271
 
272
+ def set_next_input(self, text: str) -> None:
273
+ """Set the text that will be used as the next REPL input."""
274
+ self._next_input = text
275
+ log.debug("Next input has been set")
276
+
277
+ @property
278
+ def next_input(self) -> str | None:
279
+ """Get the next input text that will be used in the prompt."""
280
+ return self._next_input
281
+
282
+ @property
283
+ def history(self) -> FileHistory:
284
+ """Get the FileHistory instance used by the REPL."""
285
+ return self._history
286
+
213
287
  def ask_yn(self, question: str) -> bool:
214
288
  """Asks user a Yes/No question."""
215
289
  try: