Sitemap
Snowflake Builders Blog: Data Engineers, App Developers, AI, & Data Science

Best practices, tips & tricks from Snowflake experts and community

A Practical Guide to Snowflake Code Bundles — Part 1: Python

A New Way to Run Code on Snowflake

10 min readOct 1, 2026

--

Notebooks are nice. But after writing code for so many years, I often prefer directly doing work in my favorite IDE and enjoy the perks of automatic linting, good formatting, and rapid testing. Furthermore, file-based organization makes it easier to configure environments, compose reusable modules, manage dependencies, and orchestrate more complex logic. They provide the structure and flexibility needed to turn exploratory work into reliable, repeatable automation workloads.

If that’s also your style, there is good news. With Snowflake Code Bundles, you can now directly execute your code files in Snowflake: develop and test code locally, then run it on Snowflake compute. Your code comes to your data, eliminating the need to transfer data out of Snowflake, which offers both scalability and peace of mind.

Press enter or click to view image in full size

This article is the first one of a series explaining how Code Bundles works in Snowflake and this one is focused on Python runtimes.

In this article, we’ll cover:

  • What is Snowflake Code Bundle
  • Code Bundle Observability
  • Keeping Code Bundle Versions
  • Advanced Code Bundle Patterns
  • Schedule Code Bundles as Jobs

What is Snowflake Code Bundle

Snowflake CODE BUNDLE is a new schema-level object type that allows you to package your code, dependencies, and configuration into a portable unit that can be tested locally and executed on Snowflake-managed compute. You can upload your files to a stage, or keep them in a workspace (private or shared). Then, create a CODE BUNDLE object with a unique name.

CREATE OR REPLACE CODE BUNDLE dummy_project
FROM @<database>.<schema>.<stage>/<path_to_project_directory>;

SHOW CODE BUNDLES;

Alternatively, create it from workspace (in my example, the workspace name is “Code Bundle Test”, which needs to be wrapped in double quote for qualification due to the spaces in the name:

CREATE OR REPLACE CODE BUNDLE dummy_project
FROM 'snow://workspace/"USER$".PUBLIC."Code Bundle Test"/versions/live/'

The above path ending with live/ is the root of the workspace, to specify a lower level path, append the project path to it.

A code bundle object is mapped to a specific path, which can be the project directory holding all of your project files. All of your project files, along with their dependencies, stay together, and you define the entry point. Hence it’s called a code bundle. Your file tree might look like this one:

my_project/
├── code_bundle.yml # runtime config file
├── requirements.txt # dependency
├── helper.py # dependency
├── main.py # entry point
└── README.md

All of your compute requirements need to be configured in a YAML file and it has to take the name code_bundle.yml —

  • bundle.type identifies the workload pattern: either spark for spark workload or custom for other type of workload.
  • compute.type allows you to choose your ideal compute resources —options include warehouse or compute_pool.
  • bundle.language specifies the runtime, from python, java, or scala.
  • compute.options provide more execution details, including the runtime version, warehouse or pool, and related settings.

Below is an example of a starter version code bundle config:

bundle:
type: custom
compute_type: warehouse
language: python
compute_options:
runtime_version: '3.12'

Executing a code bundle can be as simple as below:

EXECUTE CODE BUNDLE dummy_project
ENTRYPOINT = 'main.py';

You can effectively eliminate runtime spin-up time if you choose to execute in warehouse especially when the warehouse is already warm.

Press enter or click to view image in full size

Code Bundle Observability

One thing extremely important to remember is that anything you intend to print in a code bundle will not be surfaced in the SnowSight UI. This is quite different from directly executing a Python file interactively. The old print method will still be executed, making your code portable between different environments, just the results are not displayed anywhere.

To have better observability into the events that happened during the code bundle execution, it’s best to utilize the logging capability which is tightly integrated with Snowflake event table.

1. Logging / Event Table

Logging observability is one of the most useful features in Snowflake. In Python, it supports the standard logging library for telemetry messaging. The results will end up in the event table of your choice (either the account default or your custom database-level event table).

CREATE EVENT TABLE <database>.<schema>.code_bundle_events;

ALTER DATABASE <database>
SET EVENT_TABLE = <database>.<schema>.code_bundle_events;

-- requires MODIFY LOG LEVEL ON ACCOUNT grant
ALTER DATABASE <database>
SET LOG_LEVEL = 'INFO';

Include below snippet or alike in the entry file, re-create the code bundle and then execute the code:

# main.py
import logging
import platform

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

if __name__ == "__main__":

version = str(platform.python_version())
logger.info(f"Python Version: {version}")

if not version.startswith('3.12'):
logger.error("Wrong Version!")
raise Exception("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")

To query the event table, follow the standard Snowflake event table format to locate the records (the records may take a few minutes to show up):

SELECT 
TIMESTAMP AS event_ts,
RESOURCE_ATTRIBUTES:"snow.query.id"::STRING AS query_id,
RECORD:"severity_text"::STRING AS log_level,
VALUE::STRING AS log_value
FROM code_bundle_events
WHERE TIMESTAMP >= DATEADD('hour', -1, CURRENT_TIMESTAMP())
AND RECORD_TYPE = 'LOG';
Press enter or click to view image in full size

2. Code Bundle Execution History

SNOWFLAKE.INFORMATION_SCHEMA.CODE_BUNDLE_HISTORY is a table function that returns execution history for code bundles. It provides run-level metadata, including bundle identity, entry point, status, timing, runtime and compute type, execution identifiers, and return values. You can apply filters such as bundle name, time range, status, language, etc.

SELECT * FROM TABLE(SNOWFLAKE.INFORMATION_SCHEMA.CODE_BUNDLE_HISTORY(
DATABASE => '<database>',
SCHEMA => '<schema>',
BUNDLE_NAME => 'DUMMY_PROJECT',
RESULT_LIMIT => 20
));
Press enter or click to view image in full size
Press enter or click to view image in full size

Here’s the documentation of how to query the CODE_BUNDLE_HISTORY table.

3. Code Bundle Execution History UI

If you prefer a visualized dashboard view for the above execution history data, it is also readily available in SnowSight UI. Open the left navigation bar, under Catalog > Explorer > choose database > choose schema > then select the code bundle that you need to observe. Navigate the tabs on the right to open its Run History page. The page displays the history in a table format and supports filtering, and provides links to execution details, logs, or query history directly.

Press enter or click to view image in full size

Because I have explicitly asked you to re-create the code bundle in prior step, the execution history in both the table function results and in SnowSight UI only returns the execution records after it was re-created. To not lose historical status and insights, we can instead use ADD VERSION to an existing code bundle object, as explained below.

Keeping Code Bundle Versions

Another benefit of file-based automation program is that you get easier version control. Not only you can easily compare code delta in your local IDE or using a git service, you can also keep tracking of the versions of the code bundle objects in Snowflake directly. A Code Bundle version is an immutable snapshot of the bundle’s source files. Create the initial bundle, then add a new snapshot with ALTER CODE BUNDLE ... ADD VERSION FROM ...

ALTER CODE BUNDLE dummy_project
ADD VERSION FROM @<database>.<schema>.<stage>/<new_path>;

Or add a new version from workspace:

ALTER CODE BUNDLE dummy_project
ADD VERSION FROM
'snow://workspace/"USER$".PUBLIC.<workspace>/versions/live/<new_path>';

There are more benefits than keeping all the history. You can easily deploy changes to the same code bundle object without breaking any existing workflow, while keeping a copy of each version of your project files until you are ready to drop them. Simply organize your project folder like this:

<stage_name>/<project_folder>/V1         # initial version
<stage_name>/<project_folder>/V2 # new version
<stage_name>/<project_folder>/v20260808 # to be very specific

This way you can retain full visibility into the past versions, which makes it super easy to recover a previously working version if anything breaks. To find out the current version and path of your code bundle, simply use:


DESCRIBE CODE BUNDLE dummy_project;

Note that even if you didn’t make any changes to the files and deployed from exactly the same path, as long as you use the ADD VERSION method, your code bundle version number metadata still goes up.

Advanced Code Bundle Patterns

1. Use a Container Runtime

Sometimes Snowflake users choose a Compute Pool as runtime because containers let them use an isolated and scalable application environment. You can package the libraries and runtime your code expects, use GPUs when needed, and run substantial Python processing without forcing everything into warehouse execution. It offers flexibility when workloads outgrow SQL-centric patterns.

Get Sabrina Liu’s stories in your inbox

Join Medium for free to get updates from this writer.

To use compute pool runtime in code bundles, a compute pool needs to be provisioned, then we can slightly modify the code_bundle.yml file to include the compute pool configuration. Note that a query warehouse still needs to be specified which is used to to execute queries.

bundle:
type: custom
compute_type: compute_pool
language: python

compute_options:
compute_pool: <compute pool name>
query_warehouse: <warehouse name>
runtime_version: 'V2.5-CPU-PY3.12'

As of the date of this article, code bundle only sends telemetry to account-level event table when container runtime is used.

2. Customize Runtime on the Run

Sometimes you might need to dynamically control the runtime specs at the time of execution, instead of hard-coding it. Code Bundles allow you to use a simple keyword WITH SPECIFICATION in the EXECUTE statement to provide the runtime requirements as a string literal, which will completely override any existing YAML file in your project folder at the time of execution. This is especially useful when testing new environment compatibility.

EXECUTE CODE BUNDLE dummy_project
ENTRYPOINT = 'main.py'
WITH SPECIFICATION
$$
bundle:
type: custom
compute_type: warehouse
language: python

compute_options:
runtime_version: '3.13'
$$;

This execution failed as expected because we included a custom exception in the code to raise error if Python version is not 3.12. The process failed but the information was captured through error logging.

Press enter or click to view image in full size

3. Flexible Argument Parsing

argparse is my favorite Python library for parsing input arguments. Update your main.py to something like this in your entry point file:

# main.py
import argparse
...

if __name__ == "__main__":
...
parser = argparse.ArgumentParser(description="Demo Code Bundles")
parser.add_argument("-d", "--report-date", type=str, help="YYYY-MM-DD")
parser.add_argument("-f", "--backfill", action="store_true")
args = parser.parse_args()

logger.info(f"{vars(args)}")
...

In regular Python runtime you would be doing this:

/usr/bin/python3 /home/user/my_project/main.py -d 2026-08-01 -f

To include the input arguments in code bundle execution you need to supply them as a tuple like below:

EXECUTE CODE BUNDLE dummy_project
ENTRYPOINT = 'main.py'
ARGUMENTS = ('--report-date', '2026-08-01', '--backfill');

Querying the event table shows that the input arguments were successfully passed into the runtime:

Press enter or click to view image in full size

4. Using Custom Python Package

Sometimes a runtime’s default Python packages are not the versions your application needs. When you require a specific library version, add a properties.requirements_file entry under code_bundle.yml, then list the pinned dependency in that file.

Using jellyfish as example, modify your main file to use it:

if __name__ == "__main__":

import importlib.metadata
import jellyfish

version = importlib.metadata.version("jellyfish")
logger.info(f"jellyfish version: {version}")

Because jellyfish is not in the default runtime, trying to execute this code bundle will not work. Add this to a file named requirements.txt

# requirements.txt
snowpark-connect[jdk]==1.42.0

Then add these two lines to code_bundle.yml:

  # code_bundle.yml
...
properties:
requirements_file: requirements.txt

And the results are successfully captured in the event table:

Press enter or click to view image in full size

5. Stage Mounts Storage Path

Stage mounts are useful when code or libraries expect filesystem paths, such as open(), rather than Snowflake-specific file APIs. You can read files with SnowflakeFile, but writing back to the stage needs path resolution; @stage/path is not a local path. Currently stage mounts on compute pools are readable and writable. On warehouses, they are read-only.

bundle:
...
stage_mounts:
input_data:
stage_url: "@<database>.<schema>/<project_dir_path>"
mount_path: "/mnt/data"

Your code can then access the files normally:

with open("/mnt/data/file.csv") as f:
contents = f.read()

6. Anonymous Code Bundle Execution

An anonymous code bundle execution runs source directly from a stage or workspace without creating a named code bundle object. It is convenient for experimentation, versioned artifacts, and one-off jobs with no object lifecycle to manage.

Anonymous code bundle execution comes with limitations, such as weaker database/schema lineage, telemetry routing, less discoverability, and limited governance and history benefits.

-- anonymous code bundle execution
EXECUTE CODE BUNDLE FROM '@<stage_name>/<temp_dir>'
ENTRYPOINT = 'main.py';

EXECUTE CODE BUNDLE
FROM 'snow://workspace/"USER$".PUBLIC.<workspace>/versions/live/'
ENTRYPOINT = 'main.py';

Schedule Code Bundle as Jobs

In order to put your Code Bundle to run automatically based on a schedule, simply include it in a Snowflake TASK:

CREATE OR REPLACE TASK <database>.<schema>.<task>
WAREHOUSE = MY_WAREHOUSE
SCHEDULE = 'USING CRON 0 2 * * * UTC'
AS
EXECUTE CODE BUNDLE <database>.<schema>.<code_bundle>
ENTRYPOINT = 'main.py';

ALTER TASK <database>.<schema>.<task> RESUME;

Note that you usuallly need to define a compute resource for your TASK object and a compute resource for your CODE BUNDLE object. These are two distinct concepts and they don’t necessarily have to be the same. If you choose WAREHOUSE compute for the task itself, your code bundle can infer the warehouse resource from the session inherently and you don’t need to define it again.

What’s more? You can chain these tasks together to achieve more complex orchestration. To get a better read about Snowflake task DAG capabilities, please visit my article Orchestrate Airflow-Style Task DAGs in Snowflake.

Thank you for reading this article! Please try to run the code examples yourself and do not hesitate to contact me if you run into any issues. If you would like to see similar content in the future, please tell me what challenges you are facing or what solutions you are looking for. Let’s learn together!

--

--