Skip to content

Commit

Permalink
Merge branch 'main' into add-telemetry
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-turbaszek authored Feb 6, 2024
2 parents 577d575 + d39ac23 commit 95dd5fc
Show file tree
Hide file tree
Showing 19 changed files with 241 additions and 70 deletions.
6 changes: 2 additions & 4 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,5 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
pip check
snow --help
python -m pip install --upgrade pip hatch
python -m hatch run snow -h
8 changes: 4 additions & 4 deletions .github/workflows/e2e_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ jobs:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
python -m pip install --upgrade pip hatch
python -m hatch env create e2e
- name: Run integration tests
env:
TERM: unknown
SNOWFLAKE_CONNECTIONS_INTEGRATION_HOST: ${{ secrets.SNOWFLAKE_HOST }}
SNOWFLAKE_CONNECTIONS_INTEGRATION_USER: ${{ secrets.SNOWFLAKE_USER }}
SNOWFLAKE_CONNECTIONS_INTEGRATION_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }}
SNOWFLAKE_CONNECTIONS_INTEGRATION_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
run:
pytest -m e2e --snapshot-warn-unused --durations=0
run: python -m hatch run e2e:test
10 changes: 4 additions & 6 deletions .github/workflows/integration_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,13 @@ jobs:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
pip install -e test_external_plugins/snowpark_hello_single_command
pip install -e test_external_plugins/multilingual_hello_command_group
python -m pip install --upgrade pip hatch
python -m hatch env create integration
- name: Run integration tests
env:
TERM: unknown
SNOWFLAKE_CONNECTIONS_INTEGRATION_HOST: ${{ secrets.SNOWFLAKE_HOST }}
SNOWFLAKE_CONNECTIONS_INTEGRATION_USER: ${{ secrets.SNOWFLAKE_USER }}
SNOWFLAKE_CONNECTIONS_INTEGRATION_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }}
SNOWFLAKE_CONNECTIONS_INTEGRATION_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
run:
pytest -m integration --snapshot-warn-unused
run: python -m hatch run integration:test
9 changes: 5 additions & 4 deletions .github/workflows/performance_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ jobs:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
python -m pip install --upgrade pip hatch
python -m hatch env create performance
- name: Run performance tests
run:
pytest -m performance
env:
TERM: unknown
run: python -m hatch run performance:test
11 changes: 8 additions & 3 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]

Expand All @@ -25,8 +26,12 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Test with tox
- name: Install hatch
run: |
python -m pip install tox-gh-actions
tox
pip install -U hatch
hatch env create default
- name: Test with hatch
env:
TERM: unknown
run: hatch run test-cov
- uses: codecov/codecov-action@4fe8c5f003fae66aa5ebb77cfd3e7bfbbda0b6b0
38 changes: 30 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,56 @@
There are two ways to contribute code to the repository: directly or by use of forks. For best practices for the second approach, refer to the section on forks below. Right now, there is limited access to contributing to the repository directly, and hence using forks is the recommended approach.

## Setup a development environment
If you are interested in contributing, you will need to instantiate the pre-commit logic to help with formatting and linting of commits.
To do this, run the following in the `snowcli` cloned folder on your development machine:
If you are interested in contributing, you will need to instantiate dev virtual environments and the pre-commit logic to help with formatting and linting of commits.

We use [hatch](https://hatch.pypa.io/latest/) to manage and created development environments.
Default environment will use the python version in your shell.
```bash
pip install pre-commit
pre-commit
pip install -U hatch
hatch run pre-commit
```
This will spawn new shell with environment and all required packages installed.
This will also install snowflake cli package in editable mode.

To enter environment use following command.
```bash
hatch shell
```
This will spawn new shell with virtual environment enables. To leave just press ^D.


Currently, the required Python version for development is Python 3.8+. For local development we recommend to use
a wrapper for virtual environments like [pyenv](https://github.com/pyenv/pyenv).

Once you created a dedicated virtual environment you can install SnowCLI in editable mode with all required dependencies:
If you wish to setup environment with specific version ie. 3.8 you can use following command:

```bash
hatch env create local.py3.8```
You can see all locally supported environments with
```bash
pip install -e ".[dev]"
hatch env show
```

Please keep in mind that you need these python versions available in your `$PATH`. You can install them using `hatch` or other tool like [pyenv](https://github.com/pyenv/pyenv)

## Unit tests

Unit tests are executed in random order. If tests fail after your change, you can re-execute them in the same order using `pytest --randomly-seed=<number>`, where number is a seed printed at the beginning of the test execution output.
Random order of test execution is provided by pytest-randomly, so more details are available in [pytest-randomly docs](https://pypi.org/project/pytest-randomly/).

```bash
hatch run test
```
or by running `pytest` inside activated environment.


## Integration tests

Every integration test should have `integration` mark. By default, integration tests are not execute when running `pytest`.

To execute only integration tests run `pytest -m integration`.

To execute only integration tests run `hatch run integration:test` or `pytest -m integration` inside environment.
### Connection parameters in `config.toml`

Add the following connection to your `config.toml`
Expand Down
15 changes: 12 additions & 3 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
# v2.0.1

## Backward incompatibility

## New additions

## Fixes and improvements


# v2.0.0

## Backward incompatibility
* Introduced `snow object` group with `show`, `describe` and `drop` commands which replaces corresponding
* Introduced `snow object` group with `list`, `describe` and `drop` commands which replaces corresponding
functionalities of procedure/function/streamlit specific commands.
* `snow stage` is now `snow object stage`
* `snow stage get` and `snow stage put` are replaced by `snow object stage copy [FROM] [TO]`
* `snow warehouse status` is now `snow object show warehouse`
* `snow warehouse status` is now `snow object list warehouse`
* `snow connection test` now outputs all connection details (except for the password), along with connection status
* `snow sql` requires explicit `-i` flag to read input from stdin: `cat my.sql | snow sql -i`
* Switched to Python Connector default connection https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-connect#setting-a-default-connection
Expand Down Expand Up @@ -37,7 +46,7 @@
* Streamlit changes
* `snow streamlit deploy` is requiring `snowflake.yml` project file with a Streamlit definition.
* `snow streamlit describe` is now `snow object describe streamlit`
* `snow streamlit list` is now `snow object show streamlit`
* `snow streamlit list` is now `snow object list streamlit`
* `snow streamlit drop` is now `snow object drop streamlit`


Expand Down
47 changes: 44 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ dependencies = [
"strictyaml==1.7.3",
"tomlkit==0.12.3",
"typer==0.9.0",
"urllib3>=1.21.1,<2.2",
"urllib3>=1.21.1,<2.3",
"GitPython==3.1.41",
]
classifiers = [
Expand All @@ -39,13 +39,12 @@ classifiers = [
]

[project.optional-dependencies]
dev = [
development = [
"coverage==7.4.1",
"pre-commit>=3.5.0",
"pytest==7.4.4",
"pytest-randomly==3.15.0",
"syrupy==4.6.0",
"tox==4.11.4",
]

[project.urls]
Expand All @@ -64,6 +63,48 @@ exclude = ["/.github"]
[tool.hatch.build.targets.wheel]
packages = ["src/snowflake"]

[tool.hatch.envs.default]
features = ["development"]

[tool.hatch.envs.default.scripts]
test = ["pytest --snapshot-warn-unused tests/"]
test-cov = [
"coverage run --source=snowflake.cli --module pytest --snapshot-warn-unused tests/ ",
"coverage run --source=snowflake.cli --module pytest -m loaded_modules --snapshot-warn-unused tests/ ",
"coverage report",
]

[tool.hatch.envs.e2e]
template = "e2e"
features = ["development"]

[tool.hatch.envs.e2e.scripts]
test = ["pytest -m e2e --snapshot-warn-unused --durations=0"]

[tool.hatch.envs.performance]
template = "performance"
features = ["development"]

[tool.hatch.envs.performance.scripts]
test = ["pytest -m performance"]

[tool.hatch.envs.integration]
template = "integration"
pre-install-commands = [
"pip install test_external_plugins/snowpark_hello_single_command",
"pip install test_external_plugins/multilingual_hello_command_group",
]
features = ["development"]

[tool.hatch.envs.integration.scripts]
test = ["pytest -m integration --snapshot-warn-unused"]

[[tool.hatch.envs.local.matrix]]
python = ["3.8", "3.9", "3.10", "3.11", "3.12"]

[tool.coverage.run]
source = ["snowflake.cli"]

[tool.ruff]
line-length = 88

Expand Down
2 changes: 1 addition & 1 deletion src/snowflake/cli/__about__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from __future__ import annotations

VERSION = "2.0.0rc3"
VERSION = "2.0.1rc0"
5 changes: 5 additions & 0 deletions src/snowflake/cli/api/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ def format_message(self):
return f"Invalid logs configuration. {self.message}"


class InvalidPluginConfiguration(ClickException):
def format_message(self):
return f"Invalid plugin configuration. {self.message}"


class SnowflakeConnectionError(ClickException):
def __init__(self, snowflake_err: Exception):
super().__init__(f"Could not connect to Snowflake. Reason: {snowflake_err}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,42 @@
get_config_value,
get_plugins_config,
)
from snowflake.cli.api.exceptions import InvalidPluginConfiguration
from snowflake.cli.api.plugins.plugin_config import PluginConfig


def _assert_value_is_bool(value, *, value_name: str, plugin_name: str) -> None:
if type(value) is not bool:
raise InvalidPluginConfiguration(
f'[{plugin_name}]: "{value_name}" must be a boolean'
)


_ENABLED_KEY = "enabled"


class PluginConfigProviderImpl(PluginConfigProvider):
def get_enabled_plugin_names(self) -> List[str]:
enabled_plugins = []
for (plugin_name, plugin_config_section) in get_plugins_config().items():
if plugin_config_section.get("enabled", False):
for plugin_name, plugin_config_section in get_plugins_config().items():
enabled = plugin_config_section.get(_ENABLED_KEY, False)
_assert_value_is_bool(
enabled, value_name=_ENABLED_KEY, plugin_name=plugin_name
)
if enabled:
enabled_plugins.append(plugin_name)
return enabled_plugins

def get_config(self, plugin_name: str) -> PluginConfig:
config_path = PLUGINS_SECTION_PATH + [plugin_name]
plugin_config = PluginConfig(is_plugin_enabled=False, internal_config={})
plugin_config.is_plugin_enabled = get_config_value(
*config_path, key="enabled", default=False
*config_path, key=_ENABLED_KEY, default=False
)
_assert_value_is_bool(
plugin_config.is_plugin_enabled,
value_name=_ENABLED_KEY,
plugin_name=plugin_name,
)
if config_section_exists(*config_path, "config"):
plugin_config.internal_config = get_config_section(*config_path, "config")
Expand Down
14 changes: 14 additions & 0 deletions test_external_plugins/override_build_in_command/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "override-build-in-commands"
requires-python = ">=3.8"
dependencies = [
"snowflake-cli-labs>=1.1.0"
]
version = "0.0.1"

[project.entry-points."snowflake.cli.plugin.command"]
override = "snowflakecli.test_plugins.override_build_in_command.plugin_spec"
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import typer
from snowflake.cli.api.commands.decorators import (
with_output,
)
from snowflake.cli.api.output.types import CommandResult, MessageResult

app = typer.Typer()


@app.command("add")
@with_output
def add() -> CommandResult:
return MessageResult("This message should not be printed")
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from snowflake.cli.api.plugins.command import (
CommandPath,
CommandSpec,
CommandType,
plugin_hook_impl,
)
from snowflakecli.test_plugins.override_build_in_command import commands


@plugin_hook_impl
def command_spec():
return CommandSpec(
parent_command_path=CommandPath(["connection"]),
command_type=CommandType.SINGLE_COMMAND,
typer_instance=commands.app,
)
2 changes: 2 additions & 0 deletions tests/test_data/configs/override_plugin_config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[cli.plugins.override]
enabled = true
16 changes: 16 additions & 0 deletions tests/test_external_plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
def test_override_build_in_commands(runner, test_root_path, caplog):
import subprocess

path = test_root_path / ".." / "test_external_plugins" / "override_build_in_command"
subprocess.check_call(["pip", "install", path])

config_path = (
test_root_path / "test_data" / "configs" / "override_plugin_config.toml"
)

runner.invoke(["--config-file", config_path, "--help"])

assert (
caplog.messages[0]
== "Cannot register plugin [override]: Cannot add command [snow connection add] because it already exists."
)
Loading

0 comments on commit 95dd5fc

Please sign in to comment.