snowflake-cli-labs 3.0.0rc0__py3-none-any.whl → 3.0.0rc2__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/cli_app.py +10 -1
- snowflake/cli/_app/snow_connector.py +91 -37
- snowflake/cli/_app/telemetry.py +8 -4
- snowflake/cli/_app/version_check.py +74 -0
- snowflake/cli/_plugins/connection/commands.py +3 -2
- snowflake/cli/_plugins/git/commands.py +55 -14
- snowflake/cli/_plugins/git/manager.py +14 -6
- snowflake/cli/_plugins/nativeapp/codegen/compiler.py +18 -2
- snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +123 -42
- snowflake/cli/_plugins/nativeapp/codegen/setup/setup_driver.py.source +5 -2
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +6 -11
- snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +111 -0
- snowflake/cli/_plugins/nativeapp/exceptions.py +3 -3
- snowflake/cli/_plugins/nativeapp/manager.py +74 -144
- snowflake/cli/_plugins/nativeapp/project_model.py +2 -9
- snowflake/cli/_plugins/nativeapp/run_processor.py +56 -260
- snowflake/cli/_plugins/nativeapp/same_account_install_method.py +74 -0
- snowflake/cli/_plugins/nativeapp/teardown_processor.py +17 -246
- snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +91 -17
- snowflake/cli/_plugins/snowpark/commands.py +5 -65
- snowflake/cli/_plugins/snowpark/common.py +17 -1
- snowflake/cli/_plugins/snowpark/models.py +2 -1
- snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +1 -35
- snowflake/cli/_plugins/sql/commands.py +1 -2
- snowflake/cli/_plugins/stage/commands.py +2 -2
- snowflake/cli/_plugins/stage/manager.py +46 -15
- snowflake/cli/_plugins/streamlit/commands.py +4 -63
- snowflake/cli/_plugins/streamlit/manager.py +13 -0
- snowflake/cli/_plugins/workspace/action_context.py +7 -0
- snowflake/cli/_plugins/workspace/commands.py +145 -32
- snowflake/cli/_plugins/workspace/manager.py +21 -4
- snowflake/cli/api/cli_global_context.py +136 -313
- snowflake/cli/api/commands/decorators.py +1 -1
- snowflake/cli/api/commands/flags.py +106 -102
- snowflake/cli/api/commands/snow_typer.py +15 -6
- snowflake/cli/api/config.py +18 -5
- snowflake/cli/api/connections.py +214 -0
- snowflake/cli/api/console/abc.py +4 -2
- snowflake/cli/api/constants.py +11 -0
- snowflake/cli/api/entities/application_entity.py +687 -2
- snowflake/cli/api/entities/application_package_entity.py +407 -9
- snowflake/cli/api/entities/common.py +7 -2
- snowflake/cli/api/entities/utils.py +80 -20
- snowflake/cli/api/exceptions.py +12 -2
- snowflake/cli/api/feature_flags.py +0 -2
- snowflake/cli/api/identifiers.py +3 -0
- snowflake/cli/api/project/definition.py +35 -1
- snowflake/cli/api/project/definition_conversion.py +352 -0
- snowflake/cli/api/project/schemas/entities/application_package_entity_model.py +17 -0
- snowflake/cli/api/project/schemas/entities/common.py +0 -12
- snowflake/cli/api/project/schemas/identifier_model.py +2 -2
- snowflake/cli/api/project/schemas/project_definition.py +102 -43
- snowflake/cli/api/rendering/jinja.py +2 -16
- snowflake/cli/api/rendering/project_definition_templates.py +5 -1
- snowflake/cli/api/rendering/sql_templates.py +14 -4
- snowflake/cli/api/secure_path.py +13 -18
- snowflake/cli/api/secure_utils.py +90 -1
- snowflake/cli/api/sql_execution.py +13 -0
- snowflake/cli/api/utils/definition_rendering.py +7 -7
- {snowflake_cli_labs-3.0.0rc0.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/METADATA +9 -9
- {snowflake_cli_labs-3.0.0rc0.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/RECORD +65 -61
- snowflake/cli/api/commands/typer_pre_execute.py +0 -26
- {snowflake_cli_labs-3.0.0rc0.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-3.0.0rc0.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/entry_points.txt +0 -0
- {snowflake_cli_labs-3.0.0rc0.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/licenses/LICENSE +0 -0
|
@@ -21,18 +21,18 @@ from typing import Any, Callable, Optional
|
|
|
21
21
|
import click
|
|
22
22
|
import typer
|
|
23
23
|
from click import ClickException
|
|
24
|
-
from snowflake.cli.api.cli_global_context import
|
|
24
|
+
from snowflake.cli.api.cli_global_context import (
|
|
25
|
+
_CliGlobalContextManager,
|
|
26
|
+
get_cli_context_manager,
|
|
27
|
+
)
|
|
25
28
|
from snowflake.cli.api.commands.common import OnErrorType
|
|
26
29
|
from snowflake.cli.api.commands.overrideable_parameter import OverrideableOption
|
|
27
|
-
from snowflake.cli.api.commands.typer_pre_execute import register_pre_execute_command
|
|
28
30
|
from snowflake.cli.api.commands.utils import parse_key_value_variables
|
|
29
31
|
from snowflake.cli.api.config import get_all_connections
|
|
32
|
+
from snowflake.cli.api.connections import ConnectionContext
|
|
30
33
|
from snowflake.cli.api.console import cli_console
|
|
31
|
-
from snowflake.cli.api.exceptions import MissingConfiguration
|
|
32
34
|
from snowflake.cli.api.identifiers import FQN
|
|
33
35
|
from snowflake.cli.api.output.formats import OutputFormat
|
|
34
|
-
from snowflake.cli.api.project.definition_manager import DefinitionManager
|
|
35
|
-
from snowflake.cli.api.rendering.jinja import CONTEXT_KEY
|
|
36
36
|
|
|
37
37
|
DEFAULT_CONTEXT_SETTINGS = {"help_option_names": ["--help", "-h"]}
|
|
38
38
|
|
|
@@ -40,10 +40,39 @@ _CONNECTION_SECTION = "Connection configuration"
|
|
|
40
40
|
_CLI_BEHAVIOUR = "Global configuration"
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
def
|
|
43
|
+
def _connection_callback(prop: str):
|
|
44
|
+
"""Generates a setter for a field on the current context manager's connection context."""
|
|
45
|
+
if prop not in ConnectionContext.__dataclass_fields__:
|
|
46
|
+
raise KeyError(
|
|
47
|
+
f"Cannot generate setter for non-existent connection attr {prop}"
|
|
48
|
+
)
|
|
49
|
+
|
|
44
50
|
def callback(value):
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
try:
|
|
52
|
+
if click.get_current_context().resilient_parsing:
|
|
53
|
+
return
|
|
54
|
+
except RuntimeError:
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
setattr(get_cli_context_manager().connection_context, prop, value)
|
|
58
|
+
return value
|
|
59
|
+
|
|
60
|
+
return callback
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _context_callback(prop: str):
|
|
64
|
+
"""Generates a setter for a field on the current context manager."""
|
|
65
|
+
if prop not in _CliGlobalContextManager.__dataclass_fields__:
|
|
66
|
+
raise KeyError(f"Cannot generate setter for non-existent context attr {prop}")
|
|
67
|
+
|
|
68
|
+
def callback(value):
|
|
69
|
+
try:
|
|
70
|
+
if click.get_current_context().resilient_parsing:
|
|
71
|
+
return
|
|
72
|
+
except RuntimeError:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
setattr(get_cli_context_manager(), prop, value)
|
|
47
76
|
return value
|
|
48
77
|
|
|
49
78
|
return callback
|
|
@@ -55,9 +84,7 @@ ConnectionOption = typer.Option(
|
|
|
55
84
|
"-c",
|
|
56
85
|
"--environment",
|
|
57
86
|
help=f"Name of the connection, as defined in your `config.toml`. Default: `default`.",
|
|
58
|
-
callback=
|
|
59
|
-
lambda: get_cli_context_manager().connection_context.set_connection_name
|
|
60
|
-
),
|
|
87
|
+
callback=_connection_callback("connection_name"),
|
|
61
88
|
show_default=False,
|
|
62
89
|
rich_help_panel=_CONNECTION_SECTION,
|
|
63
90
|
shell_complete=lambda _, __, ___: list(get_all_connections()),
|
|
@@ -68,9 +95,7 @@ TemporaryConnectionOption = typer.Option(
|
|
|
68
95
|
"--temporary-connection",
|
|
69
96
|
"-x",
|
|
70
97
|
help="Uses connection defined with command line parameters, instead of one defined in config",
|
|
71
|
-
callback=
|
|
72
|
-
lambda: get_cli_context_manager().connection_context.set_temporary_connection
|
|
73
|
-
),
|
|
98
|
+
callback=_connection_callback("temporary_connection"),
|
|
74
99
|
is_flag=True,
|
|
75
100
|
rich_help_panel=_CONNECTION_SECTION,
|
|
76
101
|
)
|
|
@@ -80,9 +105,7 @@ AccountOption = typer.Option(
|
|
|
80
105
|
"--account",
|
|
81
106
|
"--accountname",
|
|
82
107
|
help="Name assigned to your Snowflake account. Overrides the value specified for the connection.",
|
|
83
|
-
callback=
|
|
84
|
-
lambda: get_cli_context_manager().connection_context.set_account
|
|
85
|
-
),
|
|
108
|
+
callback=_connection_callback("account"),
|
|
86
109
|
show_default=False,
|
|
87
110
|
rich_help_panel=_CONNECTION_SECTION,
|
|
88
111
|
)
|
|
@@ -92,7 +115,7 @@ UserOption = typer.Option(
|
|
|
92
115
|
"--user",
|
|
93
116
|
"--username",
|
|
94
117
|
help="Username to connect to Snowflake. Overrides the value specified for the connection.",
|
|
95
|
-
callback=
|
|
118
|
+
callback=_connection_callback("user"),
|
|
96
119
|
show_default=False,
|
|
97
120
|
rich_help_panel=_CONNECTION_SECTION,
|
|
98
121
|
)
|
|
@@ -105,9 +128,7 @@ def _password_callback(value: str):
|
|
|
105
128
|
if value:
|
|
106
129
|
cli_console.message(PLAIN_PASSWORD_MSG)
|
|
107
130
|
|
|
108
|
-
return
|
|
109
|
-
value
|
|
110
|
-
)
|
|
131
|
+
return _connection_callback("password")(value)
|
|
111
132
|
|
|
112
133
|
|
|
113
134
|
PasswordOption = typer.Option(
|
|
@@ -125,21 +146,18 @@ AuthenticatorOption = typer.Option(
|
|
|
125
146
|
"--authenticator",
|
|
126
147
|
help="Snowflake authenticator. Overrides the value specified for the connection.",
|
|
127
148
|
hide_input=True,
|
|
128
|
-
callback=
|
|
129
|
-
lambda: get_cli_context_manager().connection_context.set_authenticator
|
|
130
|
-
),
|
|
149
|
+
callback=_connection_callback("authenticator"),
|
|
131
150
|
show_default=False,
|
|
132
151
|
rich_help_panel=_CONNECTION_SECTION,
|
|
133
152
|
)
|
|
134
153
|
|
|
135
154
|
PrivateKeyPathOption = typer.Option(
|
|
136
155
|
None,
|
|
156
|
+
"--private-key-file",
|
|
137
157
|
"--private-key-path",
|
|
138
|
-
help="Snowflake private key path. Overrides the value specified for the connection.",
|
|
158
|
+
help="Snowflake private key file path. Overrides the value specified for the connection.",
|
|
139
159
|
hide_input=True,
|
|
140
|
-
callback=
|
|
141
|
-
lambda: get_cli_context_manager().connection_context.set_private_key_path
|
|
142
|
-
),
|
|
160
|
+
callback=_connection_callback("private_key_file"),
|
|
143
161
|
show_default=False,
|
|
144
162
|
rich_help_panel=_CONNECTION_SECTION,
|
|
145
163
|
exists=True,
|
|
@@ -152,9 +170,7 @@ SessionTokenOption = typer.Option(
|
|
|
152
170
|
"--session-token",
|
|
153
171
|
help="Snowflake session token. Can be used only in conjunction with --master-token. Overrides the value specified for the connection.",
|
|
154
172
|
hide_input=True,
|
|
155
|
-
callback=
|
|
156
|
-
lambda: get_cli_context_manager().connection_context.set_session_token
|
|
157
|
-
),
|
|
173
|
+
callback=_connection_callback("session_token"),
|
|
158
174
|
show_default=False,
|
|
159
175
|
rich_help_panel=_CONNECTION_SECTION,
|
|
160
176
|
exists=True,
|
|
@@ -168,9 +184,7 @@ MasterTokenOption = typer.Option(
|
|
|
168
184
|
"--master-token",
|
|
169
185
|
help="Snowflake master token. Can be used only in conjunction with --session-token. Overrides the value specified for the connection.",
|
|
170
186
|
hide_input=True,
|
|
171
|
-
callback=
|
|
172
|
-
lambda: get_cli_context_manager().connection_context.set_master_token
|
|
173
|
-
),
|
|
187
|
+
callback=_connection_callback("master_token"),
|
|
174
188
|
show_default=False,
|
|
175
189
|
rich_help_panel=_CONNECTION_SECTION,
|
|
176
190
|
exists=True,
|
|
@@ -183,9 +197,7 @@ TokenFilePathOption = typer.Option(
|
|
|
183
197
|
None,
|
|
184
198
|
"--token-file-path",
|
|
185
199
|
help="Path to file with an OAuth token that should be used when connecting to Snowflake",
|
|
186
|
-
callback=
|
|
187
|
-
lambda: get_cli_context_manager().connection_context.set_token_file_path
|
|
188
|
-
),
|
|
200
|
+
callback=_connection_callback("token_file_path"),
|
|
189
201
|
show_default=False,
|
|
190
202
|
rich_help_panel=_CONNECTION_SECTION,
|
|
191
203
|
exists=True,
|
|
@@ -198,9 +210,7 @@ DatabaseOption = typer.Option(
|
|
|
198
210
|
"--database",
|
|
199
211
|
"--dbname",
|
|
200
212
|
help="Database to use. Overrides the value specified for the connection.",
|
|
201
|
-
callback=
|
|
202
|
-
lambda: get_cli_context_manager().connection_context.set_database
|
|
203
|
-
),
|
|
213
|
+
callback=_connection_callback("database"),
|
|
204
214
|
show_default=False,
|
|
205
215
|
rich_help_panel=_CONNECTION_SECTION,
|
|
206
216
|
)
|
|
@@ -210,7 +220,7 @@ SchemaOption = typer.Option(
|
|
|
210
220
|
"--schema",
|
|
211
221
|
"--schemaname",
|
|
212
222
|
help="Database schema to use. Overrides the value specified for the connection.",
|
|
213
|
-
callback=
|
|
223
|
+
callback=_connection_callback("schema"),
|
|
214
224
|
show_default=False,
|
|
215
225
|
rich_help_panel=_CONNECTION_SECTION,
|
|
216
226
|
)
|
|
@@ -220,7 +230,7 @@ RoleOption = typer.Option(
|
|
|
220
230
|
"--role",
|
|
221
231
|
"--rolename",
|
|
222
232
|
help="Role to use. Overrides the value specified for the connection.",
|
|
223
|
-
callback=
|
|
233
|
+
callback=_connection_callback("role"),
|
|
224
234
|
show_default=False,
|
|
225
235
|
rich_help_panel=_CONNECTION_SECTION,
|
|
226
236
|
)
|
|
@@ -229,9 +239,7 @@ WarehouseOption = typer.Option(
|
|
|
229
239
|
None,
|
|
230
240
|
"--warehouse",
|
|
231
241
|
help="Warehouse to use. Overrides the value specified for the connection.",
|
|
232
|
-
callback=
|
|
233
|
-
lambda: get_cli_context_manager().connection_context.set_warehouse
|
|
234
|
-
),
|
|
242
|
+
callback=_connection_callback("warehouse"),
|
|
235
243
|
show_default=False,
|
|
236
244
|
rich_help_panel=_CONNECTION_SECTION,
|
|
237
245
|
)
|
|
@@ -240,9 +248,7 @@ MfaPasscodeOption = typer.Option(
|
|
|
240
248
|
None,
|
|
241
249
|
"--mfa-passcode",
|
|
242
250
|
help="Token to use for multi-factor authentication (MFA)",
|
|
243
|
-
callback=
|
|
244
|
-
lambda: get_cli_context_manager().connection_context.set_mfa_passcode
|
|
245
|
-
),
|
|
251
|
+
callback=_connection_callback("mfa_passcode"),
|
|
246
252
|
prompt="MFA passcode",
|
|
247
253
|
prompt_required=False,
|
|
248
254
|
show_default=False,
|
|
@@ -253,9 +259,7 @@ EnableDiagOption = typer.Option(
|
|
|
253
259
|
False,
|
|
254
260
|
"--enable-diag",
|
|
255
261
|
help="Run python connector diagnostic test",
|
|
256
|
-
callback=
|
|
257
|
-
lambda: get_cli_context_manager().connection_context.set_enable_diag
|
|
258
|
-
),
|
|
262
|
+
callback=_connection_callback("enable_diag"),
|
|
259
263
|
show_default=False,
|
|
260
264
|
is_flag=True,
|
|
261
265
|
rich_help_panel=_CONNECTION_SECTION,
|
|
@@ -267,18 +271,17 @@ _DIAG_LOG_DEFAULT_VALUE = "<temporary_directory>"
|
|
|
267
271
|
|
|
268
272
|
|
|
269
273
|
def _diag_log_path_callback(path: str):
|
|
270
|
-
if path
|
|
271
|
-
|
|
272
|
-
|
|
274
|
+
if path == _DIAG_LOG_DEFAULT_VALUE:
|
|
275
|
+
path = tempfile.gettempdir()
|
|
276
|
+
get_cli_context_manager().connection_context.diag_log_path = Path(path)
|
|
277
|
+
return path
|
|
273
278
|
|
|
274
279
|
|
|
275
280
|
DiagLogPathOption: Path = typer.Option(
|
|
276
281
|
_DIAG_LOG_DEFAULT_VALUE,
|
|
277
282
|
"--diag-log-path",
|
|
278
283
|
help="Diagnostic report path",
|
|
279
|
-
callback=
|
|
280
|
-
lambda: get_cli_context_manager().connection_context.set_diag_log_path
|
|
281
|
-
),
|
|
284
|
+
callback=_diag_log_path_callback,
|
|
282
285
|
show_default=False,
|
|
283
286
|
rich_help_panel=_CONNECTION_SECTION,
|
|
284
287
|
exists=True,
|
|
@@ -289,9 +292,7 @@ DiagAllowlistPathOption: Path = typer.Option(
|
|
|
289
292
|
None,
|
|
290
293
|
"--diag-allowlist-path",
|
|
291
294
|
help="Diagnostic report path to optional allowlist",
|
|
292
|
-
callback=
|
|
293
|
-
lambda: get_cli_context_manager().connection_context.set_diag_allowlist_path
|
|
294
|
-
),
|
|
295
|
+
callback=_connection_callback("diag_allowlist_path"),
|
|
295
296
|
show_default=False,
|
|
296
297
|
rich_help_panel=_CONNECTION_SECTION,
|
|
297
298
|
exists=True,
|
|
@@ -303,7 +304,7 @@ OutputFormatOption = typer.Option(
|
|
|
303
304
|
"--format",
|
|
304
305
|
help="Specifies the output format.",
|
|
305
306
|
case_sensitive=False,
|
|
306
|
-
callback=
|
|
307
|
+
callback=_context_callback("output_format"),
|
|
307
308
|
rich_help_panel=_CLI_BEHAVIOUR,
|
|
308
309
|
)
|
|
309
310
|
|
|
@@ -311,7 +312,7 @@ SilentOption = typer.Option(
|
|
|
311
312
|
False,
|
|
312
313
|
"--silent",
|
|
313
314
|
help="Turns off intermediate output to console.",
|
|
314
|
-
callback=
|
|
315
|
+
callback=_context_callback("silent"),
|
|
315
316
|
is_flag=True,
|
|
316
317
|
rich_help_panel=_CLI_BEHAVIOUR,
|
|
317
318
|
is_eager=True,
|
|
@@ -322,7 +323,7 @@ VerboseOption = typer.Option(
|
|
|
322
323
|
"--verbose",
|
|
323
324
|
"-v",
|
|
324
325
|
help="Displays log entries for log levels `info` and higher.",
|
|
325
|
-
callback=
|
|
326
|
+
callback=_context_callback("verbose"),
|
|
326
327
|
is_flag=True,
|
|
327
328
|
rich_help_panel=_CLI_BEHAVIOUR,
|
|
328
329
|
)
|
|
@@ -331,7 +332,7 @@ DebugOption = typer.Option(
|
|
|
331
332
|
False,
|
|
332
333
|
"--debug",
|
|
333
334
|
help="Displays log entries for log levels `debug` and higher; debug logs contains additional information.",
|
|
334
|
-
callback=
|
|
335
|
+
callback=_context_callback("enable_tracebacks"),
|
|
335
336
|
is_flag=True,
|
|
336
337
|
rich_help_panel=_CLI_BEHAVIOUR,
|
|
337
338
|
)
|
|
@@ -434,24 +435,49 @@ def experimental_option(
|
|
|
434
435
|
"--experimental",
|
|
435
436
|
help=help_text,
|
|
436
437
|
hidden=True,
|
|
437
|
-
callback=
|
|
438
|
+
callback=_context_callback("experimental"),
|
|
438
439
|
is_flag=True,
|
|
439
440
|
rich_help_panel=_CLI_BEHAVIOUR,
|
|
440
441
|
)
|
|
441
442
|
|
|
442
443
|
|
|
444
|
+
class IdentifierType(click.ParamType):
|
|
445
|
+
name = "TEXT"
|
|
446
|
+
|
|
447
|
+
def convert(self, value, param, ctx):
|
|
448
|
+
return FQN.from_string(value)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
class IdentifierStageType(click.ParamType):
|
|
452
|
+
name = "TEXT"
|
|
453
|
+
|
|
454
|
+
def convert(self, value, param, ctx):
|
|
455
|
+
return FQN.from_stage(value)
|
|
456
|
+
|
|
457
|
+
|
|
443
458
|
def identifier_argument(
|
|
444
|
-
sf_object: str,
|
|
459
|
+
sf_object: str,
|
|
460
|
+
example: str,
|
|
461
|
+
click_type: click.ParamType = IdentifierType(),
|
|
462
|
+
callback: Callable | None = None,
|
|
445
463
|
) -> typer.Argument:
|
|
446
464
|
return typer.Argument(
|
|
447
465
|
...,
|
|
448
466
|
help=f"Identifier of the {sf_object}. For example: {example}",
|
|
449
467
|
show_default=False,
|
|
450
|
-
click_type=
|
|
468
|
+
click_type=click_type,
|
|
451
469
|
callback=callback,
|
|
452
470
|
)
|
|
453
471
|
|
|
454
472
|
|
|
473
|
+
def identifier_stage_argument(
|
|
474
|
+
sf_object: str, example: str, callback: Callable | None = None
|
|
475
|
+
) -> typer.Argument:
|
|
476
|
+
return identifier_argument(
|
|
477
|
+
sf_object, example, click_type=IdentifierStageType(), callback=callback
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
|
|
455
481
|
def execution_identifier_argument(sf_object: str, example: str) -> typer.Argument:
|
|
456
482
|
return typer.Argument(
|
|
457
483
|
...,
|
|
@@ -460,30 +486,12 @@ def execution_identifier_argument(sf_object: str, example: str) -> typer.Argumen
|
|
|
460
486
|
)
|
|
461
487
|
|
|
462
488
|
|
|
463
|
-
def register_project_definition(is_optional: bool) -> None:
|
|
464
|
-
cli_context_manager = get_cli_context_manager()
|
|
465
|
-
project_path = cli_context_manager.project_path_arg
|
|
466
|
-
env_overrides_args = cli_context_manager.project_env_overrides_args
|
|
467
|
-
|
|
468
|
-
dm = DefinitionManager(project_path, {CONTEXT_KEY: {"env": env_overrides_args}})
|
|
469
|
-
project_definition = dm.project_definition
|
|
470
|
-
project_root = dm.project_root
|
|
471
|
-
template_context = dm.template_context
|
|
472
|
-
|
|
473
|
-
if not dm.has_definition_file and not is_optional:
|
|
474
|
-
raise MissingConfiguration(
|
|
475
|
-
"Cannot find project definition (snowflake.yml). Please provide a path to the project or run this command in a valid project directory."
|
|
476
|
-
)
|
|
477
|
-
|
|
478
|
-
cli_context_manager.set_project_definition(project_definition)
|
|
479
|
-
cli_context_manager.set_project_root(project_root)
|
|
480
|
-
cli_context_manager.set_template_context(template_context)
|
|
481
|
-
|
|
482
|
-
|
|
483
489
|
def project_definition_option(is_optional: bool):
|
|
484
|
-
def
|
|
485
|
-
get_cli_context_manager()
|
|
486
|
-
|
|
490
|
+
def project_path_callback(project_path: str) -> str:
|
|
491
|
+
ctx_mgr = get_cli_context_manager()
|
|
492
|
+
ctx_mgr.project_path_arg = project_path
|
|
493
|
+
ctx_mgr.project_is_optional = is_optional
|
|
494
|
+
return project_path
|
|
487
495
|
|
|
488
496
|
return typer.Option(
|
|
489
497
|
None,
|
|
@@ -491,23 +499,26 @@ def project_definition_option(is_optional: bool):
|
|
|
491
499
|
"--project",
|
|
492
500
|
help=f"Path where Snowflake project resides. "
|
|
493
501
|
f"Defaults to current working directory.",
|
|
494
|
-
callback=
|
|
502
|
+
callback=project_path_callback,
|
|
495
503
|
show_default=False,
|
|
496
504
|
)
|
|
497
505
|
|
|
498
506
|
|
|
499
507
|
def project_env_overrides_option():
|
|
500
|
-
def project_env_overrides_callback(
|
|
508
|
+
def project_env_overrides_callback(
|
|
509
|
+
env_overrides_args_list: list[str],
|
|
510
|
+
) -> dict[str, str]:
|
|
501
511
|
env_overrides_args_map = {
|
|
502
512
|
v.key: v.value for v in parse_key_value_variables(env_overrides_args_list)
|
|
503
513
|
}
|
|
504
|
-
get_cli_context_manager().
|
|
514
|
+
get_cli_context_manager().project_env_overrides_args = env_overrides_args_map
|
|
515
|
+
return env_overrides_args_map
|
|
505
516
|
|
|
506
517
|
return typer.Option(
|
|
507
518
|
[],
|
|
508
519
|
"--env",
|
|
509
|
-
help="String in format of key=value. Overrides variables from env section used for
|
|
510
|
-
callback=
|
|
520
|
+
help="String in format of key=value. Overrides variables from env section used for templates.",
|
|
521
|
+
callback=project_env_overrides_callback,
|
|
511
522
|
show_default=False,
|
|
512
523
|
)
|
|
513
524
|
|
|
@@ -530,10 +541,3 @@ def deprecated_flag_callback_enum(msg: str):
|
|
|
530
541
|
return value.value
|
|
531
542
|
|
|
532
543
|
return _warning_callback
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
class IdentifierType(click.ParamType):
|
|
536
|
-
name = "TEXT"
|
|
537
|
-
|
|
538
|
-
def convert(self, value, param, ctx):
|
|
539
|
-
return FQN.from_string(value)
|
|
@@ -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
|
|
|
@@ -71,6 +73,7 @@ class SnowTyper(typer.Typer):
|
|
|
71
73
|
requires_global_options: bool = True,
|
|
72
74
|
requires_connection: bool = False,
|
|
73
75
|
is_enabled: Callable[[], bool] | None = None,
|
|
76
|
+
require_warehouse: bool = False,
|
|
74
77
|
**kwargs,
|
|
75
78
|
):
|
|
76
79
|
"""
|
|
@@ -97,15 +100,15 @@ class SnowTyper(typer.Typer):
|
|
|
97
100
|
def command_callable_decorator(*args, **kw):
|
|
98
101
|
"""Wrapper around command callable. This is what happens at "runtime"."""
|
|
99
102
|
execution = ExecutionMetadata()
|
|
100
|
-
self.pre_execute(execution)
|
|
103
|
+
self.pre_execute(execution, require_warehouse=require_warehouse)
|
|
101
104
|
try:
|
|
102
105
|
result = command_callable(*args, **kw)
|
|
103
106
|
self.process_result(result)
|
|
104
107
|
execution.complete(ExecutionStatus.SUCCESS)
|
|
105
108
|
except Exception as err:
|
|
106
109
|
execution.complete(ExecutionStatus.FAILURE)
|
|
107
|
-
self.exception_handler(err, execution)
|
|
108
|
-
raise
|
|
110
|
+
exception = self.exception_handler(err, execution)
|
|
111
|
+
raise exception
|
|
109
112
|
finally:
|
|
110
113
|
self.post_execute(execution)
|
|
111
114
|
|
|
@@ -116,7 +119,7 @@ class SnowTyper(typer.Typer):
|
|
|
116
119
|
return custom_command
|
|
117
120
|
|
|
118
121
|
@staticmethod
|
|
119
|
-
def pre_execute(execution: ExecutionMetadata):
|
|
122
|
+
def pre_execute(execution: ExecutionMetadata, require_warehouse: bool = False):
|
|
120
123
|
"""
|
|
121
124
|
Callback executed before running any command callable (after context execution).
|
|
122
125
|
Pay attention to make this method safe to use if performed operations are not necessary
|
|
@@ -125,8 +128,11 @@ class SnowTyper(typer.Typer):
|
|
|
125
128
|
from snowflake.cli._app.telemetry import log_command_usage
|
|
126
129
|
|
|
127
130
|
log.debug("Executing command pre execution callback")
|
|
128
|
-
run_pre_execute_commands()
|
|
129
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
|
+
)
|
|
130
136
|
|
|
131
137
|
@staticmethod
|
|
132
138
|
def process_result(result):
|
|
@@ -150,6 +156,9 @@ class SnowTyper(typer.Typer):
|
|
|
150
156
|
|
|
151
157
|
log.debug("Executing command exception callback")
|
|
152
158
|
log_command_execution_error(exception, execution)
|
|
159
|
+
if isinstance(exception, DatabaseError):
|
|
160
|
+
return ClickException(exception.msg)
|
|
161
|
+
return exception
|
|
153
162
|
|
|
154
163
|
@staticmethod
|
|
155
164
|
def post_execute(execution: ExecutionMetadata):
|
snowflake/cli/api/config.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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: {})
|
|
@@ -160,6 +163,18 @@ def _read_config_file():
|
|
|
160
163
|
message="Bad owner or permissions.*",
|
|
161
164
|
module="snowflake.connector.config_manager",
|
|
162
165
|
)
|
|
166
|
+
|
|
167
|
+
if not file_permissions_are_strict(CONFIG_MANAGER.file_path):
|
|
168
|
+
users = ", ".join(
|
|
169
|
+
windows_get_not_whitelisted_users_with_access(
|
|
170
|
+
CONFIG_MANAGER.file_path
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
warnings.warn(
|
|
174
|
+
f"Unauthorized users ({users}) have access to configuration file {CONFIG_MANAGER.file_path}.\n"
|
|
175
|
+
f'Run `icacls "{CONFIG_MANAGER.file_path}" /deny <USER_ID>:F` on those users to restrict permissions.'
|
|
176
|
+
)
|
|
177
|
+
|
|
163
178
|
try:
|
|
164
179
|
CONFIG_MANAGER.read_config()
|
|
165
180
|
except ConfigSourceError as exception:
|
|
@@ -281,7 +296,7 @@ def _initialise_config(config_file: Path) -> None:
|
|
|
281
296
|
|
|
282
297
|
|
|
283
298
|
def get_env_variable_name(*path, key: str) -> str:
|
|
284
|
-
return "
|
|
299
|
+
return ("_".join(["snowflake", *path, key])).upper()
|
|
285
300
|
|
|
286
301
|
|
|
287
302
|
def get_env_value(*path, key: str) -> str | None:
|
|
@@ -322,8 +337,6 @@ def _dump_config(conf_file_cache: Dict):
|
|
|
322
337
|
|
|
323
338
|
|
|
324
339
|
def _check_default_config_files_permissions() -> None:
|
|
325
|
-
if IS_WINDOWS:
|
|
326
|
-
return
|
|
327
340
|
if CONNECTIONS_FILE.exists() and not file_permissions_are_strict(CONNECTIONS_FILE):
|
|
328
341
|
raise ConfigFileTooWidePermissionsError(CONNECTIONS_FILE)
|
|
329
342
|
if CONFIG_FILE.exists() and not file_permissions_are_strict(CONFIG_FILE):
|