Skip to content

Commit

Permalink
run isort and black
Browse files Browse the repository at this point in the history
  • Loading branch information
bckohan committed Dec 26, 2023
1 parent 15ab5ac commit 441f0f9
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 109 deletions.
123 changes: 62 additions & 61 deletions django_typer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,26 @@
"""

import contextlib
import sys
from types import SimpleNamespace, MethodType
import typing as t
from dataclasses import dataclass
from importlib import import_module
from types import MethodType, SimpleNamespace

import click
import typer
from importlib import import_module
from django.core.management.base import BaseCommand
from django.core.management import get_commands
from django.core.management.base import BaseCommand
from typer import Typer
from typer.core import TyperCommand as CoreTyperCommand
from typer.core import TyperGroup as CoreTyperGroup
from typer.main import get_command as get_typer_command, MarkupMode, get_params_convertors_ctx_param_name_from_function
from typer.main import MarkupMode
from typer.main import get_command as get_typer_command
from typer.main import get_params_convertors_ctx_param_name_from_function
from typer.models import CommandFunctionType
from typer.models import Context as TyperContext
from typer.models import Default
from dataclasses import dataclass
import contextlib

from .types import (
ForceColor,
Expand Down Expand Up @@ -54,21 +56,26 @@
"TyperCommandWrapper",
"callback",
"command",
"get_command"
"get_command",
]


def get_command(
command_name: str,
*subcommand: str,
stdout: t.Optional[t.IO[str]]=None,
stderr: t.Optional[t.IO[str]]=None,
no_color: bool=False,
force_color: bool=False
*subcommand: str,
stdout: t.Optional[t.IO[str]] = None,
stderr: t.Optional[t.IO[str]] = None,
no_color: bool = False,
force_color: bool = False,
):
# todo - add a __call__ method to the command class if it is not a TyperCommand and has no
# __call__ method - this will allow this interface to be used for standard commands
module = import_module(f'{get_commands()[command_name]}.management.commands.{command_name}')
cmd = module.Command(stdout=stdout, stderr=stderr, no_color=no_color, force_color=force_color)
module = import_module(
f"{get_commands()[command_name]}.management.commands.{command_name}"
)
cmd = module.Command(
stdout=stdout, stderr=stderr, no_color=no_color, force_color=force_color
)
if subcommand:
method = cmd.get_subcommand(*subcommand).command._callback.__wrapped__
return MethodType(method, cmd) # return the bound method
Expand Down Expand Up @@ -140,7 +147,9 @@ def call_with_self(*args, **kwargs):
param: val for param, val in kwargs.items() if param in expected
},
**{
self_arg: getattr(click.get_current_context(), "django_command", None)
self_arg: getattr(
click.get_current_context(), "django_command", None
)
},
)
return None
Expand Down Expand Up @@ -279,7 +288,7 @@ def __new__(
rich_help_panel: t.Union[str, None] = Default(None),
pretty_exceptions_enable: bool = True,
pretty_exceptions_show_locals: bool = True,
pretty_exceptions_short: bool = True
pretty_exceptions_short: bool = True,
):
"""
This method is called when a new class is created.
Expand All @@ -306,7 +315,7 @@ def __new__(
rich_help_panel=rich_help_panel,
pretty_exceptions_enable=pretty_exceptions_enable,
pretty_exceptions_show_locals=pretty_exceptions_show_locals,
pretty_exceptions_short=pretty_exceptions_short
pretty_exceptions_short=pretty_exceptions_short,
)

def handle(self, *args, **options):
Expand All @@ -329,13 +338,7 @@ def handle(self, *args, **options):
},
)

def __init__(
cls,
name,
bases,
attrs,
**kwargs
):
def __init__(cls, name, bases, attrs, **kwargs):
"""
This method is called after a new class is created.
"""
Expand All @@ -358,7 +361,6 @@ def __init__(


class TyperParser:

@dataclass(frozen=True)
class Action:
dest: str
Expand All @@ -380,17 +382,19 @@ def __init__(self, django_command: "TyperCommand", prog_name, subcommand):
self.django_command = django_command
self.prog_name = prog_name
self.subcommand = subcommand

def populate_params(node):
for param in node.command.params:
self._actions.append(self.Action(param.name))
for child in node.children.values():
populate_params(child)

populate_params(self.django_command.command_tree)

def print_help(self, *command_path: str):
self.django_command.command_tree.context.info_name = f'{self.prog_name} {self.subcommand}'
self.django_command.command_tree.context.info_name = (
f"{self.prog_name} {self.subcommand}"
)
command_node = self.django_command.get_subcommand(*command_path)
with contextlib.redirect_stdout(self.django_command.stdout):
command_node.print_help()
Expand All @@ -399,21 +403,20 @@ def parse_args(self, args=None, namespace=None):
try:
cmd = get_typer_command(self.django_command.typer_app)
with cmd.make_context(
info_name=f'{self.prog_name} {self.subcommand}',
info_name=f"{self.prog_name} {self.subcommand}",
django_command=self.django_command,
args=list(args or [])
args=list(args or []),
) as ctx:
params = ctx.params

def discover_parsed_args(ctx):
for child in ctx.children:
discover_parsed_args(child)
params.update(child.params)

discover_parsed_args(ctx)

return _ParsedArgs(
args=args or [], **{**_common_options(), **params}
)

return _ParsedArgs(args=args or [], **{**_common_options(), **params})
except click.exceptions.Exit:
sys.exit()

Expand Down Expand Up @@ -473,13 +476,12 @@ class TyperCommand(BaseCommand, metaclass=_TyperCommandMeta):
TODO - lazy loaded command overrides.
Should be able to attach to another TyperCommand like this and conflicts would resolve
based on INSTALLED_APP precedence.
class Command(TyperCommand, attach='app_label.command_name.subcommand1.subcommand2'):
...
"""

class CommandNode:

name: str
command: t.Union[TyperCommandWrapper, TyperGroupWrapper]
context: TyperContext
Expand All @@ -489,7 +491,7 @@ def __init__(
self,
name: str,
command: t.Union[TyperCommandWrapper, TyperGroupWrapper],
context: TyperContext
context: TyperContext,
):
self.name = name
self.command = command
Expand All @@ -513,20 +515,24 @@ def get_command(self, *command_path: str):

def __init__(
self,
stdout: t.Optional[t.IO[str]]=None,
stderr: t.Optional[t.IO[str]]=None,
no_color: bool=False,
force_color: bool=False,
**kwargs
stdout: t.Optional[t.IO[str]] = None,
stderr: t.Optional[t.IO[str]] = None,
no_color: bool = False,
force_color: bool = False,
**kwargs,
):
super().__init__(stdout=stdout, stderr=stderr, no_color=no_color, force_color=force_color, **kwargs)
self.command_tree = self._build_cmd_tree(
get_typer_command(self.typer_app)
super().__init__(
stdout=stdout,
stderr=stderr,
no_color=no_color,
force_color=force_color,
**kwargs,
)

self.command_tree = self._build_cmd_tree(get_typer_command(self.typer_app))

def get_subcommand(self, *command_path: str):
return self.command_tree.get_command(*command_path)

def _filter_commands(
self, ctx: TyperContext, cmd_filter: t.Optional[t.List[str]] = None
):
Expand All @@ -535,13 +541,14 @@ def _filter_commands(
cmd
for name, cmd in getattr(
ctx.command,
'commands',
"commands",
{
name: ctx.command.get_command(ctx, name)
for name in getattr(
ctx.command, 'list_commands', lambda _: []
)(ctx)
or cmd_filter or []
for name in getattr(ctx.command, "list_commands", lambda _: [])(
ctx
)
or cmd_filter
or []
},
).items()
if not cmd_filter or name in cmd_filter
Expand All @@ -554,29 +561,23 @@ def _build_cmd_tree(
cmd: CoreTyperCommand,
parent: t.Optional[Context] = None,
info_name: t.Optional[str] = None,
node: t.Optional[CommandNode] = None
node: t.Optional[CommandNode] = None,
):
ctx = Context(
cmd,
info_name=info_name,
parent=parent,
django_command=self
)
ctx = Context(cmd, info_name=info_name, parent=parent, django_command=self)
current = self.CommandNode(cmd.name, cmd, ctx)
if node:
node.children[cmd.name] = current
for cmd in self._filter_commands(ctx):
self._build_cmd_tree(cmd, ctx, info_name=cmd.name, node=current)
return current


def __init_subclass__(cls, **_):
"""Avoid passing typer arguments up the subclass init chain"""
return super().__init_subclass__()

def create_parser(self, prog_name: str, subcommand: str, **_):
return TyperParser(self, prog_name, subcommand)

def print_help(self, prog_name: str, subcommand: str, *cmd_path: str):
"""
Print the help message for this command, derived from
Expand Down
7 changes: 5 additions & 2 deletions django_typer/tests/click_test.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import click
from pprint import pprint

import click

params = {}

@click.group(context_settings={'allow_interspersed_args': True, 'ignore_unknown_options': True})

@click.group(
context_settings={"allow_interspersed_args": True, "ignore_unknown_options": True}
)
@click.argument("name")
@click.option("--verbose", "-v", is_flag=True, help="Enables verbose mode.")
def main(name: str, verbose: bool = False):
Expand Down
11 changes: 6 additions & 5 deletions django_typer/tests/test_app/management/commands/callback2.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json

from django_typer import TyperCommand, callback, command


Expand All @@ -9,8 +10,8 @@ class Command(TyperCommand, invoke_without_command=True):

@callback(
context_settings={
'allow_interspersed_args': True,
'ignore_unknown_options': True
"allow_interspersed_args": True,
"ignore_unknown_options": True,
}
)
def init(self, p1: int, flag1: bool = False, flag2: bool = True):
Expand All @@ -20,11 +21,11 @@ def init(self, p1: int, flag1: bool = False, flag2: bool = True):
assert self.__class__ == Command
self.parameters = {"p1": p1, "flag1": flag1, "flag2": flag2}
return json.dumps(self.parameters)

@command(
context_settings={
'allow_interspersed_args': True,
'ignore_unknown_options': True
"allow_interspersed_args": True,
"ignore_unknown_options": True,
}
)
def handle(self, arg1: str, arg2: str, arg3: float = 0.5, arg4: int = 1):
Expand Down
Loading

0 comments on commit 441f0f9

Please sign in to comment.