snowflake-cli-labs 3.0.0rc4__py3-none-any.whl → 3.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- README.md +21 -0
- {snowflake_cli_labs-3.0.0rc4.dist-info → snowflake_cli_labs-3.0.1.dist-info}/METADATA +6 -96
- snowflake_cli_labs-3.0.1.dist-info/RECORD +5 -0
- snowflake/cli/__about__.py +0 -17
- snowflake/cli/__init__.py +0 -13
- snowflake/cli/_app/__init__.py +0 -22
- snowflake/cli/_app/__main__.py +0 -31
- snowflake/cli/_app/api_impl/__init__.py +0 -13
- snowflake/cli/_app/api_impl/plugin/__init__.py +0 -13
- snowflake/cli/_app/api_impl/plugin/plugin_config_provider_impl.py +0 -66
- snowflake/cli/_app/cli_app.py +0 -252
- snowflake/cli/_app/commands_registration/__init__.py +0 -33
- snowflake/cli/_app/commands_registration/builtin_plugins.py +0 -50
- snowflake/cli/_app/commands_registration/command_plugins_loader.py +0 -169
- snowflake/cli/_app/commands_registration/commands_registration_with_callbacks.py +0 -105
- snowflake/cli/_app/commands_registration/exception_logging.py +0 -26
- snowflake/cli/_app/commands_registration/threadsafe.py +0 -48
- snowflake/cli/_app/commands_registration/typer_registration.py +0 -153
- snowflake/cli/_app/constants.py +0 -19
- snowflake/cli/_app/dev/__init__.py +0 -13
- snowflake/cli/_app/dev/commands_structure.py +0 -48
- snowflake/cli/_app/dev/docs/__init__.py +0 -13
- snowflake/cli/_app/dev/docs/commands_docs_generator.py +0 -118
- snowflake/cli/_app/dev/docs/generator.py +0 -35
- snowflake/cli/_app/dev/docs/project_definition_docs_generator.py +0 -58
- snowflake/cli/_app/dev/docs/project_definition_generate_json_schema.py +0 -227
- snowflake/cli/_app/dev/docs/template_utils.py +0 -23
- snowflake/cli/_app/dev/docs/templates/definition_description.rst.jinja2 +0 -38
- snowflake/cli/_app/dev/docs/templates/overview.rst.jinja2 +0 -9
- snowflake/cli/_app/dev/docs/templates/usage.rst.jinja2 +0 -67
- snowflake/cli/_app/dev/pycharm_remote_debug.py +0 -46
- snowflake/cli/_app/loggers.py +0 -199
- snowflake/cli/_app/main_typer.py +0 -62
- snowflake/cli/_app/printing.py +0 -181
- snowflake/cli/_app/secret.py +0 -9
- snowflake/cli/_app/snow_connector.py +0 -309
- snowflake/cli/_app/telemetry.py +0 -220
- snowflake/cli/_app/version_check.py +0 -74
- snowflake/cli/_plugins/__init__.py +0 -13
- snowflake/cli/_plugins/connection/__init__.py +0 -13
- snowflake/cli/_plugins/connection/commands.py +0 -353
- snowflake/cli/_plugins/connection/plugin_spec.py +0 -30
- snowflake/cli/_plugins/connection/util.py +0 -195
- snowflake/cli/_plugins/cortex/__init__.py +0 -13
- snowflake/cli/_plugins/cortex/commands.py +0 -332
- snowflake/cli/_plugins/cortex/constants.py +0 -17
- snowflake/cli/_plugins/cortex/manager.py +0 -189
- snowflake/cli/_plugins/cortex/plugin_spec.py +0 -30
- snowflake/cli/_plugins/cortex/types.py +0 -22
- snowflake/cli/_plugins/git/__init__.py +0 -13
- snowflake/cli/_plugins/git/commands.py +0 -358
- snowflake/cli/_plugins/git/manager.py +0 -151
- snowflake/cli/_plugins/git/plugin_spec.py +0 -30
- snowflake/cli/_plugins/helpers/__init__.py +0 -13
- snowflake/cli/_plugins/helpers/commands.py +0 -61
- snowflake/cli/_plugins/helpers/plugin_spec.py +0 -30
- snowflake/cli/_plugins/init/__init__.py +0 -13
- snowflake/cli/_plugins/init/commands.py +0 -248
- snowflake/cli/_plugins/init/plugin_spec.py +0 -30
- snowflake/cli/_plugins/nativeapp/__init__.py +0 -13
- snowflake/cli/_plugins/nativeapp/artifacts.py +0 -757
- snowflake/cli/_plugins/nativeapp/bundle_context.py +0 -31
- snowflake/cli/_plugins/nativeapp/codegen/__init__.py +0 -13
- snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +0 -91
- snowflake/cli/_plugins/nativeapp/codegen/compiler.py +0 -149
- snowflake/cli/_plugins/nativeapp/codegen/sandbox.py +0 -306
- snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +0 -249
- snowflake/cli/_plugins/nativeapp/codegen/setup/setup_driver.py.source +0 -59
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -181
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/extension_function_utils.py +0 -217
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/models.py +0 -61
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +0 -523
- snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +0 -114
- snowflake/cli/_plugins/nativeapp/commands.py +0 -559
- snowflake/cli/_plugins/nativeapp/common_flags.py +0 -44
- snowflake/cli/_plugins/nativeapp/constants.py +0 -27
- snowflake/cli/_plugins/nativeapp/entities/__init__.py +0 -0
- snowflake/cli/_plugins/nativeapp/entities/application.py +0 -878
- snowflake/cli/_plugins/nativeapp/entities/application_package.py +0 -1392
- snowflake/cli/_plugins/nativeapp/exceptions.py +0 -113
- snowflake/cli/_plugins/nativeapp/feature_flags.py +0 -24
- snowflake/cli/_plugins/nativeapp/manager.py +0 -415
- snowflake/cli/_plugins/nativeapp/plugin_spec.py +0 -30
- snowflake/cli/_plugins/nativeapp/policy.py +0 -53
- snowflake/cli/_plugins/nativeapp/project_model.py +0 -211
- snowflake/cli/_plugins/nativeapp/run_processor.py +0 -184
- snowflake/cli/_plugins/nativeapp/same_account_install_method.py +0 -70
- snowflake/cli/_plugins/nativeapp/teardown_processor.py +0 -70
- snowflake/cli/_plugins/nativeapp/utils.py +0 -98
- snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +0 -262
- snowflake/cli/_plugins/nativeapp/version/__init__.py +0 -13
- snowflake/cli/_plugins/nativeapp/version/commands.py +0 -141
- snowflake/cli/_plugins/nativeapp/version/version_processor.py +0 -98
- snowflake/cli/_plugins/notebook/__init__.py +0 -13
- snowflake/cli/_plugins/notebook/commands.py +0 -86
- snowflake/cli/_plugins/notebook/exceptions.py +0 -20
- snowflake/cli/_plugins/notebook/manager.py +0 -71
- snowflake/cli/_plugins/notebook/plugin_spec.py +0 -30
- snowflake/cli/_plugins/notebook/types.py +0 -15
- snowflake/cli/_plugins/object/__init__.py +0 -13
- snowflake/cli/_plugins/object/command_aliases.py +0 -95
- snowflake/cli/_plugins/object/commands.py +0 -180
- snowflake/cli/_plugins/object/common.py +0 -85
- snowflake/cli/_plugins/object/manager.py +0 -118
- snowflake/cli/_plugins/object/plugin_spec.py +0 -30
- snowflake/cli/_plugins/snowpark/__init__.py +0 -13
- snowflake/cli/_plugins/snowpark/commands.py +0 -450
- snowflake/cli/_plugins/snowpark/common.py +0 -268
- snowflake/cli/_plugins/snowpark/models.py +0 -150
- snowflake/cli/_plugins/snowpark/package/__init__.py +0 -13
- snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +0 -199
- snowflake/cli/_plugins/snowpark/package/commands.py +0 -195
- snowflake/cli/_plugins/snowpark/package/manager.py +0 -44
- snowflake/cli/_plugins/snowpark/package/utils.py +0 -26
- snowflake/cli/_plugins/snowpark/package_utils.py +0 -354
- snowflake/cli/_plugins/snowpark/plugin_spec.py +0 -30
- snowflake/cli/_plugins/snowpark/snowpark_entity.py +0 -29
- snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +0 -173
- snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +0 -109
- snowflake/cli/_plugins/snowpark/snowpark_shared.py +0 -59
- snowflake/cli/_plugins/snowpark/zipper.py +0 -89
- snowflake/cli/_plugins/spcs/__init__.py +0 -33
- snowflake/cli/_plugins/spcs/common.py +0 -99
- snowflake/cli/_plugins/spcs/compute_pool/__init__.py +0 -13
- snowflake/cli/_plugins/spcs/compute_pool/commands.py +0 -241
- snowflake/cli/_plugins/spcs/compute_pool/manager.py +0 -121
- snowflake/cli/_plugins/spcs/image_registry/__init__.py +0 -13
- snowflake/cli/_plugins/spcs/image_registry/commands.py +0 -65
- snowflake/cli/_plugins/spcs/image_registry/manager.py +0 -105
- snowflake/cli/_plugins/spcs/image_repository/__init__.py +0 -13
- snowflake/cli/_plugins/spcs/image_repository/commands.py +0 -202
- snowflake/cli/_plugins/spcs/image_repository/manager.py +0 -84
- snowflake/cli/_plugins/spcs/plugin_spec.py +0 -30
- snowflake/cli/_plugins/spcs/services/__init__.py +0 -13
- snowflake/cli/_plugins/spcs/services/commands.py +0 -345
- snowflake/cli/_plugins/spcs/services/manager.py +0 -208
- snowflake/cli/_plugins/sql/__init__.py +0 -13
- snowflake/cli/_plugins/sql/commands.py +0 -86
- snowflake/cli/_plugins/sql/manager.py +0 -92
- snowflake/cli/_plugins/sql/plugin_spec.py +0 -30
- snowflake/cli/_plugins/sql/snowsql_templating.py +0 -28
- snowflake/cli/_plugins/stage/__init__.py +0 -13
- snowflake/cli/_plugins/stage/commands.py +0 -264
- snowflake/cli/_plugins/stage/diff.py +0 -280
- snowflake/cli/_plugins/stage/manager.py +0 -582
- snowflake/cli/_plugins/stage/md5.py +0 -160
- snowflake/cli/_plugins/stage/plugin_spec.py +0 -30
- snowflake/cli/_plugins/stage/utils.py +0 -54
- snowflake/cli/_plugins/streamlit/__init__.py +0 -13
- snowflake/cli/_plugins/streamlit/commands.py +0 -195
- snowflake/cli/_plugins/streamlit/manager.py +0 -220
- snowflake/cli/_plugins/streamlit/plugin_spec.py +0 -30
- snowflake/cli/_plugins/streamlit/streamlit_entity.py +0 -12
- snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +0 -66
- snowflake/cli/_plugins/workspace/__init__.py +0 -13
- snowflake/cli/_plugins/workspace/action_context.py +0 -18
- snowflake/cli/_plugins/workspace/commands.py +0 -306
- snowflake/cli/_plugins/workspace/manager.py +0 -74
- snowflake/cli/_plugins/workspace/plugin_spec.py +0 -30
- snowflake/cli/api/__init__.py +0 -48
- snowflake/cli/api/cli_global_context.py +0 -247
- snowflake/cli/api/commands/__init__.py +0 -13
- snowflake/cli/api/commands/alias.py +0 -23
- snowflake/cli/api/commands/common.py +0 -25
- snowflake/cli/api/commands/decorators.py +0 -369
- snowflake/cli/api/commands/execution_metadata.py +0 -40
- snowflake/cli/api/commands/experimental_behaviour.py +0 -18
- snowflake/cli/api/commands/flags.py +0 -561
- snowflake/cli/api/commands/overrideable_parameter.py +0 -143
- snowflake/cli/api/commands/snow_typer.py +0 -247
- snowflake/cli/api/commands/utils.py +0 -18
- snowflake/cli/api/config.py +0 -380
- snowflake/cli/api/connections.py +0 -216
- snowflake/cli/api/console/__init__.py +0 -17
- snowflake/cli/api/console/abc.py +0 -94
- snowflake/cli/api/console/console.py +0 -134
- snowflake/cli/api/console/enum.py +0 -17
- snowflake/cli/api/constants.py +0 -90
- snowflake/cli/api/entities/common.py +0 -56
- snowflake/cli/api/entities/utils.py +0 -370
- snowflake/cli/api/errno.py +0 -28
- snowflake/cli/api/exceptions.py +0 -190
- snowflake/cli/api/feature_flags.py +0 -54
- snowflake/cli/api/identifiers.py +0 -190
- snowflake/cli/api/metrics.py +0 -92
- snowflake/cli/api/output/__init__.py +0 -13
- snowflake/cli/api/output/formats.py +0 -20
- snowflake/cli/api/output/types.py +0 -118
- snowflake/cli/api/plugins/__init__.py +0 -13
- snowflake/cli/api/plugins/command/__init__.py +0 -72
- snowflake/cli/api/plugins/command/plugin_hook_specs.py +0 -21
- snowflake/cli/api/plugins/plugin_config.py +0 -32
- snowflake/cli/api/project/__init__.py +0 -13
- snowflake/cli/api/project/definition.py +0 -126
- snowflake/cli/api/project/definition_conversion.py +0 -395
- snowflake/cli/api/project/definition_manager.py +0 -145
- snowflake/cli/api/project/errors.py +0 -56
- snowflake/cli/api/project/project_verification.py +0 -23
- snowflake/cli/api/project/schemas/__init__.py +0 -13
- snowflake/cli/api/project/schemas/entities/__init__.py +0 -13
- snowflake/cli/api/project/schemas/entities/common.py +0 -153
- snowflake/cli/api/project/schemas/entities/entities.py +0 -61
- snowflake/cli/api/project/schemas/project_definition.py +0 -330
- snowflake/cli/api/project/schemas/template.py +0 -77
- snowflake/cli/api/project/schemas/updatable_model.py +0 -202
- snowflake/cli/api/project/schemas/v1/__init__.py +0 -0
- snowflake/cli/api/project/schemas/v1/identifier_model.py +0 -51
- snowflake/cli/api/project/schemas/v1/native_app/__init__.py +0 -0
- snowflake/cli/api/project/schemas/v1/native_app/application.py +0 -61
- snowflake/cli/api/project/schemas/v1/native_app/native_app.py +0 -93
- snowflake/cli/api/project/schemas/v1/native_app/package.py +0 -84
- snowflake/cli/api/project/schemas/v1/native_app/path_mapping.py +0 -65
- snowflake/cli/api/project/schemas/v1/snowpark/__init__.py +0 -0
- snowflake/cli/api/project/schemas/v1/snowpark/argument.py +0 -28
- snowflake/cli/api/project/schemas/v1/snowpark/callable.py +0 -69
- snowflake/cli/api/project/schemas/v1/snowpark/snowpark.py +0 -36
- snowflake/cli/api/project/schemas/v1/streamlit/__init__.py +0 -0
- snowflake/cli/api/project/schemas/v1/streamlit/streamlit.py +0 -47
- snowflake/cli/api/project/util.py +0 -278
- snowflake/cli/api/rendering/__init__.py +0 -13
- snowflake/cli/api/rendering/jinja.py +0 -118
- snowflake/cli/api/rendering/project_definition_templates.py +0 -43
- snowflake/cli/api/rendering/project_templates.py +0 -98
- snowflake/cli/api/rendering/sql_templates.py +0 -105
- snowflake/cli/api/rest_api.py +0 -178
- snowflake/cli/api/sanitizers.py +0 -43
- snowflake/cli/api/secure_path.py +0 -360
- snowflake/cli/api/secure_utils.py +0 -118
- snowflake/cli/api/sql_execution.py +0 -280
- snowflake/cli/api/utils/__init__.py +0 -13
- snowflake/cli/api/utils/cursor.py +0 -34
- snowflake/cli/api/utils/definition_rendering.py +0 -415
- snowflake/cli/api/utils/dict_utils.py +0 -73
- snowflake/cli/api/utils/error_handling.py +0 -23
- snowflake/cli/api/utils/graph.py +0 -97
- snowflake/cli/api/utils/models.py +0 -63
- snowflake/cli/api/utils/naming_utils.py +0 -13
- snowflake/cli/api/utils/path_utils.py +0 -36
- snowflake/cli/api/utils/templating_functions.py +0 -144
- snowflake/cli/api/utils/types.py +0 -35
- snowflake_cli_labs-3.0.0rc4.dist-info/RECORD +0 -242
- snowflake_cli_labs-3.0.0rc4.dist-info/entry_points.txt +0 -2
- {snowflake_cli_labs-3.0.0rc4.dist-info → snowflake_cli_labs-3.0.1.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-3.0.0rc4.dist-info → snowflake_cli_labs-3.0.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Snowflake Inc.
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
import logging
|
|
18
|
-
import re
|
|
19
|
-
from enum import Enum
|
|
20
|
-
from typing import Dict, List, Set
|
|
21
|
-
|
|
22
|
-
from click import UsageError
|
|
23
|
-
from snowflake.cli._plugins.snowpark.models import Requirement
|
|
24
|
-
from snowflake.cli._plugins.snowpark.snowpark_entity_model import (
|
|
25
|
-
ProcedureEntityModel,
|
|
26
|
-
SnowparkEntityModel,
|
|
27
|
-
)
|
|
28
|
-
from snowflake.cli._plugins.snowpark.snowpark_project_paths import Artefact
|
|
29
|
-
from snowflake.cli.api.console import cli_console
|
|
30
|
-
from snowflake.cli.api.constants import (
|
|
31
|
-
INIT_TEMPLATE_VARIABLE_CLOSING,
|
|
32
|
-
INIT_TEMPLATE_VARIABLE_OPENING,
|
|
33
|
-
PROJECT_TEMPLATE_VARIABLE_CLOSING,
|
|
34
|
-
PROJECT_TEMPLATE_VARIABLE_OPENING,
|
|
35
|
-
ObjectType,
|
|
36
|
-
)
|
|
37
|
-
from snowflake.cli.api.sql_execution import SqlExecutionMixin
|
|
38
|
-
from snowflake.connector.cursor import SnowflakeCursor
|
|
39
|
-
|
|
40
|
-
log = logging.getLogger(__name__)
|
|
41
|
-
|
|
42
|
-
SnowparkEntities = Dict[str, SnowparkEntityModel]
|
|
43
|
-
StageToArtefactMapping = Dict[str, set[Artefact]]
|
|
44
|
-
EntityToImportPathsMapping = Dict[str, set[str]]
|
|
45
|
-
|
|
46
|
-
DEFAULT_RUNTIME = "3.10"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class SnowparkObject(Enum):
|
|
50
|
-
"""This clas is used only for Snowpark execute where choice is limited."""
|
|
51
|
-
|
|
52
|
-
PROCEDURE = str(ObjectType.PROCEDURE)
|
|
53
|
-
FUNCTION = str(ObjectType.FUNCTION)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class SnowparkObjectManager(SqlExecutionMixin):
|
|
57
|
-
def execute(
|
|
58
|
-
self, execution_identifier: str, object_type: SnowparkObject
|
|
59
|
-
) -> SnowflakeCursor:
|
|
60
|
-
if object_type == SnowparkObject.FUNCTION:
|
|
61
|
-
return self._execute_query(f"select {execution_identifier}")
|
|
62
|
-
if object_type == SnowparkObject.PROCEDURE:
|
|
63
|
-
return self._execute_query(f"call {execution_identifier}")
|
|
64
|
-
raise UsageError(f"Unknown object type: {object_type}.")
|
|
65
|
-
|
|
66
|
-
def create_or_replace(
|
|
67
|
-
self,
|
|
68
|
-
entity: SnowparkEntityModel,
|
|
69
|
-
artifact_files: set[str],
|
|
70
|
-
snowflake_dependencies: list[str],
|
|
71
|
-
) -> str:
|
|
72
|
-
entity.imports.extend(artifact_files)
|
|
73
|
-
imports = [f"'{x}'" for x in entity.imports]
|
|
74
|
-
packages_list = ",".join(f"'{p}'" for p in snowflake_dependencies)
|
|
75
|
-
|
|
76
|
-
object_type = entity.get_type()
|
|
77
|
-
|
|
78
|
-
query = [
|
|
79
|
-
f"create or replace {object_type} {entity.udf_sproc_identifier.identifier_for_sql}",
|
|
80
|
-
f"copy grants",
|
|
81
|
-
f"returns {entity.returns}",
|
|
82
|
-
"language python",
|
|
83
|
-
f"runtime_version={entity.runtime or DEFAULT_RUNTIME}",
|
|
84
|
-
f"imports=({', '.join(imports)})",
|
|
85
|
-
f"handler='{entity.handler}'",
|
|
86
|
-
f"packages=({packages_list})",
|
|
87
|
-
]
|
|
88
|
-
|
|
89
|
-
if entity.external_access_integrations:
|
|
90
|
-
query.append(entity.get_external_access_integrations_sql())
|
|
91
|
-
|
|
92
|
-
if entity.secrets:
|
|
93
|
-
query.append(entity.get_secrets_sql())
|
|
94
|
-
|
|
95
|
-
if isinstance(entity, ProcedureEntityModel) and entity.execute_as_caller:
|
|
96
|
-
query.append("execute as caller")
|
|
97
|
-
|
|
98
|
-
return self._execute_query("\n".join(query))
|
|
99
|
-
|
|
100
|
-
def deploy_entity(
|
|
101
|
-
self,
|
|
102
|
-
entity: SnowparkEntityModel,
|
|
103
|
-
existing_objects: Dict[str, SnowflakeCursor],
|
|
104
|
-
snowflake_dependencies: List[str],
|
|
105
|
-
entities_to_artifact_map: EntityToImportPathsMapping,
|
|
106
|
-
):
|
|
107
|
-
cli_console.step(f"Creating {entity.type} {entity.fqn}")
|
|
108
|
-
object_exists = entity.entity_id in existing_objects
|
|
109
|
-
replace_object = False
|
|
110
|
-
if object_exists:
|
|
111
|
-
replace_object = _check_if_replace_is_required(
|
|
112
|
-
entity=entity,
|
|
113
|
-
current_state=existing_objects[entity.entity_id],
|
|
114
|
-
snowflake_dependencies=snowflake_dependencies,
|
|
115
|
-
stage_artifact_files=entities_to_artifact_map[entity.entity_id],
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
state = {
|
|
119
|
-
"object": entity.udf_sproc_identifier.identifier_with_arg_names_types_defaults,
|
|
120
|
-
"type": entity.get_type(),
|
|
121
|
-
}
|
|
122
|
-
if object_exists and not replace_object:
|
|
123
|
-
return {**state, "status": "packages updated"}
|
|
124
|
-
|
|
125
|
-
self.create_or_replace(
|
|
126
|
-
entity=entity,
|
|
127
|
-
artifact_files=entities_to_artifact_map[entity.entity_id],
|
|
128
|
-
snowflake_dependencies=snowflake_dependencies,
|
|
129
|
-
)
|
|
130
|
-
return {
|
|
131
|
-
**state,
|
|
132
|
-
"status": "created" if not object_exists else "definition updated",
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def _check_if_replace_is_required(
|
|
137
|
-
entity: SnowparkEntityModel,
|
|
138
|
-
current_state,
|
|
139
|
-
snowflake_dependencies: List[str],
|
|
140
|
-
stage_artifact_files: set[str],
|
|
141
|
-
) -> bool:
|
|
142
|
-
object_type = entity.get_type()
|
|
143
|
-
resource_json = _convert_resource_details_to_dict(current_state)
|
|
144
|
-
old_dependencies = resource_json["packages"]
|
|
145
|
-
|
|
146
|
-
if _snowflake_dependencies_differ(old_dependencies, snowflake_dependencies):
|
|
147
|
-
log.info(
|
|
148
|
-
"Found difference of package requirements. Replacing the %s.", object_type
|
|
149
|
-
)
|
|
150
|
-
return True
|
|
151
|
-
|
|
152
|
-
if set(entity.external_access_integrations) != set(
|
|
153
|
-
resource_json.get("external_access_integrations", [])
|
|
154
|
-
):
|
|
155
|
-
log.info(
|
|
156
|
-
"Found difference of external access integrations. Replacing the %s.",
|
|
157
|
-
object_type,
|
|
158
|
-
)
|
|
159
|
-
return True
|
|
160
|
-
|
|
161
|
-
if (
|
|
162
|
-
resource_json["handler"].lower() != entity.handler.lower()
|
|
163
|
-
or _sql_to_python_return_type_mapper(resource_json["returns"]).lower()
|
|
164
|
-
!= entity.returns.lower()
|
|
165
|
-
):
|
|
166
|
-
log.info(
|
|
167
|
-
"Return type or handler types do not match. Replacing the %s.", object_type
|
|
168
|
-
)
|
|
169
|
-
return True
|
|
170
|
-
|
|
171
|
-
if _compare_imports(resource_json, entity.imports, stage_artifact_files):
|
|
172
|
-
log.info("Imports do not match. Replacing the %s", object_type)
|
|
173
|
-
return True
|
|
174
|
-
|
|
175
|
-
if entity.runtime is not None and entity.runtime != resource_json.get(
|
|
176
|
-
"runtime_version", "RUNTIME_NOT_SET"
|
|
177
|
-
):
|
|
178
|
-
log.info("Runtime versions do not match. Replacing the %s", object_type)
|
|
179
|
-
return True
|
|
180
|
-
|
|
181
|
-
if isinstance(entity, ProcedureEntityModel):
|
|
182
|
-
if resource_json.get("execute as", "OWNER") != (
|
|
183
|
-
"CALLER" if entity.execute_as_caller else "OWNER"
|
|
184
|
-
):
|
|
185
|
-
log.info(
|
|
186
|
-
"Execute as caller settings do not match. Replacing the %s", object_type
|
|
187
|
-
)
|
|
188
|
-
return True
|
|
189
|
-
|
|
190
|
-
return False
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
def _convert_resource_details_to_dict(function_details: SnowflakeCursor) -> dict:
|
|
194
|
-
import json
|
|
195
|
-
|
|
196
|
-
function_dict = {}
|
|
197
|
-
json_properties = ["packages", "installed_packages"]
|
|
198
|
-
for function in function_details:
|
|
199
|
-
if function[0] in json_properties:
|
|
200
|
-
function_dict[function[0]] = json.loads(
|
|
201
|
-
function[1].replace("'", '"'),
|
|
202
|
-
)
|
|
203
|
-
else:
|
|
204
|
-
function_dict[function[0]] = function[1]
|
|
205
|
-
return function_dict
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
def _snowflake_dependencies_differ(
|
|
209
|
-
old_dependencies: List[str], new_dependencies: List[str]
|
|
210
|
-
) -> bool:
|
|
211
|
-
def _standardize(packages: List[str]) -> Set[str]:
|
|
212
|
-
return set(
|
|
213
|
-
Requirement.parse_line(package).name_and_version for package in packages
|
|
214
|
-
)
|
|
215
|
-
|
|
216
|
-
return _standardize(old_dependencies) != _standardize(new_dependencies)
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
def _sql_to_python_return_type_mapper(resource_return_type: str) -> str:
|
|
220
|
-
"""
|
|
221
|
-
Some of the Python data types get converted to SQL types, when function/procedure is created.
|
|
222
|
-
So, to properly compare types, we use mapping based on:
|
|
223
|
-
https://docs.snowflake.com/en/developer-guide/udf-stored-procedure-data-type-mapping#sql-python-data-type-mappings
|
|
224
|
-
|
|
225
|
-
Mind you, this only applies to cases, in which Snowflake accepts Python type as return.
|
|
226
|
-
Ie. if function returns list, it has to be declared as 'array' during creation,
|
|
227
|
-
therefore any conversion is not necessary
|
|
228
|
-
"""
|
|
229
|
-
mapping = {
|
|
230
|
-
"number(38,0)": "int",
|
|
231
|
-
"timestamp_ntz(9)": "datetime",
|
|
232
|
-
"timestamp_tz(9)": "datetime",
|
|
233
|
-
"varchar(16777216)": "string",
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return mapping.get(resource_return_type.lower(), resource_return_type.lower())
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def _compare_imports(
|
|
240
|
-
resource_json: dict, imports: List[str], artifact_files: set[str]
|
|
241
|
-
) -> bool:
|
|
242
|
-
pattern = re.compile(r"(?:\[@?\w+_\w+\.)?(\w+(?:/\w+)+\.\w+)(?:\])?")
|
|
243
|
-
|
|
244
|
-
project_imports = {
|
|
245
|
-
imp
|
|
246
|
-
for import_string in [*imports, *artifact_files]
|
|
247
|
-
for imp in pattern.findall(import_string.lower())
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if "imports" not in resource_json.keys():
|
|
251
|
-
object_imports = set()
|
|
252
|
-
else:
|
|
253
|
-
object_imports = {
|
|
254
|
-
imp.lower()
|
|
255
|
-
for imp in pattern.findall(resource_json.get("imports", "").lower())
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return project_imports != object_imports
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
def is_name_a_templated_one(name: str) -> bool:
|
|
262
|
-
return (
|
|
263
|
-
PROJECT_TEMPLATE_VARIABLE_OPENING in name
|
|
264
|
-
and PROJECT_TEMPLATE_VARIABLE_CLOSING in name
|
|
265
|
-
) or (
|
|
266
|
-
INIT_TEMPLATE_VARIABLE_OPENING in name
|
|
267
|
-
and INIT_TEMPLATE_VARIABLE_CLOSING in name
|
|
268
|
-
)
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Snowflake Inc.
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
import re
|
|
18
|
-
import zipfile
|
|
19
|
-
from dataclasses import dataclass
|
|
20
|
-
from pathlib import Path
|
|
21
|
-
from typing import List
|
|
22
|
-
|
|
23
|
-
from requirements import requirement
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class Requirement(requirement.Requirement):
|
|
27
|
-
extra_pattern = re.compile("'([^']*)'")
|
|
28
|
-
|
|
29
|
-
def __init__(self, *args, **kwargs):
|
|
30
|
-
super().__init__(*args, **kwargs)
|
|
31
|
-
self.package_name = None
|
|
32
|
-
|
|
33
|
-
@classmethod
|
|
34
|
-
def parse_line(cls, line: str) -> Requirement:
|
|
35
|
-
if len(line_elements := line.split(";")) > 1:
|
|
36
|
-
line = line_elements[0]
|
|
37
|
-
result = super().parse_line(line)
|
|
38
|
-
|
|
39
|
-
if len(line_elements) > 1:
|
|
40
|
-
for element in line_elements[1:]:
|
|
41
|
-
if "extra" in element and (extras := cls.extra_pattern.search(element)):
|
|
42
|
-
result.extras.extend(extras.groups())
|
|
43
|
-
|
|
44
|
-
result.package_name = result.name
|
|
45
|
-
|
|
46
|
-
if result.uri and not result.name:
|
|
47
|
-
result.name = get_package_name(result.uri)
|
|
48
|
-
result.name = cls.standardize_name(result.name)
|
|
49
|
-
|
|
50
|
-
return result
|
|
51
|
-
|
|
52
|
-
@staticmethod
|
|
53
|
-
def standardize_name(name: str) -> str:
|
|
54
|
-
return WheelMetadata.to_wheel_name_format(name.lower())
|
|
55
|
-
|
|
56
|
-
@property
|
|
57
|
-
def formatted_specs(self):
|
|
58
|
-
return ",".join(sorted(spec[0] + spec[1] for spec in self.specs))
|
|
59
|
-
|
|
60
|
-
@property
|
|
61
|
-
def name_and_version(self):
|
|
62
|
-
return self.name + self.formatted_specs
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@dataclass
|
|
66
|
-
class RequirementWithFiles:
|
|
67
|
-
"""A dataclass to hold a requirement and the path to the
|
|
68
|
-
downloaded files/folders that belong to it"""
|
|
69
|
-
|
|
70
|
-
requirement: Requirement
|
|
71
|
-
files: List[str]
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
@dataclass
|
|
75
|
-
class RequirementWithWheel:
|
|
76
|
-
"""A dataclass to hold a requirement and corresponding .whl file."""
|
|
77
|
-
|
|
78
|
-
requirement: Requirement
|
|
79
|
-
wheel_path: Path | None
|
|
80
|
-
|
|
81
|
-
def extract_files(self, destination: Path) -> None:
|
|
82
|
-
if self.wheel_path is not None:
|
|
83
|
-
zipfile.ZipFile(self.wheel_path).extractall(destination)
|
|
84
|
-
|
|
85
|
-
def namelist(self) -> List[str]:
|
|
86
|
-
if self.wheel_path is None:
|
|
87
|
-
return []
|
|
88
|
-
return zipfile.ZipFile(self.wheel_path).namelist()
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
@dataclass
|
|
92
|
-
class WheelMetadata:
|
|
93
|
-
"""A dataclass to hold metadata from .whl file.
|
|
94
|
-
[name] is the name of the package standardized according to
|
|
95
|
-
https://peps.python.org/pep-0491/#escaping-and-unicode
|
|
96
|
-
"""
|
|
97
|
-
|
|
98
|
-
name: str
|
|
99
|
-
wheel_path: Path
|
|
100
|
-
dependencies: List[str]
|
|
101
|
-
|
|
102
|
-
@classmethod
|
|
103
|
-
def from_wheel(cls, wheel_path: Path):
|
|
104
|
-
"""Parses wheel metadata according to
|
|
105
|
-
https://peps.python.org/pep-0491/#file-contents"""
|
|
106
|
-
with zipfile.ZipFile(wheel_path, "r") as whl:
|
|
107
|
-
metadata_path = [
|
|
108
|
-
path for path in whl.namelist() if path.endswith(".dist-info/METADATA")
|
|
109
|
-
]
|
|
110
|
-
if len(metadata_path) != 1:
|
|
111
|
-
# malformatted wheel package
|
|
112
|
-
return None
|
|
113
|
-
|
|
114
|
-
root = zipfile.Path(whl)
|
|
115
|
-
metadata = (root / metadata_path[0]).read_text(encoding="utf-8")
|
|
116
|
-
|
|
117
|
-
dep_keyword = "Requires-Dist:"
|
|
118
|
-
dependencies = [
|
|
119
|
-
line[len(dep_keyword) :].strip()
|
|
120
|
-
for line in metadata.splitlines()
|
|
121
|
-
if line.startswith(dep_keyword)
|
|
122
|
-
]
|
|
123
|
-
name = cls._get_name_from_wheel_filename(wheel_path.name)
|
|
124
|
-
|
|
125
|
-
return cls(name=name, wheel_path=wheel_path, dependencies=dependencies)
|
|
126
|
-
|
|
127
|
-
@staticmethod
|
|
128
|
-
def _get_name_from_wheel_filename(wheel_filename: str) -> str:
|
|
129
|
-
# wheel filename is in format {name}-{version}[-{extra info}]
|
|
130
|
-
# https://peps.python.org/pep-0491/#file-name-convention
|
|
131
|
-
return wheel_filename.split("-")[0].lower()
|
|
132
|
-
|
|
133
|
-
@staticmethod
|
|
134
|
-
def to_wheel_name_format(package_name: str) -> str:
|
|
135
|
-
# https://peps.python.org/pep-0491/#escaping-and-unicode
|
|
136
|
-
return re.sub(r"[^\w\d.]+", "_", package_name, re.UNICODE)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def get_package_name(name: str) -> str:
|
|
140
|
-
if name.lower().startswith(("git+", "http")):
|
|
141
|
-
pattern = re.compile(r"github\.com\/[^\/]+\/([^\/][^.@$/]+)")
|
|
142
|
-
if match := pattern.search(name):
|
|
143
|
-
return match.group(1)
|
|
144
|
-
else:
|
|
145
|
-
return name
|
|
146
|
-
|
|
147
|
-
elif name.endswith(".zip"):
|
|
148
|
-
return name.replace(".zip", "")
|
|
149
|
-
else:
|
|
150
|
-
return name
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Snowflake Inc.
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Snowflake Inc.
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
import logging
|
|
18
|
-
from dataclasses import dataclass
|
|
19
|
-
from typing import Dict, List, Set
|
|
20
|
-
|
|
21
|
-
from packaging.requirements import InvalidRequirement
|
|
22
|
-
from packaging.requirements import Requirement as PkgRequirement
|
|
23
|
-
from packaging.version import InvalidVersion, parse
|
|
24
|
-
from snowflake.cli._plugins.snowpark.models import Requirement
|
|
25
|
-
from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
|
|
26
|
-
from snowflake.cli.api.secure_path import SecurePath
|
|
27
|
-
from snowflake.cli.api.sql_execution import SqlExecutionMixin
|
|
28
|
-
from snowflake.connector import DictCursor
|
|
29
|
-
|
|
30
|
-
log = logging.getLogger(__name__)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@dataclass
|
|
34
|
-
class FilterRequirementsResult:
|
|
35
|
-
"""A dataclass to hold the results of parsing requirements files and dividing them into
|
|
36
|
-
snowflake-supported vs other packages.
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
in_snowflake: List[Requirement]
|
|
40
|
-
unavailable: List[Requirement]
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@dataclass
|
|
44
|
-
class AvailablePackage:
|
|
45
|
-
snowflake_name: str
|
|
46
|
-
versions: Set[str]
|
|
47
|
-
|
|
48
|
-
def iter_versions(self):
|
|
49
|
-
for version in self.versions:
|
|
50
|
-
yield parse(version)
|
|
51
|
-
|
|
52
|
-
def is_required_version_available(self, requirement: Requirement) -> bool:
|
|
53
|
-
try:
|
|
54
|
-
package_specifiers = PkgRequirement(requirement.line).specifier
|
|
55
|
-
return any(
|
|
56
|
-
version in package_specifiers for version in self.iter_versions()
|
|
57
|
-
)
|
|
58
|
-
except (InvalidVersion, InvalidRequirement):
|
|
59
|
-
# fail-safe for non-pep508 formats
|
|
60
|
-
return False
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class AnacondaPackages:
|
|
64
|
-
def __init__(self, packages: Dict[str, AvailablePackage]):
|
|
65
|
-
"""
|
|
66
|
-
[packages] should be a dictionary mapping package name to AnacondaPackageData object.
|
|
67
|
-
All package names should be provided in wheel escape format:
|
|
68
|
-
https://peps.python.org/pep-0491/#escaping-and-unicode
|
|
69
|
-
"""
|
|
70
|
-
self._packages = packages
|
|
71
|
-
|
|
72
|
-
@classmethod
|
|
73
|
-
def empty(cls):
|
|
74
|
-
return cls({})
|
|
75
|
-
|
|
76
|
-
def is_package_available(
|
|
77
|
-
self, package: Requirement, skip_version_check: bool = False
|
|
78
|
-
) -> bool:
|
|
79
|
-
"""
|
|
80
|
-
Checks of a requirement is available in the Snowflake Anaconda Channel.
|
|
81
|
-
|
|
82
|
-
As Snowflake currently doesn't support extra syntax (ex. `jinja2[diagrams]`), if such
|
|
83
|
-
extra is present in the dependency, we mark it as unavailable.
|
|
84
|
-
"""
|
|
85
|
-
if not package.name or package.extras:
|
|
86
|
-
return False
|
|
87
|
-
if package.name not in self._packages:
|
|
88
|
-
return False
|
|
89
|
-
if skip_version_check or not package.specs:
|
|
90
|
-
return True
|
|
91
|
-
return self._packages[package.name].is_required_version_available(package)
|
|
92
|
-
|
|
93
|
-
def package_latest_version(self, package: Requirement) -> str | None:
|
|
94
|
-
"""Returns the latest version of the package or None if the latest version can't be determined."""
|
|
95
|
-
if package.name not in self._packages:
|
|
96
|
-
return None
|
|
97
|
-
try:
|
|
98
|
-
return str(max(self._packages[package.name].iter_versions()))
|
|
99
|
-
except InvalidVersion:
|
|
100
|
-
# fail-safe for non-pep8 versions
|
|
101
|
-
return None
|
|
102
|
-
|
|
103
|
-
def package_versions(self, package: Requirement) -> List[str]:
|
|
104
|
-
"""Returns list of available versions of the package."""
|
|
105
|
-
if package.name not in self._packages:
|
|
106
|
-
return []
|
|
107
|
-
package_data = self._packages[package.name]
|
|
108
|
-
try:
|
|
109
|
-
return list(
|
|
110
|
-
str(x) for x in sorted(package_data.iter_versions(), reverse=True)
|
|
111
|
-
)
|
|
112
|
-
except InvalidVersion:
|
|
113
|
-
return list(sorted(package_data.versions, reverse=True))
|
|
114
|
-
|
|
115
|
-
def filter_available_packages(
|
|
116
|
-
self, packages: List[Requirement], skip_version_check: bool = False
|
|
117
|
-
) -> FilterRequirementsResult:
|
|
118
|
-
"""
|
|
119
|
-
Checks if a list of packages are available in the Snowflake Anaconda channel.
|
|
120
|
-
Returns an object with two attributes: 'snowflake' and 'other'.
|
|
121
|
-
Each key contains a list of Requirement object.
|
|
122
|
-
|
|
123
|
-
Parameters:
|
|
124
|
-
packages (List[Requirement]) - list of requirements to be checked
|
|
125
|
-
skip_version_check (bool) - skip comparing versions of packages
|
|
126
|
-
|
|
127
|
-
Returns:
|
|
128
|
-
result (FilterRequirementsResult) - object containing two arguments:
|
|
129
|
-
- in_snowflake - packages available in conda
|
|
130
|
-
- unavailable - packages not available in conda
|
|
131
|
-
"""
|
|
132
|
-
result = FilterRequirementsResult([], [])
|
|
133
|
-
for package in packages:
|
|
134
|
-
if self.is_package_available(
|
|
135
|
-
package, skip_version_check=skip_version_check
|
|
136
|
-
):
|
|
137
|
-
result.in_snowflake.append(package)
|
|
138
|
-
else:
|
|
139
|
-
log.info(
|
|
140
|
-
"'%s' not found in Snowflake Anaconda channel (or ignored)...",
|
|
141
|
-
package.name,
|
|
142
|
-
)
|
|
143
|
-
result.unavailable.append(package)
|
|
144
|
-
return result
|
|
145
|
-
|
|
146
|
-
def write_requirements_file_in_snowflake_format(
|
|
147
|
-
self,
|
|
148
|
-
file_path: SecurePath,
|
|
149
|
-
requirements: List[Requirement],
|
|
150
|
-
):
|
|
151
|
-
"""Saves requirements to a file in format accepted by Snowflake SQL commands."""
|
|
152
|
-
log.info("Writing requirements into file %s", file_path.path)
|
|
153
|
-
formatted_requirements = []
|
|
154
|
-
for requirement in requirements:
|
|
155
|
-
if requirement.name and requirement.name in self._packages:
|
|
156
|
-
snowflake_name = self._packages[requirement.name].snowflake_name
|
|
157
|
-
formatted_requirements.append(
|
|
158
|
-
snowflake_name + requirement.formatted_specs
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
if formatted_requirements:
|
|
162
|
-
file_path.write_text("\n".join(formatted_requirements))
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
class AnacondaPackagesManager(SqlExecutionMixin):
|
|
166
|
-
_snowflake_channel_url: str = (
|
|
167
|
-
"https://repo.anaconda.com/pkgs/snowflake/channeldata.json"
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
def find_packages_available_in_snowflake_anaconda(self) -> AnacondaPackages:
|
|
171
|
-
"""
|
|
172
|
-
Finds python packages available in Snowflake to use in functions and stored procedures.
|
|
173
|
-
It tries to get the list of packages using SQL query
|
|
174
|
-
but if the try fails then the fallback is to parse JSON containing info about Snowflake's Anaconda channel.
|
|
175
|
-
"""
|
|
176
|
-
packages = self._query_snowflake_for_available_packages()
|
|
177
|
-
return AnacondaPackages(packages)
|
|
178
|
-
|
|
179
|
-
def _query_snowflake_for_available_packages(self) -> dict[str, AvailablePackage]:
|
|
180
|
-
cursor = self._execute_query(
|
|
181
|
-
"select package_name, version from information_schema.packages where language = 'python'",
|
|
182
|
-
cursor_class=DictCursor,
|
|
183
|
-
)
|
|
184
|
-
if cursor.rowcount is None or cursor.rowcount == 0:
|
|
185
|
-
raise SnowflakeSQLExecutionError()
|
|
186
|
-
packages: dict[str, AvailablePackage] = {}
|
|
187
|
-
for row in cursor:
|
|
188
|
-
if not (package_name := row["PACKAGE_NAME"]):
|
|
189
|
-
continue
|
|
190
|
-
if not (version := row["VERSION"]):
|
|
191
|
-
continue
|
|
192
|
-
standardized_name = Requirement.standardize_name(package_name)
|
|
193
|
-
if standardized_name in packages:
|
|
194
|
-
packages[standardized_name].versions.add(version)
|
|
195
|
-
else:
|
|
196
|
-
packages[standardized_name] = AvailablePackage(
|
|
197
|
-
snowflake_name=package_name, versions={version}
|
|
198
|
-
)
|
|
199
|
-
return packages
|