Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/django-commons/django-typer
Use Typer (type hints) to define the interface for your Django management commands.
https://github.com/django-commons/django-typer
admin cli click command-line commands django management python python3 shell terminal typehints typer
Last synced: 6 days ago
JSON representation
Use Typer (type hints) to define the interface for your Django management commands.
- Host: GitHub
- URL: https://github.com/django-commons/django-typer
- Owner: django-commons
- License: mit
- Created: 2023-10-23T22:31:55.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2024-10-27T01:10:48.000Z (17 days ago)
- Last Synced: 2024-10-27T01:44:07.351Z (17 days ago)
- Topics: admin, cli, click, command-line, commands, django, management, python, python3, shell, terminal, typehints, typer
- Language: Python
- Homepage: https://django-typer.rtfd.io
- Size: 3.52 MB
- Stars: 73
- Watchers: 2
- Forks: 2
- Open Issues: 15
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
Awesome Lists containing this project
README
# django-typer
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![PyPI version](https://badge.fury.io/py/django-typer.svg)](https://pypi.python.org/pypi/django-typer/)
[![PyPI pyversions](https://img.shields.io/pypi/pyversions/django-typer.svg)](https://pypi.python.org/pypi/django-typer/)
[![PyPI djversions](https://img.shields.io/pypi/djversions/django-typer.svg)](https://pypi.org/project/django-typer/)
[![PyPI status](https://img.shields.io/pypi/status/django-typer.svg)](https://pypi.python.org/pypi/django-typer)
[![Documentation Status](https://readthedocs.org/projects/django-typer/badge/?version=latest)](http://django-typer.readthedocs.io/?badge=latest/)
[![Code Cov](https://codecov.io/gh/django-commons/django-typer/branch/main/graph/badge.svg?token=0IZOKN2DYL)](https://codecov.io/gh/django-commons/django-typer)
[![Test Status](https://github.com/django-commons/django-typer/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/django-commons/django-typer/actions/workflows/test.yml?query=branch:main)
[![Lint Status](https://github.com/django-commons/django-typer/actions/workflows/lint.yml/badge.svg?branch=main)](https://github.com/django-commons/django-typer/actions/workflows/lint.yml?query=branch:main)
[![Published on Django Packages](https://img.shields.io/badge/Published%20on-Django%20Packages-0c3c26)](https://djangopackages.org/packages/p/django-typer/)Use static typing to define the CLI for your [Django](https://www.djangoproject.com/) management commands with [Typer](https://typer.tiangolo.com/). Optionally use the provided [TyperCommand](https://django-typer.readthedocs.io/en/latest/reference.html#django_typer.TyperCommand) class that inherits from [BaseCommand](https://docs.djangoproject.com/en/stable/howto/custom-management-commands/#django.core.management.BaseCommand). This class maps the [Typer](https://typer.tiangolo.com/) interface onto a class based interface that Django developers will be familiar with. All of the [BaseCommand](https://docs.djangoproject.com/en/stable/howto/custom-management-commands/#django.core.management.BaseCommand) functionality is inherited, so that [TyperCommand](https://django-typer.readthedocs.io/en/latest/reference.html#django_typer.TyperCommand) can be a drop in replacement.
**django-typer makes it easy to:**
* Define your command CLI interface in a clear, DRY and safe way using type hints
* Create subcommands and hierarchical groups of commands.
* Use the full power of [Typer](https://typer.tiangolo.com/)'s parameter types to validate and parse command line inputs.
* Create beautiful and information dense help outputs.
* Configure the rendering of exception stack traces using [rich](https://rich.readthedocs.io/en/latest/).
* [Install shell tab-completion support](https://django-typer.readthedocs.io/en/latest/shell_completion.html) for [bash](https://www.gnu.org/software/bash/), [zsh](https://www.zsh.org/), [fish](https://fishshell.com/), and [powershell](https://learn.microsoft.com/en-us/powershell/scripting/overview).
* [Create custom and portable shell tab-completions for your CLI parameters.](https://django-typer.readthedocs.io/en/latest/shell_completion.html#defining-custom-completions)
* Port existing commands ([TyperCommand](https://django-typer.readthedocs.io/en/latest/reference.html#django_typer.TyperCommand) is interface compatible with [BaseCommand](https://docs.djangoproject.com/en/stable/howto/custom-management-commands/#django.core.management.BaseCommand)).
* Use either a Django-style class-based interface or the Typer-style interface to define commands.
* Add plugins to upstream commands.Please refer to the [full documentation](https://django-typer.readthedocs.io/) for more information.
![django-typer example](https://raw.githubusercontent.com/django-commons/django-typer/main/doc/source/_static/img/closepoll_example.gif)
## 🚨 Deprecation Notice
**Imports from ``django_typer`` have been deprecated and will be removed in 3.0! Imports have moved to ``django_typer.management``:**
```python
# old way
from django_typer import TyperCommand, command, group, initialize, Typer# new way!
from django_typer.management import TyperCommand, command, group, initialize, Typer
```## Installation
1. Clone django-typer from GitHub or install a release off [PyPI](https://pypi.org/project/django-typer/):
```bash
pip install django-typer
```[rich](https://rich.readthedocs.io/en/latest/) is a powerful library for rich text and beautiful formatting in the terminal. It is not required but highly recommended for the best experience:
```bash
pip install "django-typer[rich]"
```2. Optionally add `django_typer` to your `INSTALLED_APPS` setting:
```python
INSTALLED_APPS = [
...
'django_typer',
]
```*You only need to install django_typer as an app if you want to use the shell completion command to enable tab-completion or if you would like django-typer to install [rich traceback rendering](https://django-typer.readthedocs.io/en/latest/howto.html#configure-rich-stack-traces) for you - which it does by default if rich is also installed.*
## Basic Example
[TyperCommand](https://django-typer.readthedocs.io/en/latest/reference.html#django_typer.TyperCommand) is a drop in extension to [BaseCommand](https://docs.djangoproject.com/en/stable/howto/custom-management-commands/#django.core.management.BaseCommand). All of the documented features of [BaseCommand](https://docs.djangoproject.com/en/stable/howto/custom-management-commands/#django.core.management.BaseCommand) work the same way!
```python
from django_typer.management import TyperCommandclass Command(TyperCommand):
def handle(self, arg1: str, arg2: str, arg3: float = 0.5, arg4: int = 1):
"""
A basic command that uses Typer
"""
```Or, you may also use an interface identical to [Typer](https://typer.tiangolo.com/)'s. Simply import [Typer](https://typer.tiangolo.com/) from django_typer instead of typer.
```python
from django_typer.management import Typerapp = Typer()
@app.command()
def main(arg1: str, arg2: str, arg3: float = 0.5, arg4: int = 1):
"""
A basic command that uses Typer
"""
```![Basic Example](https://raw.githubusercontent.com/django-commons/django-typer/main/examples/helps/basic.svg)
## Multiple Subcommands Example
Commands with multiple subcommands can be defined:
```python
import typing as tfrom django.utils.translation import gettext_lazy as _
from typer import Argumentfrom django_typer.management import TyperCommand, command
class Command(TyperCommand):
"""
A command that defines subcommands.
"""@command()
def create(
self,
name: t.Annotated[str, Argument(help=_("The name of the object to create."))],
):
"""
Create an object.
"""@command()
def delete(
self, id: t.Annotated[int, Argument(help=_("The id of the object to delete."))]
):
"""
Delete an object.
"""```
Or using the typer-style interface this could be written:
```python
from django_typer.management import Typer
import typing as tfrom django.utils.translation import gettext_lazy as _
from typer import Argumentapp = Typer(help="A command that defines subcommands.")
@app.command()
def create(
name: t.Annotated[str, Argument(help=_("The name of the object to create."))],
):
"""
Create an object.
"""@app.command()
def delete(
id: t.Annotated[int, Argument(help=_("The id of the object to delete."))]
):
"""
Delete an object.
"""
```![Multiple Subcommands Example](https://raw.githubusercontent.com/django-commons/django-typer/main/examples/helps/multi.svg)
![Multiple Subcommands Example - create](https://raw.githubusercontent.com/django-commons/django-typer/main/examples/helps/multi_create.svg)
![Multiple Subcommands Example - delete](https://raw.githubusercontent.com/django-commons/django-typer/main/examples/helps/multi_delete.svg)## Grouping and Hierarchies Example
More complex groups and subcommand hierarchies can be defined. For example, this command defines a group of commands called math, with subcommands divide and multiply. The group has a common initializer that optionally sets a float precision value. We would invoke this command like so:
```bash
./manage.py hierarchy math --precision 5 divide 10 2.1
./manage.py hierarchy math multiply 10 2
```Using the class-based interface we could define the command like this:
```python
import typing as t
from functools import reducefrom django.utils.translation import gettext_lazy as _
from typer import Argument, Optionfrom django_typer.management import TyperCommand, group
class Command(TyperCommand):
help = _("A more complex command that defines a hierarchy of subcommands.")
precision = 2
@group(help=_("Do some math at the given precision."))
def math(
self,
precision: t.Annotated[
int, Option(help=_("The number of decimal places to output."))
] = precision,
):
self.precision = precision# helps can be passed to the decorators
@math.command(help=_("Multiply the given numbers."))
def multiply(
self,
numbers: t.Annotated[
t.List[float], Argument(help=_("The numbers to multiply"))
],
):
return f"{reduce(lambda x, y: x * y, [1, *numbers]):.{self.precision}f}"# or if no help is supplied to the decorators, the docstring if present
# will be used!
@math.command()
def divide(
self,
numerator: t.Annotated[float, Argument(help=_("The numerator"))],
denominator: t.Annotated[float, Argument(help=_("The denominator"))],
floor: t.Annotated[bool, Option(help=_("Use floor division"))] = False,
):
"""
Divide the given numbers.
"""
if floor:
return str(numerator // denominator)
return f"{numerator / denominator:.{self.precision}f}"```
The typer-style interface builds a [TyperCommand](https://django-typer.readthedocs.io/en/latest/reference.html#django_typer.TyperCommand) class for us **that allows you to optionally accept the self argument in your commands.** We could define the above command using the typer interface like this:
```python
import typing as t
from functools import reducefrom django.utils.translation import gettext_lazy as _
from typer import Argument, Optionfrom django_typer.management import Typer
app = Typer(help=_("A more complex command that defines a hierarchy of subcommands."))
math_grp = Typer(help=_("Do some math at the given precision."))
app.add_typer(math_grp)
@math_grp.callback()
def math(
self,
precision: t.Annotated[
int, Option(help=_("The number of decimal places to output."))
] = 2,
):
self.precision = precision@math_grp.command(help=_("Multiply the given numbers."))
def multiply(
self,
numbers: t.Annotated[
t.List[float], Argument(help=_("The numbers to multiply"))
],
):
return f"{reduce(lambda x, y: x * y, [1, *numbers]):.{self.precision}f}"@math_grp.command()
def divide(
self,
numerator: t.Annotated[float, Argument(help=_("The numerator"))],
denominator: t.Annotated[float, Argument(help=_("The denominator"))],
floor: t.Annotated[bool, Option(help=_("Use floor division"))] = False,
):
"""
Divide the given numbers.
"""
if floor:
return str(numerator // denominator)
return f"{numerator / denominator:.{self.precision}f}"
```![Grouping and Hierarchies Example](https://raw.githubusercontent.com/django-commons/django-typer/main/examples/helps/hierarchy.svg)
![Grouping and Hierarchies Example - math](https://raw.githubusercontent.com/django-commons/django-typer/main/examples/helps/hierarchy_math.svg)
![Grouping and Hierarchies Example - math multiply](https://raw.githubusercontent.com/django-commons/django-typer/main/examples/helps/hierarchy_math_multiply.svg)
![Grouping and Hierarchies Example - math divide](https://raw.githubusercontent.com/django-commons/django-typer/main/examples/helps/hierarchy_math_divide.svg)