diff --git a/README.md b/README.md index 3cd7a3e..f70b641 100644 --- a/README.md +++ b/README.md @@ -31,22 +31,22 @@ using the System Properties menu (on Windows). ### Install dependencies -Set up a virtual environment using [Anaconda](https://conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#creating-an-environment-with-commands) or [virtualenv](https://docs.python.org/3/library/venv.html). - -#### Anaconda +Create and activate a conda environment using [Anaconda](https://conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#creating-an-environment-with-commands): ```bash -conda env create -f environment.yml +conda env create --file environment.yml conda activate snowpark ``` -#### Virtualenv +### Configure IDE -```bash -python3 -m venv venv -source venv/bin/activate -pip install -r requirements.txt -``` +#### VS Code + +Press `Ctrl`+`Shift`+`P` to open the command palette, then select **Python: Select Interpreter** and select the **snowpark** interpeter under the **Conda** list. + +#### PyCharm + +Go to **File** > **Settings** > **Project** > **Python Interpreter** and select the snowpark interpreter. ## Prereqs @@ -60,9 +60,8 @@ To develop your applications locally, you will need Once you've set your credentials and installed the packages, you can test your connection to Snowflake by executing the stored procedure in [`app.py`](src/procs/app.py): -``` -cd src -python procs/app.py +```bash +python src/procs/app.py ``` You should see the following output: @@ -80,8 +79,7 @@ You should see the following output: You can run the test suite locally from the project root: -``` -pip install -r requirements-test.txt +```bash python -m pytest ``` diff --git a/environment.yml b/environment.yml index e376b9d..2c178d1 100644 --- a/environment.yml +++ b/environment.yml @@ -7,8 +7,6 @@ channels: dependencies: - python=3.8 - pip - - snowflake-snowpark-python - - toml - - tomli - pip: - - "-r requirements-test.txt" + - "-r requirements.txt" + - "--editable ." diff --git a/requirements-test.txt b/requirements-test.txt deleted file mode 100644 index 6ca258f..0000000 --- a/requirements-test.txt +++ /dev/null @@ -1,2 +0,0 @@ -pytest -snowflake-vcrpy @ git+https://github.com/Snowflake-Labs/snowflake-vcrpy.git@v0.1.1 diff --git a/requirements.txt b/requirements.txt index c32f41c..0a2851b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ snowflake-snowpark-python tomli toml +pytest +snowflake-vcrpy @ git+https://github.com/Snowflake-Labs/snowflake-vcrpy.git@v0.1.1 diff --git a/resources.sql b/resources.sql index ba46761..338b75c 100644 --- a/resources.sql +++ b/resources.sql @@ -6,7 +6,7 @@ CREATE STAGE IF NOT EXISTS artifacts; PUT file://&artifact_name @artifacts AUTO_COMPRESS=FALSE OVERWRITE=TRUE; CREATE OR REPLACE PROCEDURE HELLO_WORLD_PROC() - RETURNS integer + RETURNS TABLE(hello_world string) LANGUAGE PYTHON RUNTIME_VERSION = 3.8 IMPORTS = ('@artifacts/&artifact_name') diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..19a6bf4 --- /dev/null +++ b/setup.py @@ -0,0 +1,12 @@ +""" +Run `conda env create --file environment.yaml` to create an editable +install of this project +""" + +from setuptools import setup, find_packages + +setup( + name="Example Snowpark Python project", + version="0.1.0", + packages=find_packages() +) diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/procs/app.py b/src/procs/app.py index 7d18916..99ee3ce 100644 --- a/src/procs/app.py +++ b/src/procs/app.py @@ -8,7 +8,7 @@ from snowflake.snowpark.types import StringType -def run(snowpark_session: Session) -> int: +def run(snowpark_session: Session) -> DataFrame: """ A sample stored procedure which creates a small DataFrame, prints it to the console, and returns the number of rows in the table. @@ -17,13 +17,6 @@ def run(snowpark_session: Session) -> int: # Register UDF from src.udf.functions import combine - snowpark_session.add_import( - path="../src/udf/functions.py", import_path="src.udf.functions" - ) - combine = snowpark_session.udf.register( - combine, StringType(), input_types=[StringType(), StringType()] - ) - schema = ["col_1", "col_2"] data = [ @@ -31,27 +24,25 @@ def run(snowpark_session: Session) -> int: ("Learn more: ", "https://www.snowflake.com/snowpark/"), ] - df: DataFrame = snowpark_session.create_dataframe(data, schema) + df = snowpark_session.create_dataframe(data, schema) - df2 = df.select(combine(col("col_1"), col("col_2")).as_("Hello world")).sort( - "Hello world", ascending=False + df2 = df.select(combine(col("col_1"), col("col_2")).as_("hello_world")).sort( + "hello_world", ascending=False ) - df2.show() - return df2.count() + return df2 if __name__ == "__main__": # This entrypoint is used for local development. - import sys - - sys.path.insert(0, "..") # Necessary to import from udf and util directories - from src.util.local import get_env_var_config print("Creating session...") session = Session.builder.configs(get_env_var_config()).create() - print("Running stored proc...") - run(session) + print("Running stored procedure...") + result = run(session) + + print("Stored procedure complete:") + result.show() diff --git a/src/udf/functions.py b/src/udf/functions.py index 0e37926..405f3dd 100644 --- a/src/udf/functions.py +++ b/src/udf/functions.py @@ -2,7 +2,9 @@ This module contains the UDFs for the project. """ +from snowflake.snowpark.functions import udf +@udf(is_permanent=False) def combine(string_a: str, string_b: str) -> str: """ A sample UDF implementation diff --git a/test/procs/test_app.py b/test/procs/test_app.py index cfe38d7..c5f5a4a 100644 --- a/test/procs/test_app.py +++ b/test/procs/test_app.py @@ -1,24 +1,24 @@ -import os + import pytest from snowflake.snowpark.session import Session -from snowflake.snowpark.types import StringType from src.util.local import get_env_var_config from src.procs.app import run -@pytest.fixture(autouse=True) -def set_working_directory(): - # Sets the working directory to sources root so relative imports resolve properly - os.chdir("src") - - @pytest.fixture -def local_session(): +def session(): return Session.builder.configs(get_env_var_config()).create() -@pytest.mark.snowflake_vcr -def test_app_dim(local_session): - expected_n_rows = 2 - actual_n_rows = run(local_session) - assert expected_n_rows == actual_n_rows + +def test_app_dim(session: Session): + expected = session.create_dataframe( + [["Welcome to Snowflake!"], ["Learn more: https://www.snowflake.com/snowpark/"]], + ["hello_world"]) + actual = run(session) + assert expected.collect() == actual.collect() + + # Alertnate impl: + # expected = [Row("Welcome to Snowflake!"), Row("Learn more: https://www.snowflake.com/snowpark/")] + # actual = run(session) + # assert expected.select("hello_world").collect() == actual \ No newline at end of file