Skip to content

Commit

Permalink
[components] Add --use-editable-dagster to dg generate code-location (#…
Browse files Browse the repository at this point in the history
…26295)

## Summary & Motivation

Add an `--use-editable-dagster` flag to `dg generate code-location`.
This causes the command to read the `$DAGSTER_GIT_REPO_DIR` environment
variable and set up the code location to use editable installs from this
location. This is done by adding `tool.uv.sources` to the generated
pyproject.toml:

```
# If DAGSTER_GIT_REPO_DIR=/path/to/dagster

[tool.uv.sources]
dagster = { path = "/path/to/dagster/python_modules/dagster", editable=true }
dagster-webserver = { path="/path/to/dagster/python_modules/dagster-webserver", editable=true }
dagster-pipes = { path="/path/to/dagster/python_modules/dagster-pipes", editable=true }
dagster-components = { path = "/path/to/dagster/python_modules/libraries/dagster-components", editable=true }
```

We may also wish a path to be passable on the command line, or for this
to be somehow hidden from users since this is basically a dev tool.

## How I Tested These Changes

New unit test. Manual test generating a code location and loading it
with `dagster dev`.
  • Loading branch information
smackesey authored Dec 10, 2024
1 parent ca9dcc6 commit 80ecfc4
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ def generate_deployment_command(path: str) -> None:

@generate_cli.command(name="code-location")
@click.argument("name", type=str)
def generate_code_location_command(name: str) -> None:
@click.option("--use-editable-dagster", is_flag=True, default=False)
def generate_code_location_command(name: str, use_editable_dagster: bool) -> None:
"""Generate a Dagster code location inside a component."""
if not is_inside_deployment_project(Path(".")):
click.echo(
Expand All @@ -55,8 +56,22 @@ def generate_code_location_command(name: str) -> None:
click.echo(click.style(f"A code location named {name} already exists.", fg="red"))
sys.exit(1)

if use_editable_dagster:
if "DAGSTER_GIT_REPO_DIR" not in os.environ:
click.echo(
click.style(
"The `--use-editable-dagster` flag requires the `DAGSTER_GIT_REPO_DIR` environment variable to be set.",
fg="red",
)
)
sys.exit(1)
editable_dagster_root = os.environ["DAGSTER_GIT_REPO_DIR"]
else:
editable_dagster_root = None

code_location_path = os.path.join(context.code_location_root_path, name)
generate_code_location(code_location_path)

generate_code_location(code_location_path, editable_dagster_root)


@generate_cli.command(name="component-type")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import textwrap
from pathlib import Path
from typing import Any, Type
from typing import Any, Optional, Type

import click
import yaml
Expand Down Expand Up @@ -30,15 +31,27 @@ def generate_deployment(path: str) -> None:
)


def generate_code_location(path: str) -> None:
def generate_code_location(path: str, editable_dagster_root: Optional[str] = None) -> None:
click.echo(f"Creating a Dagster code location at {path}.")

if editable_dagster_root:
uv_sources = textwrap.dedent(f"""
[tool.uv.sources]
dagster = {{ path = "{editable_dagster_root}/python_modules/dagster", editable = true }}
dagster-components = {{ path = "{editable_dagster_root}/python_modules/libraries/dagster-components", editable = true }}
dagster-pipes = {{ path = "{editable_dagster_root}/python_modules/dagster-pipes", editable = true }}
dagster-webserver = {{ path = "{editable_dagster_root}/python_modules/dagster-webserver", editable = true }}
""")
else:
uv_sources = ""

generate_project(
path=path,
name_placeholder="CODE_LOCATION_NAME_PLACEHOLDER",
templates_path=os.path.join(
os.path.dirname(__file__), "templates", "CODE_LOCATION_NAME_PLACEHOLDER"
),
uv_sources=uv_sources,
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
name = "{{ project_name }}"
requires-python = ">=3.9,<3.13"
version = "0.1.0"
dependencies = []
dependencies = [
"dagster",
]
[project.optional-dependencies]
dev = []
dev = [
"dagster-webserver",
]
[build-system]
requires = ["setuptools"]
Expand All @@ -17,3 +21,5 @@ project_name = "{{ project_name }}"
[tool.setuptools.packages.find]
exclude=["{{ project_name }}_tests"]
{{ uv_sources }}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from typing import Iterator

import pytest
import tomli
from click.testing import CliRunner
from dagster._utils import pushd
from dagster_components.cli.generate import (
Expand Down Expand Up @@ -137,6 +138,49 @@ def test_generate_code_location_success() -> None:
assert Path("code_locations/bar/bar_tests").exists()
assert Path("code_locations/bar/pyproject.toml").exists()

with open("code_locations/bar/pyproject.toml") as f:
toml = tomli.loads(f.read())
# No tool.uv.sources added without --use-editable-dagster
assert "uv" not in toml["tool"]


def _find_git_root():
current = Path.cwd()
while current != current.parent:
if (current / ".git").exists():
return current
current = current.parent
raise Exception("Could not find git root")


def test_generate_code_location_editable_dagster_success(monkeypatch) -> None:
runner = CliRunner()
dagster_git_repo_dir = _find_git_root()
monkeypatch.setenv("DAGSTER_GIT_REPO_DIR", dagster_git_repo_dir)
with isolated_example_deployment_foo(runner):
result = runner.invoke(generate_code_location_command, ["--use-editable-dagster", "bar"])
assert result.exit_code == 0
assert Path("code_locations/bar").exists()
assert Path("code_locations/bar/pyproject.toml").exists()
with open("code_locations/bar/pyproject.toml") as f:
toml = tomli.loads(f.read())
assert toml["tool"]["uv"]["sources"]["dagster"] == {
"path": f"{dagster_git_repo_dir}/python_modules/dagster",
"editable": True,
}
assert toml["tool"]["uv"]["sources"]["dagster-pipes"] == {
"path": f"{dagster_git_repo_dir}/python_modules/dagster-pipes",
"editable": True,
}
assert toml["tool"]["uv"]["sources"]["dagster-webserver"] == {
"path": f"{dagster_git_repo_dir}/python_modules/dagster-webserver",
"editable": True,
}
assert toml["tool"]["uv"]["sources"]["dagster-components"] == {
"path": f"{dagster_git_repo_dir}/python_modules/libraries/dagster-components",
"editable": True,
}


def test_generate_code_location_outside_deployment_fails() -> None:
runner = CliRunner()
Expand Down
1 change: 1 addition & 0 deletions python_modules/libraries/dagster-components/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def get_version() -> str:
packages=find_packages(exclude=["dagster_components_tests*", "examples*"]),
install_requires=[
f"dagster{pin}",
"tomli",
],
zip_safe=False,
entry_points={
Expand Down

0 comments on commit 80ecfc4

Please sign in to comment.