Skip to content

Commit

Permalink
Merge branch 'main' into missing-log-directory-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-turbaszek authored Jul 12, 2024
2 parents 6723f3c + b58e2d2 commit 8ca448c
Show file tree
Hide file tree
Showing 27 changed files with 584 additions and 119 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test_integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip hatch
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ gen_docs/
/venv/
.env
.vscode
tmp/

^app.zip
^snowflake.yml
3 changes: 2 additions & 1 deletion RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@
## New additions
* Added connection option `--token-file-path` allowing passing OAuth token using a file. The function is also
supported by setting `token_file_path` in connection definition.
* Support for Python remote execution via `snow stage execute` and `snow git execute` similar to existing EXECUTE IMMEDIATE support.

## Fixes and improvements
* The `snow app run` command now allows upgrading to unversioned mode from a versioned or release mode application installation
* The `snow app teardown` command now allows dropping a package with versions when the `--force` flag is provided
* The `snow app version create` command now allows operating on application packages created outside the CLI
* Added support for user stages in stage execute command
* Added support for user stages in stage and git copy commands

* Improved support for quoted identifiers in snowpark commands.

# v2.6.0
## Backward incompatibility
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies = [
"setuptools==70.2.0",
'snowflake.core==0.8.0; python_version < "3.12"',
"snowflake-connector-python[secure-local-storage]==3.11.0",
'snowflake-snowpark-python>=1.15.0;python_version < "3.12"',
"tomlkit==0.12.5",
"typer==0.12.3",
"urllib3>=1.24.3,<2.3",
Expand Down
1 change: 1 addition & 0 deletions snyk/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ requirements-parser==0.9.0
setuptools==70.2.0
snowflake.core==0.8.0; python_version < "3.12"
snowflake-connector-python[secure-local-storage]==3.11.0
snowflake-snowpark-python>=1.15.0;python_version < "3.12"
tomlkit==0.12.5
typer==0.12.3
urllib3>=1.24.3,<2.3
Expand Down
8 changes: 6 additions & 2 deletions src/snowflake/cli/api/commands/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,10 @@ def _password_callback(value: str):
None,
"--variable",
"-D",
help="Variables for the template. For example: `-D \"<key>=<value>\"`, string values must be in `''`.",
help='Variables for the execution context. For example: `-D "<key>=<value>"`. '
"For SQL files variables are use to expand the template and any unknown variable will cause an error. "
"For Python files variables are used to update os.environ dictionary. Provided keys are capitalized to adhere to best practices."
"In case of SQL files string values must be quoted in `''` (consider embedding quoting in the file).",
show_default=False,
)

Expand Down Expand Up @@ -612,10 +615,11 @@ def __init__(self, key: str, value: str):

def parse_key_value_variables(variables: Optional[List[str]]) -> List[Variable]:
"""Util for parsing key=value input. Useful for commands accepting multiple input options."""
if not variables:
return []
result: List[Variable] = []
if not variables:
return result

for p in variables:
if "=" not in p:
raise ClickException(f"Invalid variable: '{p}'")
Expand Down
19 changes: 16 additions & 3 deletions src/snowflake/cli/api/identifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,17 @@ def name(self) -> str:
return self._name

@property
def identifier(self) -> str:
def prefix(self) -> str:
if self.database:
return f"{self.database}.{self.schema if self.schema else 'PUBLIC'}.{self.name}"
return f"{self.database}.{self.schema if self.schema else 'PUBLIC'}"
if self.schema:
return f"{self.schema}.{self.name}"
return f"{self.schema}"
return ""

@property
def identifier(self) -> str:
if self.prefix:
return f"{self.prefix}.{self.name}"
return self.name

@property
Expand Down Expand Up @@ -96,6 +102,13 @@ def from_string(cls, identifier: str) -> "FQN":
unqualified_name = unqualified_name + signature
return cls(name=unqualified_name, schema=schema, database=database)

@classmethod
def from_stage(cls, stage: str) -> "FQN":
name = stage
if stage.startswith("@"):
name = stage[1:]
return cls.from_string(name)

@classmethod
def from_identifier_model(cls, model: ObjectIdentifierBaseModel) -> "FQN":
"""Create an instance from object model."""
Expand Down
12 changes: 11 additions & 1 deletion src/snowflake/cli/api/sql_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,22 @@

class SqlExecutionMixin:
def __init__(self):
pass
self._snowpark_session = None

@property
def _conn(self):
return cli_context.connection

@property
def snowpark_session(self):
if not self._snowpark_session:
from snowflake.snowpark.session import Session

self._snowpark_session = Session.builder.configs(
{"connection": self._conn}
).create()
return self._snowpark_session

@cached_property
def _log(self):
return logging.getLogger(__name__)
Expand Down
10 changes: 9 additions & 1 deletion src/snowflake/cli/app/snow_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@

import snowflake.connector
from click.exceptions import ClickException
from snowflake.cli.api.config import get_connection_dict, get_default_connection_dict
from snowflake.cli.api.cli_global_context import cli_context
from snowflake.cli.api.config import (
get_connection_dict,
get_default_connection_dict,
get_default_connection_name,
)
from snowflake.cli.api.constants import DEFAULT_SIZE_LIMIT_MB
from snowflake.cli.api.exceptions import (
InvalidConnectionConfiguration,
Expand Down Expand Up @@ -70,6 +75,9 @@ def connect_to_snowflake(
connection_parameters = {} # we will apply overrides in next step
else:
connection_parameters = get_default_connection_dict()
cli_context.connection_context.set_connection_name(
get_default_connection_name()
)

# Apply overrides to connection details
for key, value in overrides.items():
Expand Down
3 changes: 2 additions & 1 deletion src/snowflake/cli/plugins/cortex/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from snowflake.cli.api.cli_global_context import cli_context
from snowflake.cli.api.commands.flags import readable_file_option
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
from snowflake.cli.api.constants import PYTHON_3_12
from snowflake.cli.api.output.types import (
CollectionResult,
CommandResult,
Expand All @@ -45,7 +46,7 @@
help="Provides access to Snowflake Cortex.",
)

SEARCH_COMMAND_ENABLED = sys.version_info < (3, 12)
SEARCH_COMMAND_ENABLED = sys.version_info < PYTHON_3_12


@app.command(
Expand Down
39 changes: 18 additions & 21 deletions src/snowflake/cli/plugins/snowpark/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@
from snowflake.cli.plugins.object.manager import ObjectManager
from snowflake.cli.plugins.snowpark import package_utils
from snowflake.cli.plugins.snowpark.common import (
build_udf_sproc_identifier,
FunctionOrProcedure,
UdfSprocIdentifier,
check_if_replace_is_required,
)
from snowflake.cli.plugins.snowpark.manager import FunctionManager, ProcedureManager
Expand Down Expand Up @@ -220,7 +221,7 @@ def deploy(


def _assert_object_definitions_are_correct(
object_type, object_definitions: List[FunctionSchema | ProcedureSchema]
object_type, object_definitions: List[FunctionOrProcedure]
):
for definition in object_definitions:
database = definition.database
Expand All @@ -239,14 +240,14 @@ def _assert_object_definitions_are_correct(

def _find_existing_objects(
object_type: ObjectType,
objects: List[Dict],
objects: List[FunctionOrProcedure],
om: ObjectManager,
):
existing_objects = {}
for object_definition in objects:
identifier = build_udf_sproc_identifier(
object_definition, om, include_parameter_names=False
)
identifier = UdfSprocIdentifier.from_definition(
object_definition
).identifier_with_arg_types
try:
current_state = om.describe(
object_type=object_type.value.sf_name,
Expand Down Expand Up @@ -295,33 +296,29 @@ def get_app_stage_path(stage_name: Optional[str], project_name: str) -> str:
def _deploy_single_object(
manager: FunctionManager | ProcedureManager,
object_type: ObjectType,
object_definition: FunctionSchema | ProcedureSchema,
object_definition: FunctionOrProcedure,
existing_objects: Dict[str, Dict],
snowflake_dependencies: List[str],
stage_artifact_path: str,
):
identifier = build_udf_sproc_identifier(
object_definition, manager, include_parameter_names=False
)
identifier_with_default_values = build_udf_sproc_identifier(
object_definition,
manager,
include_parameter_names=True,
include_default_values=True,

identifiers = UdfSprocIdentifier.from_definition(object_definition)

log.info(
"Deploying %s: %s", object_type, identifiers.identifier_with_arg_names_types
)
log.info("Deploying %s: %s", object_type, identifier_with_default_values)

handler = object_definition.handler
returns = object_definition.returns
imports = object_definition.imports
external_access_integrations = object_definition.external_access_integrations
replace_object = False

object_exists = identifier in existing_objects
object_exists = identifiers.identifier_with_arg_types in existing_objects
if object_exists:
replace_object = check_if_replace_is_required(
object_type=object_type,
current_state=existing_objects[identifier],
current_state=existing_objects[identifiers.identifier_with_arg_types],
handler=handler,
return_type=returns,
snowflake_dependencies=snowflake_dependencies,
Expand All @@ -332,13 +329,13 @@ def _deploy_single_object(

if object_exists and not replace_object:
return {
"object": identifier_with_default_values,
"object": identifiers.identifier_with_arg_names_types_defaults,
"type": str(object_type),
"status": "packages updated",
}

create_or_replace_kwargs = {
"identifier": identifier_with_default_values,
"identifier": identifiers,
"handler": handler,
"return_type": returns,
"artifact_file": stage_artifact_path,
Expand All @@ -356,7 +353,7 @@ def _deploy_single_object(

status = "created" if not object_exists else "definition updated"
return {
"object": identifier_with_default_values,
"object": identifiers.identifier_with_arg_names_types_defaults,
"type": str(object_type),
"status": status,
}
Expand Down
Loading

0 comments on commit 8ca448c

Please sign in to comment.