snowflake-cli-labs 2.3.0rc1__py3-none-any.whl → 2.4.0rc0__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/api/__init__.py +2 -0
- snowflake/cli/api/cli_global_context.py +8 -1
- snowflake/cli/api/commands/decorators.py +2 -2
- snowflake/cli/api/commands/flags.py +49 -4
- snowflake/cli/api/commands/snow_typer.py +2 -0
- snowflake/cli/api/console/abc.py +2 -0
- snowflake/cli/api/console/console.py +6 -5
- snowflake/cli/api/constants.py +5 -0
- snowflake/cli/api/exceptions.py +12 -0
- snowflake/cli/api/identifiers.py +123 -0
- snowflake/cli/api/plugins/command/__init__.py +2 -0
- snowflake/cli/api/plugins/plugin_config.py +2 -0
- snowflake/cli/api/project/definition.py +2 -0
- snowflake/cli/api/project/errors.py +3 -3
- snowflake/cli/api/project/schemas/identifier_model.py +35 -0
- snowflake/cli/api/project/schemas/native_app/native_app.py +4 -0
- snowflake/cli/api/project/schemas/native_app/path_mapping.py +21 -3
- snowflake/cli/api/project/schemas/project_definition.py +58 -6
- snowflake/cli/api/project/schemas/snowpark/argument.py +2 -0
- snowflake/cli/api/project/schemas/snowpark/callable.py +8 -17
- snowflake/cli/api/project/schemas/streamlit/streamlit.py +2 -2
- snowflake/cli/api/project/schemas/updatable_model.py +2 -0
- snowflake/cli/api/project/util.py +2 -0
- snowflake/cli/api/secure_path.py +2 -0
- snowflake/cli/api/sql_execution.py +14 -54
- snowflake/cli/api/utils/cursor.py +2 -0
- snowflake/cli/api/utils/models.py +23 -0
- snowflake/cli/api/utils/naming_utils.py +0 -27
- snowflake/cli/api/utils/rendering.py +178 -23
- snowflake/cli/app/api_impl/plugin/plugin_config_provider_impl.py +2 -0
- snowflake/cli/app/cli_app.py +4 -1
- snowflake/cli/app/commands_registration/builtin_plugins.py +8 -0
- snowflake/cli/app/commands_registration/command_plugins_loader.py +2 -0
- snowflake/cli/app/commands_registration/commands_registration_with_callbacks.py +2 -0
- snowflake/cli/app/commands_registration/typer_registration.py +2 -0
- snowflake/cli/app/dev/pycharm_remote_debug.py +2 -0
- snowflake/cli/app/loggers.py +2 -0
- snowflake/cli/app/main_typer.py +1 -1
- snowflake/cli/app/printing.py +3 -1
- snowflake/cli/app/snow_connector.py +2 -2
- snowflake/cli/plugins/connection/commands.py +5 -14
- snowflake/cli/plugins/connection/util.py +1 -1
- snowflake/cli/plugins/cortex/__init__.py +0 -0
- snowflake/cli/plugins/cortex/commands.py +312 -0
- snowflake/cli/plugins/cortex/constants.py +3 -0
- snowflake/cli/plugins/cortex/manager.py +175 -0
- snowflake/cli/plugins/cortex/plugin_spec.py +16 -0
- snowflake/cli/plugins/cortex/types.py +8 -0
- snowflake/cli/plugins/git/commands.py +15 -0
- snowflake/cli/plugins/nativeapp/artifacts.py +368 -123
- snowflake/cli/plugins/nativeapp/codegen/artifact_processor.py +45 -0
- snowflake/cli/plugins/nativeapp/codegen/compiler.py +104 -0
- snowflake/cli/plugins/nativeapp/codegen/sandbox.py +2 -0
- snowflake/cli/plugins/nativeapp/codegen/snowpark/callback_source.py.jinja +181 -0
- snowflake/cli/plugins/nativeapp/codegen/snowpark/extension_function_utils.py +196 -0
- snowflake/cli/plugins/nativeapp/codegen/snowpark/models.py +47 -0
- snowflake/cli/plugins/nativeapp/codegen/snowpark/python_processor.py +489 -0
- snowflake/cli/plugins/nativeapp/commands.py +11 -4
- snowflake/cli/plugins/nativeapp/common_flags.py +12 -5
- snowflake/cli/plugins/nativeapp/constants.py +1 -0
- snowflake/cli/plugins/nativeapp/manager.py +49 -16
- snowflake/cli/plugins/nativeapp/policy.py +2 -0
- snowflake/cli/plugins/nativeapp/run_processor.py +28 -10
- snowflake/cli/plugins/nativeapp/teardown_processor.py +78 -8
- snowflake/cli/plugins/nativeapp/utils.py +7 -6
- snowflake/cli/plugins/nativeapp/version/commands.py +6 -5
- snowflake/cli/plugins/nativeapp/version/version_processor.py +2 -0
- snowflake/cli/plugins/notebook/commands.py +21 -0
- snowflake/cli/plugins/notebook/exceptions.py +6 -0
- snowflake/cli/plugins/notebook/manager.py +46 -3
- snowflake/cli/plugins/notebook/types.py +2 -0
- snowflake/cli/plugins/object/command_aliases.py +80 -0
- snowflake/cli/plugins/object/commands.py +10 -6
- snowflake/cli/plugins/object/common.py +2 -0
- snowflake/cli/plugins/object_stage_deprecated/__init__.py +1 -0
- snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +20 -0
- snowflake/cli/plugins/snowpark/commands.py +62 -6
- snowflake/cli/plugins/snowpark/common.py +17 -6
- snowflake/cli/plugins/spcs/compute_pool/commands.py +22 -1
- snowflake/cli/plugins/spcs/compute_pool/manager.py +2 -0
- snowflake/cli/plugins/spcs/image_repository/commands.py +25 -1
- snowflake/cli/plugins/spcs/image_repository/manager.py +3 -1
- snowflake/cli/plugins/spcs/services/commands.py +39 -5
- snowflake/cli/plugins/spcs/services/manager.py +2 -0
- snowflake/cli/plugins/sql/commands.py +13 -5
- snowflake/cli/plugins/sql/manager.py +40 -19
- snowflake/cli/plugins/stage/commands.py +29 -3
- snowflake/cli/plugins/stage/diff.py +2 -0
- snowflake/cli/plugins/streamlit/commands.py +26 -10
- snowflake/cli/plugins/streamlit/manager.py +9 -10
- {snowflake_cli_labs-2.3.0rc1.dist-info → snowflake_cli_labs-2.4.0rc0.dist-info}/METADATA +4 -2
- {snowflake_cli_labs-2.3.0rc1.dist-info → snowflake_cli_labs-2.4.0rc0.dist-info}/RECORD +97 -77
- /snowflake/cli/plugins/{object/stage_deprecated → object_stage_deprecated}/commands.py +0 -0
- {snowflake_cli_labs-2.3.0rc1.dist-info → snowflake_cli_labs-2.4.0rc0.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-2.3.0rc1.dist-info → snowflake_cli_labs-2.4.0rc0.dist-info}/entry_points.txt +0 -0
- {snowflake_cli_labs-2.3.0rc1.dist-info → snowflake_cli_labs-2.4.0rc0.dist-info}/licenses/LICENSE +0 -0
snowflake/cli/__about__.py
CHANGED
snowflake/cli/api/__init__.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import re
|
|
2
4
|
from pathlib import Path
|
|
3
5
|
from typing import Dict, Optional
|
|
4
6
|
|
|
5
7
|
from snowflake.cli.api.exceptions import InvalidSchemaError
|
|
6
8
|
from snowflake.cli.api.output.formats import OutputFormat
|
|
9
|
+
from snowflake.cli.api.project.schemas.project_definition import ProjectDefinition
|
|
7
10
|
from snowflake.connector import SnowflakeConnection
|
|
8
11
|
|
|
9
12
|
schema_pattern = re.compile(r".+\..+")
|
|
@@ -280,6 +283,10 @@ class _CliGlobalContextAccess:
|
|
|
280
283
|
def connection(self) -> SnowflakeConnection:
|
|
281
284
|
return self._manager.connection
|
|
282
285
|
|
|
286
|
+
@property
|
|
287
|
+
def connection_context(self) -> _ConnectionContext:
|
|
288
|
+
return self._manager.connection_context
|
|
289
|
+
|
|
283
290
|
@property
|
|
284
291
|
def enable_tracebacks(self) -> bool:
|
|
285
292
|
return self._manager.enable_tracebacks
|
|
@@ -297,7 +304,7 @@ class _CliGlobalContextAccess:
|
|
|
297
304
|
return self._manager.experimental
|
|
298
305
|
|
|
299
306
|
@property
|
|
300
|
-
def project_definition(self):
|
|
307
|
+
def project_definition(self) -> ProjectDefinition | None:
|
|
301
308
|
return self._manager.project_definition
|
|
302
309
|
|
|
303
310
|
@property
|
|
@@ -29,7 +29,7 @@ from snowflake.cli.api.commands.flags import (
|
|
|
29
29
|
VerboseOption,
|
|
30
30
|
WarehouseOption,
|
|
31
31
|
experimental_option,
|
|
32
|
-
|
|
32
|
+
project_type_option,
|
|
33
33
|
)
|
|
34
34
|
from snowflake.cli.api.exceptions import CommandReturnTypeError
|
|
35
35
|
from snowflake.cli.api.output.formats import OutputFormat
|
|
@@ -67,7 +67,7 @@ def with_project_definition(project_name: str):
|
|
|
67
67
|
"project_definition",
|
|
68
68
|
inspect.Parameter.KEYWORD_ONLY,
|
|
69
69
|
annotation=Optional[str],
|
|
70
|
-
default=
|
|
70
|
+
default=project_type_option(project_name),
|
|
71
71
|
)
|
|
72
72
|
],
|
|
73
73
|
)
|
|
@@ -12,6 +12,7 @@ import typer
|
|
|
12
12
|
from click import ClickException
|
|
13
13
|
from snowflake.cli.api.cli_global_context import cli_context_manager
|
|
14
14
|
from snowflake.cli.api.console import cli_console
|
|
15
|
+
from snowflake.cli.api.exceptions import MissingConfiguration
|
|
15
16
|
from snowflake.cli.api.output.formats import OutputFormat
|
|
16
17
|
|
|
17
18
|
DEFAULT_CONTEXT_SETTINGS = {"help_option_names": ["--help", "-h"]}
|
|
@@ -416,7 +417,6 @@ VariablesOption = typer.Option(
|
|
|
416
417
|
"--variable",
|
|
417
418
|
"-D",
|
|
418
419
|
help="Variables for the template. For example: `-D \"<key>=<value>\"`, string values must be in `''`.",
|
|
419
|
-
hidden=True,
|
|
420
420
|
show_default=False,
|
|
421
421
|
)
|
|
422
422
|
|
|
@@ -469,17 +469,21 @@ def experimental_option(
|
|
|
469
469
|
|
|
470
470
|
def identifier_argument(sf_object: str, example: str) -> typer.Argument:
|
|
471
471
|
return typer.Argument(
|
|
472
|
-
...,
|
|
472
|
+
...,
|
|
473
|
+
help=f"Identifier of the {sf_object}. For example: {example}",
|
|
474
|
+
show_default=False,
|
|
473
475
|
)
|
|
474
476
|
|
|
475
477
|
|
|
476
478
|
def execution_identifier_argument(sf_object: str, example: str) -> typer.Argument:
|
|
477
479
|
return typer.Argument(
|
|
478
|
-
...,
|
|
480
|
+
...,
|
|
481
|
+
help=f"Execution identifier of the {sf_object}. For example: {example}",
|
|
482
|
+
show_default=False,
|
|
479
483
|
)
|
|
480
484
|
|
|
481
485
|
|
|
482
|
-
def
|
|
486
|
+
def project_type_option(project_name: str):
|
|
483
487
|
from snowflake.cli.api.exceptions import NoProjectDefinitionError
|
|
484
488
|
from snowflake.cli.api.project.definition_manager import DefinitionManager
|
|
485
489
|
|
|
@@ -515,6 +519,47 @@ def project_definition_option(project_name: str):
|
|
|
515
519
|
)
|
|
516
520
|
|
|
517
521
|
|
|
522
|
+
def project_definition_option(optional: bool = False):
|
|
523
|
+
from snowflake.cli.api.project.definition_manager import DefinitionManager
|
|
524
|
+
|
|
525
|
+
def _callback(project_path: Optional[str]):
|
|
526
|
+
try:
|
|
527
|
+
dm = DefinitionManager(project_path)
|
|
528
|
+
project_definition = dm.project_definition
|
|
529
|
+
project_root = dm.project_root
|
|
530
|
+
except MissingConfiguration:
|
|
531
|
+
if optional:
|
|
532
|
+
project_definition = None
|
|
533
|
+
project_root = None
|
|
534
|
+
else:
|
|
535
|
+
raise
|
|
536
|
+
cli_context_manager.set_project_definition(project_definition)
|
|
537
|
+
cli_context_manager.set_project_root(project_root)
|
|
538
|
+
return project_definition
|
|
539
|
+
|
|
540
|
+
return typer.Option(
|
|
541
|
+
None,
|
|
542
|
+
"-p",
|
|
543
|
+
"--project",
|
|
544
|
+
help=f"Path where Snowflake project resides. Defaults to current working directory.",
|
|
545
|
+
callback=_callback,
|
|
546
|
+
show_default=False,
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def readable_file_option(param_name: str, help_str: str) -> typer.Option:
|
|
551
|
+
return typer.Option(
|
|
552
|
+
None,
|
|
553
|
+
param_name,
|
|
554
|
+
exists=True,
|
|
555
|
+
file_okay=True,
|
|
556
|
+
dir_okay=False,
|
|
557
|
+
readable=True,
|
|
558
|
+
help=help_str,
|
|
559
|
+
show_default=False,
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
|
|
518
563
|
def deprecated_flag_callback(msg: str):
|
|
519
564
|
def _warning_callback(ctx: click.Context, param: click.Parameter, value: Any):
|
|
520
565
|
if ctx.get_parameter_source(param.name) != click.core.ParameterSource.DEFAULT: # type: ignore[attr-defined]
|
snowflake/cli/api/console/abc.py
CHANGED
|
@@ -57,11 +57,12 @@ class CliConsole(AbstractConsole):
|
|
|
57
57
|
self._print(self._format_message(enter_message, Output.PHASE))
|
|
58
58
|
self._in_phase = True
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
try:
|
|
61
|
+
yield self.step
|
|
62
|
+
finally:
|
|
63
|
+
self._in_phase = False
|
|
64
|
+
if exit_message:
|
|
65
|
+
self._print(self._format_message(exit_message, Output.PHASE))
|
|
65
66
|
|
|
66
67
|
def step(self, message: str):
|
|
67
68
|
"""Displays a message to output.
|
snowflake/cli/api/constants.py
CHANGED
|
@@ -23,6 +23,11 @@ class ObjectType(Enum):
|
|
|
23
23
|
DATABASE = ObjectNames("database", "database", "databases")
|
|
24
24
|
FUNCTION = ObjectNames("function", "function", "functions")
|
|
25
25
|
INTEGRATION = ObjectNames("integration", "integration", "integrations")
|
|
26
|
+
EXTERNAL_ACCESS_INTEGRATION = ObjectNames(
|
|
27
|
+
"external-access-integration",
|
|
28
|
+
"external access integration",
|
|
29
|
+
"external access integrations",
|
|
30
|
+
)
|
|
26
31
|
# JOB = ObjectNames("job", "job", "jobs")
|
|
27
32
|
NETWORK_RULE = ObjectNames("network-rule", "network rule", "network rules")
|
|
28
33
|
PROCEDURE = ObjectNames("procedure", "procedure", "procedures")
|
snowflake/cli/api/exceptions.py
CHANGED
|
@@ -128,3 +128,15 @@ class SchemaNotProvidedError(ClickException):
|
|
|
128
128
|
super().__init__(
|
|
129
129
|
"Schema not specified. Please update connection to add `schema` parameter, or re-run command using `--schema` option. Use `snow connection list` to list existing connections."
|
|
130
130
|
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class FQNNameError(ClickException):
|
|
134
|
+
def __init__(self, name: str):
|
|
135
|
+
super().__init__(f"Specified name '{name}' is not valid name.")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class FQNInconsistencyError(ClickException):
|
|
139
|
+
def __init__(self, part: str, name: str):
|
|
140
|
+
super().__init__(
|
|
141
|
+
f"{part.capitalize()} provided but name '{name}' is fully qualified name."
|
|
142
|
+
)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from click import ClickException
|
|
6
|
+
from snowflake.cli.api.cli_global_context import cli_context
|
|
7
|
+
from snowflake.cli.api.exceptions import FQNInconsistencyError, FQNNameError
|
|
8
|
+
from snowflake.cli.api.project.schemas.identifier_model import ObjectIdentifierBaseModel
|
|
9
|
+
from snowflake.cli.api.project.util import VALID_IDENTIFIER_REGEX, unquote_identifier
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class FQN:
|
|
13
|
+
"""
|
|
14
|
+
Class representing an identifier and supporting fully qualified names.
|
|
15
|
+
|
|
16
|
+
The instance supports builder pattern that allows updating the identifier with database and
|
|
17
|
+
schema from different sources. For example:
|
|
18
|
+
|
|
19
|
+
fqn = FQN.from_string("my_schema.object").using_connection(conn)
|
|
20
|
+
fqn = FQN.from_identifier_model(cli_context.project_definition.streamlit).using_context()
|
|
21
|
+
fqn = FQN.from_string("my_name").set_database("db").set_schema("foo")
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, database: str | None, schema: str | None, name: str):
|
|
25
|
+
self._database = database
|
|
26
|
+
self._schema = schema
|
|
27
|
+
self._name = name
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def database(self) -> str | None:
|
|
31
|
+
return self._database
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def schema(self) -> str | None:
|
|
35
|
+
return self._schema
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def name(self) -> str:
|
|
39
|
+
return self._name
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def identifier(self) -> str:
|
|
43
|
+
if self.database:
|
|
44
|
+
return f"{self.database}.{self.schema if self.schema else 'PUBLIC'}.{self.name}"
|
|
45
|
+
if self.schema:
|
|
46
|
+
return f"{self.schema}.{self.name}"
|
|
47
|
+
return self.name
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def url_identifier(self) -> str:
|
|
51
|
+
return ".".join(unquote_identifier(part) for part in self.identifier.split("."))
|
|
52
|
+
|
|
53
|
+
def __str__(self):
|
|
54
|
+
return self.identifier
|
|
55
|
+
|
|
56
|
+
def __eq__(self, other):
|
|
57
|
+
return self.identifier == other.identifier
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def from_string(cls, identifier: str) -> "FQN":
|
|
61
|
+
"""
|
|
62
|
+
Takes in an object name in the form [[database.]schema.]name. Returns a FQN instance.
|
|
63
|
+
"""
|
|
64
|
+
qualifier_pattern = rf"(?:(?P<first_qualifier>{VALID_IDENTIFIER_REGEX})\.)?(?:(?P<second_qualifier>{VALID_IDENTIFIER_REGEX})\.)?(?P<name>{VALID_IDENTIFIER_REGEX})(?P<signature>\(.*\))?"
|
|
65
|
+
result = re.fullmatch(qualifier_pattern, identifier)
|
|
66
|
+
|
|
67
|
+
if result is None:
|
|
68
|
+
raise FQNNameError(identifier)
|
|
69
|
+
|
|
70
|
+
unqualified_name = result.group("name")
|
|
71
|
+
if result.group("second_qualifier") is not None:
|
|
72
|
+
database = result.group("first_qualifier")
|
|
73
|
+
schema = result.group("second_qualifier")
|
|
74
|
+
else:
|
|
75
|
+
database = None
|
|
76
|
+
schema = result.group("first_qualifier")
|
|
77
|
+
if signature := result.group("signature"):
|
|
78
|
+
unqualified_name = unqualified_name + signature
|
|
79
|
+
return cls(name=unqualified_name, schema=schema, database=database)
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def from_identifier_model(cls, model: ObjectIdentifierBaseModel) -> "FQN":
|
|
83
|
+
"""Create an instance from object model."""
|
|
84
|
+
if not isinstance(model, ObjectIdentifierBaseModel):
|
|
85
|
+
raise ClickException(
|
|
86
|
+
f"Expected {type(ObjectIdentifierBaseModel)}, got {model}."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
fqn = cls.from_string(model.name)
|
|
90
|
+
|
|
91
|
+
if fqn.database and model.database:
|
|
92
|
+
raise FQNInconsistencyError("database", model.name)
|
|
93
|
+
if fqn.schema and model.schema_name:
|
|
94
|
+
raise FQNInconsistencyError("schema", model.name)
|
|
95
|
+
|
|
96
|
+
return fqn.set_database(model.database).set_schema(model.schema_name)
|
|
97
|
+
|
|
98
|
+
def set_database(self, database: str | None) -> "FQN":
|
|
99
|
+
if database:
|
|
100
|
+
self._database = database
|
|
101
|
+
return self
|
|
102
|
+
|
|
103
|
+
def set_schema(self, schema: str | None) -> "FQN":
|
|
104
|
+
if schema:
|
|
105
|
+
self._schema = schema
|
|
106
|
+
return self
|
|
107
|
+
|
|
108
|
+
def set_name(self, name: str) -> "FQN":
|
|
109
|
+
self._name = name
|
|
110
|
+
return self
|
|
111
|
+
|
|
112
|
+
def using_connection(self, conn) -> "FQN":
|
|
113
|
+
"""Update the instance with database and schema from connection."""
|
|
114
|
+
# Update the identifier only it if wasn't already a qualified name
|
|
115
|
+
if conn.database and not self.database:
|
|
116
|
+
self.set_database(conn.database)
|
|
117
|
+
if conn.schema and not self.schema:
|
|
118
|
+
self.set_schema(conn.schema)
|
|
119
|
+
return self
|
|
120
|
+
|
|
121
|
+
def using_context(self) -> "FQN":
|
|
122
|
+
"""Update the instance with database and schema from connection in current cli context."""
|
|
123
|
+
return self.using_connection(cli_context.connection)
|
|
@@ -6,14 +6,14 @@ from pydantic import ValidationError
|
|
|
6
6
|
class SchemaValidationError(Exception):
|
|
7
7
|
generic_message = "For field {loc} you provided '{loc}'. This caused: {msg}"
|
|
8
8
|
message_templates = {
|
|
9
|
-
"string_type": "{msg} for field '{loc}', you provided '{input}'",
|
|
10
|
-
"extra_forbidden": "{msg}. You provided field '{loc}' with value '{input}' that is not
|
|
9
|
+
"string_type": "{msg} for field '{loc}', you provided '{input}'.",
|
|
10
|
+
"extra_forbidden": "{msg}. You provided field '{loc}' with value '{input}' that is not supported in given version.",
|
|
11
11
|
"missing": "Your project definition is missing following fields: {loc}",
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
def __init__(self, error: ValidationError):
|
|
15
15
|
errors = error.errors()
|
|
16
|
-
message = f"During evaluation of {error.title}
|
|
16
|
+
message = f"During evaluation of {error.title} in project definition following errors were encountered:\n"
|
|
17
17
|
message += "\n".join(
|
|
18
18
|
[
|
|
19
19
|
self.message_templates.get(e["type"], self.generic_message).format(**e)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Optional, cast
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
from snowflake.cli.api.project.schemas.updatable_model import IdentifierField
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ObjectIdentifierBaseModel:
|
|
10
|
+
"""
|
|
11
|
+
Type representing a base class defining object that can be identified by fully qualified name (db.schema.name).
|
|
12
|
+
This is not a Pydantic model and the purpose of this class is to provide typing support to Pydantic models
|
|
13
|
+
generated using a factory class ObjectIdentifierModel.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
name: str
|
|
17
|
+
database: Optional[str]
|
|
18
|
+
schema_name: Optional[str]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def ObjectIdentifierModel(object_name: str) -> ObjectIdentifierBaseModel: # noqa: N802
|
|
22
|
+
"""Generates ObjectIdentifierBaseModel but with object specific descriptions."""
|
|
23
|
+
|
|
24
|
+
class _ObjectIdentifierModel(ObjectIdentifierBaseModel):
|
|
25
|
+
name: str = Field(title=f"{object_name} name")
|
|
26
|
+
database: Optional[str] = IdentifierField(
|
|
27
|
+
title=f"Name of the database for the {object_name}", default=None
|
|
28
|
+
)
|
|
29
|
+
schema_name: Optional[str] = IdentifierField(
|
|
30
|
+
title=f"Name of the schema for the {object_name}",
|
|
31
|
+
default=None,
|
|
32
|
+
alias="schema",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
return cast(ObjectIdentifierBaseModel, _ObjectIdentifierModel)
|
|
@@ -24,6 +24,10 @@ class NativeApp(UpdatableModel):
|
|
|
24
24
|
title="Folder at the root of your project where the build step copies the artifacts.",
|
|
25
25
|
default="output/deploy/",
|
|
26
26
|
)
|
|
27
|
+
generated_root: Optional[str] = Field(
|
|
28
|
+
title="Subdirectory of the deploy root where files generated by the Snowflake CLI will be written.",
|
|
29
|
+
default="__generated/",
|
|
30
|
+
)
|
|
27
31
|
source_stage: Optional[str] = Field(
|
|
28
32
|
title="Identifier of the stage that stores the application artifacts.",
|
|
29
33
|
default="app_src.stage",
|
|
@@ -2,11 +2,11 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Dict, List, Optional, Union
|
|
4
4
|
|
|
5
|
-
from pydantic import Field
|
|
5
|
+
from pydantic import Field, field_validator
|
|
6
6
|
from snowflake.cli.api.project.schemas.updatable_model import UpdatableModel
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class ProcessorMapping(UpdatableModel):
|
|
10
10
|
name: str = Field(
|
|
11
11
|
title="Name of a processor to invoke on a collection of artifacts."
|
|
12
12
|
)
|
|
@@ -19,4 +19,22 @@ class AnnotationProcessor(UpdatableModel):
|
|
|
19
19
|
class PathMapping(UpdatableModel):
|
|
20
20
|
src: str
|
|
21
21
|
dest: Optional[str] = None
|
|
22
|
-
processors: Optional[List[Union[str,
|
|
22
|
+
processors: Optional[List[Union[str, ProcessorMapping]]] = []
|
|
23
|
+
|
|
24
|
+
@field_validator("processors")
|
|
25
|
+
@classmethod
|
|
26
|
+
def transform_processors(
|
|
27
|
+
cls, input_values: Optional[List[Union[str, Dict, ProcessorMapping]]]
|
|
28
|
+
):
|
|
29
|
+
if input_values is None:
|
|
30
|
+
return []
|
|
31
|
+
|
|
32
|
+
transformed_processors: List[ProcessorMapping] = []
|
|
33
|
+
for input_processor in input_values:
|
|
34
|
+
if isinstance(input_processor, str):
|
|
35
|
+
transformed_processors.append(ProcessorMapping(name=input_processor))
|
|
36
|
+
elif isinstance(input_processor, Dict):
|
|
37
|
+
transformed_processors.append(ProcessorMapping(**input_processor))
|
|
38
|
+
else:
|
|
39
|
+
transformed_processors.append(input_processor)
|
|
40
|
+
return transformed_processors
|
|
@@ -1,20 +1,36 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Any, Dict, Optional, Union
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from packaging.version import Version
|
|
6
|
+
from pydantic import Field, field_validator
|
|
6
7
|
from snowflake.cli.api.project.schemas.native_app.native_app import NativeApp
|
|
7
8
|
from snowflake.cli.api.project.schemas.snowpark.snowpark import Snowpark
|
|
8
9
|
from snowflake.cli.api.project.schemas.streamlit.streamlit import Streamlit
|
|
9
10
|
from snowflake.cli.api.project.schemas.updatable_model import UpdatableModel
|
|
11
|
+
from snowflake.cli.api.utils.models import EnvironWithDefinedDictFallback
|
|
10
12
|
|
|
11
13
|
|
|
12
|
-
class
|
|
13
|
-
definition_version: int = Field(
|
|
14
|
+
class _BaseDefinition(UpdatableModel):
|
|
15
|
+
definition_version: Union[str, int] = Field(
|
|
14
16
|
title="Version of the project definition schema, which is currently 1",
|
|
15
|
-
ge=1,
|
|
16
|
-
le=1,
|
|
17
17
|
)
|
|
18
|
+
|
|
19
|
+
@field_validator("definition_version")
|
|
20
|
+
@classmethod
|
|
21
|
+
def _is_supported_version(cls, version: str) -> str:
|
|
22
|
+
version = str(version)
|
|
23
|
+
if version not in _version_map:
|
|
24
|
+
raise ValueError(
|
|
25
|
+
f'Version {version} is not supported. Supported versions: {", ".join(_version_map)}'
|
|
26
|
+
)
|
|
27
|
+
return version
|
|
28
|
+
|
|
29
|
+
def meets_version_requirement(self, required_version: str) -> bool:
|
|
30
|
+
return Version(self.definition_version) >= Version(required_version)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class _DefinitionV10(_BaseDefinition):
|
|
18
34
|
native_app: Optional[NativeApp] = Field(
|
|
19
35
|
title="Native app definitions for the project", default=None
|
|
20
36
|
)
|
|
@@ -25,3 +41,39 @@ class ProjectDefinition(UpdatableModel):
|
|
|
25
41
|
streamlit: Optional[Streamlit] = Field(
|
|
26
42
|
title="Streamlit definitions for the project", default=None
|
|
27
43
|
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class _DefinitionV11(_DefinitionV10):
|
|
47
|
+
env: Optional[Dict] = Field(
|
|
48
|
+
title="Environment specification for this project.",
|
|
49
|
+
default=None,
|
|
50
|
+
validation_alias="env",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@field_validator("env")
|
|
54
|
+
@classmethod
|
|
55
|
+
def _convert_env(cls, env: Optional[Dict]) -> EnvironWithDefinedDictFallback:
|
|
56
|
+
variables = EnvironWithDefinedDictFallback(env if env else {})
|
|
57
|
+
return variables
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ProjectDefinition(_DefinitionV11):
|
|
61
|
+
def __init__(self, **kwargs):
|
|
62
|
+
super().__init__(**kwargs)
|
|
63
|
+
self._validate(kwargs)
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def _validate(data: Any):
|
|
67
|
+
if not isinstance(data, dict):
|
|
68
|
+
return
|
|
69
|
+
if version := str(data.get("definition_version")):
|
|
70
|
+
version_model = _version_map.get(version)
|
|
71
|
+
if not version_model:
|
|
72
|
+
raise ValueError(
|
|
73
|
+
f"Unknown schema version: {version}. Supported version: {_supported_version}"
|
|
74
|
+
)
|
|
75
|
+
version_model(**data)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
_version_map = {"1": _DefinitionV10, "1.1": _DefinitionV11}
|
|
79
|
+
_supported_version = tuple(_version_map.keys())
|
|
@@ -1,26 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from typing import Dict, List, Optional, Union
|
|
2
4
|
|
|
3
5
|
from pydantic import Field, field_validator
|
|
6
|
+
from snowflake.cli.api.project.schemas.identifier_model import ObjectIdentifierModel
|
|
4
7
|
from snowflake.cli.api.project.schemas.snowpark.argument import Argument
|
|
5
8
|
from snowflake.cli.api.project.schemas.updatable_model import (
|
|
6
|
-
IdentifierField,
|
|
7
9
|
UpdatableModel,
|
|
8
10
|
)
|
|
9
11
|
|
|
10
12
|
|
|
11
|
-
class
|
|
12
|
-
name: str = Field(
|
|
13
|
-
title="Object identifier"
|
|
14
|
-
) # TODO: implement validator. If a name is filly qualified, database and schema cannot be specified
|
|
15
|
-
database: Optional[str] = IdentifierField(
|
|
16
|
-
title="Name of the database for the function or procedure", default=None
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
schema_name: Optional[str] = IdentifierField(
|
|
20
|
-
title="Name of the schema for the function or procedure",
|
|
21
|
-
default=None,
|
|
22
|
-
alias="schema",
|
|
23
|
-
)
|
|
13
|
+
class _CallableBase(UpdatableModel):
|
|
24
14
|
handler: str = Field(
|
|
25
15
|
title="Function’s or procedure’s implementation of the object inside source module",
|
|
26
16
|
examples=["functions.hello_function"],
|
|
@@ -55,12 +45,13 @@ class Callable(UpdatableModel):
|
|
|
55
45
|
return runtime_input
|
|
56
46
|
|
|
57
47
|
|
|
58
|
-
class FunctionSchema(
|
|
48
|
+
class FunctionSchema(_CallableBase, ObjectIdentifierModel(object_name="function")): # type: ignore
|
|
59
49
|
pass
|
|
60
50
|
|
|
61
51
|
|
|
62
|
-
class ProcedureSchema(
|
|
52
|
+
class ProcedureSchema(_CallableBase, ObjectIdentifierModel(object_name="procedure")): # type: ignore
|
|
63
53
|
execute_as_caller: Optional[bool] = Field(
|
|
64
|
-
title="Determine whether the procedure is executed with the privileges of
|
|
54
|
+
title="Determine whether the procedure is executed with the privileges of "
|
|
55
|
+
"the owner (you) or with the privileges of the caller",
|
|
65
56
|
default=False,
|
|
66
57
|
)
|
|
@@ -3,11 +3,11 @@ from __future__ import annotations
|
|
|
3
3
|
from typing import List, Optional
|
|
4
4
|
|
|
5
5
|
from pydantic import Field
|
|
6
|
+
from snowflake.cli.api.project.schemas.identifier_model import ObjectIdentifierModel
|
|
6
7
|
from snowflake.cli.api.project.schemas.updatable_model import UpdatableModel
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
class Streamlit(UpdatableModel):
|
|
10
|
-
name: str = Field(title="App identifier")
|
|
10
|
+
class Streamlit(UpdatableModel, ObjectIdentifierModel(object_name="Streamlit")): # type: ignore
|
|
11
11
|
stage: Optional[str] = Field(
|
|
12
12
|
title="Stage in which the app’s artifacts will be stored", default="streamlit"
|
|
13
13
|
)
|