snowflake-cli-labs 2.8.0rc0__py3-none-any.whl → 3.0.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/{app → _app}/__main__.py +1 -1
- snowflake/cli/{app → _app}/cli_app.py +12 -12
- snowflake/cli/{app → _app}/commands_registration/builtin_plugins.py +13 -19
- snowflake/cli/{app → _app}/commands_registration/command_plugins_loader.py +9 -9
- snowflake/cli/{app → _app}/commands_registration/commands_registration_with_callbacks.py +4 -4
- snowflake/cli/{app → _app}/commands_registration/exception_logging.py +2 -2
- snowflake/cli/{app → _app}/commands_registration/typer_registration.py +2 -2
- snowflake/cli/{app → _app}/dev/docs/commands_docs_generator.py +30 -12
- snowflake/cli/{app → _app}/dev/docs/generator.py +3 -3
- snowflake/cli/{app → _app}/dev/docs/project_definition_docs_generator.py +4 -4
- snowflake/cli/{app → _app}/dev/docs/templates/usage.rst.jinja2 +14 -4
- snowflake/cli/{app → _app}/main_typer.py +2 -2
- snowflake/cli/{app → _app}/printing.py +2 -2
- snowflake/cli/{app → _app}/snow_connector.py +6 -6
- snowflake/cli/{app → _app}/telemetry.py +4 -5
- snowflake/cli/{plugins → _plugins}/connection/commands.py +22 -5
- snowflake/cli/_plugins/connection/plugin_spec.py +30 -0
- snowflake/cli/{plugins → _plugins}/connection/util.py +16 -0
- snowflake/cli/{plugins → _plugins}/cortex/commands.py +54 -49
- snowflake/cli/{plugins → _plugins}/cortex/constants.py +1 -1
- snowflake/cli/{plugins → _plugins}/cortex/manager.py +5 -5
- snowflake/cli/{plugins → _plugins}/cortex/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/git/commands.py +32 -20
- snowflake/cli/{plugins → _plugins}/git/manager.py +6 -5
- snowflake/cli/{plugins → _plugins}/git/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/init/commands.py +10 -6
- snowflake/cli/{plugins → _plugins}/init/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/nativeapp/artifacts.py +14 -0
- snowflake/cli/_plugins/nativeapp/bundle_context.py +31 -0
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/artifact_processor.py +3 -3
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/compiler.py +16 -18
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/setup/native_app_setup_processor.py +24 -28
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/extension_function_utils.py +4 -4
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/python_processor.py +20 -24
- snowflake/cli/{plugins → _plugins}/nativeapp/commands.py +171 -42
- snowflake/cli/{plugins → _plugins}/nativeapp/common_flags.py +1 -1
- snowflake/cli/{plugins → _plugins}/nativeapp/init.py +1 -1
- snowflake/cli/_plugins/nativeapp/manager.py +601 -0
- snowflake/cli/{plugins/connection → _plugins/nativeapp}/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/nativeapp/project_model.py +34 -11
- snowflake/cli/{plugins → _plugins}/nativeapp/run_processor.py +25 -23
- snowflake/cli/{plugins → _plugins}/nativeapp/teardown_processor.py +8 -8
- snowflake/cli/{plugins → _plugins}/nativeapp/v2_conversions/v2_to_v1_decorator.py +47 -28
- snowflake/cli/{plugins → _plugins}/nativeapp/version/commands.py +15 -12
- snowflake/cli/{plugins → _plugins}/nativeapp/version/version_processor.py +22 -20
- snowflake/cli/{plugins → _plugins}/notebook/commands.py +8 -6
- snowflake/cli/{plugins → _plugins}/notebook/manager.py +14 -14
- snowflake/cli/{plugins → _plugins}/notebook/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/notebook/types.py +0 -1
- snowflake/cli/{plugins → _plugins}/object/command_aliases.py +6 -5
- snowflake/cli/{plugins → _plugins}/object/commands.py +16 -10
- snowflake/cli/{plugins → _plugins}/object/manager.py +7 -6
- snowflake/cli/{plugins → _plugins}/object/plugin_spec.py +1 -1
- snowflake/cli/_plugins/snowpark/commands.py +510 -0
- snowflake/cli/_plugins/snowpark/common.py +252 -0
- snowflake/cli/{plugins → _plugins}/snowpark/models.py +0 -7
- snowflake/cli/{plugins → _plugins}/snowpark/package/anaconda_packages.py +1 -1
- snowflake/cli/{plugins → _plugins}/snowpark/package/commands.py +13 -74
- snowflake/cli/{plugins → _plugins}/snowpark/package/manager.py +4 -3
- snowflake/cli/{plugins → _plugins}/snowpark/package_utils.py +5 -5
- snowflake/cli/{plugins/nativeapp → _plugins/snowpark}/plugin_spec.py +1 -1
- snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +109 -0
- snowflake/cli/{plugins → _plugins}/snowpark/snowpark_shared.py +0 -36
- snowflake/cli/{plugins → _plugins}/snowpark/zipper.py +16 -8
- snowflake/cli/{plugins → _plugins}/spcs/__init__.py +5 -7
- snowflake/cli/{plugins → _plugins}/spcs/compute_pool/commands.py +29 -28
- snowflake/cli/{plugins → _plugins}/spcs/compute_pool/manager.py +3 -3
- snowflake/cli/{plugins → _plugins}/spcs/image_registry/commands.py +3 -3
- snowflake/cli/{plugins → _plugins}/spcs/image_repository/commands.py +25 -19
- snowflake/cli/{plugins → _plugins}/spcs/image_repository/manager.py +1 -1
- snowflake/cli/{plugins → _plugins}/spcs/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/spcs/services/commands.py +66 -32
- snowflake/cli/{plugins → _plugins}/spcs/services/manager.py +43 -5
- snowflake/cli/{plugins → _plugins}/sql/commands.py +19 -15
- snowflake/cli/{plugins → _plugins}/sql/manager.py +1 -1
- snowflake/cli/{plugins → _plugins}/sql/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/stage/commands.py +20 -17
- snowflake/cli/{plugins → _plugins}/stage/diff.py +1 -47
- snowflake/cli/{plugins → _plugins}/stage/manager.py +8 -6
- snowflake/cli/{plugins → _plugins}/stage/plugin_spec.py +1 -1
- snowflake/cli/_plugins/stage/utils.py +54 -0
- snowflake/cli/_plugins/streamlit/commands.py +242 -0
- snowflake/cli/{plugins → _plugins}/streamlit/manager.py +47 -70
- snowflake/cli/_plugins/streamlit/plugin_spec.py +30 -0
- snowflake/cli/_plugins/workspace/action_context.py +11 -0
- snowflake/cli/_plugins/workspace/commands.py +113 -0
- snowflake/cli/_plugins/workspace/manager.py +57 -0
- snowflake/cli/{plugins → _plugins}/workspace/plugin_spec.py +1 -1
- snowflake/cli/api/cli_global_context.py +34 -7
- snowflake/cli/api/commands/common.py +25 -0
- snowflake/cli/api/commands/decorators.py +4 -3
- snowflake/cli/api/commands/experimental_behaviour.py +2 -3
- snowflake/cli/api/commands/flags.py +73 -174
- snowflake/cli/api/commands/overrideable_parameter.py +143 -0
- snowflake/cli/api/commands/snow_typer.py +5 -4
- snowflake/cli/api/commands/typer_pre_execute.py +3 -3
- snowflake/cli/api/commands/utils.py +18 -0
- snowflake/cli/api/config.py +1 -1
- snowflake/cli/api/console/abc.py +5 -2
- snowflake/cli/api/entities/application_entity.py +12 -0
- snowflake/cli/api/entities/application_package_entity.py +260 -0
- snowflake/cli/api/entities/common.py +47 -0
- snowflake/cli/api/entities/snowpark_entity.py +29 -0
- snowflake/cli/api/entities/streamlit_entity.py +12 -0
- snowflake/cli/api/entities/utils.py +321 -0
- snowflake/cli/api/exceptions.py +19 -3
- snowflake/cli/api/feature_flags.py +2 -1
- snowflake/cli/api/identifiers.py +41 -9
- snowflake/cli/api/project/definition.py +13 -5
- snowflake/cli/api/project/definition_manager.py +12 -1
- snowflake/cli/api/project/errors.py +16 -1
- snowflake/cli/api/project/project_verification.py +3 -3
- snowflake/cli/api/project/schemas/entities/{application_entity.py → application_entity_model.py} +21 -9
- snowflake/cli/api/project/schemas/entities/{application_package_entity.py → application_package_entity_model.py} +26 -15
- snowflake/cli/api/project/schemas/entities/common.py +80 -6
- snowflake/cli/api/project/schemas/entities/entities.py +38 -8
- snowflake/cli/api/project/schemas/entities/snowpark_entity.py +176 -0
- snowflake/cli/api/project/schemas/entities/streamlit_entity_model.py +73 -0
- snowflake/cli/api/project/schemas/identifier_model.py +10 -1
- snowflake/cli/api/project/schemas/native_app/application.py +8 -9
- snowflake/cli/api/project/schemas/native_app/package.py +7 -1
- snowflake/cli/api/project/schemas/project_definition.py +97 -23
- snowflake/cli/api/project/schemas/updatable_model.py +11 -3
- snowflake/cli/api/project/util.py +23 -6
- snowflake/cli/api/rendering/jinja.py +28 -8
- snowflake/cli/api/rendering/sql_templates.py +41 -12
- snowflake/cli/api/secure_path.py +3 -0
- snowflake/cli/api/sql_execution.py +35 -19
- snowflake/cli/api/utils/definition_rendering.py +14 -2
- {snowflake_cli_labs-2.8.0rc0.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/METADATA +12 -12
- snowflake_cli_labs-3.0.0rc0.dist-info/RECORD +234 -0
- snowflake_cli_labs-3.0.0rc0.dist-info/entry_points.txt +2 -0
- snowflake/cli/api/commands/project_initialisation.py +0 -65
- snowflake/cli/app/build_and_push.sh +0 -8
- snowflake/cli/plugins/nativeapp/manager.py +0 -819
- snowflake/cli/plugins/object_stage_deprecated/__init__.py +0 -15
- snowflake/cli/plugins/object_stage_deprecated/commands.py +0 -122
- snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +0 -32
- snowflake/cli/plugins/snowpark/commands.py +0 -548
- snowflake/cli/plugins/snowpark/common.py +0 -307
- snowflake/cli/plugins/snowpark/manager.py +0 -109
- snowflake/cli/plugins/snowpark/plugin_spec.py +0 -30
- snowflake/cli/plugins/snowpark/snowpark_package_paths.py +0 -65
- snowflake/cli/plugins/spcs/jobs/commands.py +0 -78
- snowflake/cli/plugins/spcs/jobs/manager.py +0 -53
- snowflake/cli/plugins/streamlit/commands.py +0 -186
- snowflake/cli/plugins/streamlit/plugin_spec.py +0 -30
- snowflake/cli/plugins/workspace/commands.py +0 -35
- snowflake/cli/templates/default_snowpark/.gitignore +0 -4
- snowflake/cli/templates/default_snowpark/app/__init__.py +0 -0
- snowflake/cli/templates/default_snowpark/app/common.py +0 -2
- snowflake/cli/templates/default_snowpark/app/functions.py +0 -15
- snowflake/cli/templates/default_snowpark/app/procedures.py +0 -22
- snowflake/cli/templates/default_snowpark/requirements.txt +0 -1
- snowflake/cli/templates/default_snowpark/snowflake.yml +0 -23
- snowflake/cli/templates/default_streamlit/.gitignore +0 -4
- snowflake/cli/templates/default_streamlit/common/hello.py +0 -2
- snowflake/cli/templates/default_streamlit/environment.yml +0 -6
- snowflake/cli/templates/default_streamlit/pages/my_page.py +0 -3
- snowflake/cli/templates/default_streamlit/snowflake.yml +0 -10
- snowflake/cli/templates/default_streamlit/streamlit_app.py +0 -4
- snowflake_cli_labs-2.8.0rc0.dist-info/RECORD +0 -240
- snowflake_cli_labs-2.8.0rc0.dist-info/entry_points.txt +0 -2
- /snowflake/cli/{app → _app}/__init__.py +0 -0
- /snowflake/cli/{app → _app}/api_impl/__init__.py +0 -0
- /snowflake/cli/{app → _app}/api_impl/plugin/__init__.py +0 -0
- /snowflake/cli/{app → _app}/api_impl/plugin/plugin_config_provider_impl.py +0 -0
- /snowflake/cli/{app → _app}/commands_registration/__init__.py +0 -0
- /snowflake/cli/{app → _app}/commands_registration/threadsafe.py +0 -0
- /snowflake/cli/{app → _app}/constants.py +0 -0
- /snowflake/cli/{app → _app}/dev/__init__.py +0 -0
- /snowflake/cli/{app → _app}/dev/commands_structure.py +0 -0
- /snowflake/cli/{app → _app}/dev/docs/__init__.py +0 -0
- /snowflake/cli/{app → _app}/dev/docs/project_definition_generate_json_schema.py +0 -0
- /snowflake/cli/{app → _app}/dev/docs/template_utils.py +0 -0
- /snowflake/cli/{app → _app}/dev/docs/templates/definition_description.rst.jinja2 +0 -0
- /snowflake/cli/{app → _app}/dev/docs/templates/overview.rst.jinja2 +0 -0
- /snowflake/cli/{app → _app}/dev/pycharm_remote_debug.py +0 -0
- /snowflake/cli/{app → _app}/loggers.py +0 -0
- /snowflake/cli/{plugins → _plugins}/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/connection/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/cortex/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/cortex/types.py +0 -0
- /snowflake/cli/{plugins → _plugins}/git/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/init/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/sandbox.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/setup/setup_driver.py.source +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/models.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/constants.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/exceptions.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/feature_flags.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/policy.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/utils.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/version/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/notebook/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/notebook/exceptions.py +0 -0
- /snowflake/cli/{plugins → _plugins}/object/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/object/common.py +0 -0
- /snowflake/cli/{plugins → _plugins}/snowpark/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/snowpark/package/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/snowpark/package/utils.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/common.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/compute_pool/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/image_registry/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/image_registry/manager.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/image_repository/__init__.py +0 -0
- /snowflake/cli/{plugins/spcs/jobs → _plugins/spcs/services}/__init__.py +0 -0
- /snowflake/cli/{plugins/spcs/services → _plugins/sql}/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/sql/snowsql_templating.py +0 -0
- /snowflake/cli/{plugins/sql → _plugins/stage}/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/stage/md5.py +0 -0
- /snowflake/cli/{plugins/stage → _plugins/streamlit}/__init__.py +0 -0
- /snowflake/cli/{plugins/streamlit → _plugins/workspace}/__init__.py +0 -0
- /snowflake/cli/{plugins/workspace → api/project/schemas/entities}/__init__.py +0 -0
- {snowflake_cli_labs-2.8.0rc0.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-2.8.0rc0.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from textwrap import dedent
|
|
4
|
+
from typing import Any, Callable, Dict, List, NoReturn, Optional
|
|
5
|
+
|
|
6
|
+
import jinja2
|
|
7
|
+
from click import ClickException
|
|
8
|
+
from snowflake.cli._plugins.nativeapp.artifacts import (
|
|
9
|
+
BundleMap,
|
|
10
|
+
resolve_without_follow,
|
|
11
|
+
)
|
|
12
|
+
from snowflake.cli._plugins.nativeapp.constants import OWNER_COL
|
|
13
|
+
from snowflake.cli._plugins.nativeapp.exceptions import (
|
|
14
|
+
InvalidScriptError,
|
|
15
|
+
MissingScriptError,
|
|
16
|
+
UnexpectedOwnerError,
|
|
17
|
+
)
|
|
18
|
+
from snowflake.cli._plugins.nativeapp.utils import verify_exists, verify_no_directories
|
|
19
|
+
from snowflake.cli._plugins.stage.diff import (
|
|
20
|
+
DiffResult,
|
|
21
|
+
StagePath,
|
|
22
|
+
compute_stage_diff,
|
|
23
|
+
preserve_from_diff,
|
|
24
|
+
sync_local_diff_with_stage,
|
|
25
|
+
to_stage_path,
|
|
26
|
+
)
|
|
27
|
+
from snowflake.cli._plugins.stage.utils import print_diff_to_console
|
|
28
|
+
from snowflake.cli.api.console.abc import AbstractConsole
|
|
29
|
+
from snowflake.cli.api.entities.common import get_sql_executor
|
|
30
|
+
from snowflake.cli.api.errno import (
|
|
31
|
+
DOES_NOT_EXIST_OR_CANNOT_BE_PERFORMED,
|
|
32
|
+
NO_WAREHOUSE_SELECTED_IN_SESSION,
|
|
33
|
+
)
|
|
34
|
+
from snowflake.cli.api.project.schemas.entities.common import PostDeployHook
|
|
35
|
+
from snowflake.cli.api.project.util import unquote_identifier
|
|
36
|
+
from snowflake.cli.api.rendering.sql_templates import (
|
|
37
|
+
snowflake_sql_jinja_render,
|
|
38
|
+
)
|
|
39
|
+
from snowflake.cli.api.secure_path import UNLIMITED, SecurePath
|
|
40
|
+
from snowflake.connector import ProgrammingError
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def generic_sql_error_handler(
|
|
44
|
+
err: ProgrammingError, role: Optional[str] = None, warehouse: Optional[str] = None
|
|
45
|
+
) -> NoReturn:
|
|
46
|
+
# Potential refactor: If moving away from Python 3.8 and 3.9 to >= 3.10, use match ... case
|
|
47
|
+
if err.errno == DOES_NOT_EXIST_OR_CANNOT_BE_PERFORMED:
|
|
48
|
+
raise ProgrammingError(
|
|
49
|
+
msg=dedent(
|
|
50
|
+
f"""\
|
|
51
|
+
Received error message '{err.msg}' while executing SQL statement.
|
|
52
|
+
'{role}' may not have access to warehouse '{warehouse}'.
|
|
53
|
+
Please grant usage privilege on warehouse to this role.
|
|
54
|
+
"""
|
|
55
|
+
),
|
|
56
|
+
errno=err.errno,
|
|
57
|
+
)
|
|
58
|
+
elif err.errno == NO_WAREHOUSE_SELECTED_IN_SESSION:
|
|
59
|
+
raise ProgrammingError(
|
|
60
|
+
msg=dedent(
|
|
61
|
+
f"""\
|
|
62
|
+
Received error message '{err.msg}' while executing SQL statement.
|
|
63
|
+
Please provide a warehouse for the active session role in your project definition file, config.toml file, or via command line.
|
|
64
|
+
"""
|
|
65
|
+
),
|
|
66
|
+
errno=err.errno,
|
|
67
|
+
)
|
|
68
|
+
elif "does not exist or not authorized" in err.msg:
|
|
69
|
+
raise ProgrammingError(
|
|
70
|
+
msg=dedent(
|
|
71
|
+
f"""\
|
|
72
|
+
Received error message '{err.msg}' while executing SQL statement.
|
|
73
|
+
Please check the name of the resource you are trying to query or the permissions of the role you are using to run the query.
|
|
74
|
+
"""
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
raise err
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def ensure_correct_owner(row: dict, role: str, obj_name: str) -> None:
|
|
81
|
+
"""
|
|
82
|
+
Check if an object has the right owner role
|
|
83
|
+
"""
|
|
84
|
+
actual_owner = row[
|
|
85
|
+
OWNER_COL
|
|
86
|
+
].upper() # Because unquote_identifier() always returns uppercase str
|
|
87
|
+
if actual_owner != unquote_identifier(role):
|
|
88
|
+
raise UnexpectedOwnerError(obj_name, role, actual_owner)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _get_stage_paths_to_sync(
|
|
92
|
+
local_paths_to_sync: List[Path], deploy_root: Path
|
|
93
|
+
) -> List[StagePath]:
|
|
94
|
+
"""
|
|
95
|
+
Takes a list of paths (files and directories), returning a list of all files recursively relative to the deploy root.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
stage_paths = []
|
|
99
|
+
for path in local_paths_to_sync:
|
|
100
|
+
if path.is_dir():
|
|
101
|
+
for current_dir, _dirs, files in os.walk(path):
|
|
102
|
+
for file in files:
|
|
103
|
+
deploy_path = Path(current_dir, file).relative_to(deploy_root)
|
|
104
|
+
stage_paths.append(to_stage_path(deploy_path))
|
|
105
|
+
else:
|
|
106
|
+
stage_paths.append(to_stage_path(path.relative_to(deploy_root)))
|
|
107
|
+
return stage_paths
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def sync_deploy_root_with_stage(
|
|
111
|
+
console: AbstractConsole,
|
|
112
|
+
deploy_root: Path,
|
|
113
|
+
package_name: str,
|
|
114
|
+
stage_schema: str,
|
|
115
|
+
bundle_map: BundleMap,
|
|
116
|
+
role: str,
|
|
117
|
+
prune: bool,
|
|
118
|
+
recursive: bool,
|
|
119
|
+
stage_fqn: str,
|
|
120
|
+
local_paths_to_sync: List[Path] | None = None,
|
|
121
|
+
print_diff: bool = True,
|
|
122
|
+
) -> DiffResult:
|
|
123
|
+
"""
|
|
124
|
+
Ensures that the files on our remote stage match the artifacts we have in
|
|
125
|
+
the local filesystem.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
bundle_map (BundleMap): The artifact mapping computed by the `build_bundle` function.
|
|
129
|
+
role (str): The name of the role to use for queries and commands.
|
|
130
|
+
prune (bool): Whether to prune artifacts from the stage that don't exist locally.
|
|
131
|
+
recursive (bool): Whether to traverse directories recursively.
|
|
132
|
+
stage_fqn (str): The name of the stage to diff against and upload to.
|
|
133
|
+
local_paths_to_sync (List[Path], optional): List of local paths to sync. Defaults to None to sync all
|
|
134
|
+
local paths. Note that providing an empty list here is equivalent to None.
|
|
135
|
+
print_diff (bool): Whether to print the diff between the local files and the remote stage. Defaults to True
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
A `DiffResult` instance describing the changes that were performed.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
sql_executor = get_sql_executor()
|
|
142
|
+
# Does a stage already exist within the application package, or we need to create one?
|
|
143
|
+
# Using "if not exists" should take care of either case.
|
|
144
|
+
console.step(
|
|
145
|
+
f"Checking if stage {stage_fqn} exists, or creating a new one if none exists."
|
|
146
|
+
)
|
|
147
|
+
with sql_executor.use_role(role):
|
|
148
|
+
sql_executor.execute_query(
|
|
149
|
+
f"create schema if not exists {package_name}.{stage_schema}"
|
|
150
|
+
)
|
|
151
|
+
sql_executor.execute_query(
|
|
152
|
+
f"""
|
|
153
|
+
create stage if not exists {stage_fqn}
|
|
154
|
+
encryption = (TYPE = 'SNOWFLAKE_SSE')
|
|
155
|
+
DIRECTORY = (ENABLE = TRUE)"""
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Perform a diff operation and display results to the user for informational purposes
|
|
159
|
+
if print_diff:
|
|
160
|
+
console.step(
|
|
161
|
+
"Performing a diff between the Snowflake stage and your local deploy_root ('%s') directory."
|
|
162
|
+
% deploy_root.resolve()
|
|
163
|
+
)
|
|
164
|
+
diff: DiffResult = compute_stage_diff(deploy_root, stage_fqn)
|
|
165
|
+
|
|
166
|
+
if local_paths_to_sync:
|
|
167
|
+
# Deploying specific files/directories
|
|
168
|
+
resolved_paths_to_sync = [
|
|
169
|
+
resolve_without_follow(p) for p in local_paths_to_sync
|
|
170
|
+
]
|
|
171
|
+
if not recursive:
|
|
172
|
+
verify_no_directories(resolved_paths_to_sync)
|
|
173
|
+
|
|
174
|
+
deploy_paths_to_sync = []
|
|
175
|
+
for resolved_path in resolved_paths_to_sync:
|
|
176
|
+
verify_exists(resolved_path)
|
|
177
|
+
deploy_paths = bundle_map.to_deploy_paths(resolved_path)
|
|
178
|
+
if not deploy_paths:
|
|
179
|
+
if resolved_path.is_dir() and recursive:
|
|
180
|
+
# No direct artifact mapping found for this path. Check to see
|
|
181
|
+
# if there are subpaths of this directory that are matches. We
|
|
182
|
+
# loop over sources because it's likely a much smaller list
|
|
183
|
+
# than the project directory.
|
|
184
|
+
for src in bundle_map.all_sources(absolute=True):
|
|
185
|
+
if resolved_path in src.parents:
|
|
186
|
+
# There is a source that contains this path, get its dest path(s)
|
|
187
|
+
deploy_paths.extend(bundle_map.to_deploy_paths(src))
|
|
188
|
+
|
|
189
|
+
if not deploy_paths:
|
|
190
|
+
raise ClickException(f"No artifact found for {resolved_path}")
|
|
191
|
+
deploy_paths_to_sync.extend(deploy_paths)
|
|
192
|
+
|
|
193
|
+
stage_paths_to_sync = _get_stage_paths_to_sync(
|
|
194
|
+
deploy_paths_to_sync, resolve_without_follow(deploy_root)
|
|
195
|
+
)
|
|
196
|
+
diff = preserve_from_diff(diff, stage_paths_to_sync)
|
|
197
|
+
else:
|
|
198
|
+
# Full deploy
|
|
199
|
+
if not recursive:
|
|
200
|
+
verify_no_directories(deploy_root.resolve().iterdir())
|
|
201
|
+
|
|
202
|
+
if not prune:
|
|
203
|
+
files_not_removed = [str(path) for path in diff.only_on_stage]
|
|
204
|
+
diff.only_on_stage = []
|
|
205
|
+
|
|
206
|
+
if len(files_not_removed) > 0:
|
|
207
|
+
files_not_removed_str = "\n".join(files_not_removed)
|
|
208
|
+
console.warning(
|
|
209
|
+
f"The following files exist only on the stage:\n{files_not_removed_str}\n\nUse the --prune flag to delete them from the stage."
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
if print_diff:
|
|
213
|
+
print_diff_to_console(diff, bundle_map)
|
|
214
|
+
|
|
215
|
+
# Upload diff-ed files to application package stage
|
|
216
|
+
if diff.has_changes():
|
|
217
|
+
console.step(
|
|
218
|
+
"Updating the Snowflake stage from your local %s directory."
|
|
219
|
+
% deploy_root.resolve(),
|
|
220
|
+
)
|
|
221
|
+
sync_local_diff_with_stage(
|
|
222
|
+
role=role,
|
|
223
|
+
deploy_root_path=deploy_root,
|
|
224
|
+
diff_result=diff,
|
|
225
|
+
stage_fqn=stage_fqn,
|
|
226
|
+
)
|
|
227
|
+
return diff
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _execute_sql_script(
|
|
231
|
+
script_content: str,
|
|
232
|
+
database_name: Optional[str] = None,
|
|
233
|
+
) -> None:
|
|
234
|
+
"""
|
|
235
|
+
Executing the provided SQL script content.
|
|
236
|
+
This assumes that a relevant warehouse is already active.
|
|
237
|
+
If database_name is passed in, it will be used first.
|
|
238
|
+
"""
|
|
239
|
+
try:
|
|
240
|
+
sql_executor = get_sql_executor()
|
|
241
|
+
if database_name is not None:
|
|
242
|
+
sql_executor.execute_query(f"use database {database_name}")
|
|
243
|
+
sql_executor.execute_queries(script_content)
|
|
244
|
+
except ProgrammingError as err:
|
|
245
|
+
generic_sql_error_handler(err)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def execute_post_deploy_hooks(
|
|
249
|
+
console: AbstractConsole,
|
|
250
|
+
project_root: Path,
|
|
251
|
+
post_deploy_hooks: Optional[List[PostDeployHook]],
|
|
252
|
+
deployed_object_type: str,
|
|
253
|
+
database_name: str,
|
|
254
|
+
) -> None:
|
|
255
|
+
"""
|
|
256
|
+
Executes post-deploy hooks for the given object type.
|
|
257
|
+
While executing SQL post deploy hooks, it first switches to the database provided in the input.
|
|
258
|
+
All post deploy scripts templates will first be expanded using the global template context.
|
|
259
|
+
"""
|
|
260
|
+
if not post_deploy_hooks:
|
|
261
|
+
return
|
|
262
|
+
|
|
263
|
+
with console.phase(f"Executing {deployed_object_type} post-deploy actions"):
|
|
264
|
+
sql_scripts_paths = []
|
|
265
|
+
for hook in post_deploy_hooks:
|
|
266
|
+
if hook.sql_script:
|
|
267
|
+
sql_scripts_paths.append(hook.sql_script)
|
|
268
|
+
else:
|
|
269
|
+
raise ValueError(
|
|
270
|
+
f"Unsupported {deployed_object_type} post-deploy hook type: {hook}"
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
scripts_content_list = render_script_templates(
|
|
274
|
+
project_root,
|
|
275
|
+
snowflake_sql_jinja_render,
|
|
276
|
+
{},
|
|
277
|
+
sql_scripts_paths,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
for index, sql_script_path in enumerate(sql_scripts_paths):
|
|
281
|
+
console.step(f"Executing SQL script: {sql_script_path}")
|
|
282
|
+
_execute_sql_script(
|
|
283
|
+
script_content=scripts_content_list[index],
|
|
284
|
+
database_name=database_name,
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def render_script_templates(
|
|
289
|
+
project_root: Path,
|
|
290
|
+
render_from_str: Callable[[str, Dict[str, Any]], str],
|
|
291
|
+
jinja_context: dict[str, Any],
|
|
292
|
+
scripts: List[str],
|
|
293
|
+
) -> List[str]:
|
|
294
|
+
"""
|
|
295
|
+
Input:
|
|
296
|
+
- project_root: path to project root
|
|
297
|
+
- render_from_str: function which renders a jinja template from a string and jinja context
|
|
298
|
+
- jinja_context: a dictionary with the jinja context
|
|
299
|
+
- scripts: list of script paths relative to the project root
|
|
300
|
+
Returns:
|
|
301
|
+
- List of rendered scripts content
|
|
302
|
+
Size of the return list is the same as the size of the input scripts list.
|
|
303
|
+
"""
|
|
304
|
+
scripts_contents = []
|
|
305
|
+
for relpath in scripts:
|
|
306
|
+
script_full_path = SecurePath(project_root) / relpath
|
|
307
|
+
try:
|
|
308
|
+
template_content = script_full_path.read_text(file_size_limit_mb=UNLIMITED)
|
|
309
|
+
result = render_from_str(template_content, jinja_context)
|
|
310
|
+
scripts_contents.append(result)
|
|
311
|
+
|
|
312
|
+
except FileNotFoundError as e:
|
|
313
|
+
raise MissingScriptError(relpath) from e
|
|
314
|
+
|
|
315
|
+
except jinja2.TemplateSyntaxError as e:
|
|
316
|
+
raise InvalidScriptError(relpath, e, e.lineno) from e
|
|
317
|
+
|
|
318
|
+
except jinja2.UndefinedError as e:
|
|
319
|
+
raise InvalidScriptError(relpath, e) from e
|
|
320
|
+
|
|
321
|
+
return scripts_contents
|
snowflake/cli/api/exceptions.py
CHANGED
|
@@ -17,7 +17,7 @@ from __future__ import annotations
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
from typing import Optional
|
|
19
19
|
|
|
20
|
-
from click.exceptions import ClickException
|
|
20
|
+
from click.exceptions import ClickException, UsageError
|
|
21
21
|
from snowflake.cli.api.constants import ObjectType
|
|
22
22
|
|
|
23
23
|
|
|
@@ -101,9 +101,16 @@ class ObjectAlreadyExistsError(ClickException):
|
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
class NoProjectDefinitionError(ClickException):
|
|
104
|
-
def __init__(self, project_type: str,
|
|
104
|
+
def __init__(self, project_type: str, project_root: str | Path):
|
|
105
105
|
super().__init__(
|
|
106
|
-
f"No {project_type} project definition found in {
|
|
106
|
+
f"No {project_type} project definition found in {project_root}"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class InvalidProjectDefinitionVersionError(ClickException):
|
|
111
|
+
def __init__(self, expected_version: str, actual_version: str):
|
|
112
|
+
super().__init__(
|
|
113
|
+
f"This command only supports definition version {expected_version}, got {actual_version}."
|
|
107
114
|
)
|
|
108
115
|
|
|
109
116
|
|
|
@@ -162,3 +169,12 @@ class FQNInconsistencyError(ClickException):
|
|
|
162
169
|
super().__init__(
|
|
163
170
|
f"{part.capitalize()} provided but name '{name}' is fully qualified name."
|
|
164
171
|
)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class IncompatibleParametersError(UsageError):
|
|
175
|
+
def __init__(self, options: list[str]):
|
|
176
|
+
options_with_quotes = [f"'{option}'" for option in options]
|
|
177
|
+
comma_separated_options = ", ".join(options_with_quotes[:-1])
|
|
178
|
+
super().__init__(
|
|
179
|
+
f"Parameters {comma_separated_options} and {options_with_quotes[-1]} are incompatible and cannot be used simultaneously."
|
|
180
|
+
)
|
|
@@ -52,4 +52,5 @@ class FeatureFlag(FeatureFlagMixin):
|
|
|
52
52
|
ENABLE_STREAMLIT_VERSIONED_STAGE = BooleanFlag(
|
|
53
53
|
"ENABLE_STREAMLIT_VERSIONED_STAGE", False
|
|
54
54
|
)
|
|
55
|
-
|
|
55
|
+
# TODO: remove in 3.0
|
|
56
|
+
ENABLE_PROJECT_DEFINITION_V2 = BooleanFlag("ENABLE_PROJECT_DEFINITION_V2", True)
|
snowflake/cli/api/identifiers.py
CHANGED
|
@@ -17,9 +17,11 @@ from __future__ import annotations
|
|
|
17
17
|
import re
|
|
18
18
|
|
|
19
19
|
from click import ClickException
|
|
20
|
-
from snowflake.cli.api.cli_global_context import cli_context
|
|
21
20
|
from snowflake.cli.api.exceptions import FQNInconsistencyError, FQNNameError
|
|
22
|
-
from snowflake.cli.api.project.schemas.identifier_model import
|
|
21
|
+
from snowflake.cli.api.project.schemas.identifier_model import (
|
|
22
|
+
Identifier,
|
|
23
|
+
ObjectIdentifierBaseModel,
|
|
24
|
+
)
|
|
23
25
|
from snowflake.cli.api.project.util import VALID_IDENTIFIER_REGEX, identifier_for_url
|
|
24
26
|
|
|
25
27
|
|
|
@@ -35,10 +37,17 @@ class FQN:
|
|
|
35
37
|
fqn = FQN.from_string("my_name").set_database("db").set_schema("foo")
|
|
36
38
|
"""
|
|
37
39
|
|
|
38
|
-
def __init__(
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
database: str | None,
|
|
43
|
+
schema: str | None,
|
|
44
|
+
name: str,
|
|
45
|
+
signature: str | None = None,
|
|
46
|
+
):
|
|
39
47
|
self._database = database
|
|
40
48
|
self._schema = schema
|
|
41
49
|
self._name = name
|
|
50
|
+
self.signature = signature
|
|
42
51
|
|
|
43
52
|
@property
|
|
44
53
|
def database(self) -> str | None:
|
|
@@ -72,6 +81,8 @@ class FQN:
|
|
|
72
81
|
|
|
73
82
|
@property
|
|
74
83
|
def sql_identifier(self) -> str:
|
|
84
|
+
if self.signature:
|
|
85
|
+
return f"IDENTIFIER('{self.identifier}'){self.signature}"
|
|
75
86
|
return f"IDENTIFIER('{self.identifier}')"
|
|
76
87
|
|
|
77
88
|
def __str__(self):
|
|
@@ -98,9 +109,13 @@ class FQN:
|
|
|
98
109
|
else:
|
|
99
110
|
database = None
|
|
100
111
|
schema = result.group("first_qualifier")
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
112
|
+
|
|
113
|
+
signature = None
|
|
114
|
+
if result.group("signature"):
|
|
115
|
+
signature = result.group("signature")
|
|
116
|
+
return cls(
|
|
117
|
+
name=unqualified_name, schema=schema, database=database, signature=signature
|
|
118
|
+
)
|
|
104
119
|
|
|
105
120
|
@classmethod
|
|
106
121
|
def from_stage(cls, stage: str) -> "FQN":
|
|
@@ -110,11 +125,11 @@ class FQN:
|
|
|
110
125
|
return cls.from_string(name)
|
|
111
126
|
|
|
112
127
|
@classmethod
|
|
113
|
-
def
|
|
128
|
+
def from_identifier_model_v1(cls, model: ObjectIdentifierBaseModel) -> "FQN":
|
|
114
129
|
"""Create an instance from object model."""
|
|
115
130
|
if not isinstance(model, ObjectIdentifierBaseModel):
|
|
116
131
|
raise ClickException(
|
|
117
|
-
f"Expected {type(ObjectIdentifierBaseModel)}, got {model}."
|
|
132
|
+
f"Expected {type(ObjectIdentifierBaseModel).__name__}, got {model}."
|
|
118
133
|
)
|
|
119
134
|
|
|
120
135
|
fqn = cls.from_string(model.name)
|
|
@@ -126,6 +141,21 @@ class FQN:
|
|
|
126
141
|
|
|
127
142
|
return fqn.set_database(model.database).set_schema(model.schema_name)
|
|
128
143
|
|
|
144
|
+
@classmethod
|
|
145
|
+
def from_identifier_model_v2(cls, model: Identifier) -> "FQN":
|
|
146
|
+
"""Create an instance from object model."""
|
|
147
|
+
if not isinstance(model, Identifier):
|
|
148
|
+
raise ClickException(f"Expected {type(Identifier).__name__}, got {model}.")
|
|
149
|
+
|
|
150
|
+
fqn = cls.from_string(model.name)
|
|
151
|
+
|
|
152
|
+
if fqn.database and model.database:
|
|
153
|
+
raise FQNInconsistencyError("database", model.name)
|
|
154
|
+
if fqn.schema and model.schema_:
|
|
155
|
+
raise FQNInconsistencyError("schema", model.name)
|
|
156
|
+
|
|
157
|
+
return fqn.set_database(model.database).set_schema(model.schema_)
|
|
158
|
+
|
|
129
159
|
def set_database(self, database: str | None) -> "FQN":
|
|
130
160
|
if database:
|
|
131
161
|
self._database = database
|
|
@@ -151,4 +181,6 @@ class FQN:
|
|
|
151
181
|
|
|
152
182
|
def using_context(self) -> "FQN":
|
|
153
183
|
"""Update the instance with database and schema from connection in current cli context."""
|
|
154
|
-
|
|
184
|
+
from snowflake.cli.api.cli_global_context import get_cli_context
|
|
185
|
+
|
|
186
|
+
return self.using_connection(get_cli_context().connection)
|
|
@@ -18,7 +18,7 @@ from pathlib import Path
|
|
|
18
18
|
from typing import List, Optional
|
|
19
19
|
|
|
20
20
|
import yaml
|
|
21
|
-
from snowflake.cli.api.cli_global_context import
|
|
21
|
+
from snowflake.cli.api.cli_global_context import get_cli_context
|
|
22
22
|
from snowflake.cli.api.constants import DEFAULT_SIZE_LIMIT_MB
|
|
23
23
|
from snowflake.cli.api.project.schemas.project_definition import (
|
|
24
24
|
ProjectProperties,
|
|
@@ -30,7 +30,10 @@ from snowflake.cli.api.project.util import (
|
|
|
30
30
|
to_identifier,
|
|
31
31
|
)
|
|
32
32
|
from snowflake.cli.api.secure_path import SecurePath
|
|
33
|
-
from snowflake.cli.api.utils.definition_rendering import
|
|
33
|
+
from snowflake.cli.api.utils.definition_rendering import (
|
|
34
|
+
raw_project_properties,
|
|
35
|
+
render_definition_template,
|
|
36
|
+
)
|
|
34
37
|
from snowflake.cli.api.utils.dict_utils import deep_merge_dicts
|
|
35
38
|
from snowflake.cli.api.utils.types import Context, Definition
|
|
36
39
|
|
|
@@ -58,7 +61,9 @@ def _get_merged_definitions(paths: List[Path]) -> Optional[Definition]:
|
|
|
58
61
|
|
|
59
62
|
|
|
60
63
|
def load_project(
|
|
61
|
-
paths: List[Path],
|
|
64
|
+
paths: List[Path],
|
|
65
|
+
context_overrides: Optional[Context] = None,
|
|
66
|
+
render_templates: bool = True,
|
|
62
67
|
) -> ProjectProperties:
|
|
63
68
|
"""
|
|
64
69
|
Loads project definition, optionally overriding values. Definition values
|
|
@@ -66,7 +71,10 @@ def load_project(
|
|
|
66
71
|
Templating is also applied after the merging process.
|
|
67
72
|
"""
|
|
68
73
|
merged_definitions = _get_merged_definitions(paths)
|
|
69
|
-
|
|
74
|
+
if render_templates:
|
|
75
|
+
return render_definition_template(merged_definitions, context_overrides or {})
|
|
76
|
+
else:
|
|
77
|
+
return raw_project_properties(merged_definitions)
|
|
70
78
|
|
|
71
79
|
|
|
72
80
|
def default_app_package(project_name: str):
|
|
@@ -75,7 +83,7 @@ def default_app_package(project_name: str):
|
|
|
75
83
|
|
|
76
84
|
|
|
77
85
|
def default_role():
|
|
78
|
-
conn =
|
|
86
|
+
conn = get_cli_context().connection
|
|
79
87
|
return conn.role
|
|
80
88
|
|
|
81
89
|
|
|
@@ -20,7 +20,10 @@ from pathlib import Path
|
|
|
20
20
|
from typing import List, Optional
|
|
21
21
|
|
|
22
22
|
from snowflake.cli.api.project.definition import ProjectProperties, load_project
|
|
23
|
-
from snowflake.cli.api.project.schemas.project_definition import
|
|
23
|
+
from snowflake.cli.api.project.schemas.project_definition import (
|
|
24
|
+
ProjectDefinition,
|
|
25
|
+
ProjectDefinitionV1,
|
|
26
|
+
)
|
|
24
27
|
from snowflake.cli.api.utils.types import Context
|
|
25
28
|
|
|
26
29
|
|
|
@@ -125,10 +128,18 @@ class DefinitionManager:
|
|
|
125
128
|
def _project_properties(self) -> ProjectProperties:
|
|
126
129
|
return load_project(self._project_config_paths, self._context_overrides)
|
|
127
130
|
|
|
131
|
+
@functools.cached_property
|
|
132
|
+
def _raw_project_data(self) -> ProjectProperties:
|
|
133
|
+
return load_project(self._project_config_paths, {}, False)
|
|
134
|
+
|
|
128
135
|
@functools.cached_property
|
|
129
136
|
def project_definition(self) -> ProjectDefinitionV1:
|
|
130
137
|
return self._project_properties.project_definition
|
|
131
138
|
|
|
139
|
+
@functools.cached_property
|
|
140
|
+
def unrendered_project_definition(self) -> ProjectDefinition:
|
|
141
|
+
return self._raw_project_data.project_definition
|
|
142
|
+
|
|
132
143
|
@functools.cached_property
|
|
133
144
|
def template_context(self) -> Context:
|
|
134
145
|
return self._project_properties.project_context
|
|
@@ -29,10 +29,25 @@ class SchemaValidationError(ClickException):
|
|
|
29
29
|
def __init__(self, error: ValidationError):
|
|
30
30
|
errors = error.errors()
|
|
31
31
|
message = f"During evaluation of {error.title} in project definition following errors were encountered:\n"
|
|
32
|
+
|
|
33
|
+
def calculate_location(e):
|
|
34
|
+
if e["loc"] is None:
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
# show numbers as list indexes and strings as dictionary keys. Example: key1[0].key2
|
|
38
|
+
result = "".join(
|
|
39
|
+
f"[{item}]" if isinstance(item, int) else f".{item}"
|
|
40
|
+
for item in e["loc"]
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# remove leading dot from the string if any:
|
|
44
|
+
return result[1:] if result.startswith(".") else result
|
|
45
|
+
|
|
32
46
|
message += "\n".join(
|
|
33
47
|
[
|
|
34
48
|
self.message_templates.get(e["type"], self.generic_message).format(
|
|
35
|
-
**e,
|
|
49
|
+
**e,
|
|
50
|
+
location=calculate_location(e),
|
|
36
51
|
)
|
|
37
52
|
for e in errors
|
|
38
53
|
]
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
-
|
|
15
|
-
from snowflake.cli.api.cli_global_context import cli_context
|
|
14
|
+
from snowflake.cli.api.cli_global_context import get_cli_context
|
|
16
15
|
from snowflake.cli.api.exceptions import NoProjectDefinitionError
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
def assert_project_type(project_type: str):
|
|
19
|
+
cli_context = get_cli_context()
|
|
20
20
|
if not getattr(cli_context.project_definition, project_type, None):
|
|
21
21
|
raise NoProjectDefinitionError(
|
|
22
|
-
project_type=project_type,
|
|
22
|
+
project_type=project_type, project_root=cli_context.project_root
|
|
23
23
|
)
|
snowflake/cli/api/project/schemas/entities/{application_entity.py → application_entity_model.py}
RENAMED
|
@@ -16,25 +16,24 @@ from __future__ import annotations
|
|
|
16
16
|
|
|
17
17
|
from typing import Literal, Optional
|
|
18
18
|
|
|
19
|
-
from pydantic import Field
|
|
20
|
-
from snowflake.cli.api.project.schemas.entities.
|
|
21
|
-
|
|
19
|
+
from pydantic import Field, field_validator
|
|
20
|
+
from snowflake.cli.api.project.schemas.entities.application_package_entity_model import (
|
|
21
|
+
ApplicationPackageEntityModel,
|
|
22
22
|
)
|
|
23
23
|
from snowflake.cli.api.project.schemas.entities.common import (
|
|
24
|
-
|
|
24
|
+
EntityModelBase,
|
|
25
25
|
TargetField,
|
|
26
26
|
)
|
|
27
|
+
from snowflake.cli.api.project.schemas.identifier_model import Identifier
|
|
27
28
|
from snowflake.cli.api.project.schemas.updatable_model import (
|
|
28
29
|
DiscriminatorField,
|
|
29
30
|
)
|
|
31
|
+
from snowflake.cli.api.project.util import append_test_resource_suffix
|
|
30
32
|
|
|
31
33
|
|
|
32
|
-
class
|
|
34
|
+
class ApplicationEntityModel(EntityModelBase):
|
|
33
35
|
type: Literal["application"] = DiscriminatorField() # noqa A003
|
|
34
|
-
|
|
35
|
-
title="Name of the application created when this entity is deployed"
|
|
36
|
-
)
|
|
37
|
-
from_: TargetField[ApplicationPackageEntity] = Field(
|
|
36
|
+
from_: TargetField[ApplicationPackageEntityModel] = Field(
|
|
38
37
|
alias="from",
|
|
39
38
|
title="An application package this entity should be created from",
|
|
40
39
|
)
|
|
@@ -42,3 +41,16 @@ class ApplicationEntity(EntityBase):
|
|
|
42
41
|
title="Whether to enable debug mode when using a named stage to create an application object",
|
|
43
42
|
default=None,
|
|
44
43
|
)
|
|
44
|
+
|
|
45
|
+
@field_validator("identifier")
|
|
46
|
+
@classmethod
|
|
47
|
+
def append_test_resource_suffix_to_identifier(
|
|
48
|
+
cls, input_value: Identifier | str
|
|
49
|
+
) -> Identifier | str:
|
|
50
|
+
identifier = (
|
|
51
|
+
input_value.name if isinstance(input_value, Identifier) else input_value
|
|
52
|
+
)
|
|
53
|
+
with_suffix = append_test_resource_suffix(identifier)
|
|
54
|
+
if isinstance(input_value, Identifier):
|
|
55
|
+
return input_value.model_copy(update=dict(name=with_suffix))
|
|
56
|
+
return with_suffix
|