From ccb807ccd4b622f793d44c0a63abd590226e4d18 Mon Sep 17 00:00:00 2001 From: Brian Kohan Date: Fri, 22 Dec 2023 02:12:23 -0800 Subject: [PATCH] dev tool upgrades, add black --- .github/workflows/test.yml | 6 +- CONTRIBUTING.rst | 2 +- README.rst | 5 +- django_typer/__init__.py | 215 +++++++-------- django_typer/tests/click_test.py | 11 +- django_typer/tests/settings.py | 70 ++--- django_typer/tests/test_app/apps.py | 5 +- .../test_app/management/commands/basic.py | 10 +- .../test_app/management/commands/callback1.py | 24 +- .../test_app/management/commands/click.py | 17 +- .../test_app/management/commands/multi.py | 15 +- .../management/commands/test_command1.py | 26 +- .../management/commands/test_subcommand.py | 4 +- .../test_app/management/commands/vanilla.py | 4 +- django_typer/tests/tests.py | 254 ++++++++++-------- django_typer/tests/typer_test.py | 13 +- django_typer/tests/typer_test2.py | 5 +- django_typer/types.py | 58 ++-- doc/.readthedocs.yaml | 22 ++ doc/requirements.txt | 8 - doc/source/conf.py | 6 +- doc/source/index.rst | 4 +- doc/source/reference.rst | 2 +- doc/source/{ref.rst => refs.rst} | 0 pyproject.toml | 92 ++++++- setup.cfg | 82 ------ 26 files changed, 478 insertions(+), 482 deletions(-) create mode 100644 doc/.readthedocs.yaml delete mode 100644 doc/requirements.txt rename doc/source/{ref.rst => refs.rst} (100%) delete mode 100644 setup.cfg diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2d91dc6..23db593 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,6 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.5.1 virtualenvs-create: true virtualenvs-in-project: true - name: Install Dependencies @@ -55,9 +54,11 @@ jobs: python-version: ['3.9', '3.10', '3.11', '3.12'] django-version: - 'Django~=3.2.0' # LTS April 2024 - - 'Django~=4.1.0' # December 2023 - 'Django~=4.2.0' # LTS April 2026 + - 'Django~=5.0.0' # April 2025 exclude: + - python-version: '3.9' + django-version: 'Django~=5.0.0' - python-version: '3.11' django-version: 'Django~=3.2.0' - python-version: '3.12' @@ -76,7 +77,6 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.5.1 virtualenvs-create: true virtualenvs-in-project: true - name: Install Release Dependencies diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index f4abe28..59fefe8 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -57,12 +57,12 @@ justified is acceptable: .. code-block:: poetry run isort django_typer + poetry run black django_typer poetry run pylint django_typer poetry run mypy django_typer poetry run doc8 -q doc poetry check poetry run pip check - poetry run safety check --full-report poetry run python -m readme_renderer ./README.rst diff --git a/README.rst b/README.rst index d8c684a..c65921d 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,5 @@ |MIT license| |PyPI version fury.io| |PyPI pyversions| |PyPi djversions| |PyPI status| |Documentation Status| -|Code Cov| |Test Status| +|Code Cov| |Test Status| |Code Style| .. |MIT license| image:: https://img.shields.io/badge/License-MIT-blue.svg :target: https://lbesson.mit-license.org/ @@ -25,6 +25,9 @@ .. |Test Status| image:: https://github.com/bckohan/django-typer/workflows/test/badge.svg :target: https://github.com/bckohan/django-typer/actions +.. |Code Style| image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + django-typer ############ diff --git a/django_typer/__init__.py b/django_typer/__init__.py index 672ddc7..8a69610 100644 --- a/django_typer/__init__.py +++ b/django_typer/__init__.py @@ -16,12 +16,10 @@ import typer from django.core.management.base import BaseCommand from typer import Typer +from typer.core import TyperArgument from typer.core import TyperCommand as CoreTyperCommand from typer.core import TyperGroup as CoreTyperGroup -from typer.main import ( - get_command, - get_params_convertors_ctx_param_name_from_function, -) +from typer.main import get_command, 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 @@ -38,36 +36,32 @@ Version, ) -VERSION = (0, 1, '0b') +VERSION = (0, 1, 0) -__title__ = 'Django Typer' -__version__ = '.'.join(str(i) for i in VERSION) -__author__ = 'Brian Kohan' -__license__ = 'MIT' -__copyright__ = 'Copyright 2023 Brian Kohan' +__title__ = "Django Typer" +__version__ = ".".join(str(i) for i in VERSION) +__author__ = "Brian Kohan" +__license__ = "MIT" +__copyright__ = "Copyright 2023 Brian Kohan" __all__ = [ - 'TyperCommand', - 'Context', - 'TyperGroupWrapper', - 'TyperCommandWrapper', - 'callback', - 'command' + "TyperCommand", + "Context", + "TyperGroupWrapper", + "TyperCommandWrapper", + "callback", + "command", ] class _ParsedArgs(SimpleNamespace): # pylint: disable=too-few-public-methods - def __init__(self, args, **kwargs): super().__init__(**kwargs) self.args = args def _get_kwargs(self): - return { - "args": self.args, - **_common_options() - } + return {"args": self.args, **_common_options()} class Context(TyperContext): @@ -80,15 +74,15 @@ class Context(TyperContext): within the Version type itself. """ - django_command: 'TyperCommand' + django_command: "TyperCommand" def __init__( self, command: click.Command, # pylint: disable=redefined-outer-name - parent: Optional['Context'] = None, - django_command: Optional['TyperCommand'] = None, + parent: Optional["Context"] = None, + django_command: Optional["TyperCommand"] = None, _resolved_params: Optional[Dict[str, Any]] = None, - **kwargs + **kwargs, ): super().__init__(command, **kwargs) self.django_command = django_command @@ -108,41 +102,36 @@ def __init__( Callable[..., Any] ] = None, params: Optional[List[click.Parameter]] = None, - **kwargs + **kwargs, ): params = params or [] self._callback = callback expected = [param.name for param in params[1:]] - self_arg = params[0].name if params else 'self' + self_arg = params[0].name if params else "self" def do_callback(*args, **kwargs): if callback: return callback( *args, **{ - param: val for param, val in kwargs.items() - if param in expected + param: val for param, val in kwargs.items() if param in expected }, **{ self_arg: getattr( - click.get_current_context(), - 'django_command', - None + click.get_current_context(), "django_command", None ) - } + }, ) + return None super().__init__( # type: ignore *args, params=[ *params[1:], - *[ - param for param in COMMON_PARAMS - if param.name not in expected - ] + *[param for param in COMMON_PARAMS if param.name not in expected], ], callback=do_callback, - **kwargs + **kwargs, ) @@ -154,7 +143,7 @@ class TyperGroupWrapper(DjangoAdapterMixin, CoreTyperGroup): pass -def callback( +def callback( # pylint: disable=too-many-local-variables name: Optional[str] = Default(None), *, cls: Type[TyperGroupWrapper] = TyperGroupWrapper, @@ -174,9 +163,8 @@ def callback( deprecated: bool = Default(False), # Rich settings rich_help_panel: Union[str, None] = Default(None), - **kwargs + **kwargs, ): - def decorator(func: CommandFunctionType): func._typer_constructor_ = lambda cmd, **extra: cmd.typer_app.callback( name=name, @@ -196,7 +184,7 @@ def decorator(func: CommandFunctionType): deprecated=deprecated, rich_help_panel=rich_help_panel, **kwargs, - **extra + **extra, )(func) return func @@ -218,9 +206,8 @@ def command( deprecated: bool = False, # Rich settings rich_help_panel: Union[str, None] = Default(None), - **kwargs + **kwargs, ): - def decorator(func: CommandFunctionType): func._typer_constructor_ = lambda cmd, **extra: cmd.typer_app.command( name=name, @@ -238,7 +225,7 @@ def decorator(func: CommandFunctionType): # Rich settings rich_help_panel=rich_help_panel, **kwargs, - **extra + **extra, )(func) return func @@ -246,16 +233,15 @@ def decorator(func: CommandFunctionType): class _TyperCommandMeta(type): - def __new__(mcs, name, bases, attrs, **kwargs): """ This method is called when a new class is created. """ typer_app = Typer( - name=mcs.__module__.rsplit('.', maxsplit=1)[-1], + name=mcs.__module__.rsplit(".", maxsplit=1)[-1], cls=TyperGroupWrapper, - help=attrs.get('help', typer.models.Default(None)), - **kwargs + help=attrs.get("help", typer.models.Default(None)), + **kwargs, ) def handle(self, *args, **options): @@ -263,7 +249,7 @@ def handle(self, *args, **options): args=args, standalone_mode=False, _resolved_params=options, - django_command=self + django_command=self, ) return super().__new__( @@ -271,33 +257,29 @@ def handle(self, *args, **options): name, bases, { - '_handle': attrs.pop('handle', None), + "_handle": attrs.pop("handle", None), **attrs, - 'handle': handle, - 'typer_app': typer_app - } + "handle": handle, + "typer_app": typer_app, + }, ) def __init__(cls, name, bases, attrs, **kwargs): """ This method is called after a new class is created. """ - cls.typer_app.info.name = cls.__module__.rsplit('.', maxsplit=1)[-1] + cls.typer_app.info.name = cls.__module__.rsplit(".", maxsplit=1)[-1] if cls._handle: - if hasattr(cls._handle, '_typer_constructor_'): - cls._handle._typer_constructor_( - cls, - name=cls.typer_app.info.name - ) + if hasattr(cls._handle, "_typer_constructor_"): + cls._handle._typer_constructor_(cls, name=cls.typer_app.info.name) del cls._handle._typer_constructor_ else: - cls.typer_app.command( - cls.typer_app.info.name, - cls=TyperCommandWrapper - )(cls._handle) + cls.typer_app.command(cls.typer_app.info.name, cls=TyperCommandWrapper)( + cls._handle + ) for attr in attrs.values(): - if hasattr(attr, '_typer_constructor_'): + if hasattr(attr, "_typer_constructor_"): attr._typer_constructor_(cls) del attr._typer_constructor_ @@ -305,75 +287,60 @@ def __init__(cls, name, bases, attrs, **kwargs): class _TyperParserAdapter: - _actions: List[Any] = [] _mutually_exclusive_groups: List[Any] = [] - django_command: 'TyperCommand' + django_command: "TyperCommand" prog_name: str subcommand: str - def __init__( - self, - django_command: 'TyperCommand', - prog_name, - subcommand - ): + def __init__(self, django_command: "TyperCommand", prog_name, subcommand): self.django_command = django_command self.prog_name = prog_name self.subcommand = subcommand def print_help(self): - typer.echo( - CliRunner().invoke( - self.django_command.typer_app, - ['--help'] - ).output - ) + typer.echo(CliRunner().invoke(self.django_command.typer_app, ["--help"]).output) - def parse_args(self, args = None, namespace=None): + def parse_args(self, args=None, namespace=None): try: cmd = get_command(self.django_command.typer_app) with cmd.make_context( - f'{self.prog_name} {self.subcommand}', + f"{self.prog_name} {self.subcommand}", list(args or []), - django_command=self.django_command + django_command=self.django_command, ) as ctx: if ctx.protected_args: p_args = [*ctx.protected_args, *ctx.args] if not cmd.chain: # type: ignore - ( - cmd_name, - cmd, - c_args - ) = cmd.resolve_command( # type: ignore - ctx, - p_args + (cmd_name, cmd, c_args) = cmd.resolve_command( # type: ignore + ctx, p_args ) assert cmd is not None sub_ctx = cmd.make_context( cmd_name, c_args, parent=ctx, - django_command=self.django_command + django_command=self.django_command, ) + + c_args = [] + for param in ctx.command.params: + if isinstance(param, TyperArgument): + import ipdb + + ipdb.set_trace() + c_args.append(ctx.params.pop(param.name)) + return _ParsedArgs( - args=p_args, - **{ - **_common_options(), - **ctx.params, - **sub_ctx.params - } + args=[*c_args, *p_args], + **{**_common_options(), **ctx.params, **sub_ctx.params}, ) else: pass else: return _ParsedArgs( - args=args or [], - **{ - **_common_options(), - **ctx.params - } + args=args or [], **{**_common_options(), **ctx.params} ) except click.exceptions.Exit: @@ -386,52 +353,50 @@ def add_argument(self, *args, **kwargs): def _common_options( version: Version = False, verbosity: Verbosity = 1, - settings: Settings = '', + settings: Settings = "", pythonpath: PythonPath = None, traceback: Traceback = False, no_color: NoColor = False, force_color: ForceColor = False, - skip_checks: SkipChecks = False + skip_checks: SkipChecks = False, ): return { - 'version': version, - 'verbosity': verbosity, - 'settings': settings, - 'pythonpath': pythonpath, - 'traceback': traceback, - 'no_color': no_color, - 'force_color': force_color, - 'skip_checks': skip_checks + "version": version, + "verbosity": verbosity, + "settings": settings, + "pythonpath": pythonpath, + "traceback": traceback, + "no_color": no_color, + "force_color": force_color, + "skip_checks": skip_checks, } -COMMON_PARAMS = get_params_convertors_ctx_param_name_from_function( - _common_options -)[0] +COMMON_PARAMS = get_params_convertors_ctx_param_name_from_function(_common_options)[0] COMMON_PARAM_NAMES = [param.name for param in COMMON_PARAMS] class TyperCommand(BaseCommand, metaclass=_TyperCommandMeta): """ - A BaseCommand extension class that uses the Typer library to parse + A BaseCommand extension class that uses the Typer library to parse arguments and options. This class adapts BaseCommand using a light touch that relies on most of the original BaseCommand implementation to handle default arguments and behaviors. - The goal of django_typer is to provide full typer style functionality + The goal of django_typer is to provide full typer style functionality while maintaining compatibility with the Django management command system. - This means that the BaseCommand interface is preserved and the Typer + This means that the BaseCommand interface is preserved and the Typer interface is added on top of it. This means that this code base is more robust to changes in the Django management command system - because most - of the base class functionality is preserved but many typer and click - internals are used directly to achieve this. We rely on robust CI to + of the base class functionality is preserved but many typer and click + internals are used directly to achieve this. We rely on robust CI to catch breaking changes in the click/typer dependencies. TODO - there is a problem with subcommand resolution and make_context() - that needs to be addressed. Need to understand exactly how click/typer - does this so it can be broken apart and be interface compatible with - Django. Also when are callbacks invoked, etc - during make_context? or + that needs to be addressed. Need to understand exactly how click/typer + does this so it can be broken apart and be interface compatible with + Django. Also when are callbacks invoked, etc - during make_context? or invoke? There is a complexity here with execute(). """ @@ -440,7 +405,7 @@ class TyperCommand(BaseCommand, metaclass=_TyperCommandMeta): @property def stealth_options(self): """ - This is the only way to inject the set of valid parameters into + This is the only way to inject the set of valid parameters into call_command because it does its own parameter validation - otherwise TypeErrors are thrown. """ @@ -460,6 +425,6 @@ def __call__(self, *args, **kwargs): """ Call this command's handle() directly. """ - if hasattr(self, '_handle'): + if hasattr(self, "_handle"): return self._handle(*args, **kwargs) - raise NotImplementedError(f'{self.__class__}') + raise NotImplementedError(f"{self.__class__}") diff --git a/django_typer/tests/click_test.py b/django_typer/tests/click_test.py index dd151fe..9edeebe 100644 --- a/django_typer/tests/click_test.py +++ b/django_typer/tests/click_test.py @@ -6,18 +6,19 @@ def cli(): """ Help text for the main command """ - print('cli()') + print("cli()") + @cli.command() def command1(): - click.echo('command1') + click.echo("command1") @cli.command() -@click.argument('model') +@click.argument("model") def command2(model): - click.echo('model: {}'.format(model), fg='red') + click.echo("model: {}".format(model), fg="red") -if __name__ == '__main__': +if __name__ == "__main__": cli() diff --git a/django_typer/tests/settings.py b/django_typer/tests/settings.py index f33b14d..c6cd196 100644 --- a/django_typer/tests/settings.py +++ b/django_typer/tests/settings.py @@ -18,7 +18,7 @@ # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-$druy$m$nio-bkagw_%=@(1w)q0=k^mk_5sfk3zi9#4v!%mh*u' +SECRET_KEY = "django-insecure-$druy$m$nio-bkagw_%=@(1w)q0=k^mk_5sfk3zi9#4v!%mh*u" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -29,37 +29,37 @@ # Application definition INSTALLED_APPS = [ - 'django_typer.tests.test_app', - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', + "django_typer.tests.test_app", + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, @@ -70,9 +70,9 @@ # https://docs.djangoproject.com/en/3.2/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", } } @@ -82,16 +82,16 @@ AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] @@ -99,9 +99,9 @@ # Internationalization # https://docs.djangoproject.com/en/3.2/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -113,9 +113,9 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.2/howto/static-files/ -STATIC_URL = '/static/' +STATIC_URL = "/static/" # Default primary key field type # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/django_typer/tests/test_app/apps.py b/django_typer/tests/test_app/apps.py index c098dec..ec2190f 100644 --- a/django_typer/tests/test_app/apps.py +++ b/django_typer/tests/test_app/apps.py @@ -2,7 +2,6 @@ class TestAppConfig(AppConfig): - name = 'django_typer.tests.test_app' - label = name.replace('.', '_') + name = "django_typer.tests.test_app" + label = name.replace(".", "_") verbose_name = "Test App" - diff --git a/django_typer/tests/test_app/management/commands/basic.py b/django_typer/tests/test_app/management/commands/basic.py index 3afd1c4..7970290 100644 --- a/django_typer/tests/test_app/management/commands/basic.py +++ b/django_typer/tests/test_app/management/commands/basic.py @@ -4,15 +4,9 @@ class Command(TyperCommand): - - help = 'Test minimal TyperCommand subclass' + help = "Test minimal TyperCommand subclass" def handle(self, arg1: str, arg2: str, arg3: float = 0.5, arg4: int = 1): assert self.__class__ == Command - opts = { - 'arg1': arg1, - 'arg2': arg2, - 'arg3': arg3, - 'arg4': arg4 - } + opts = {"arg1": arg1, "arg2": arg2, "arg3": arg3, "arg4": arg4} return json.dumps(opts) diff --git a/django_typer/tests/test_app/management/commands/callback1.py b/django_typer/tests/test_app/management/commands/callback1.py index 907af03..1ded6e1 100644 --- a/django_typer/tests/test_app/management/commands/callback1.py +++ b/django_typer/tests/test_app/management/commands/callback1.py @@ -1,31 +1,23 @@ -from django_typer import TyperCommand, callback -from typing import List import json +from typing import List + +from django_typer import TyperCommand, callback class Command(TyperCommand): - - help = 'Test basic callback command.' + help = "Test basic callback command." parameters = {} @callback() - def init(self, p1: str, flag1: bool = False, flag2: bool = True): + def init(self, p1: int, flag1: bool = False, flag2: bool = True): """ The callback to initialize the command. """ assert self.__class__ == Command - self.parameters = { - 'flag1': flag1, - 'flag2': flag2 - } - + self.parameters = {"p1": p1, "flag1": flag1, "flag2": flag2} + def handle(self, arg1: str, arg2: str, arg3: float = 0.5, arg4: int = 1): assert self.__class__ == Command - self.parameters.update({ - 'arg1': arg1, - 'arg2': arg2, - 'arg3': arg3, - 'arg4': arg4 - }) + self.parameters.update({"arg1": arg1, "arg2": arg2, "arg3": arg3, "arg4": arg4}) return json.dumps(self.parameters) diff --git a/django_typer/tests/test_app/management/commands/click.py b/django_typer/tests/test_app/management/commands/click.py index 5def587..2f99fe6 100644 --- a/django_typer/tests/test_app/management/commands/click.py +++ b/django_typer/tests/test_app/management/commands/click.py @@ -2,19 +2,18 @@ @click.group() -@click.option('--option', default='default', help='Help text for the option') -def cli(option='default'): - click.echo('cli(option={})'.format(option)) +@click.option("--option", default="default", help="Help text for the option") +def cli(option="default"): + click.echo("cli(option={})".format(option)) + @cli.command() -@click.argument('name') +@click.argument("name") def command1(name): - click.echo('Hello, {}'.format(name)) + click.echo("Hello, {}".format(name)) @cli.command() -@click.argument('model') +@click.argument("model") def command2(model): - click.echo('model: {}'.format(model)) - - + click.echo("model: {}".format(model)) diff --git a/django_typer/tests/test_app/management/commands/multi.py b/django_typer/tests/test_app/management/commands/multi.py index 4142d85..620fe4b 100644 --- a/django_typer/tests/test_app/management/commands/multi.py +++ b/django_typer/tests/test_app/management/commands/multi.py @@ -1,11 +1,11 @@ -from django_typer import TyperCommand, command -from typing import List import json +from typing import List + +from django_typer import TyperCommand, command class Command(TyperCommand): - - help = 'Test multiple sub-commands.' + help = "Test multiple sub-commands." @command() def cmd1(self, files: List[str], flag1: bool = False): @@ -13,11 +13,8 @@ def cmd1(self, files: List[str], flag1: bool = False): A command that takes a list of files and a flag. """ assert self.__class__ == Command - return json.dumps({ - 'files': files, - 'flag1': flag1 - }) - + return json.dumps({"files": files, "flag1": flag1}) + @command() def sum(self, numbers: List[float]): """ diff --git a/django_typer/tests/test_app/management/commands/test_command1.py b/django_typer/tests/test_app/management/commands/test_command1.py index 8ef4120..106ca6e 100644 --- a/django_typer/tests/test_app/management/commands/test_command1.py +++ b/django_typer/tests/test_app/management/commands/test_command1.py @@ -3,27 +3,21 @@ from django_typer import TyperCommand, callback, command -class Command( - TyperCommand, - add_completion=False -): - - help = 'This is a test help message' +class Command(TyperCommand, add_completion=False): + help = "This is a test help message" - @callback(epilog='This is a test callback epilog') - def preamble(self, pre_arg: str = 'pre_arg'): - print(f'This is a test preamble, {pre_arg}') + @callback(epilog="This is a test callback epilog") + def preamble(self, pre_arg: str = "pre_arg"): + print(f"This is a test preamble, {pre_arg}") - - @command(epilog='This is a test epilog') + @command(epilog="This is a test epilog") def delete(self, name: str, formal: bool = False, throw: bool = False): """Delete something""" if throw: - raise Exception('This is a test exception') - print(json.dumps({'name': name, 'formal': formal})) - + raise Exception("This is a test exception") + print(json.dumps({"name": name, "formal": formal})) - @command(epilog='This is a test epilog') + @command(epilog="This is a test epilog") def create(self, name: str, number: int, switch: bool = False): """This is a test create command""" - print(json.dumps({'name': name, 'number': number, 'switch': switch})) + print(json.dumps({"name": name, "number": number, "switch": switch})) diff --git a/django_typer/tests/test_app/management/commands/test_subcommand.py b/django_typer/tests/test_app/management/commands/test_subcommand.py index 0dcdcbb..7b34f0b 100644 --- a/django_typer/tests/test_app/management/commands/test_subcommand.py +++ b/django_typer/tests/test_app/management/commands/test_subcommand.py @@ -4,9 +4,7 @@ class Command(TyperCommand): - - @subcommand def subcommand1(self, name: str, formal: bool = False): """This is a subcommand""" - json.dumps({'name': name, 'formal': formal}) + json.dumps({"name": name, "formal": formal}) diff --git a/django_typer/tests/test_app/management/commands/vanilla.py b/django_typer/tests/test_app/management/commands/vanilla.py index 4439b63..0f43ccf 100644 --- a/django_typer/tests/test_app/management/commands/vanilla.py +++ b/django_typer/tests/test_app/management/commands/vanilla.py @@ -5,10 +5,8 @@ class Command(BaseCommand): - def add_arguments(self, parser: CommandParser) -> None: - parser.add_argument('--name', type=str) + parser.add_argument("--name", type=str) def handle(self, **options): pprint(json.dumps(options)) - diff --git a/django_typer/tests/tests.py b/django_typer/tests/tests.py index cdd851a..41e0d02 100644 --- a/django_typer/tests/tests.py +++ b/django_typer/tests/tests.py @@ -1,35 +1,30 @@ +import inspect import json import subprocess import sys from io import StringIO from pathlib import Path -import typer import django -from django.core.management import ( - call_command, - load_command_class, - get_commands -) +import typer +from django.core.management import call_command, get_commands, load_command_class from django.test import TestCase -import inspect -manage_py = Path(__file__).parent.parent.parent / 'manage.py' +manage_py = Path(__file__).parent.parent.parent / "manage.py" + def get_named_arguments(function): sig = inspect.signature(function) return [ - name for name, param in sig.parameters.items() + name + for name, param in sig.parameters.items() if param.default != inspect.Parameter.empty ] def run_command(command, *args): - result = subprocess.run( - [sys.executable, manage_py, command, *args], - capture_output=True, - text=True + [sys.executable, manage_py, command, *args], capture_output=True, text=True ) # Check the return code to ensure the script ran successfully @@ -45,52 +40,54 @@ def run_command(command, *args): class BasicTests(TestCase): - def test_command_line(self): self.assertEqual( - run_command('basic', 'a1', 'a2'), - {'arg1': 'a1', 'arg2': 'a2', 'arg3': 0.5, 'arg4': 1} + run_command("basic", "a1", "a2"), + {"arg1": "a1", "arg2": "a2", "arg3": 0.5, "arg4": 1}, ) self.assertEqual( - run_command('basic', 'a1', 'a2', '--arg3', '0.75', '--arg4', '2'), - {'arg1': 'a1', 'arg2': 'a2', 'arg3': 0.75, 'arg4': 2} + run_command("basic", "a1", "a2", "--arg3", "0.75", "--arg4", "2"), + {"arg1": "a1", "arg2": "a2", "arg3": 0.75, "arg4": 2}, ) def test_call_command(self): out = StringIO() - returned_options = json.loads(call_command('basic', ['a1', 'a2'], stdout=out)) - self.assertEqual(returned_options, {'arg1': 'a1', 'arg2': 'a2', 'arg3': 0.5, 'arg4': 1}) + returned_options = json.loads(call_command("basic", ["a1", "a2"], stdout=out)) + self.assertEqual( + returned_options, {"arg1": "a1", "arg2": "a2", "arg3": 0.5, "arg4": 1} + ) def test_call_command_stdout(self): out = StringIO() - call_command('basic', ['a1', 'a2'], stdout=out) + call_command("basic", ["a1", "a2"], stdout=out) printed_options = json.loads(out.getvalue()) - self.assertEqual(printed_options, {'arg1': 'a1', 'arg2': 'a2', 'arg3': 0.5, 'arg4': 1}) + self.assertEqual( + printed_options, {"arg1": "a1", "arg2": "a2", "arg3": 0.5, "arg4": 1} + ) def test_get_version(self): self.assertEqual( - run_command('basic', '--version').strip(), - django.get_version() + run_command("basic", "--version").strip(), django.get_version() ) def test_call_direct(self): - basic = load_command_class(get_commands()['basic'], 'basic') + basic = load_command_class(get_commands()["basic"], "basic") self.assertEqual( - json.loads( - basic.handle('a1', 'a2') - ), - {'arg1': 'a1', 'arg2': 'a2', 'arg3': 0.5, 'arg4': 1} + json.loads(basic.handle("a1", "a2")), + {"arg1": "a1", "arg2": "a2", "arg3": 0.5, "arg4": 1}, + ) + + from django_typer.tests.test_app.management.commands.basic import ( + Command as Basic, ) - from django_typer.tests.test_app.management.commands.basic import Command as Basic self.assertEqual( - json.loads( - Basic()('a1', 'a2', arg3=0.75, arg4=2) - ), - {'arg1': 'a1', 'arg2': 'a2', 'arg3': 0.75, 'arg4': 2} + json.loads(Basic()("a1", "a2", arg3=0.75, arg4=2)), + {"arg1": "a1", "arg2": "a2", "arg3": 0.75, "arg4": 2}, ) + class InterfaceTests(TestCase): """ Make sure the django_typer decorator inerfaces match the @@ -98,17 +95,14 @@ class InterfaceTests(TestCase): to the typer decorator because we want the IDE to offer auto complete suggestions. """ - + def test_command_interface_matches(self): from django_typer import command command_params = set(get_named_arguments(command)) typer_params = set(get_named_arguments(typer.Typer.command)) - self.assertFalse( - command_params.symmetric_difference(typer_params) - ) - + self.assertFalse(command_params.symmetric_difference(typer_params)) def test_callback_interface_matches(self): from django_typer import callback @@ -116,135 +110,183 @@ def test_callback_interface_matches(self): callback_params = set(get_named_arguments(callback)) typer_params = set(get_named_arguments(typer.Typer.callback)) - self.assertFalse( - callback_params.symmetric_difference(typer_params) - ) + self.assertFalse(callback_params.symmetric_difference(typer_params)) class MultiTests(TestCase): - def test_command_line(self): - self.assertEqual( - run_command('multi', 'cmd1', '/path/one', '/path/two'), - {'files': ['/path/one', '/path/two'], 'flag1': False} + run_command("multi", "cmd1", "/path/one", "/path/two"), + {"files": ["/path/one", "/path/two"], "flag1": False}, ) self.assertEqual( - run_command('multi', 'cmd1', '/path/four', '/path/three', '--flag1'), - {'files': ['/path/four', '/path/three'], 'flag1': True} + run_command("multi", "cmd1", "/path/four", "/path/three", "--flag1"), + {"files": ["/path/four", "/path/three"], "flag1": True}, ) self.assertEqual( - run_command('multi', 'sum', '1.2', '3.5', ' -12.3'), - sum([1.2, 3.5, -12.3]) + run_command("multi", "sum", "1.2", "3.5", " -12.3"), sum([1.2, 3.5, -12.3]) ) - self.assertEqual( - run_command('multi', 'cmd3'), - {} - ) + self.assertEqual(run_command("multi", "cmd3"), {}) def test_call_command(self): - ret = json.loads(call_command('multi', ['cmd1', '/path/one', '/path/two'])) - self.assertEqual(ret, {'files': ['/path/one', '/path/two'], 'flag1': False}) + ret = json.loads(call_command("multi", ["cmd1", "/path/one", "/path/two"])) + self.assertEqual(ret, {"files": ["/path/one", "/path/two"], "flag1": False}) - ret = json.loads(call_command('multi', ['cmd1', '/path/four', '/path/three', '--flag1'])) - self.assertEqual(ret, {'files': ['/path/four', '/path/three'], 'flag1': True}) + ret = json.loads( + call_command("multi", ["cmd1", "/path/four", "/path/three", "--flag1"]) + ) + self.assertEqual(ret, {"files": ["/path/four", "/path/three"], "flag1": True}) - ret = json.loads(call_command('multi', ['sum', '1.2', '3.5', ' -12.3'])) + ret = json.loads(call_command("multi", ["sum", "1.2", "3.5", " -12.3"])) self.assertEqual(ret, sum([1.2, 3.5, -12.3])) - ret = json.loads(call_command('multi', ['cmd3'])) + ret = json.loads(call_command("multi", ["cmd3"])) self.assertEqual(ret, {}) - def test_call_command_stdout(self): out = StringIO() - call_command('multi', ['cmd1', '/path/one', '/path/two'], stdout=out) - self.assertEqual(json.loads(out.getvalue()), {'files': ['/path/one', '/path/two'], 'flag1': False}) + call_command("multi", ["cmd1", "/path/one", "/path/two"], stdout=out) + self.assertEqual( + json.loads(out.getvalue()), + {"files": ["/path/one", "/path/two"], "flag1": False}, + ) out = StringIO() - call_command('multi', ['cmd1', '/path/four', '/path/three', '--flag1'], stdout=out) - self.assertEqual(json.loads(out.getvalue()), {'files': ['/path/four', '/path/three'], 'flag1': True}) + call_command( + "multi", ["cmd1", "/path/four", "/path/three", "--flag1"], stdout=out + ) + self.assertEqual( + json.loads(out.getvalue()), + {"files": ["/path/four", "/path/three"], "flag1": True}, + ) out = StringIO() - call_command('multi', ['sum', '1.2', '3.5', ' -12.3'], stdout=out) + call_command("multi", ["sum", "1.2", "3.5", " -12.3"], stdout=out) self.assertEqual(json.loads(out.getvalue()), sum([1.2, 3.5, -12.3])) out = StringIO() - call_command('multi', ['cmd3'], stdout=out) + call_command("multi", ["cmd3"], stdout=out) self.assertEqual(json.loads(out.getvalue()), {}) def test_get_version(self): self.assertEqual( - run_command('multi', '--version').strip(), - django.get_version() + run_command("multi", "--version").strip(), django.get_version() ) self.assertEqual( - run_command('multi', 'cmd1', '--version').strip(), - django.get_version() + run_command("multi", "cmd1", "--version").strip(), django.get_version() ) self.assertEqual( - run_command('multi', 'sum', '--version').strip(), - django.get_version() + run_command("multi", "sum", "--version").strip(), django.get_version() ) self.assertEqual( - run_command('multi', 'cmd3', '--version').strip(), - django.get_version() + run_command("multi", "cmd3", "--version").strip(), django.get_version() ) def test_call_direct(self): - multi = load_command_class(get_commands()['multi'], 'multi') + multi = load_command_class(get_commands()["multi"], "multi") self.assertEqual( - json.loads(multi.cmd1(['/path/one', '/path/two'])), - {'files': ['/path/one', '/path/two'], 'flag1': False} + json.loads(multi.cmd1(["/path/one", "/path/two"])), + {"files": ["/path/one", "/path/two"], "flag1": False}, ) self.assertEqual( - json.loads(multi.cmd1(['/path/four', '/path/three'], flag1=True)), - {'files': ['/path/four', '/path/three'], 'flag1': True} + json.loads(multi.cmd1(["/path/four", "/path/three"], flag1=True)), + {"files": ["/path/four", "/path/three"], "flag1": True}, ) - self.assertEqual( - float(multi.sum([1.2, 3.5, -12.3])), - sum([1.2, 3.5, -12.3]) - ) + self.assertEqual(float(multi.sum([1.2, 3.5, -12.3])), sum([1.2, 3.5, -12.3])) - self.assertEqual( - json.loads(multi.cmd3()), - {} - ) + self.assertEqual(json.loads(multi.cmd3()), {}) class CallbackTests(TestCase): - def test_command_line(self): - self.assertEqual( - run_command('callback1', 'callback1', 'a1', 'a2'), - {'flag1': False, 'flag2': True, 'arg1': 'a1', 'arg2': 'a2', 'arg3': 0.5, 'arg4': 1} + run_command("callback1", "5", "callback1", "a1", "a2"), + { + "p1": 5, + "flag1": False, + "flag2": True, + "arg1": "a1", + "arg2": "a2", + "arg3": 0.5, + "arg4": 1, + }, ) self.assertEqual( - run_command('callback1', '--flag1', '--no-flag2', 'callback1', 'a1', 'a2', '--arg3', '0.75', '--arg4', '2'), - {'flag1': True, 'flag2': False, 'arg1': 'a1', 'arg2': 'a2', 'arg3': 0.75, 'arg4': 2} + run_command( + "callback1", + "6", + "--flag1", + "--no-flag2", + "callback1", + "a1", + "a2", + "--arg3", + "0.75", + "--arg4", + "2", + ), + { + "p1": 6, + "flag1": True, + "flag2": False, + "arg1": "a1", + "arg2": "a2", + "arg3": 0.75, + "arg4": 2, + }, ) - # def test_call_command(self): - # ret = json.loads(call_command('multi', ['cmd1', '/path/one', '/path/two'])) - # self.assertEqual(ret, {'files': ['/path/one', '/path/two'], 'flag1': False}) - - # ret = json.loads(call_command('multi', ['cmd1', '/path/four', '/path/three', '--flag1'])) - # self.assertEqual(ret, {'files': ['/path/four', '/path/three'], 'flag1': True}) - - # ret = json.loads(call_command('multi', ['sum', '1.2', '3.5', ' -12.3'])) - # self.assertEqual(ret, sum([1.2, 3.5, -12.3])) - - # ret = json.loads(call_command('multi', ['cmd3'])) - # self.assertEqual(ret, {}) + def test_call_command(self): + ret = json.loads(call_command("callback1", ["5", "callback1", "a1", "a2"])) + self.assertEqual( + ret, + { + "p1": 5, + "flag1": False, + "flag2": True, + "arg1": "a1", + "arg2": "a2", + "arg3": 0.5, + "arg4": 1, + }, + ) + ret = json.loads( + call_command( + "callback1", + [ + "6", + "--flag1", + "--no-flag2", + "callback1", + "a1", + "a2", + "--arg3", + "0.75", + "--arg4", + "2", + ], + ) + ) + self.assertEqual( + ret, + { + "p1": 6, + "flag1": True, + "flag2": False, + "arg1": "a1", + "arg2": "a2", + "arg3": 0.75, + "arg4": 2, + }, + ) # def test_call_command_stdout(self): # out = StringIO() diff --git a/django_typer/tests/typer_test.py b/django_typer/tests/typer_test.py index 53c7ae9..ca04e3e 100755 --- a/django_typer/tests/typer_test.py +++ b/django_typer/tests/typer_test.py @@ -1,21 +1,22 @@ import typer + from django_typer import TyperCommandWrapper, _common_options app = typer.Typer() state = {"verbose": False} -@app.command(context_settings={'allow_interspersed_args': True}) +@app.command(context_settings={"allow_interspersed_args": True}) def create(username: str, flag: bool = False): if state["verbose"]: print("About to create a user") print(f"Creating user: {username}") if state["verbose"]: print("Just created a user") - print(f'flag: {flag}') + print(f"flag: {flag}") -@app.command(epilog='Delete Epilog') +@app.command(epilog="Delete Epilog") def delete(username: str): if state["verbose"]: print("About to delete a user") @@ -24,7 +25,7 @@ def delete(username: str): print("Just deleted a user") -@app.callback(epilog='Main Epilog') +@app.callback(epilog="Main Epilog") def main(verbose: bool = False): """ Manage users in the awesome CLI app. @@ -33,16 +34,18 @@ def main(verbose: bool = False): print("Will write verbose output") state["verbose"] = True + app.command(name="common")(_common_options) @app.command(cls=TyperCommandWrapper) def wrapped(name: str): """This is a wrapped command""" - print('wrapped(%s)' % name) + print("wrapped(%s)" % name) if __name__ == "__main__": import ipdb + ipdb.set_trace() app() diff --git a/django_typer/tests/typer_test2.py b/django_typer/tests/typer_test2.py index dcaa064..e227915 100755 --- a/django_typer/tests/typer_test2.py +++ b/django_typer/tests/typer_test2.py @@ -1,10 +1,11 @@ import typer + from django_typer import TyperCommandWrapper, _common_options -app = typer.Typer(epilog='Typer Epilog') +app = typer.Typer(epilog="Typer Epilog") -@app.command(epilog='Main Epilog') +@app.command(epilog="Main Epilog") def main(verbose: bool = False): """ Manage users in the awesome CLI app. diff --git a/django_typer/types.py b/django_typer/types.py index 85e6fd8..232a538 100644 --- a/django_typer/types.py +++ b/django_typer/types.py @@ -8,12 +8,12 @@ from typer import Option, echo -_COMMON_PANEL = 'Django' +_COMMON_PANEL = "Django" def print_version(context, _, value): """ - A callback to run the get_version() routine of the + A callback to run the get_version() routine of the command when --version is specified. """ if value: @@ -24,12 +24,12 @@ def print_version(context, _, value): Version = Annotated[ bool, Option( - '--version', + "--version", help="Show program's version number and exit.", callback=print_version, is_eager=True, - rich_help_panel=_COMMON_PANEL - ) + rich_help_panel=_COMMON_PANEL, + ), ] Verbosity = Annotated[ @@ -42,65 +42,63 @@ def print_version(context, _, value): show_choices=True, min=0, max=3, - rich_help_panel=_COMMON_PANEL - ) + rich_help_panel=_COMMON_PANEL, + ), ] Settings = Annotated[ str, Option( help=( - 'The Python path to a settings module, e.g. ' + "The Python path to a settings module, e.g. " '"myproject.settings.main". If this isn\'t provided, the ' - 'DJANGO_SETTINGS_MODULE environment variable will be used.' + "DJANGO_SETTINGS_MODULE environment variable will be used." ), - rich_help_panel=_COMMON_PANEL - ) + rich_help_panel=_COMMON_PANEL, + ), ] PythonPath = Annotated[ Optional[Path], Option( help=( - 'A directory to add to the Python path, e.g. ' + "A directory to add to the Python path, e.g. " '"/home/djangoprojects/myproject".' ), - rich_help_panel=_COMMON_PANEL - ) + rich_help_panel=_COMMON_PANEL, + ), ] Traceback = Annotated[ bool, Option( - '--traceback', - help=('Raise on CommandError exceptions'), - rich_help_panel=_COMMON_PANEL - ) + "--traceback", + help=("Raise on CommandError exceptions"), + rich_help_panel=_COMMON_PANEL, + ), ] NoColor = Annotated[ bool, Option( - '--no-color', - help=('Don\'t colorize the command output.'), - rich_help_panel=_COMMON_PANEL - ) + "--no-color", + help=("Don't colorize the command output."), + rich_help_panel=_COMMON_PANEL, + ), ] ForceColor = Annotated[ bool, Option( - '--force-color', - help=('Force colorization of the command output.'), - rich_help_panel=_COMMON_PANEL - ) + "--force-color", + help=("Force colorization of the command output."), + rich_help_panel=_COMMON_PANEL, + ), ] SkipChecks = Annotated[ bool, Option( - '--skip-checks', - help=('Skip system checks.'), - rich_help_panel=_COMMON_PANEL - ) + "--skip-checks", help=("Skip system checks."), rich_help_panel=_COMMON_PANEL + ), ] diff --git a/doc/.readthedocs.yaml b/doc/.readthedocs.yaml new file mode 100644 index 0000000..54a4fd5 --- /dev/null +++ b/doc/.readthedocs.yaml @@ -0,0 +1,22 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.12" + jobs: + post_create_environment: + - pip install poetry + - poetry config virtualenvs.create false + post_install: + - poetry install + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: doc/source/conf.py diff --git a/doc/requirements.txt b/doc/requirements.txt deleted file mode 100644 index 920c2d3..0000000 --- a/doc/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -sphinx-rtd-theme==2.0.0 -sphinxcontrib-applehelp==1.0.7; python_version >= "3.5" -sphinxcontrib-devhelp==1.0.5; python_version >= "3.5" -sphinxcontrib-htmlhelp==2.0.4; python_version >= "3.5" -sphinxcontrib-jsmath==1.0.1; python_version >= "3.5" -sphinxcontrib-qthelp==1.0.6; python_version >= "3.5" -sphinxcontrib-serializinghtml==1.1.9; python_version >= "3.5" -django-typer==0.1.0b diff --git a/doc/source/conf.py b/doc/source/conf.py index 7cd929a..fc042ed 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -30,7 +30,7 @@ author = 'Brian Kohan' # The full version, including alpha/beta/rc tags -release = django_enum.__version__ +release = django_typer.__version__ # -- General configuration --------------------------------------------------- @@ -41,8 +41,8 @@ extensions = [ 'sphinx_rtd_theme', 'sphinx.ext.autodoc', - 'sphinxarg.ext', - 'sphinx.ext.todo' + 'sphinx.ext.todo', + 'sphinxcontrib.typer' ] # Add any paths that contain templates here, relative to this directory. diff --git a/doc/source/index.rst b/doc/source/index.rst index 65fc4af..91427e0 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1,10 +1,10 @@ -.. include:: refs.rst +.. include:: ./refs.rst ============ Django Typer ============ -Full and natural support for enumerations_ as Django model fields. +Use Typer as the CLI interface for Django management commands. Installation diff --git a/doc/source/reference.rst b/doc/source/reference.rst index a916e91..76649c0 100644 --- a/doc/source/reference.rst +++ b/doc/source/reference.rst @@ -1,4 +1,4 @@ -.. include:: refs.rst +.. include:: ./refs.rst .. _reference: diff --git a/doc/source/ref.rst b/doc/source/refs.rst similarity index 100% rename from doc/source/ref.rst rename to doc/source/refs.rst diff --git a/pyproject.toml b/pyproject.toml index 463e4e3..68b806e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,13 +43,93 @@ Django = ">=3.2,<5.0" typer = "^0.9.0" [tool.poetry.group.dev.dependencies] -django-click = "^2.3.0" ipdb = "^0.13.13" rich = "^13.7.0" -pytest-django = "^4.5.2" -isort = "^5.6.4" -pytest-cov = "^4.0.0" -Sphinx = ">=5.0.2,<8.0.0" +pytest-django = "^4.7.0" +isort = "^5.13.0" +pytest-cov = "^4.1.0" +Sphinx = ">=7.2.0" sphinx-rtd-theme = "^2.0.0" -mypy = ">=0.991,<1.8" +mypy = "^1.8" pylint = '^3.0' +black = "^23.12.0" +doc8 = "^1.1.1" +aiohttp = "^3.9.1" +readme-renderer = "^42.0" +sphinxcontrib-typer = "^0.1.4" + +[tool.mypy] +# The mypy configurations: http://bit.ly/2zEl9WI +allow_redefinition = false +check_untyped_defs = true +disallow_untyped_decorators = false +disallow_any_explicit = false +disallow_any_generics = false +disallow_untyped_calls = true +ignore_errors = false +ignore_missing_imports = true +implicit_reexport = false +strict_optional = true +strict_equality = true +local_partial_types = true +no_implicit_optional = true +warn_unused_ignores = true +warn_redundant_casts = true +warn_unused_configs = true +warn_unreachable = true +warn_no_return = true +exclude = "tests" + + +[tool.doc8] +ignore-path = "doc/_build" +max-line-length = 100 +sphinx = true + +[isort] +# isort configuration: +# https://github.com/timothycrosley/isort/wiki/isort-Settings +include_trailing_comma = true +use_parentheses = true +# See https://github.com/timothycrosley/isort#multi-line-output-modes +multi_line_output = 3 +default_section = "FIRSTPARTY" +line_length = 88 + + +[tool.pytest.ini_options] +# py.test options: +DJANGO_SETTINGS_MODULE = "django_typer.tests.settings" +python_files = "tests.py" +norecursedirs = "*.egg .eggs dist build docs .tox .git __pycache__" + +addopts = [ + "--strict-markers", + "--cov=django_typer", + "--cov-branch", + "--cov-report=term-missing:skip-covered", + "--cov-report=html", + "--cov-report=xml", + "--cov-fail-under=70" +] + +[tool.coverage] +# dont exempt tests from coverage - useful to make sure they're being run +omit = [ + "django_typer/tests/**/*py" +] + +[tool.black] +line-length = 88 +target-version = ["py39", "py310", "py311", "py312"] +include = '\.pyi?$' + +[pylint] +output-format = "colorized" +max-line-length = 88 + +[tool.pylint.'DESIGN'] +max-args=30 + +[tool.pylint.'MASTER'] +ignore="tests" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index bde286a..0000000 --- a/setup.cfg +++ /dev/null @@ -1,82 +0,0 @@ -# All configuration for plugins and other utils is defined here. -# Read more about `setup.cfg`: -# https://docs.python.org/3/distutils/configfile.html - -[pylint] -output-format = colorized -max-line-length = 79 # PEP 8 - -[pylint.DESIGN] -max-args=30 - -[pylint.MASTER] -ignore=tests - -[pylint.MESSAGES CONTROL] - -[darglint] -# Darglint integrates with flake8 -# https://github.com/terrencepreilly/darglint -docstring_style=sphinx -strictness=long - - -[isort] -# isort configuration: -# https://github.com/timothycrosley/isort/wiki/isort-Settings -include_trailing_comma = true -use_parentheses = true -# See https://github.com/timothycrosley/isort#multi-line-output-modes -multi_line_output = 3 -default_section = FIRSTPARTY -line_length = 79 - - -[tool:pytest] -# py.test options: -DJANGO_SETTINGS_MODULE = django_typer.tests.settings -python_files = tests.py -norecursedirs = *.egg .eggs dist build docs .tox .git __pycache__ - -addopts = - --strict-markers - --cov=django_typer - --cov-branch - --cov-report=term-missing:skip-covered - --cov-report=html - --cov-report=xml - --cov-fail-under=70 - --cov-config=setup.cfg - -[coverage:run] -# dont exempt tests from coverage - useful to make sure they're being run -omit = - django_typer/tests/**/*py - -[mypy] -# The mypy configurations: http://bit.ly/2zEl9WI -allow_redefinition = False -check_untyped_defs = True -disallow_untyped_decorators = False -disallow_any_explicit = False -disallow_any_generics = False -disallow_untyped_calls = True -ignore_errors = False -ignore_missing_imports = True -implicit_reexport = False -strict_optional = True -strict_equality = True -local_partial_types = True -no_implicit_optional = True -warn_unused_ignores = True -warn_redundant_casts = True -warn_unused_configs = True -warn_unreachable = True -warn_no_return = True -exclude = tests - - -[doc8] -ignore-path = doc/_build -max-line-length = 100 -sphinx = True