Skip to content

Commit

Permalink
Moved Snowpark and Streamlit artifacts to separate directory in outpu…
Browse files Browse the repository at this point in the history
…t/deploy
  • Loading branch information
sfc-gh-astus committed Dec 3, 2024
1 parent c20fb7c commit 877d282
Show file tree
Hide file tree
Showing 13 changed files with 200 additions and 116 deletions.
6 changes: 3 additions & 3 deletions src/snowflake/cli/_plugins/snowpark/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,19 +220,19 @@ def build_artifacts_mappings(
) -> Tuple[EntityToImportPathsMapping, StageToArtefactMapping]:
stages_to_artifact_map: StageToArtefactMapping = defaultdict(set)
entities_to_imports_map: EntityToImportPathsMapping = defaultdict(set)
for entity_id, entity in snowpark_entities.items():
for name, entity in snowpark_entities.items():
stage = entity.stage
required_artifacts = set()
for artefact in entity.artifacts:
artefact_dto = project_paths.get_artefact_dto(artefact)
required_artifacts.add(artefact_dto)
entities_to_imports_map[entity_id].add(artefact_dto.import_path(stage))
entities_to_imports_map[name].add(artefact_dto.import_path(stage))
stages_to_artifact_map[stage].update(required_artifacts)

deps_artefact = project_paths.get_dependencies_artefact()
if deps_artefact.post_build_path.exists():
stages_to_artifact_map[stage].add(deps_artefact)
entities_to_imports_map[entity_id].add(deps_artefact.import_path(stage))
entities_to_imports_map[name].add(deps_artefact.import_path(stage))
return entities_to_imports_map, stages_to_artifact_map


Expand Down
27 changes: 19 additions & 8 deletions src/snowflake/cli/_plugins/snowpark/snowpark_project_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from snowflake.cli.api.constants import DEPLOYMENT_STAGE
from snowflake.cli.api.feature_flags import FeatureFlag
from snowflake.cli.api.identifiers import FQN
from snowflake.cli.api.project.project_paths import ProjectPaths
from snowflake.cli.api.project.project_paths import ProjectPaths, deploy_root
from snowflake.cli.api.project.schemas.entities.common import PathMapping
from snowflake.cli.api.secure_path import SecurePath

Expand All @@ -45,6 +45,7 @@ def get_artefact_dto(self, artifact_path: PathMapping) -> Artefact:
if FeatureFlag.ENABLE_SNOWPARK_GLOB_SUPPORT.is_enabled():
return Artefact(
project_root=self.project_root,
deploy_root=self.deploy_root,
dest=artifact_path.dest,
path=Path(artifact_path.src),
)
Expand All @@ -57,7 +58,10 @@ def get_artefact_dto(self, artifact_path: PathMapping) -> Artefact:
def get_dependencies_artefact(self) -> Artefact:
if FeatureFlag.ENABLE_SNOWPARK_GLOB_SUPPORT.is_enabled():
return Artefact(
project_root=self.project_root, dest=None, path=Path("dependencies.zip")
project_root=self.project_root,
deploy_root=self.deploy_root,
dest=None,
path=Path("dependencies.zip"),
)
else:
return ArtefactOldBuild(
Expand All @@ -74,19 +78,29 @@ def snowflake_requirements(self) -> SecurePath:
def requirements(self) -> SecurePath:
return SecurePath(self.path_relative_to_root(Path("requirements.txt")))

@property
def deploy_root(self) -> Path:
return deploy_root(self.project_root, "snowpark")


@dataclass(unsafe_hash=True)
class Artefact:
"""Helper for getting paths related to given artefact."""

project_root: Path
deploy_root: Path
path: Path
dest: str | None = None

def __init__(
self, project_root: Path, path: Path, dest: Optional[str] = None
self,
project_root: Path,
deploy_root: Path,
path: Path,
dest: Optional[str] = None,
) -> None:
self.project_root = project_root
self.deploy_root = deploy_root
self.path = path
self.dest = dest
if self.dest and not self._is_dest_a_file() and not self.dest.endswith("/"):
Expand Down Expand Up @@ -117,7 +131,7 @@ def post_build_path(self) -> Path:
"""
Returns post-build artefact path. Directories are mapped to corresponding .zip files.
"""
deploy_root = self.deploy_root()
deploy_root = self.deploy_root
path = (
self._path_until_asterisk()
if glob.has_magic(str(self.path))
Expand Down Expand Up @@ -153,9 +167,6 @@ def import_path(self, stage: FQN | str | None) -> str:
"""Path for UDF/sproc imports clause."""
return self.upload_path(stage) + self._artefact_name

def deploy_root(self) -> Path:
return self.project_root / "output"

def _is_dest_a_file(self) -> bool:
if not self.dest:
return False
Expand Down Expand Up @@ -183,7 +194,7 @@ class ArtefactOldBuild(Artefact):
dest: str | None = None

def __init__(self, path: Path, dest: Optional[str] = None) -> None:
super().__init__(project_root=Path(), path=path, dest=dest)
super().__init__(project_root=Path(), deploy_root=Path(), path=path, dest=dest)

@property
def _artefact_name(self) -> str:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@
from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

from snowflake.cli.api.project.project_paths import ProjectPaths
from snowflake.cli.api.project.project_paths import ProjectPaths, deploy_root


@dataclass
class StreamlitProjectPaths(ProjectPaths):
"""
This class allows you to manage files paths related to given project.
"""

@property
def deploy_root(self) -> Path:
return deploy_root(self.project_root, "streamlit")
8 changes: 7 additions & 1 deletion src/snowflake/cli/api/project/project_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ class ProjectPaths:

@property
def deploy_root(self) -> Path:
return self.project_root / "output"
return deploy_root(self.project_root)

def remove_up_deploy_root(self) -> None:
if self.deploy_root.exists():
rmtree(self.deploy_root)


def deploy_root(root: Path, app_type: str | None = None) -> Path:
if app_type:
return root / "output" / "deploy" / app_type
return root / "output" / "deploy"
42 changes: 22 additions & 20 deletions tests/snowpark/test_artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,42 +12,44 @@
lambda _: True,
)

deploy_root = Path("output") / "deploy" / "snowpark"


@pytest.mark.parametrize(
"artifacts, local_path, stage_path",
[
("src", Path("output") / "src.zip", "/"),
("src/", Path("output") / "src.zip", "/"),
("src/*", Path("output") / "src.zip", "/"),
("src/*.py", Path("output") / "src.zip", "/"),
("src", deploy_root / "src.zip", "/"),
("src/", deploy_root / "src.zip", "/"),
("src/*", deploy_root / "src.zip", "/"),
("src/*.py", deploy_root / "src.zip", "/"),
(
"src/dir/dir_app.py",
Path("output") / "src" / "dir" / "dir_app.py",
deploy_root / "src" / "dir" / "dir_app.py",
"/src/dir/",
),
(
{"src": "src/**/*", "dest": "source/"},
Path("output") / "source" / "src.zip",
deploy_root / "source" / "src.zip",
"/source/",
),
(
{"src": "src", "dest": "source/"},
Path("output") / "source" / "src.zip",
deploy_root / "source" / "src.zip",
"/source/",
),
(
{"src": "src/", "dest": "source/"},
Path("output") / "source" / "src.zip",
deploy_root / "source" / "src.zip",
"/source/",
),
(
{"src": "src/*", "dest": "source/"},
Path("output") / "source" / "src.zip",
deploy_root / "source" / "src.zip",
"/source/",
),
(
{"src": "src/dir/dir_app.py", "dest": "source/dir/apps/"},
"output/source/dir/apps/dir_app.py",
deploy_root / "source" / "dir" / "apps" / "dir_app.py",
"/source/dir/apps/",
),
],
Expand Down Expand Up @@ -111,38 +113,38 @@ def test_build_and_deploy_with_artifacts(
@pytest.mark.parametrize(
"artifact, local_path, stage_path",
[
("src", Path("output") / "src.zip", "/"),
("src/", Path("output") / "src.zip", "/"),
("src/*", Path("output") / "src.zip", "/"),
("src/*.py", Path("output") / "src.zip", "/"),
("src", deploy_root / "src.zip", "/"),
("src/", deploy_root / "src.zip", "/"),
("src/*", deploy_root / "src.zip", "/"),
("src/*.py", deploy_root / "src.zip", "/"),
(
"src/dir/dir_app.py",
Path("output") / "src" / "dir" / "dir_app.py",
deploy_root / "src" / "dir" / "dir_app.py",
"/src/dir/",
),
(
{"src": "src/**/*", "dest": "source/"},
Path("output") / "source" / "src.zip",
deploy_root / "source" / "src.zip",
"/source/",
),
(
{"src": "src", "dest": "source/"},
Path("output") / "source" / "src.zip",
deploy_root / "source" / "src.zip",
"/source/",
),
(
{"src": "src/", "dest": "source/"},
Path("output") / "source" / "src.zip",
deploy_root / "source" / "src.zip",
"/source/",
),
(
{"src": "src/*", "dest": "source/"},
Path("output") / "source" / "src.zip",
deploy_root / "source" / "src.zip",
"/source/",
),
(
{"src": "src/dir/dir_app.py", "dest": "source/dir/apps/"},
Path("output") / "source" / "dir" / "apps" / "dir_app.py",
deploy_root / "source" / "dir" / "apps" / "dir_app.py",
"/source/dir/apps/",
),
],
Expand Down
4 changes: 3 additions & 1 deletion tests/snowpark/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ def test_build_with_glob_patterns_in_artifacts(

result = runner.invoke(["snowpark", "build", "--ignore-anaconda"])
assert result.exit_code == 0, result.output
_assert_zip_contains(tmp_dir / "output" / zip_name, expected_files)
_assert_zip_contains(
tmp_dir / "output" / "deploy" / "snowpark" / zip_name, expected_files
)


def _assert_zip_contains(app_zip: str, expected_files: Set[str]):
Expand Down
6 changes: 3 additions & 3 deletions tests/snowpark/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ def test_deploy_function_no_changes(
]
assert queries == [
"create stage if not exists IDENTIFIER('MockDatabase.MockSchema.dev_deployment') comment='deployments managed by Snowflake CLI'",
f"put file://{Path(project_dir).resolve()}/output/my_snowpark_project/app.py @MockDatabase.MockSchema.dev_deployment/my_snowpark_project/ auto_compress=false parallel=4 overwrite=True",
f"put file://{Path(project_dir).resolve()}/output/deploy/snowpark/my_snowpark_project/app.py @MockDatabase.MockSchema.dev_deployment/my_snowpark_project/ auto_compress=false parallel=4 overwrite=True",
]


Expand Down Expand Up @@ -259,7 +259,7 @@ def test_deploy_function_needs_update_because_packages_changes(
]
assert queries == [
"create stage if not exists IDENTIFIER('MockDatabase.MockSchema.dev_deployment') comment='deployments managed by Snowflake CLI'",
f"put file://{Path(project_dir).resolve()}/output/my_snowpark_project/app.py @MockDatabase.MockSchema.dev_deployment/my_snowpark_project/ auto_compress=false parallel=4 overwrite=True",
f"put file://{Path(project_dir).resolve()}/output/deploy/snowpark/my_snowpark_project/app.py @MockDatabase.MockSchema.dev_deployment/my_snowpark_project/ auto_compress=false parallel=4 overwrite=True",
dedent(
"""\
create or replace function IDENTIFIER('MockDatabase.MockSchema.func1')(a string default 'default value', b variant)
Expand Down Expand Up @@ -316,7 +316,7 @@ def test_deploy_function_needs_update_because_handler_changes(
]
assert queries == [
"create stage if not exists IDENTIFIER('MockDatabase.MockSchema.dev_deployment') comment='deployments managed by Snowflake CLI'",
f"put file://{Path(project_dir).resolve()}/output/my_snowpark_project/app.py @MockDatabase.MockSchema.dev_deployment/my_snowpark_project/"
f"put file://{Path(project_dir).resolve()}/output/deploy/snowpark/my_snowpark_project/app.py @MockDatabase.MockSchema.dev_deployment/my_snowpark_project/"
f" auto_compress=false parallel=4 overwrite=True",
dedent(
"""\
Expand Down
4 changes: 2 additions & 2 deletions tests/snowpark/test_procedure.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def test_deploy_procedure(
)
assert ctx.get_queries() == [
"create stage if not exists IDENTIFIER('MockDatabase.MockSchema.dev_deployment') comment='deployments managed by Snowflake CLI'",
f"put file://{Path(tmp).resolve()}/output/my_snowpark_project/app.py @MockDatabase.MockSchema.dev_deployment/my_snowpark_project/ auto_compress=false parallel=4 overwrite=True",
f"put file://{Path(tmp).resolve()}/output/deploy/snowpark/my_snowpark_project/app.py @MockDatabase.MockSchema.dev_deployment/my_snowpark_project/ auto_compress=false parallel=4 overwrite=True",
dedent(
"""\
create or replace procedure IDENTIFIER('MockDatabase.MockSchema.procedureName')(name string)
Expand Down Expand Up @@ -190,7 +190,7 @@ def test_deploy_procedure_with_external_access(
)
assert ctx.get_queries() == [
"create stage if not exists IDENTIFIER('MockDatabase.MockSchema.dev_deployment') comment='deployments managed by Snowflake CLI'",
f"put file://{Path(project_dir).resolve()}/output/my_snowpark_project/app.py @MockDatabase.MockSchema.dev_deployment/my_snowpark_project/"
f"put file://{Path(project_dir).resolve()}/output/deploy/snowpark/my_snowpark_project/app.py @MockDatabase.MockSchema.dev_deployment/my_snowpark_project/"
f" auto_compress=false parallel=4 overwrite=True",
dedent(
"""\
Expand Down
Loading

0 comments on commit 877d282

Please sign in to comment.