Skip to content

Commit

Permalink
Move domain logic to individual processors (#1926)
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-bdufour authored Dec 9, 2024
1 parent a6368d4 commit daa2e22
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 22 deletions.
53 changes: 37 additions & 16 deletions src/snowflake/cli/_plugins/nativeapp/codegen/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,14 @@
from snowflake.cli._plugins.nativeapp.codegen.templates.templates_processor import (
TemplatesProcessor,
)
from snowflake.cli._plugins.nativeapp.feature_flags import FeatureFlag
from snowflake.cli.api.cli_global_context import get_cli_context
from snowflake.cli.api.console import cli_console as cc
from snowflake.cli.api.metrics import CLICounterField
from snowflake.cli.api.project.schemas.v1.native_app.path_mapping import (
ProcessorMapping,
)

SNOWPARK_PROCESSOR = "snowpark"
NA_SETUP_PROCESSOR = "native app setup"
TEMPLATES_PROCESSOR = "templates"

_REGISTERED_PROCESSORS_BY_NAME = {
SNOWPARK_PROCESSOR: SnowparkAnnotationProcessor,
NA_SETUP_PROCESSOR: NativeAppSetupProcessor,
TEMPLATES_PROCESSOR: TemplatesProcessor,
}
ProcessorClassType = type[ArtifactProcessor]


class NativeAppCompiler:
Expand All @@ -66,10 +57,28 @@ def __init__(
bundle_ctx: BundleContext,
):
self._assert_absolute_paths(bundle_ctx)
self._processor_classes_by_name: Dict[str, ProcessorClassType] = {}
self._bundle_ctx = bundle_ctx
# dictionary of all processors created and shared between different artifact objects.
self.cached_processors: Dict[str, ArtifactProcessor] = {}

self.register(SnowparkAnnotationProcessor)
self.register(NativeAppSetupProcessor)
self.register(TemplatesProcessor)

def register(self, processor_cls: ProcessorClassType):
"""
Registers a processor class to enable.
"""

name = getattr(processor_cls, "NAME", None)
assert name is not None

if name in self._processor_classes_by_name:
raise ValueError(f"Processor {name} is already registered")

self._processor_classes_by_name[str(name)] = processor_cls

@staticmethod
def _assert_absolute_paths(bundle_ctx: BundleContext):
for name in ["Project", "Deploy", "Bundle", "Generated"]:
Expand Down Expand Up @@ -128,8 +137,8 @@ def _try_create_processor(
if current_processor is not None:
return current_processor

processor_factory = _REGISTERED_PROCESSORS_BY_NAME.get(processor_name)
if processor_factory is None:
processor_cls = self._processor_classes_by_name.get(processor_name)
if processor_cls is None:
# No registered processor with the specified name
return None

Expand All @@ -141,7 +150,7 @@ def _try_create_processor(
processor_ctx.generated_root = (
self._bundle_ctx.generated_root / processor_subdirectory
)
current_processor = processor_factory(processor_ctx)
current_processor = processor_cls(processor_ctx)
self.cached_processors[processor_name] = current_processor

return current_processor
Expand All @@ -154,6 +163,18 @@ def _should_invoke_processors(self):
return False

def _is_enabled(self, processor: ProcessorMapping) -> bool:
if processor.name.lower() == NA_SETUP_PROCESSOR:
return FeatureFlag.ENABLE_NATIVE_APP_PYTHON_SETUP.is_enabled()
return True
"""
Determines is a process is enabled. All processors are considered enabled
unless they are explicitly disabled, typically via a feature flag.
"""
processor_name = processor.name.lower()
processor_cls = self._processor_classes_by_name.get(processor_name)
if processor_cls is None:
# Unknown processor, consider it enabled, even though trying to
# invoke it later will raise an exception
return True

# if the processor class defines a static method named "is_enabled", then
# call it. Otherwise, it's considered enabled by default.
is_enabled_fn = getattr(processor_cls, "is_enabled", lambda: True)
return is_enabled_fn()
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
SandboxEnvBuilder,
execute_script_in_sandbox,
)
from snowflake.cli._plugins.nativeapp.feature_flags import FeatureFlag
from snowflake.cli._plugins.stage.diff import to_stage_path
from snowflake.cli.api.console import cli_console as cc
from snowflake.cli.api.project.schemas.v1.native_app.path_mapping import (
Expand Down Expand Up @@ -74,9 +75,15 @@ def safe_set(d: dict, *keys: str, **kwargs) -> None:


class NativeAppSetupProcessor(ArtifactProcessor):
NAME = "native app setup"

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

@staticmethod
def is_enabled() -> bool:
return FeatureFlag.ENABLE_NATIVE_APP_PYTHON_SETUP.is_enabled()

def process(
self,
artifact_to_process: PathMapping,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
and generate SQL code for creation of extension functions based on those discovered objects.
"""

NAME = "snowpark"

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class TemplatesProcessor(ArtifactProcessor):
Processor class to perform template expansion on all relevant artifacts (specified in the project definition file).
"""

NAME = "templates"

def expand_templates_in_file(
self, src: Path, dest: Path, template_context: dict[str, Any] | None = None
) -> None:
Expand Down
3 changes: 1 addition & 2 deletions src/snowflake/cli/api/project/definition_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
bundle_artifacts,
)
from snowflake.cli._plugins.nativeapp.bundle_context import BundleContext
from snowflake.cli._plugins.nativeapp.codegen.compiler import TEMPLATES_PROCESSOR
from snowflake.cli._plugins.nativeapp.codegen.templates.templates_processor import (
TemplatesProcessor,
)
Expand Down Expand Up @@ -457,7 +456,7 @@ def _convert_templates_in_files(
artifact
for artifact in pkg_model.artifacts
for processor in artifact.processors
if processor.name == TEMPLATES_PROCESSOR
if processor.name.lower() == TemplatesProcessor.NAME
]
if not in_memory and artifacts_to_template:
metrics.set_counter(CLICounterField.TEMPLATES_PROCESSOR, 1)
Expand Down
38 changes: 38 additions & 0 deletions tests/nativeapp/codegen/test_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
# limitations under the License.
import re
from pathlib import Path
from typing import Optional

import pytest
from snowflake.cli._plugins.nativeapp.bundle_context import BundleContext
from snowflake.cli._plugins.nativeapp.codegen.artifact_processor import (
ArtifactProcessor,
UnsupportedArtifactProcessorError,
)
from snowflake.cli._plugins.nativeapp.codegen.compiler import NativeAppCompiler
Expand All @@ -29,6 +31,10 @@
from snowflake.cli.api.project.schemas.project_definition import (
build_project_definition,
)
from snowflake.cli.api.project.schemas.v1.native_app.path_mapping import (
PathMapping,
ProcessorMapping,
)


@pytest.fixture()
Expand Down Expand Up @@ -114,3 +120,35 @@ def test_find_and_execute_processors_exception(test_proj_def, test_compiler):

with pytest.raises(UnsupportedArtifactProcessorError):
test_compiler.compile_artifacts()


class TestProcessor(ArtifactProcessor):
NAME = "test_processor"

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
assert False # never invoked

@staticmethod
def is_enabled():
return False

def process(
self,
artifact_to_process: PathMapping,
processor_mapping: Optional[ProcessorMapping],
**kwargs,
) -> None:
assert False # never invoked


def test_skips_disabled_processors(test_proj_def, test_compiler):
pkg_model = test_proj_def.entities["pkg"]
pkg_model.artifacts = [
{"dest": "./", "src": "app/*", "processors": ["test_processor"]}
]
test_compiler = NativeAppCompiler(_get_bundle_context(pkg_model))
test_compiler.register(TestProcessor)

# TestProcessor is never invoked, otherwise calling its methods will make the test fail
test_compiler.compile_artifacts()
10 changes: 6 additions & 4 deletions tests_integration/helpers/test_v1_to_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import pytest

from snowflake.cli._plugins.nativeapp.codegen.compiler import TEMPLATES_PROCESSOR
from snowflake.cli._plugins.nativeapp.codegen.templates.templates_processor import (
TemplatesProcessor,
)
from tests.nativeapp.factories import ProjectV11Factory


Expand Down Expand Up @@ -44,10 +46,10 @@ def test_v1_to_v2_converts_templates_in_files(temp_dir, runner):
pdf__native_app__package__name="my_pkg",
pdf__native_app__application__name="my_app",
pdf__native_app__artifacts=[
dict(src="templated.txt", processors=[TEMPLATES_PROCESSOR]),
dict(src="templated.txt", processors=[TemplatesProcessor.NAME]),
dict(src="untemplated.txt"),
dict(src="app/*", processors=[TEMPLATES_PROCESSOR]),
dict(src="nested/*", processors=[TEMPLATES_PROCESSOR]),
dict(src="app/*", processors=[TemplatesProcessor.NAME]),
dict(src="nested/*", processors=[TemplatesProcessor.NAME]),
],
files={
filename: source_contents
Expand Down

0 comments on commit daa2e22

Please sign in to comment.