Skip to content

Commit

Permalink
feat(cli): add dagster project scaffold --excludes foo option
Browse files Browse the repository at this point in the history
Fixed flakey tests by refactor `os.path` to pathlib.Path.
  • Loading branch information
dbrtly committed Oct 27, 2024
1 parent 751f67e commit a0f13e9
Show file tree
Hide file tree
Showing 16 changed files with 210 additions and 220 deletions.
113 changes: 60 additions & 53 deletions python_modules/dagster/dagster/_cli/project.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
import sys
from typing import NamedTuple, Optional, Sequence, Tuple, Union
from typing import NamedTuple, Optional, Sequence

import click
import requests
Expand All @@ -20,14 +20,14 @@ def project_cli():
FLAGGED_PACKAGE_KEYWORDS = ["dagster", "dbt"]

scaffold_repository_command_help_text = (
"(DEPRECATED; Use `dagster project scaffold-code-location` instead) "
"(DEPRECATED; Use `dagster project scaffold --excludes readme` instead) "
"Create a folder structure with a single Dagster repository, in the current directory. "
"This CLI helps you to scaffold a new Dagster repository within a folder structure that "
"includes multiple Dagster repositories"
)

scaffold_code_location_command_help_text = (
"(DEPRECATED; Use `dagster project scaffold --excludes README.md` instead) "
"(DEPRECATED; Use `dagster project scaffold --excludes readme` instead) "
"Create a folder structure with a single Dagster code location, in the current directory. "
"This CLI helps you to scaffold a new Dagster code location within a folder structure that "
"includes multiple Dagster code locations."
Expand All @@ -52,7 +52,7 @@ class PackageConflictCheckResult(NamedTuple):
conflict_exists: bool = False


def check_if_pypi_package_conflict_exists(project_name: str) -> PackageConflictCheckResult:
def _get_pypi_package_conflict_result(project_name: str) -> PackageConflictCheckResult:
"""Checks if the project name contains any flagged keywords. If so, raises a warning if a PyPI
package with the same name exists. This is to prevent import errors from occurring due to a
project name that conflicts with an imported package.
Expand All @@ -71,7 +71,35 @@ def check_if_pypi_package_conflict_exists(project_name: str) -> PackageConflictC
return PackageConflictCheckResult(request_error_msg=None, conflict_exists=False)


# start deprecated commands
def check_pypi_package_conflict(project_name: str) -> None:
package_check_result: PackageConflictCheckResult = _get_pypi_package_conflict_result(
project_name
)
if package_check_result.request_error_msg:
click.echo(
click.style(
"An error occurred while checking for package conflicts:"
f" {package_check_result.request_error_msg}. \n\nConflicting package names will"
" cause import errors in your project if the existing PyPI package is included"
" as a dependency in your scaffolded project. If desired, this check can be"
" skipped by adding the `--ignore-package-conflict` flag.",
fg="red",
)
)
sys.exit(1)

if package_check_result.conflict_exists:
click.echo(
click.style(
f"The project '{project_name}' conflicts with an existing PyPI package."
" Conflicting package names will cause import errors in your project if the"
" existing PyPI package is included as a dependency in your scaffolded"
" project. Please choose another name, or add the `--ignore-package-conflict`"
" flag to bypass this check.",
fg="yellow",
)
)
sys.exit(1)


@project_cli.command(
Expand All @@ -85,7 +113,7 @@ def check_if_pypi_package_conflict_exists(project_name: str) -> PackageConflictC
type=click.STRING,
help="Name of the new Dagster repository",
)
def scaffold_repository_command(name: str):
def scaffold_repository_command(name: str) -> None:
dir_abspath = os.path.abspath(name)
if os.path.isdir(dir_abspath) and os.path.exists(dir_abspath):
click.echo(
Expand All @@ -96,7 +124,7 @@ def scaffold_repository_command(name: str):

click.echo(
click.style(
"WARNING: This command is deprecated. Use `dagster project scaffold` instead.",
"WARNING: command is deprecated. Use `dagster project scaffold --excludes readme` instead.",
fg="yellow",
)
)
Expand All @@ -115,47 +143,23 @@ def scaffold_repository_command(name: str):
type=click.STRING,
help="Name of the new Dagster code location",
)
@click.pass_context
def scaffold_code_location_command(context, name: str):
click.echo(
click.style(
"WARNING: This command is deprecated. Use `dagster project scaffold --excludes README.md` instead.",
fg="yellow",
)
)
context.invoke(scaffold_command, name=name, excludes=["README.md"])


# end deprecated commands


def _check_and_error_on_package_conflicts(project_name: str) -> None:
package_check_result = check_if_pypi_package_conflict_exists(project_name)
if package_check_result.request_error_msg:
def scaffold_code_location_command(name: str) -> None:
dir_abspath = os.path.abspath(name)
if os.path.isdir(dir_abspath) and os.path.exists(dir_abspath):
click.echo(
click.style(
"An error occurred while checking for package conflicts:"
f" {package_check_result.request_error_msg}. \n\nConflicting package names will"
" cause import errors in your project if the existing PyPI package is included"
" as a dependency in your scaffolded project. If desired, this check can be"
" skipped by adding the `--ignore-package-conflict` flag.",
fg="red",
)
click.style(f"The directory {dir_abspath} already exists. ", fg="red")
+ "\nPlease delete the contents of this path or choose another location."
)
sys.exit(1)

if package_check_result.conflict_exists:
click.echo(
click.style(
f"The project '{project_name}' conflicts with an existing PyPI package."
" Conflicting package names will cause import errors in your project if the"
" existing PyPI package is included as a dependency in your scaffolded"
" project. Please choose another name, or add the `--ignore-package-conflict`"
" flag to bypass this check.",
fg="yellow",
)
click.echo(
click.style(
"WARNING: command is deprecated. Use `dagster project scaffold --excludes readme` instead.",
fg="yellow",
)
sys.exit(1)
)
generate_project(dir_abspath, excludes=["README"])
click.echo(_styled_success_statement(name, dir_abspath))


@project_cli.command(
Expand All @@ -174,7 +178,7 @@ def _check_and_error_on_package_conflicts(project_name: str) -> None:
multiple=True,
type=click.STRING,
default=[],
help="Exclude file patterns from the project template",
help="Exclude case-insensitive file patterns from the project template",
)
@click.option(
"--ignore-package-conflict",
Expand All @@ -183,9 +187,12 @@ def _check_and_error_on_package_conflicts(project_name: str) -> None:
help="Controls whether the project name can conflict with an existing PyPI package.",
)
def scaffold_command(
name: str, excludes: Union[Tuple, list], ignore_package_conflict: bool = False
):
excludes = list(excludes)
name: str, excludes: list[str] | tuple | None = None, ignore_package_conflict: bool = False
) -> None:
excludes = [] if not excludes else excludes
if isinstance(excludes, tuple):
excludes = list(excludes)

dir_abspath = os.path.abspath(name)
if os.path.isdir(dir_abspath) and os.path.exists(dir_abspath):
click.echo(
Expand All @@ -195,9 +202,9 @@ def scaffold_command(
sys.exit(1)

if not ignore_package_conflict:
_check_and_error_on_package_conflicts(name)
check_pypi_package_conflict(name)

generate_project(dir_abspath, excludes)
generate_project(dir_abspath, excludes=excludes)
click.echo(_styled_success_statement(name, dir_abspath))


Expand Down Expand Up @@ -228,7 +235,7 @@ def scaffold_command(
default=dagster_version,
show_default=True,
)
def from_example_command(name: Optional[str], example: str, version: str):
def from_example_command(name: Optional[str], example: str, version: str) -> None:
name = name or example
dir_abspath = os.path.abspath(name) + "/"
if os.path.isdir(dir_abspath) and os.path.exists(dir_abspath):
Expand All @@ -238,7 +245,7 @@ def from_example_command(name: Optional[str], example: str, version: str):
)
sys.exit(1)
else:
os.mkdir(dir_abspath)
os.makedirs(dir_abspath, exist_ok=True)

download_example_from_github(dir_abspath, example, version)

Expand All @@ -250,7 +257,7 @@ def from_example_command(name: Optional[str], example: str, version: str):
short_help=list_examples_command_help_text,
help=list_examples_command_help_text,
)
def from_example_list_command():
def from_example_list_command() -> None:
click.echo("Examples available in `dagster project from-example`:")

click.echo(_styled_list_examples_prints(AVAILABLE_EXAMPLES))
Expand All @@ -260,7 +267,7 @@ def _styled_list_examples_prints(examples: Sequence[str]) -> str:
return "\n".join([f"* {name}" for name in examples])


def _styled_success_statement(name: str, path: str):
def _styled_success_statement(name: str, path: str) -> None:
return (
click.style("Success!", fg="green")
+ " Created "
Expand Down
6 changes: 4 additions & 2 deletions python_modules/dagster/dagster/_generate/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
# Examples aren't that can't be downloaded from the dagster project CLI
EXAMPLES_TO_IGNORE = [
"deploy_k8s_beta",
"docs_snippets",
"docs_beta_snippets",
"docs_snippets",
"experimental",
"temp_pins.txt",
"use_case_repository",
"pyproject.toml",
"README.md",
"temp_pins.txt",
]
# Hardcoded list of available examples. The list is tested against the examples folder in this mono
# repo to make sure it's up-to-date.
Expand Down
Loading

0 comments on commit a0f13e9

Please sign in to comment.