Skip to content

Commit

Permalink
feat: add cli
Browse files Browse the repository at this point in the history
  • Loading branch information
Goldziher committed Jul 14, 2024
1 parent 314f122 commit 9787695
Show file tree
Hide file tree
Showing 40 changed files with 1,015 additions and 684 deletions.
11 changes: 7 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,17 @@ repos:
additional_dependencies:
- tomli
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.13.0
rev: v2.14.0
hooks:
- id: pretty-format-yaml
args: [--autofix, --indent, '2']
- repo: https://github.com/jsh9/pydoclint
rev: 0.5.4
hooks:
- id: pydoclint
args: [--style=google, --check-return-types=False, --arg-type-hints-in-docstring=False]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.1
rev: v0.5.2
hooks:
- id: ruff
args: [--fix]
Expand All @@ -41,8 +46,6 @@ repos:
hooks:
- id: mypy
name: mypy
#currently awaiting mypy 3.11 release
# entry: pdm run mypy --enable-incomplete-feature=NewGenericSyntax
entry: pdm run mypy
require_serial: true
language: system
Expand Down
4 changes: 4 additions & 0 deletions gitmind/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from gitmind.cli import cli

if __name__ == "__main__":
cli()
3 changes: 3 additions & 0 deletions gitmind/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from gitmind.cli.main import cli

__all__ = ["cli"]
77 changes: 77 additions & 0 deletions gitmind/cli/_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from collections.abc import Callable
from typing import Any, NotRequired, ParamSpec, TypedDict, TypeVar, _LiteralGenericAlias, cast

from click import Choice
from pygit2 import Repository
from rich_click import Context, option

from gitmind.config import GitMindSettings

T = TypeVar("T")
P = ParamSpec("P")


class CLIContext(TypedDict):
"""The CLI context. This dict is set on the cli 'ctx.obj' attribute."""

settings: GitMindSettings
"""The gitmind settings instance."""
repo: Repository
"""The repository object."""
commit_hex: NotRequired[str]
"""The commit hash."""


def get_or_set_cli_context(ctx: Context, **kwargs: Any) -> CLIContext:
"""Get settings from context.
Args:
ctx (Context): The context.
Returns:
GitMindSettings: The settings
"""
return cast(CLIContext, ctx.obj)


def global_options() -> Callable[[Callable[P, T]], Callable[P, T]]:
"""Global CLI options.
Notes:
- this decorator is used to add global options to root group
Returns:
Callable[[Callable[P, T]], Callable[P, T]]: The decorator.
"""
options: list[Callable[[Callable[P, T]], Callable[P, T]]] = []

for field_name, field_info in sorted(GitMindSettings.model_fields.items()):
option_param = f"--{field_name.replace("_", "-")}"
if isinstance(field_info.annotation, _LiteralGenericAlias):
# we have a generic type and all its args are strings, we can assume this is a Literal - so we can trust that args are choice values
option_type: Choice | type = Choice(list(field_info.annotation.__args__))
elif any(field_info.annotation is t for t in (str, int, float, bool)):
option_type = field_info.annotation
else:
option_type = str

options.append(
option(
option_param,
help=field_info.description,
type=option_type,
required=False,
)
)

def wrapper(fn: Callable[P, T]) -> Callable[P, T]:
for opt in reversed(options):
fn = opt(fn)
return fn

return wrapper


if __name__ == "__main__":
global_options()
3 changes: 3 additions & 0 deletions gitmind/cli/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .commit import commit

__all__ = ["commit"]
34 changes: 34 additions & 0 deletions gitmind/cli/commands/commit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from click import option
from rich_click import Context, argument, echo, group, pass_context

from gitmind.utils.sync import run_as_sync


@group()
def commit() -> None:
pass


@commit.command()
@option("--commit-hash", required=True, type=str)
@pass_context
@run_as_sync
async def grade(ctx: Context, commit_hash: str) -> None:
echo(f"Commit {commit_hash}")


@commit.command()
@argument("commit_hash", required=True, type=str)
@pass_context
@run_as_sync
async def describe(ctx: Context, commit_hash: str) -> None:
"""Describe a commit."""
echo(f"Commit {commit_hash}")
# try:
# settings = get_settings_from_context(ctx)
# repo = get_repo(getcwd())
# commit = get_commit(repo=repo, hexsha=commit_hash)
# echo(settings.model_dump_json())
# echo(f"Commit {commit_hash}: {commit.message}")
# except BadName as e:
# raise UsageError(f"Invalid commit hash: {commit_hash}", ctx=ctx) from e
20 changes: 20 additions & 0 deletions gitmind/cli/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from rich_click import group, rich_click

from gitmind.cli._utils import global_options
from gitmind.cli.commands import commit

rich_click.USE_RICH_MARKUP = True
rich_click.SHOW_ARGUMENTS = True
rich_click.GROUP_ARGUMENTS_OPTIONS = True
rich_click.STYLE_ERRORS_SUGGESTION = "magenta italic"
rich_click.SHOW_METAVARS_COLUMN = True
rich_click.APPEND_METAVARS_HELP = True


@group() # type: ignore[arg-type]
@global_options()
def cli() -> None:
"""GitMind CLI."""


cli.add_command(commit)
107 changes: 0 additions & 107 deletions gitmind/commit_processing/commit.py

This file was deleted.

Loading

0 comments on commit 9787695

Please sign in to comment.