snowflake-cli 3.7.2__py3-none-any.whl → 3.8.1__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 (57) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/snow_connector.py +14 -0
  3. snowflake/cli/_app/telemetry.py +11 -0
  4. snowflake/cli/_app/version_check.py +4 -3
  5. snowflake/cli/_plugins/connection/commands.py +4 -2
  6. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +1 -1
  7. snowflake/cli/_plugins/nativeapp/entities/application_package.py +20 -7
  8. snowflake/cli/_plugins/nativeapp/sf_sql_facade.py +5 -3
  9. snowflake/cli/_plugins/project/commands.py +16 -6
  10. snowflake/cli/_plugins/snowpark/common.py +31 -0
  11. snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +3 -0
  12. snowflake/cli/_plugins/snowpark/snowpark_entity.py +21 -1
  13. snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +23 -1
  14. snowflake/cli/_plugins/spcs/common.py +7 -0
  15. snowflake/cli/_plugins/spcs/image_repository/commands.py +7 -2
  16. snowflake/cli/_plugins/spcs/image_repository/manager.py +6 -2
  17. snowflake/cli/_plugins/spcs/services/commands.py +2 -2
  18. snowflake/cli/_plugins/spcs/services/manager.py +36 -1
  19. snowflake/cli/_plugins/sql/commands.py +57 -6
  20. snowflake/cli/_plugins/sql/lexer/__init__.py +7 -0
  21. snowflake/cli/_plugins/sql/lexer/completer.py +12 -0
  22. snowflake/cli/_plugins/sql/lexer/functions.py +421 -0
  23. snowflake/cli/_plugins/sql/lexer/keywords.py +529 -0
  24. snowflake/cli/_plugins/sql/lexer/lexer.py +56 -0
  25. snowflake/cli/_plugins/sql/lexer/types.py +37 -0
  26. snowflake/cli/_plugins/sql/manager.py +43 -9
  27. snowflake/cli/_plugins/sql/repl.py +221 -0
  28. snowflake/cli/_plugins/sql/snowsql_commands.py +331 -0
  29. snowflake/cli/_plugins/sql/statement_reader.py +296 -0
  30. snowflake/cli/_plugins/streamlit/commands.py +30 -15
  31. snowflake/cli/_plugins/streamlit/manager.py +0 -183
  32. snowflake/cli/_plugins/streamlit/streamlit_entity.py +163 -23
  33. snowflake/cli/api/artifacts/upload.py +5 -0
  34. snowflake/cli/api/artifacts/utils.py +0 -2
  35. snowflake/cli/api/cli_global_context.py +7 -3
  36. snowflake/cli/api/commands/decorators.py +70 -0
  37. snowflake/cli/api/commands/flags.py +95 -3
  38. snowflake/cli/api/config.py +10 -0
  39. snowflake/cli/api/connections.py +10 -0
  40. snowflake/cli/api/console/abc.py +8 -2
  41. snowflake/cli/api/console/console.py +16 -0
  42. snowflake/cli/api/console/enum.py +1 -1
  43. snowflake/cli/api/entities/common.py +99 -10
  44. snowflake/cli/api/entities/utils.py +1 -0
  45. snowflake/cli/api/feature_flags.py +6 -0
  46. snowflake/cli/api/project/project_paths.py +5 -0
  47. snowflake/cli/api/rendering/sql_templates.py +2 -1
  48. snowflake/cli/api/sql_execution.py +16 -4
  49. snowflake/cli/api/utils/path_utils.py +15 -0
  50. snowflake/cli/api/utils/python_api_utils.py +12 -0
  51. {snowflake_cli-3.7.2.dist-info → snowflake_cli-3.8.1.dist-info}/METADATA +10 -6
  52. {snowflake_cli-3.7.2.dist-info → snowflake_cli-3.8.1.dist-info}/RECORD +55 -47
  53. snowflake/cli/_plugins/nativeapp/feature_flags.py +0 -28
  54. snowflake/cli/_plugins/sql/source_reader.py +0 -230
  55. {snowflake_cli-3.7.2.dist-info → snowflake_cli-3.8.1.dist-info}/WHEEL +0 -0
  56. {snowflake_cli-3.7.2.dist-info → snowflake_cli-3.8.1.dist-info}/entry_points.txt +0 -0
  57. {snowflake_cli-3.7.2.dist-info → snowflake_cli-3.8.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,28 +0,0 @@
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 enum import unique
16
-
17
- from snowflake.cli.api.feature_flags import BooleanFlag, FeatureFlagMixin
18
-
19
-
20
- @unique
21
- class FeatureFlag(
22
- FeatureFlagMixin
23
- ): # TODO move this to snowflake.cli.api.feature_flags
24
- ENABLE_NATIVE_APP_PYTHON_SETUP = BooleanFlag(
25
- "ENABLE_NATIVE_APP_PYTHON_SETUP", False
26
- )
27
- ENABLE_NATIVE_APP_CHILDREN = BooleanFlag("ENABLE_NATIVE_APP_CHILDREN", False)
28
- ENABLE_RELEASE_CHANNELS = BooleanFlag("ENABLE_RELEASE_CHANNELS", None)
@@ -1,230 +0,0 @@
1
- import enum
2
- import io
3
- import re
4
- import urllib.error
5
- from typing import Any, Callable, Generator, Literal, Sequence
6
- from urllib.request import urlopen
7
-
8
- from jinja2 import UndefinedError
9
- from snowflake.cli.api.secure_path import UNLIMITED, SecurePath
10
- from snowflake.connector.util_text import split_statements
11
-
12
- SOURCE_PATTERN = re.compile(
13
- r"^!(source|load)\s+[\"']?(.*?)[\"']?\s*(?:;|$)",
14
- flags=re.IGNORECASE,
15
- )
16
-
17
- URL_PATTERN = re.compile(r"^(\w+?):\/(\/.*)", flags=re.IGNORECASE)
18
-
19
- SplitedStatements = Generator[
20
- tuple[str, bool | None] | tuple[str, Literal[False]],
21
- Any,
22
- None,
23
- ]
24
-
25
- SqlTransformFunc = Callable[[str], str]
26
- OperatorFunctions = Sequence[SqlTransformFunc]
27
-
28
-
29
- class SourceType(enum.Enum):
30
- FILE = "file"
31
- QUERY = "query"
32
- UNKNOWN = "unknown"
33
- URL = "url"
34
-
35
-
36
- class ParsedSource:
37
- """Container for parsed statement.
38
-
39
- Holds:
40
- - source: statement on command content
41
- - source_type: type of source
42
- - source_path: in case of URL or FILE path of the origin
43
- - error: optional message
44
- """
45
-
46
- __slots__ = ("source", "source_type", "source_path", "error")
47
- __match_args__ = ("source_type", "error")
48
-
49
- source: io.StringIO
50
- source_type: SourceType | None
51
- source_path: str | None
52
- error: str | None
53
-
54
- def __init__(
55
- self,
56
- source: str,
57
- source_type: SourceType,
58
- source_path: str | None,
59
- error: str | None = None,
60
- ):
61
- self.source = io.StringIO(source)
62
- self.source_type = source_type
63
- self.source_path = source_path
64
- self.error = error
65
-
66
- def __bool__(self):
67
- return not self.error
68
-
69
- def __eq__(self, other):
70
- result = (
71
- self.source_type == other.source_type,
72
- self.source_path == other.source_path,
73
- self.error == other.error,
74
- self.source.read() == other.source.read(),
75
- )
76
- self.source.seek(0)
77
- other.source.seek(0)
78
- return all(result)
79
-
80
- def __repr__(self):
81
- return f"{self.__class__.__name__}(source_type={self.source_type}, source_path={self.source_path}, error={self.error})"
82
-
83
- @classmethod
84
- def from_url(cls, path_part: str, raw_source: str) -> "ParsedSource":
85
- """Constructor for loading from URL."""
86
- try:
87
- payload = urlopen(path_part, timeout=10.0).read().decode()
88
- return cls(payload, SourceType.URL, path_part)
89
-
90
- except urllib.error.HTTPError as err:
91
- error = f"Could not fetch {path_part}: {err}"
92
- return cls(path_part, SourceType.URL, raw_source, error)
93
-
94
- @classmethod
95
- def from_file(cls, path_part: str, raw_source: str) -> "ParsedSource":
96
- """Constructor for loading from file."""
97
- path = SecurePath(path_part)
98
-
99
- if path.is_file():
100
- payload = path.read_text(file_size_limit_mb=UNLIMITED)
101
- return cls(payload, SourceType.FILE, path.as_posix())
102
-
103
- error_msg = f"Could not read: {path_part}"
104
- return cls(path_part, SourceType.FILE, raw_source, error_msg)
105
-
106
-
107
- RecursiveStatementReader = Generator[ParsedSource, Any, Any]
108
-
109
-
110
- def parse_source(source: str, operators: OperatorFunctions) -> ParsedSource:
111
- """Evaluates templating and source commands.
112
-
113
- Returns parsed source according to origin."""
114
- try:
115
- statement = source
116
- for operator in operators:
117
- statement = operator(statement)
118
- except UndefinedError as e:
119
- error_msg = f"SQL template rendering error: {e}"
120
- return ParsedSource(source, SourceType.UNKNOWN, source, error_msg)
121
-
122
- split_result = SOURCE_PATTERN.split(statement, maxsplit=1)
123
- split_result = [p.strip() for p in split_result]
124
-
125
- if len(split_result) == 1:
126
- return ParsedSource(statement, SourceType.QUERY, None)
127
-
128
- _, command, source_path, *_ = split_result
129
- _path_match = URL_PATTERN.split(source_path.lower())
130
-
131
- match command.lower(), _path_match:
132
- # load content from an URL
133
- case "source" | "load", ("", "http" | "https", *_):
134
- return ParsedSource.from_url(source_path, statement)
135
-
136
- # load content from a local file
137
- case "source" | "load", (str(),):
138
- return ParsedSource.from_file(source_path, statement)
139
-
140
- case _:
141
- error_msg = f"Unknown source: {source_path}"
142
-
143
- return ParsedSource(source_path, SourceType.UNKNOWN, source, error_msg)
144
-
145
-
146
- def recursive_source_reader(
147
- source: SplitedStatements,
148
- seen_files: list,
149
- operators: OperatorFunctions,
150
- remove_comments: bool,
151
- ) -> RecursiveStatementReader:
152
- """Based on detected source command reads content of the source and tracks for recursion cycles."""
153
- for stmt, _ in source:
154
- if not stmt:
155
- continue
156
- parsed_source = parse_source(stmt, operators)
157
-
158
- match parsed_source:
159
- case ParsedSource(SourceType.FILE | SourceType.URL, None):
160
- if parsed_source.source_path in seen_files:
161
- error = f"Recursion detected: {' -> '.join(seen_files)}"
162
- parsed_source.error = error
163
- yield parsed_source
164
- continue
165
-
166
- seen_files.append(parsed_source.source_path)
167
-
168
- yield from recursive_source_reader(
169
- split_statements(parsed_source.source, remove_comments),
170
- seen_files,
171
- operators,
172
- remove_comments,
173
- )
174
-
175
- seen_files.pop()
176
-
177
- case ParsedSource(SourceType.URL, error) if error:
178
- yield parsed_source
179
-
180
- case _:
181
- yield parsed_source
182
- return
183
-
184
-
185
- def files_reader(
186
- paths: Sequence[SecurePath],
187
- operators: OperatorFunctions,
188
- remove_comments: bool = False,
189
- ) -> RecursiveStatementReader:
190
- """Entry point for reading statements from files.
191
-
192
- Returns a generator with statements."""
193
- for path in paths:
194
- with path.open(read_file_limit_mb=UNLIMITED) as f:
195
- stmts = split_statements(io.StringIO(f.read()), remove_comments)
196
- yield from recursive_source_reader(
197
- stmts,
198
- [path.as_posix()],
199
- operators,
200
- remove_comments,
201
- )
202
-
203
-
204
- def query_reader(
205
- source: str,
206
- operators: OperatorFunctions,
207
- remove_comments: bool = False,
208
- ) -> RecursiveStatementReader:
209
- """Entry point for reading statements from query.
210
-
211
- Returns a generator with statements."""
212
- stmts = split_statements(io.StringIO(source), remove_comments)
213
- yield from recursive_source_reader(stmts, [], operators, remove_comments)
214
-
215
-
216
- def compile_statements(source: RecursiveStatementReader):
217
- """Tracks statements evaluation and collects errors."""
218
- errors = []
219
- cnt = 0
220
- compiled = []
221
-
222
- for stmt in source:
223
- if stmt.source_type == SourceType.QUERY:
224
- cnt += 1
225
- if not stmt.error:
226
- compiled.append(stmt.source.read())
227
- if stmt.error:
228
- errors.append(stmt.error)
229
-
230
- return errors, cnt, compiled