snowflake-cli 3.7.1__py3-none-any.whl → 3.8.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.
- snowflake/cli/__about__.py +1 -1
- snowflake/cli/_app/snow_connector.py +14 -0
- snowflake/cli/_app/telemetry.py +11 -0
- snowflake/cli/_plugins/connection/commands.py +4 -2
- snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +1 -1
- snowflake/cli/_plugins/nativeapp/entities/application_package.py +20 -7
- snowflake/cli/_plugins/nativeapp/sf_sql_facade.py +5 -3
- snowflake/cli/_plugins/project/commands.py +16 -6
- snowflake/cli/_plugins/snowpark/common.py +31 -0
- snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +3 -0
- snowflake/cli/_plugins/snowpark/snowpark_entity.py +21 -1
- snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +23 -1
- snowflake/cli/_plugins/spcs/common.py +7 -0
- snowflake/cli/_plugins/spcs/image_repository/commands.py +7 -2
- snowflake/cli/_plugins/spcs/image_repository/manager.py +6 -2
- snowflake/cli/_plugins/spcs/services/commands.py +2 -2
- snowflake/cli/_plugins/spcs/services/manager.py +36 -1
- snowflake/cli/_plugins/sql/commands.py +57 -6
- snowflake/cli/_plugins/sql/lexer/__init__.py +7 -0
- snowflake/cli/_plugins/sql/lexer/completer.py +12 -0
- snowflake/cli/_plugins/sql/lexer/functions.py +421 -0
- snowflake/cli/_plugins/sql/lexer/keywords.py +529 -0
- snowflake/cli/_plugins/sql/lexer/lexer.py +56 -0
- snowflake/cli/_plugins/sql/lexer/types.py +37 -0
- snowflake/cli/_plugins/sql/manager.py +43 -9
- snowflake/cli/_plugins/sql/repl.py +221 -0
- snowflake/cli/_plugins/sql/snowsql_commands.py +331 -0
- snowflake/cli/_plugins/sql/statement_reader.py +296 -0
- snowflake/cli/_plugins/streamlit/commands.py +30 -15
- snowflake/cli/_plugins/streamlit/manager.py +0 -183
- snowflake/cli/_plugins/streamlit/streamlit_entity.py +163 -23
- snowflake/cli/api/artifacts/upload.py +5 -0
- snowflake/cli/api/artifacts/utils.py +0 -2
- snowflake/cli/api/cli_global_context.py +7 -3
- snowflake/cli/api/commands/decorators.py +70 -0
- snowflake/cli/api/commands/flags.py +95 -3
- snowflake/cli/api/config.py +10 -0
- snowflake/cli/api/connections.py +10 -0
- snowflake/cli/api/console/abc.py +8 -2
- snowflake/cli/api/console/console.py +16 -0
- snowflake/cli/api/console/enum.py +1 -1
- snowflake/cli/api/entities/common.py +99 -10
- snowflake/cli/api/entities/utils.py +1 -0
- snowflake/cli/api/feature_flags.py +6 -0
- snowflake/cli/api/project/project_paths.py +5 -0
- snowflake/cli/api/rendering/sql_templates.py +2 -1
- snowflake/cli/api/sql_execution.py +16 -4
- snowflake/cli/api/utils/path_utils.py +15 -0
- snowflake/cli/api/utils/python_api_utils.py +12 -0
- {snowflake_cli-3.7.1.dist-info → snowflake_cli-3.8.0.dist-info}/METADATA +12 -8
- {snowflake_cli-3.7.1.dist-info → snowflake_cli-3.8.0.dist-info}/RECORD +54 -46
- snowflake/cli/_plugins/nativeapp/feature_flags.py +0 -28
- snowflake/cli/_plugins/sql/source_reader.py +0 -230
- {snowflake_cli-3.7.1.dist-info → snowflake_cli-3.8.0.dist-info}/WHEEL +0 -0
- {snowflake_cli-3.7.1.dist-info → snowflake_cli-3.8.0.dist-info}/entry_points.txt +0 -0
- {snowflake_cli-3.7.1.dist-info → snowflake_cli-3.8.0.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|