diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml deleted file mode 100644 index 9feaefa9..00000000 --- a/.github/workflows/documentation.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Sphinx Documentation - -on: - push: - branches: ["dev"] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - build: - runs-on: ubuntu-latest - - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - - steps: - - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: true - fetch-tags: true - - - name: Install poetry - run: pipx install poetry - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: 3.12 - cache: 'poetry' - - - run: poetry install - - - run: | - poetry run make -C docs html - echo "google-site-verification: google72e937875c790d13.html" > docs/_build/html/google72e937875c790d13.html - - - name: Setup Pages - uses: actions/configure-pages@v4 - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: 'docs/_build/html' - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 - diff --git a/docs/changelogs/unreleased.rst b/docs/changelogs/unreleased.rst index 572fe253..feb8be4b 100644 --- a/docs/changelogs/unreleased.rst +++ b/docs/changelogs/unreleased.rst @@ -9,5 +9,6 @@ Added Fixed ::::: -* UI: Fixed the topology UI where changing the state of a remote would cause the page to crash (#578, @ArthurD1). +* DX: Add the ability to use --set x=y or --set x y (both equivalent) to all commands (@masterivanic, #590) * UI: Correctly display header values containing a semicolon character (#577, @ArthurD1) +* UI: Fixed the topology UI where changing the state of a remote would cause the page to crash (#578, @ArthurD1). diff --git a/docs/contribute/intro-harp-start.excalidraw.png b/docs/contribute/intro-harp-start.excalidraw.png new file mode 100644 index 00000000..704033d9 Binary files /dev/null and b/docs/contribute/intro-harp-start.excalidraw.png differ diff --git a/docs/contribute/introduction.rst b/docs/contribute/introduction.rst index b19e8ca7..a374fb21 100644 --- a/docs/contribute/introduction.rst +++ b/docs/contribute/introduction.rst @@ -53,7 +53,9 @@ environment. Running ::::::: -You can start your first HARP server based on your local working copy, we'll use one of the built-in examples: +You can start your first HARP development server based on your local working copy. + +Here, we'll run one using one of the built-in examples (but the same applies to any configuration, of course): .. code-block:: bash @@ -61,6 +63,23 @@ You can start your first HARP server based on your local working copy, we'll use Open your browser at http://localhost:4080 to have a look at the HARP dashboard. +Using ``harp start`` (or ``poetry run harp start`` to let poetry manage the env, our preference) will spawn +a bunch of processes, managed by `honcho `_, with some free cherries. + +The default processes are: + +- ``harp server``: the main server, python based, which will listen for incoming requests on different ports. By + default, it listens to the 4080 port for the dashboard. Wrapped using `watchfiles + `_ to restart on code changes. +- the dashboard's devserver (`vite `_ based): listens to a high random unprivileged port by default, + and the main server will forward requests tto this port. Meaning that harp dashboard's goes through the http proxy. + Kinda meta, right? + +.. figure:: ./intro-harp-start.excalidraw.png + :scale: 40% + +You can read more about the various ``harp`` commands in the :doc:`Command Line Reference `. + Interfaces :::::::::: diff --git a/harp/commandline/config.py b/harp/commandline/config.py index 107fac67..9296b258 100644 --- a/harp/commandline/config.py +++ b/harp/commandline/config.py @@ -6,14 +6,13 @@ from rich.syntax import Syntax from rich.tree import Tree -from harp.commandline.options.server import CommonServerOptions, add_harp_server_click_options +from harp.commandline.options.server import CommonServerOptions, server_command from harp.config import ConfigurationBuilder from harp.config.asdict import asdict from harp.utils.commandline import click -@click.command("config", short_help="Prints the current configuration.") -@add_harp_server_click_options +@server_command("config", short_help="Prints the current configuration.") @click.option("--raw", is_flag=True, help="Prints the raw configuration as a dictionary.") @click.option("--json", is_flag=True, help="Prints the raw configuration as JSON.") @click.option( diff --git a/harp/commandline/cookiecutters/project/LICENSE b/harp/commandline/cookiecutters/project/LICENSE index 7d3841d0..7af87514 100644 --- a/harp/commandline/cookiecutters/project/LICENSE +++ b/harp/commandline/cookiecutters/project/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 {{cookiecutter.author}} +Copyright (c) 2024 {{cookiecutter.author_name}} <{{cookiecutter.author_email}}> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/harp/commandline/cookiecutters/project/cookiecutter.json b/harp/commandline/cookiecutters/project/cookiecutter.json index ad277b3a..5f5661e7 100644 --- a/harp/commandline/cookiecutters/project/cookiecutter.json +++ b/harp/commandline/cookiecutters/project/cookiecutter.json @@ -2,7 +2,15 @@ "name": "HARP Project", "__dir_name": "{{cookiecutter.name | slugify}}", "__pkg_name": "{{cookiecutter.name | slugify(separator='_')}}", - "author": "Smart Anonymous Harpist", + "author_name": "Smart Anonymous Harpist", + "author_email": "anonymous@harp-proxy.net", "create_application": false, - "create_config": true + "create_config": true, + "__prompts__": { + "name": "Project name", + "author_name": "Author name", + "author_email": "Author email", + "create_application": "Create application folder ?", + "create_config": "Create config file ?" + } } diff --git a/harp/commandline/cookiecutters/project/hooks/post_gen_project.sh b/harp/commandline/cookiecutters/project/hooks/post_gen_project.sh index fb6c56d3..7219129b 100644 --- a/harp/commandline/cookiecutters/project/hooks/post_gen_project.sh +++ b/harp/commandline/cookiecutters/project/hooks/post_gen_project.sh @@ -1,12 +1,16 @@ #! /bin/bash if [ "{{cookiecutter.create_application}}" == "False" ]; then - echo "Removing application folder (create_application == False)." rm -rf ./{{cookiecutter.__pkg_name}} fi if [ "{{cookiecutter.create_config}}" == "False" ]; then - echo "Removing config file (create_config == False)." rm -f ./config.yml fi + +echo "Congratulations, your HARP project «{{cookiecutter.name}}» has been created in «{{cookiecutter.__dir_name}}»." +echo +echo "To install the project dependencies, run '(cd {{cookiecutter.__dir_name}} && make install)'." +echo "To run the tests, run '(cd {{cookiecutter.__dir_name}} && make test)'." +echo "To start your project, run '(cd {{cookiecutter.__dir_name}} && make)'." diff --git a/harp/commandline/cookiecutters/project/{{cookiecutter.__dir_name}}/pyproject.toml b/harp/commandline/cookiecutters/project/{{cookiecutter.__dir_name}}/pyproject.toml index 67c27232..5c99621d 100644 --- a/harp/commandline/cookiecutters/project/{{cookiecutter.__dir_name}}/pyproject.toml +++ b/harp/commandline/cookiecutters/project/{{cookiecutter.__dir_name}}/pyproject.toml @@ -2,14 +2,18 @@ name = "{{cookiecutter.__pkg_name}}" version = "0.1.0" description = "" -authors = ["{{cookiecutter.author}}"] +authors = [ + "{{cookiecutter.author_name}} <{{cookiecutter.author_email}}>", +] readme = "README.rst" +{% if not cookiecutter.create_application -%} +package-mode = false +{%- endif %} [tool.poetry.dependencies] python = "^3.12" harp-proxy = "*" - [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/harp/commandline/create.py b/harp/commandline/create.py index d1000816..2a947da7 100644 --- a/harp/commandline/create.py +++ b/harp/commandline/create.py @@ -10,7 +10,10 @@ def create(template): """Creates a new project using cookiecutter.""" if not check_packages("cookiecutter"): - raise click.UsageError("You need to install cookiecutter to use this command (or use the harp[dev] extra).") + raise click.UsageError( + "You need to install cookiecutter to use this command (or use the `dev` extra, for example using " + "`pip install harp-proxy[dev]` or `poetry install -E dev`)." + ) from cookiecutter.main import cookiecutter diff --git a/harp/commandline/migrations.py b/harp/commandline/migrations.py index 2ca5d9e9..a8bbaacd 100644 --- a/harp/commandline/migrations.py +++ b/harp/commandline/migrations.py @@ -9,7 +9,7 @@ from sqlalchemy.ext.asyncio import create_async_engine from harp import get_logger -from harp.commandline.options.server import add_harp_server_click_options +from harp.commandline.options.server import server_command from harp.utils.commandline import click from harp_apps.storage.utils.migrations import ( create_alembic_config, @@ -21,8 +21,7 @@ logger = get_logger(__name__) -@click.command("db:migrate") -@add_harp_server_click_options +@server_command("db:migrate") @click.argument("operation", nargs=1, type=click.Choice(["up", "down"])) @click.argument("revision", nargs=1) @click.option("--reset", is_flag=True, help="Reset the database (drop all before migrations).") @@ -44,8 +43,7 @@ def migrate(*, operation, revision, reset=False, **kwargs): migrate = cast(BaseCommand, migrate) -@click.command("db:create-migration") -@add_harp_server_click_options +@server_command("db:create-migration") @click.argument("message", nargs=1) def create_migration(*, message, **kwargs): settings = create_harp_settings_with_storage_from_command_line_options(kwargs) @@ -56,8 +54,7 @@ def create_migration(*, message, **kwargs): create_migration = cast(BaseCommand, create_migration) -@click.command("db:merge") -@add_harp_server_click_options +@server_command("db:merge") @click.argument("message", nargs=1) @click.argument("revisions", nargs=-1) def run_db_merge_command(*, message, revisions, **kwargs): @@ -69,10 +66,9 @@ def run_db_merge_command(*, message, revisions, **kwargs): run_db_merge_command = cast(BaseCommand, run_db_merge_command) -@click.command("db:feature") +@server_command("db:feature") @click.argument("operation", nargs=1, type=click.Choice(["add", "remove"])) @click.argument("features", nargs=-1) -@add_harp_server_click_options def feature(features, operation, **kwargs): settings = create_harp_settings_with_storage_from_command_line_options(kwargs) alembic_cfg = create_alembic_config(settings.get("storage").url) @@ -96,8 +92,7 @@ def feature(features, operation, **kwargs): feature = cast(BaseCommand, feature) -@click.command("db:history") -@add_harp_server_click_options +@server_command("db:history") def history(**kwargs): settings = create_harp_settings_with_storage_from_command_line_options(kwargs) alembic_cfg = create_alembic_config(settings.get("storage").url) @@ -107,8 +102,7 @@ def history(**kwargs): history = cast(BaseCommand, history) -@click.command("db:reset") -@add_harp_server_click_options +@server_command("db:reset") def reset(**kwargs): settings = create_harp_settings_with_storage_from_command_line_options(kwargs) alembic_cfg = create_alembic_config(settings.get("storage").url) diff --git a/harp/commandline/options/server.py b/harp/commandline/options/server.py index 669bb81f..58c56c6c 100644 --- a/harp/commandline/options/server.py +++ b/harp/commandline/options/server.py @@ -1,7 +1,10 @@ from dataclasses import dataclass, field from itertools import chain from shlex import quote -from typing import Iterable +from typing import Any, Callable, Iterable, List, Optional, Type, Union + +from click import Command +from click.decorators import CmdType from harp.utils.commandline import click, code @@ -16,8 +19,8 @@ def __post_init__(self): self.options = dict(map(lambda x: x.split("=", 1), self.options)) -def _parse_option(x): - key, value = x.split("=", 1) +def _parse_option(option: tuple[str, str]) -> tuple[str, Union[str, bool]]: + key, value = option if value == "true": value = True elif value == "false": @@ -57,7 +60,7 @@ def __post_init__(self): self.endpoints = dict(map(lambda x: x.split("=", 1), self.endpoints)) -def add_harp_config_options(f): +def _config_click_options(f): """ Decorate a click command to add configuration options, in the right order. """ @@ -83,7 +86,8 @@ def add_harp_config_options(f): "--set", "options", multiple=True, - help=f"Add configuration options (e.g. {code('--set foo=bar')}, can be used multiple times).", + type=(str, str), + help=f"Add configuration options (e.g. {code('--set foo=bar')} or {code('--set foo bar')}, can be used multiple times).", ), ] @@ -95,7 +99,7 @@ def add_harp_config_options(f): return f -def add_harp_server_click_options(f): +def _server_click_options(f): """ Decorate a click command to add common server options, in the right order. """ @@ -123,6 +127,64 @@ def add_harp_server_click_options(f): for option in reversed(options): f = option(f) - f = add_harp_config_options(f) + f = _config_click_options(f) return f + + +class _EnhancedParserCommand(click.Command): + """ + This class override parse_args click function parse args enter in cli when type command with space or = for --set + command: + + eg: --set arg=value or arg value + """ + + def parse_args(self, ctx: click.Context, args: List[str]) -> List[str]: + index = 0 + while index < len(args): + if args[index] == "--set": + if index + 1 < len(args): + # if an equal sign is present, we expand the argument into two separated arguments + if "=" in args[index + 1]: + args = args[: index + 1] + args[index + 1].split("=") + args[index + 2 :] + index += 1 + index += 1 + return super().parse_args(ctx, args) + + +def server_command( + name: Union[Optional[str], Callable[..., Any]] = None, + cls: Type[CmdType] = _EnhancedParserCommand, + **attrs: Any, +) -> Union[Command, Callable[[Callable[..., Any]], Union[Command, CmdType]]]: + """ + Creates a click command with server options (--enable, --disable, --applications, --endpoint and all configuration + options). + """ + + def decorator(f: [Callable[..., Any]]) -> CmdType: + command_decorator = click.command(name=name, cls=cls, **attrs) + f = _server_click_options(f) + f = command_decorator(f) + return f + + return decorator + + +def config_command( + name: Union[Optional[str], Callable[..., Any]] = None, + cls: Type[CmdType] = _EnhancedParserCommand, + **attrs: Any, +) -> Union[Command, Callable[[Callable[..., Any]], Union[Command, CmdType]]]: + """ + Creates a click command with configuration options (--set, --example, --file). + """ + + def decorator(f: [Callable[..., Any]]) -> CmdType: + command_decorator = click.command(name=name, cls=cls, **attrs) + f = _config_click_options(f) + f = command_decorator(f) + return f + + return decorator diff --git a/harp/commandline/server.py b/harp/commandline/server.py index 7af41735..508f6234 100644 --- a/harp/commandline/server.py +++ b/harp/commandline/server.py @@ -3,18 +3,16 @@ from click import BaseCommand from harp import run -from harp.commandline.options.server import CommonServerOptions, add_harp_server_click_options +from harp.commandline.options.server import CommonServerOptions, server_command from harp.config import ConfigurationBuilder from harp.settings import USE_PROMETHEUS -from harp.utils.commandline import click -@click.command( +@server_command( short_help="Starts HARP server.", help="""Starts HARP server, using the provided configuration. This is the main process and will be the only process you need on a live server, it will serve both the proxy ports and the compiled frontend assets (dashboard).""", ) -@add_harp_server_click_options def server(**kwargs): _info = None if USE_PROMETHEUS: diff --git a/harp/commandline/start.py b/harp/commandline/start.py index db2e8809..14e3d660 100644 --- a/harp/commandline/start.py +++ b/harp/commandline/start.py @@ -1,7 +1,8 @@ import importlib.util import sys -from harp.commandline.server import CommonServerOptions, add_harp_server_click_options +from harp.commandline.options.server import server_command +from harp.commandline.server import CommonServerOptions from harp.commandline.utils.manager import HARP_DASHBOARD_SERVICE from harp.utils.commandline import click, code @@ -22,7 +23,7 @@ def assert_development_packages_are_available(): assert_package_is_available("watchfiles") -@click.command( +@server_command( short_help="Starts the local development environment.", help=f"""Starts the local development environment, using honcho to spawn a configurable set of processes that you can adapt to your needs. By default, it will starts the `dashboard` (frontend dev server) and `server` (python @@ -51,7 +52,6 @@ def assert_development_packages_are_available(): multiple=True, help="Add a server subprocess to the list of services to start (experimental, can be used multiple times).", ) -@add_harp_server_click_options @click.argument("services", nargs=-1) def start(with_docs, with_ui, services, server_subprocesses, mock, **kwargs): try: diff --git a/harp/commandline/tests/test_options_server.py b/harp/commandline/tests/test_options_server.py new file mode 100644 index 00000000..ac3836b0 --- /dev/null +++ b/harp/commandline/tests/test_options_server.py @@ -0,0 +1,77 @@ +import click +import pytest +from click import Context, UsageError + +from harp.commandline.options.server import _EnhancedParserCommand +from harp.commandline.server import CommonServerOptions + + +def test_default(): + options = CommonServerOptions(options=(), files=(), enable=(), disable=()) + assert options.as_list() == [] + + +def test_applications(): + options = CommonServerOptions(options=(), files=(), enable=("foo", "bar"), disable=("baz", "blurp")) + assert options.as_list() == [ + "--enable foo", + "--enable bar", + "--disable baz", + "--disable blurp", + ] + + +def test_parse_args_default_behaviour(): + @click.command(cls=_EnhancedParserCommand) + @click.option("--other") + def cmd(): ... + + assert _call_command(cmd, "--other", "value") == {"other": "value"} + with pytest.raises(UsageError): + _call_command(cmd, "--other", "value", "unknown") + + +def test_parse_args_set_behaviour(): + @click.command(cls=_EnhancedParserCommand) + @click.option("--set", "options", multiple=True, type=(str, str)) + def cmd(): ... + + assert _call_command(cmd, "--set", "arg=value", "--set", "foo", "bar") == { + "options": (("arg", "value"), ("foo", "bar")) + } + + +def test_parse_args_mixed_arguments_behaviour(): + @click.command(cls=_EnhancedParserCommand) + @click.option("--foo") + @click.option("--bar") + @click.option("--set", "options", multiple=True, type=(str, str)) + @click.argument("positional", nargs=-1) + def cmd(): ... + + assert _call_command( + cmd, + "--foo", + "foo", + "something", + "--set", + "arg=value", + "completely", + "--set", + "foo", + "bar", + "different", + "--bar", + "bar", + ) == { + "foo": "foo", + "bar": "bar", + "options": (("arg", "value"), ("foo", "bar")), + "positional": ("something", "completely", "different"), + } + + +def _call_command(cmd, *args): + ctx = Context(cmd) + cmd.parse_args(ctx, list(args)) + return ctx.params diff --git a/harp/commandline/tests/test_server_options.py b/harp/commandline/tests/test_server_options.py deleted file mode 100644 index 2f02b0e5..00000000 --- a/harp/commandline/tests/test_server_options.py +++ /dev/null @@ -1,16 +0,0 @@ -from harp.commandline.server import CommonServerOptions - - -def test_default(): - options = CommonServerOptions(options=(), files=(), enable=(), disable=()) - assert options.as_list() == [] - - -def test_applications(): - options = CommonServerOptions(options=(), files=(), enable=("foo", "bar"), disable=("baz", "blurp")) - assert options.as_list() == [ - "--enable foo", - "--enable bar", - "--disable baz", - "--disable blurp", - ] diff --git a/harp_apps/rules/commandline/lint.py b/harp_apps/rules/commandline/lint.py index e1146bb1..0bea40a6 100644 --- a/harp_apps/rules/commandline/lint.py +++ b/harp_apps/rules/commandline/lint.py @@ -1,15 +1,13 @@ from rich.syntax import Syntax from rich.tree import Tree -from harp.commandline.options.server import add_harp_config_options -from harp.utils.commandline import click +from harp.commandline.options.server import config_command from harp.utils.console import console from .utils.loaders import load_ruleset_from_files -@click.command("lint") -@add_harp_config_options +@config_command("lint") def lint_command(files, examples, options): """Lint the rules.""" diff --git a/harp_apps/rules/commandline/run.py b/harp_apps/rules/commandline/run.py index 975f3ae1..f398f881 100644 --- a/harp_apps/rules/commandline/run.py +++ b/harp_apps/rules/commandline/run.py @@ -4,7 +4,7 @@ import click from whistle import IAsyncEventDispatcher -from harp.commandline.options.server import add_harp_config_options +from harp.commandline.options.server import config_command from harp.config import ConfigurationBuilder from harp.event_dispatcher import LoggingAsyncEventDispatcher from harp.http import HttpRequest @@ -25,8 +25,7 @@ from .utils.subscribers import DebugRulesSubscriber -@click.command("run") -@add_harp_config_options +@config_command("run") @click.argument("endpoint") @click.argument("method") @click.argument("path") diff --git a/poetry.lock b/poetry.lock index 0419781c..ffeae22f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3208,29 +3208,29 @@ files = [ [[package]] name = "ruff" -version = "0.7.2" +version = "0.7.3" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.7.2-py3-none-linux_armv6l.whl", hash = "sha256:b73f873b5f52092e63ed540adefc3c36f1f803790ecf2590e1df8bf0a9f72cb8"}, - {file = "ruff-0.7.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5b813ef26db1015953daf476202585512afd6a6862a02cde63f3bafb53d0b2d4"}, - {file = "ruff-0.7.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:853277dbd9675810c6826dad7a428d52a11760744508340e66bf46f8be9701d9"}, - {file = "ruff-0.7.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21aae53ab1490a52bf4e3bf520c10ce120987b047c494cacf4edad0ba0888da2"}, - {file = "ruff-0.7.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ccc7e0fc6e0cb3168443eeadb6445285abaae75142ee22b2b72c27d790ab60ba"}, - {file = "ruff-0.7.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd77877a4e43b3a98e5ef4715ba3862105e299af0c48942cc6d51ba3d97dc859"}, - {file = "ruff-0.7.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e00163fb897d35523c70d71a46fbaa43bf7bf9af0f4534c53ea5b96b2e03397b"}, - {file = "ruff-0.7.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3c54b538633482dc342e9b634d91168fe8cc56b30a4b4f99287f4e339103e88"}, - {file = "ruff-0.7.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b792468e9804a204be221b14257566669d1db5c00d6bb335996e5cd7004ba80"}, - {file = "ruff-0.7.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dba53ed84ac19ae4bfb4ea4bf0172550a2285fa27fbb13e3746f04c80f7fa088"}, - {file = "ruff-0.7.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b19fafe261bf741bca2764c14cbb4ee1819b67adb63ebc2db6401dcd652e3748"}, - {file = "ruff-0.7.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:28bd8220f4d8f79d590db9e2f6a0674f75ddbc3847277dd44ac1f8d30684b828"}, - {file = "ruff-0.7.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9fd67094e77efbea932e62b5d2483006154794040abb3a5072e659096415ae1e"}, - {file = "ruff-0.7.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:576305393998b7bd6c46018f8104ea3a9cb3fa7908c21d8580e3274a3b04b691"}, - {file = "ruff-0.7.2-py3-none-win32.whl", hash = "sha256:fa993cfc9f0ff11187e82de874dfc3611df80852540331bc85c75809c93253a8"}, - {file = "ruff-0.7.2-py3-none-win_amd64.whl", hash = "sha256:dd8800cbe0254e06b8fec585e97554047fb82c894973f7ff18558eee33d1cb88"}, - {file = "ruff-0.7.2-py3-none-win_arm64.whl", hash = "sha256:bb8368cd45bba3f57bb29cbb8d64b4a33f8415d0149d2655c5c8539452ce7760"}, - {file = "ruff-0.7.2.tar.gz", hash = "sha256:2b14e77293380e475b4e3a7a368e14549288ed2931fce259a6f99978669e844f"}, + {file = "ruff-0.7.3-py3-none-linux_armv6l.whl", hash = "sha256:34f2339dc22687ec7e7002792d1f50712bf84a13d5152e75712ac08be565d344"}, + {file = "ruff-0.7.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:fb397332a1879b9764a3455a0bb1087bda876c2db8aca3a3cbb67b3dbce8cda0"}, + {file = "ruff-0.7.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:37d0b619546103274e7f62643d14e1adcbccb242efda4e4bdb9544d7764782e9"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d59f0c3ee4d1a6787614e7135b72e21024875266101142a09a61439cb6e38a5"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:44eb93c2499a169d49fafd07bc62ac89b1bc800b197e50ff4633aed212569299"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d0242ce53f3a576c35ee32d907475a8d569944c0407f91d207c8af5be5dae4e"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6b6224af8b5e09772c2ecb8dc9f3f344c1aa48201c7f07e7315367f6dd90ac29"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c50f95a82b94421c964fae4c27c0242890a20fe67d203d127e84fbb8013855f5"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f3eff9961b5d2644bcf1616c606e93baa2d6b349e8aa8b035f654df252c8c67"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8963cab06d130c4df2fd52c84e9f10d297826d2e8169ae0c798b6221be1d1d2"}, + {file = "ruff-0.7.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:61b46049d6edc0e4317fb14b33bd693245281a3007288b68a3f5b74a22a0746d"}, + {file = "ruff-0.7.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:10ebce7696afe4644e8c1a23b3cf8c0f2193a310c18387c06e583ae9ef284de2"}, + {file = "ruff-0.7.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3f36d56326b3aef8eeee150b700e519880d1aab92f471eefdef656fd57492aa2"}, + {file = "ruff-0.7.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5d024301109a0007b78d57ab0ba190087b43dce852e552734ebf0b0b85e4fb16"}, + {file = "ruff-0.7.3-py3-none-win32.whl", hash = "sha256:4ba81a5f0c5478aa61674c5a2194de8b02652f17addf8dfc40c8937e6e7d79fc"}, + {file = "ruff-0.7.3-py3-none-win_amd64.whl", hash = "sha256:588a9ff2fecf01025ed065fe28809cd5a53b43505f48b69a1ac7707b1b7e4088"}, + {file = "ruff-0.7.3-py3-none-win_arm64.whl", hash = "sha256:1713e2c5545863cdbfe2cbce21f69ffaf37b813bfd1fb3b90dc9a6f1963f5a8c"}, + {file = "ruff-0.7.3.tar.gz", hash = "sha256:e1d1ba2e40b6e71a61b063354d04be669ab0d39c352461f3d789cac68b54a313"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index a5699388..e7f77623 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -115,6 +115,9 @@ line-length = 120 [tool.ruff] line-length = 120 +exclude = [ + "harp/commandline/cookiecutters", +] [tool.ruff.format] docstring-code-format = true