Skip to content

Commit

Permalink
Refactor: factorise plugins app implementation (#1166)
Browse files Browse the repository at this point in the history
* Add e2e test

* SnowTyperCreator class

* connection

* cortex

* git

* notebooks

* snowpark

* sql

* stage

* streamlit

* object.stage

* object

* add typing

* update release notes

* fix decorator

* Refactor: replace SnowTyper implementation

* fix mock

* refactor

* fix test_adds_init_command

* refactor: renames

* Add comment

* fix integration tests
  • Loading branch information
sfc-gh-pczajka committed Jun 11, 2024
1 parent 89aee7c commit 1cde2d0
Show file tree
Hide file tree
Showing 43 changed files with 203 additions and 102 deletions.
2 changes: 1 addition & 1 deletion RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
* Template variables can now be used anywhere in the the project definition file.

## Fixes and improvements
* Fixed error handling for malformatted `config.toml`
* Fixed ZIP packaging of Snowpark project dependencies containing implicit namespace packages like `snowflake`.


# v2.4.0
## Backward incompatibility

Expand Down
7 changes: 5 additions & 2 deletions src/snowflake/cli/api/commands/project_initialisation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from typing import Optional

from snowflake.cli.api.commands.snow_typer import SnowTyper
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
from snowflake.cli.api.constants import TEMPLATES_PATH
from snowflake.cli.api.output.types import CommandResult, MessageResult
from snowflake.cli.api.secure_path import SecurePath
Expand All @@ -16,7 +16,10 @@ def _create_project_template(template_name: str, project_directory: str):


def add_init_command(
app: SnowTyper, project_type: str, template: str, help_message: Optional[str] = None
app: SnowTyperFactory,
project_type: str,
template: str,
help_message: Optional[str] = None,
):
@app.command()
def init(
Expand Down
77 changes: 76 additions & 1 deletion src/snowflake/cli/api/commands/snow_typer.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from __future__ import annotations

import dataclasses
import logging
from functools import wraps
from typing import Callable, Optional
from typing import Any, Callable, Dict, List, Optional, Tuple

import typer
from snowflake.cli.api.commands.decorators import (
Expand Down Expand Up @@ -111,3 +112,77 @@ def post_execute():

log.debug("Executing command post execution callback")
flush_telemetry()


@dataclasses.dataclass
class SnowTyperCommandData:
"""
Class for storing data of commands to be registered in SnowTyper instances created by SnowTyperFactory.
"""

func: Callable
args: Tuple[Any, ...]
kwargs: Dict[str, Any]


class SnowTyperFactory:
"""
SnowTyper factory. Usage is similar to SnowTyper, except that create_instance()
creates actual SnowTyper instance.
"""

def __init__(
self,
/,
name: Optional[str] = None,
help: Optional[str] = None, # noqa: A002
short_help: Optional[str] = None,
is_hidden: Optional[Callable[[], bool]] = None,
deprecated: bool = False,
):
self.name = name
self.help = help
self.short_help = short_help
self.is_hidden = is_hidden
self.deprecated = deprecated
self.commands_to_register: List[SnowTyperCommandData] = []
self.subapps_to_register: List[SnowTyperFactory] = []
self.callbacks_to_register: List[Callable] = []

def create_instance(self) -> SnowTyper:
app = SnowTyper(
name=self.name,
help=self.help,
short_help=self.short_help,
hidden=self.is_hidden() if self.is_hidden else False,
deprecated=self.deprecated,
)
# register commands
for command in self.commands_to_register:
app.command(*command.args, **command.kwargs)(command.func)
# register callbacks
for callback in self.callbacks_to_register:
app.callback()(callback)
# add subgroups
for subapp in self.subapps_to_register:
app.add_typer(subapp.create_instance())
return app

def command(self, *args, **kwargs):
def decorator(command):
self.commands_to_register.append(
SnowTyperCommandData(command, args=args, kwargs=kwargs)
)
return command

return decorator

def add_typer(self, snow_typer: SnowTyperFactory) -> None:
self.subapps_to_register.append(snow_typer)

def callback(self):
def decorator(callback):
self.callbacks_to_register.append(callback)
return callback

return decorator
4 changes: 2 additions & 2 deletions src/snowflake/cli/plugins/connection/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from snowflake.cli.api.commands.flags import (
PLAIN_PASSWORD_MSG,
)
from snowflake.cli.api.commands.snow_typer import SnowTyper
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
from snowflake.cli.api.config import (
ConnectionConfig,
add_connection,
Expand All @@ -32,7 +32,7 @@
from snowflake.connector import ProgrammingError
from snowflake.connector.config_manager import CONFIG_MANAGER

app = SnowTyper(
app = SnowTyperFactory(
name="connection",
help="Manages connections to Snowflake.",
)
Expand Down
2 changes: 1 addition & 1 deletion src/snowflake/cli/plugins/connection/plugin_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ def command_spec():
return CommandSpec(
parent_command_path=SNOWCLI_ROOT_COMMAND_PATH,
command_type=CommandType.COMMAND_GROUP,
typer_instance=commands.app,
typer_instance=commands.app.create_instance(),
)
4 changes: 2 additions & 2 deletions src/snowflake/cli/plugins/cortex/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from click import UsageError
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 SnowTyper
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
from snowflake.cli.api.output.types import (
CollectionResult,
CommandResult,
Expand All @@ -26,7 +26,7 @@
Text,
)

app = SnowTyper(
app = SnowTyperFactory(
name="cortex",
help="Provides access to Snowflake Cortex.",
)
Expand Down
2 changes: 1 addition & 1 deletion src/snowflake/cli/plugins/cortex/plugin_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ def command_spec():
return CommandSpec(
parent_command_path=SNOWCLI_ROOT_COMMAND_PATH,
command_type=CommandType.COMMAND_GROUP,
typer_instance=commands.app,
typer_instance=commands.app.create_instance(),
)
4 changes: 2 additions & 2 deletions src/snowflake/cli/plugins/git/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
identifier_argument,
like_option,
)
from snowflake.cli.api.commands.snow_typer import SnowTyper
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
from snowflake.cli.api.console.console import cli_console
from snowflake.cli.api.constants import ObjectType
from snowflake.cli.api.output.types import CollectionResult, CommandResult, QueryResult
Expand All @@ -26,7 +26,7 @@
from snowflake.cli.plugins.stage.commands import get
from snowflake.cli.plugins.stage.manager import OnErrorType

app = SnowTyper(
app = SnowTyperFactory(
name="git",
help="Manages git repositories in Snowflake.",
)
Expand Down
2 changes: 1 addition & 1 deletion src/snowflake/cli/plugins/git/plugin_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ def command_spec():
return CommandSpec(
parent_command_path=SNOWCLI_ROOT_COMMAND_PATH,
command_type=CommandType.COMMAND_GROUP,
typer_instance=commands.app,
typer_instance=commands.app.create_instance(),
)
4 changes: 2 additions & 2 deletions src/snowflake/cli/plugins/nativeapp/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from snowflake.cli.api.commands.decorators import (
with_project_definition,
)
from snowflake.cli.api.commands.snow_typer import SnowTyper
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
from snowflake.cli.api.output.types import (
CollectionResult,
CommandResult,
Expand Down Expand Up @@ -37,7 +37,7 @@
)
from snowflake.cli.plugins.nativeapp.version.commands import app as versions_app

app = SnowTyper(
app = SnowTyperFactory(
name="app",
help="Manages a Snowflake Native App",
)
Expand Down
2 changes: 1 addition & 1 deletion src/snowflake/cli/plugins/nativeapp/plugin_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ def command_spec():
return CommandSpec(
parent_command_path=SNOWCLI_ROOT_COMMAND_PATH,
command_type=CommandType.COMMAND_GROUP,
typer_instance=commands.app,
typer_instance=commands.app.create_instance(),
)
4 changes: 2 additions & 2 deletions src/snowflake/cli/plugins/nativeapp/version/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from snowflake.cli.api.commands.decorators import (
with_project_definition,
)
from snowflake.cli.api.commands.snow_typer import SnowTyper
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
from snowflake.cli.api.output.types import CommandResult, MessageResult, QueryResult
from snowflake.cli.plugins.nativeapp.common_flags import ForceOption, InteractiveOption
from snowflake.cli.plugins.nativeapp.policy import (
Expand All @@ -23,7 +23,7 @@
NativeAppVersionDropProcessor,
)

app = SnowTyper(
app = SnowTyperFactory(
name="version",
help="Manages versions defined in an application package",
)
Expand Down
4 changes: 2 additions & 2 deletions src/snowflake/cli/plugins/notebook/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import typer
from snowflake.cli.api.commands.flags import identifier_argument
from snowflake.cli.api.commands.snow_typer import SnowTyper
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
from snowflake.cli.api.output.types import MessageResult
from snowflake.cli.plugins.notebook.manager import NotebookManager
from snowflake.cli.plugins.notebook.types import NotebookName, NotebookStagePath
from typing_extensions import Annotated

app = SnowTyper(
app = SnowTyperFactory(
name="notebook",
help="Manages notebooks in Snowflake.",
)
Expand Down
2 changes: 1 addition & 1 deletion src/snowflake/cli/plugins/notebook/plugin_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ def command_spec():
return CommandSpec(
parent_command_path=SNOWCLI_ROOT_COMMAND_PATH,
command_type=CommandType.COMMAND_GROUP,
typer_instance=commands.app,
typer_instance=commands.app.create_instance(),
)
11 changes: 0 additions & 11 deletions src/snowflake/cli/plugins/object/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +0,0 @@
from snowflake.cli.api.commands.snow_typer import SnowTyper
from snowflake.cli.plugins.object.commands import app as show_app
from snowflake.cli.plugins.stage.commands import app as stage_app

app = SnowTyper(
name="object",
help="Manages Snowflake objects like warehouses and stages",
)

app.add_typer(stage_app) # type: ignore
app.add_typer(show_app) # type: ignore
12 changes: 6 additions & 6 deletions src/snowflake/cli/plugins/object/command_aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import typer
from click import ClickException
from snowflake.cli.api.commands.snow_typer import SnowTyper
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
from snowflake.cli.api.constants import ObjectType
from snowflake.cli.plugins.object.commands import (
ScopeOption,
Expand All @@ -16,7 +16,7 @@


def add_object_command_aliases(
app: SnowTyper,
app: SnowTyperFactory,
object_type: ObjectType,
name_argument: typer.Argument,
like_option: Optional[typer.Option],
Expand All @@ -31,7 +31,7 @@ def add_object_command_aliases(

@app.command("list", requires_connection=True)
def list_cmd(like: str = like_option, **options): # type: ignore
list_(
return list_(
object_type=object_type.value.cli_name,
like=like,
scope=ScopeOption.default,
Expand All @@ -46,7 +46,7 @@ def list_cmd(
scope: Tuple[str, str] = scope_option, # type: ignore
**options,
):
list_(
return list_(
object_type=object_type.value.cli_name,
like=like,
scope=scope,
Expand All @@ -59,7 +59,7 @@ def list_cmd(

@app.command("drop", requires_connection=True)
def drop_cmd(name: str = name_argument, **options):
drop(
return drop(
object_type=object_type.value.cli_name,
object_name=name,
**options,
Expand All @@ -71,7 +71,7 @@ def drop_cmd(name: str = name_argument, **options):

@app.command("describe", requires_connection=True)
def describe_cmd(name: str = name_argument, **options):
describe(
return describe(
object_type=object_type.value.cli_name,
object_name=name,
**options,
Expand Down
4 changes: 2 additions & 2 deletions src/snowflake/cli/plugins/object/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
import typer
from click import ClickException
from snowflake.cli.api.commands.flags import like_option
from snowflake.cli.api.commands.snow_typer import SnowTyper
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
from snowflake.cli.api.constants import SUPPORTED_OBJECTS, VALID_SCOPES
from snowflake.cli.api.output.types import QueryResult
from snowflake.cli.api.project.util import is_valid_identifier
from snowflake.cli.plugins.object.manager import ObjectManager

app = SnowTyper(
app = SnowTyperFactory(
name="object",
help="Manages Snowflake objects like warehouses and stages",
)
Expand Down
4 changes: 2 additions & 2 deletions src/snowflake/cli/plugins/object/plugin_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
CommandType,
plugin_hook_impl,
)
from snowflake.cli.plugins.object.commands import app as object_app
from snowflake.cli.plugins.object import commands


@plugin_hook_impl
def command_spec():
return CommandSpec(
parent_command_path=SNOWCLI_ROOT_COMMAND_PATH,
command_type=CommandType.COMMAND_GROUP,
typer_instance=object_app,
typer_instance=commands.app.create_instance(),
)
4 changes: 2 additions & 2 deletions src/snowflake/cli/plugins/object_stage_deprecated/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from snowflake.cli.api.commands.flags import (
PatternOption,
)
from snowflake.cli.api.commands.snow_typer import SnowTyper
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
from snowflake.cli.api.console import cli_console
from snowflake.cli.api.plugins.command import CommandPath
from snowflake.cli.plugins.stage.commands import (
Expand All @@ -23,7 +23,7 @@
f" Please use `{CommandPath(['stage'])}` instead."
)

app = SnowTyper(name="stage", help="Manages stages.", deprecated=True)
app = SnowTyperFactory(name="stage", help="Manages stages.", deprecated=True)


@app.callback()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@
CommandType,
plugin_hook_impl,
)
from snowflake.cli.plugins.object_stage_deprecated.commands import (
app as stage_deprecated_app,
)
from snowflake.cli.plugins.object_stage_deprecated import commands


@plugin_hook_impl
def command_spec():
return CommandSpec(
parent_command_path=CommandPath(["object"]),
command_type=CommandType.COMMAND_GROUP,
typer_instance=stage_deprecated_app,
typer_instance=commands.app.create_instance(),
)
Loading

0 comments on commit 1cde2d0

Please sign in to comment.