Skip to content

Commit

Permalink
feat: Support uv as an optional virtualenv backend (meltano#8465)
Browse files Browse the repository at this point in the history
* feat: Support `uv` as an optional `venv` backend

* Use `utf-8` instead of `unicode_replace`

* Add a copyright notice to the `UvVenvService._find_uv` function

* Fix types

* Rename `upgrade_pip` → `upgrade_installer`

* Fail if uv can not be found
  • Loading branch information
edgarrmondragon committed Apr 12, 2024
1 parent 5141dc8 commit 5b81b88
Show file tree
Hide file tree
Showing 18 changed files with 473 additions and 64 deletions.
2 changes: 1 addition & 1 deletion docker/meltano/Dockerfile
Expand Up @@ -22,6 +22,6 @@ ENV PATH="/venv/bin:${PATH}"

# Installing the application the same way our users do when using PyPI
RUN pip install --upgrade pip wheel && \
pip install "meltano[azure,gcs,mssql,postgres,psycopg2,s3]==${MELTANO_VERSION}"
pip install "meltano[azure,gcs,mssql,postgres,psycopg2,s3,uv]==${MELTANO_VERSION}"

ENTRYPOINT ["meltano"]
10 changes: 5 additions & 5 deletions docs/docs/concepts/python_virtual_environments.md
Expand Up @@ -5,7 +5,7 @@ layout: doc
sidebar_position: 12
---

# What are Python Virtual Environments
## What are Python Virtual Environments

Python Virtual Environments, also known as a venv, are a way to allow a
Python application access to specific versions of the libraries it needs to run properly. In the Singer ecosystem
Expand All @@ -20,7 +20,7 @@ We're going to focus on the Meltano use case for Virtual Environments which is u
1. Installing Meltano
1. Installing Plugins (Taps, Targets, Transformers, etc)

# Why should I care about Virtual Environments?
## Why should I care about Virtual Environments?

Ideally, you don't have to to worry about Virtual Environments while using Meltano,
we recommend using pipx to install Meltano which manages the creation of venvs for you.
Expand All @@ -30,7 +30,7 @@ However, if you ever need to customize or build your own production pipeline (or
how to install Meltano in an isolated way so that you don't have a conflict with dependecies within your own Operating System
or other Python applications running on the same machine.

# How do I use Virtual Environments?
## How do I use Virtual Environments?

We strongly suggest you create a directory where you want your virtual environments to be saved (e.g. `.venv/meltano/`).
This can be any directory in your environment, but we recommend saving it in your Meltano project to make it easier to keep
Expand All @@ -48,7 +48,7 @@ And that's it! You've created a virtual environment.
Feel free to explore the directory and compare it to your global python directory!
You can navigate to the directory by running `cd .venv/meltano/`.

## Activating Your Virtual Environment
### Activating Your Virtual Environment

Activate the virtual environment, and upgrade pip using:

Expand Down Expand Up @@ -86,7 +86,7 @@ pip install meltano
Ensure you have `pipx` installed by reviewing the <a href="/getting-started/installation#install-pipx">Install pipx</a> instructions.</p>
:::

# How does meltano use Virtual Environments internally?
## How does meltano use Virtual Environments internally?

Whenever you run `meltano install`, Meltano creates a `.meltano/` directory in your project.
This directory has a number of sub-directories (subject to change at any point, as this is an
Expand Down
12 changes: 12 additions & 0 deletions docs/docs/guide/advanced-topics.md
Expand Up @@ -25,6 +25,18 @@ The following extras add support for other [state backends](/concepts/state_back
- `gcs` - Google Cloud Storage.
- `azure` - Azure Blob Storage.

### Virtual Environment backends

The following extras add support for different [virtual environment backends](/concepts/python_virtual_environments#how-does-meltano-use-virtual-environments-internally):

- `uv` - Manage virtual environments with [astral.sh/uv](https://github.com/astral-sh/uv/).

:::info
<p>
After you switch to a different virtual environment backend, it's recommended to reinstall all plugins with `meltano install --clean`.
</p>
:::

## Extension Developer Kit (EDK)

Meltano extensions are lightweight executables which allow you to integrate existing data tools with Meltano.
Expand Down
8 changes: 8 additions & 0 deletions docs/docs/reference/settings.mdx
Expand Up @@ -876,6 +876,14 @@ export MELTANO_STATE_BACKEND_GCS_APPLICATION_CREDENTIALS="path/to/creds.json"
</TabItem>
</Tabs>

## Virtual environments

### `venv.backend`

- [Environment variable](/guide/configuration#configuring-settings): `MELTANO_VENV_BACKEND`
- Options: `virtualenv`, `uv`
- Default: `virtualenv`

## Snowplow Tracking

### `snowplow.collector_endpoints`
Expand Down
Expand Up @@ -60,7 +60,8 @@
"MELTANO_SNOWPLOW_COLLECTOR_ENDPOINTS": "[\"https://sp.meltano.com\"]",
"MELTANO_STATE_BACKEND_LOCK_RETRY_SECONDS": "1",
"MELTANO_STATE_BACKEND_LOCK_TIMEOUT_SECONDS": "10",
"MELTANO_STATE_BACKEND_URI": "systemdb"
"MELTANO_STATE_BACKEND_URI": "systemdb",
"MELTANO_VENV_BACKEND": "virtualenv"
},
"jobs": [
{
Expand Down
Expand Up @@ -46,6 +46,7 @@
"MELTANO_STATE_BACKEND_LOCK_RETRY_SECONDS": "1",
"MELTANO_STATE_BACKEND_LOCK_TIMEOUT_SECONDS": "10",
"MELTANO_STATE_BACKEND_URI": "systemdb",
"MELTANO_VENV_BACKEND": "virtualenv",
"SUPERSET_API_URL": "http://localhost:8088",
"SUPERSET_PASS": "admin",
"SUPERSET_USER": "admin",
Expand Down
Expand Up @@ -40,7 +40,8 @@
"MELTANO_SNOWPLOW_COLLECTOR_ENDPOINTS": "[\"https://sp.meltano.com\"]",
"MELTANO_STATE_BACKEND_LOCK_RETRY_SECONDS": "1",
"MELTANO_STATE_BACKEND_LOCK_TIMEOUT_SECONDS": "10",
"MELTANO_STATE_BACKEND_URI": "systemdb"
"MELTANO_STATE_BACKEND_URI": "systemdb",
"MELTANO_VENV_BACKEND": "virtualenv"
},
"jobs": [
{
Expand Down
Expand Up @@ -42,6 +42,7 @@
"MELTANO_STATE_BACKEND_LOCK_RETRY_SECONDS": "1",
"MELTANO_STATE_BACKEND_LOCK_TIMEOUT_SECONDS": "10",
"MELTANO_STATE_BACKEND_URI": "systemdb",
"MELTANO_VENV_BACKEND": "virtualenv",
"PERMISSION_BOT_DATABASE": "SNOWFLAKE_SAMPLE_DATA",
"PERMISSION_BOT_ROLE": "SECURITYADMIN",
"PERMISSION_BOT_USER": "permission_bot",
Expand Down
Expand Up @@ -41,7 +41,8 @@
"MELTANO_SNOWPLOW_COLLECTOR_ENDPOINTS": "[\"https://sp.meltano.com\"]",
"MELTANO_STATE_BACKEND_LOCK_RETRY_SECONDS": "1",
"MELTANO_STATE_BACKEND_LOCK_TIMEOUT_SECONDS": "10",
"MELTANO_STATE_BACKEND_URI": "systemdb"
"MELTANO_STATE_BACKEND_URI": "systemdb",
"MELTANO_VENV_BACKEND": "virtualenv"
},
"jobs": [
{
Expand Down
Expand Up @@ -49,6 +49,7 @@
"MELTANO_STATE_BACKEND_LOCK_RETRY_SECONDS": "1",
"MELTANO_STATE_BACKEND_LOCK_TIMEOUT_SECONDS": "10",
"MELTANO_STATE_BACKEND_URI": "systemdb",
"MELTANO_VENV_BACKEND": "virtualenv",
"SUPERSET_API_URL": "http://localhost:8088",
"SUPERSET_PASS": "admin",
"SUPERSET_USER": "admin",
Expand Down
4 changes: 2 additions & 2 deletions noxfile.py
Expand Up @@ -100,7 +100,7 @@ def pytest_meltano(session: Session) -> None:
session: Nox session.
"""
backend_db = os.environ.get("PYTEST_BACKEND", "sqlite")
extras = ["azure", "gcs", "s3"]
extras = ["azure", "gcs", "s3", "uv"]

if backend_db == "mssql":
extras.append("mssql")
Expand Down Expand Up @@ -160,7 +160,7 @@ def mypy(session: Session) -> None:
session: Nox session.
"""
session.install(
".[mssql,azure,gcs,s3]",
".[mssql,azure,gcs,s3,uv]",
"boto3-stubs",
"mypy",
"types-croniter",
Expand Down
29 changes: 28 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pyproject.toml
Expand Up @@ -86,6 +86,7 @@ typing-extensions = "^4.11.0"
tzlocal = "^5.2"
# Compatibility issues with boto: https://github.com/boto/botocore/pull/3034
urllib3 = "<2"
uv = { version = ">=0.1.24,<0.2", optional = true }
virtualenv = "^20.25.1"
yaspin = "^2.3.0"

Expand All @@ -96,6 +97,7 @@ mssql = ["pymssql"]
postgres = ["psycopg"]
psycopg2 = ["psycopg2-binary"]
s3 = ["boto3"]
uv = ["uv"]

[tool.poetry.group.dev.dependencies]
backoff = "^2.1.2"
Expand Down
10 changes: 10 additions & 0 deletions src/meltano/core/bundle/settings.yml
Expand Up @@ -96,6 +96,16 @@ settings:
kind: array
value: ["https://sp.meltano.com"]

# venv
- name: venv.backend
value: virtualenv
kind: options
options:
- label: virtualenv
value: virtualenv
- label: uv
value: uv

# Feature Flags
# Global "experimental" flag.
- name: experimental
Expand Down
32 changes: 25 additions & 7 deletions src/meltano/core/plugin_install_service.py
Expand Up @@ -23,7 +23,7 @@
from meltano.core.plugin.settings_service import PluginSettingsService
from meltano.core.settings_service import FeatureFlags
from meltano.core.utils import EnvVarMissingBehavior, expand_env_vars, noop
from meltano.core.venv_service import VenvService
from meltano.core.venv_service import UvVenvService, VenvService

if t.TYPE_CHECKING:
from meltano.core.plugin.project_plugin import ProjectPlugin
Expand Down Expand Up @@ -521,15 +521,33 @@ async def install_pip_plugin(
force: Whether to ignore the Python version required by plugins.
env: Environment variables to use when expanding the pip install args.
kwargs: Unused additional arguments for the installation of the plugin.
Raises:
ValueError: If the venv backend is not supported.
"""
pip_install_args = get_pip_install_args(project, plugin, env=env)
backend = project.settings.get("venv.backend", "virtualenv")

if backend == "virtualenv":
service = VenvService(
project=project,
python=plugin.python,
namespace=plugin.type,
name=plugin.venv_name,
)
elif backend == "uv": # pragma: no cover
logger.warning("The uv backend is experimental")
service = UvVenvService(
project=project,
python=plugin.python,
namespace=plugin.type,
name=plugin.venv_name,
)
else: # pragma: no cover
msg = f"Unsupported venv backend: {backend}"
raise ValueError(msg)

await VenvService(
project=project,
python=plugin.python,
namespace=plugin.type,
name=plugin.venv_name,
).install(
await service.install(
pip_install_args=("--ignore-requires-python", *pip_install_args)
if force
else pip_install_args,
Expand Down

0 comments on commit 5b81b88

Please sign in to comment.