Skip to content

Uv #197

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 13, 2025
Merged

Uv #197

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Lint

permissions: read-all
permissions:
contents: read

on:
push:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ jobs:
'${{ github.ref_name }}'
--repo '${{ github.repository }}'
--generate-notes
--prerelease
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Test

permissions: read-all
permissions:
contents: read

on:
push:
Expand Down
3 changes: 2 additions & 1 deletion doc/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ v3.1.0 (2024-03-xx)
* Fixed `fish completion installs should respect XDG_CONFIG_HOME <https://github.com/django-commons/django-typer/issues/193>`_
* Fixed `zsh completion installs should respect ZDOTDIR <https://github.com/django-commons/django-typer/issues/192>`_
* Implemented `Support Django 5.2 <https://github.com/django-commons/django-typer/issues/188>`_
* Implemented `Use intersphinx for external document references. <https://github.com/django-commons/django-typer/issues/187>`
* Implemented `Switch poetry -> uv <https://github.com/django-commons/django-typer/issues/185>`_
* Implemented `Require tests to pass before release action runs. <<https://github.com/django-commons/django-typer/issues/173>`_
* Implemented `Require tests to pass before release action runs. <https://github.com/django-commons/django-typer/issues/173>`_


v3.0.0 (2024-02-16)
Expand Down
15 changes: 14 additions & 1 deletion doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinxcontrib_django',
'sphinx.ext.autodoc',
'sphinx.ext.todo',
'sphinxcontrib.typer',
'sphinx_tabs.tabs',
"sphinx.ext.viewcode"
"sphinx.ext.viewcode",
'sphinx.ext.intersphinx'
]

# Add any paths that contain templates here, relative to this directory.
Expand Down Expand Up @@ -96,6 +98,17 @@
autodoc_typehints = "description" # or signature
autodoc_typehints_format = "short"

intersphinx_mapping = {
"django": (
"https://docs.djangoproject.com/en/stable",
"https://docs.djangoproject.com/en/stable/_objects/",
),
"click": ("https://click.palletsprojects.com/en/stable", None),
"rich": ("https://rich.readthedocs.io/en/stable", None),
"python": ('https://docs.python.org/3', None)
}


def setup(app):
# Register a sphinx.ext.autodoc.between listener to ignore everything
# between lines that contain the word IGNORE
Expand Down
10 changes: 5 additions & 5 deletions doc/source/extensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ Tutorial: Inheritance & Plugins

Adding to, or altering the behavior of, commands from upstream Django_ apps is a common use case.
Doing so allows you to keep your CLI_ stable while adding additional behaviors by installing new
apps in ``INSTALLED_APPS``. There are three main extension patterns you may wish to employ:
apps in :setting:`INSTALLED_APPS`. There are three main extension patterns you may wish to employ:

1. Override the behavior of a command in an upstream app.
2. Add additional subcommands or groups to a command in an upstream app.
3. Hook implementations of custom logic into upstream command extension points.
(`Inversion of Control <https://en.wikipedia.org/wiki/Inversion_of_control>`_)

The django-typer_ plugin mechanism supports all three of these use cases in a way that respects
the precedence order of apps in the ``INSTALLED_APPS`` setting. In this tutorial we walk through
an example of each using a :ref:`generic backup command <generic_backup>`. First we'll see how we
might :ref:`use inheritance (1) <inheritance>` to override and change the behavior of a
the precedence order of apps in the :setting:`INSTALLED_APPS` setting. In this tutorial we walk
through an example of each using a :ref:`generic backup command <generic_backup>`. First we'll see
how we might :ref:`use inheritance (1) <inheritance>` to override and change the behavior of a
subcommand. Then we'll see how we can :ref:`add subcommands (2) <plugin>` to an upstream command
using plugins. Finally we'll use pluggy_ to implement a hook system that allows us to
:ref:`add custom logic (3) <hooks>` to an upstream command.
Expand Down Expand Up @@ -259,7 +259,7 @@ directory in the ``apps.py`` file of the media and my_app apps like this:
Because we explicitly register our plugins we can call the package whatever we want.
django-typer does not require it to be named ``plugins``. It is also important to
do this inside ready() because conflicts are resolved in the order in which the extension
modules are registered and ready() methods are called in ``INSTALLED_APPS`` order.
modules are registered and ready() methods are called in :setting:`INSTALLED_APPS` order.

For plugins to work, we'll need to re-implement media from above as a composed extension
like this:
Expand Down
99 changes: 52 additions & 47 deletions doc/source/howto.rst
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,10 @@ decorator. This is like defining a group at the command root and is an extension
Collect Results with @finalize
------------------------------

Typer_ and Click_ have a ``results_callback`` mechanism on ``MultiCommands`` that allow a function
hook to be registered to operate on the results of subroutines before the command exits. You may
use this same ``results_callback`` mechanism directly through the Typer_ interface, but
django-typer_ offers a more convenient class-aware way to do this with the
Typer_ and :doc:`Click <click:index>` have a ``results_callback`` mechanism on ``MultiCommands``
that allow a function hook to be registered to operate on the results of subroutines before the
command exits. You may use this same ``results_callback`` mechanism directly through the Typer_
interface, but django-typer_ offers a more convenient class-aware way to do this with the
:func:`~django_typer.management.finalize` decorator.

For example lets say we have two subcommands that return strings, we could turn them into a csv
Expand Down Expand Up @@ -334,17 +334,19 @@ finalizers at higher levels in the command hierarchy.

.. tip::

Finalizers can be overridden just like groups and initializers using the :ref:`plugin pattern. <plugins>`
Finalizers can be overridden just like groups and initializers using the
:ref:`plugin pattern. <plugins>`


Call Commands from Code
-----------------------

There are two options for invoking a :class:`~django_typer.management.TyperCommand` from code
without spawning off a subprocess. The first is to use Django_'s builtin call_command_ function.
This function will work exactly as it does for normal BaseCommand_ derived commands. django-typer_
however adds another mechanism that can be more efficient, especially if your options and
arguments are already of the correct type and require no parsing:
without spawning off a subprocess. The first is to use Django_'s builtin
:func:`~django.core.management.call_command` function. This function will work exactly as it does
for normal :class:`~django.core.management.BaseCommand` derived commands. django-typer_ however adds
another mechanism that can be more efficient, especially if your options and arguments are already
of the correct type and require no parsing:

Say we have this command, called ``mycommand``:

Expand Down Expand Up @@ -378,7 +380,7 @@ Say we have this command, called ``mycommand``:

The rule of thumb is this:

- Use call_command_ if your options and arguments need parsing.
- Use :func:`~django.core.management.call_command` if your options and arguments need parsing.
- Use :func:`~django_typer.management.get_command` and invoke the command functions directly if
your options and arguments are already of the correct type.

Expand Down Expand Up @@ -411,15 +413,16 @@ You may also fetch a subcommand function directly by passing its path:
Change Default Django Options
-----------------------------

:class:`~django_typer.management.TyperCommand` classes preserve all of the functionality of BaseCommand_ derivatives.
This means that you can still use class members like `suppressed_base_arguments
<https://docs.djangoproject.com/en/5.0/howto/custom-management-commands/#django.core.management.BaseCommand.suppressed_base_arguments>`_
to suppress default options.
:class:`~django_typer.management.TyperCommand` classes preserve all of the functionality of
:class:`~django.core.management.BaseCommand` derivatives. This means that you can still use class
members like :attr:`~django.core.management.BaseCommand.suppressed_base_arguments` to suppress
default options.

By default :class:`~django_typer.management.TyperCommand` suppresses ``--verbosity``. You can add
it back by setting ``suppressed_base_arguments`` to an empty list. If you want to use verbosity you
can simply redefine it or use one of django-typer_'s :ref:`provided type hints <types>` for the
default BaseCommand_ options:
By default :class:`~django_typer.management.TyperCommand` suppresses :option:`--verbosity`. You can
add it back by setting :attr:`~django.core.management.BaseCommand.suppressed_base_arguments` to an
empty list. If you want to use verbosity you can simply redefine it or use one of django-typer_'s
:ref:`provided type hints <types>` for the default :class:`~django.core.management.BaseCommand`
options:

.. tabs::

Expand Down Expand Up @@ -631,28 +634,30 @@ and if it is not provided the function will be treated as a

.. note::

**Conflicting extensions are resolved in INSTALLED_APPS order.** For a detailed discussion
about the utility of this pattern, see the tutorial on :ref:`Extending Commands <plugins>`.
**Conflicting extensions are resolved in** :setting:`INSTALLED_APPS` **order.** For a detailed
discussion about the utility of this pattern, see the tutorial on
:ref:`Extending Commands <plugins>`.

.. warning::

Take care not to import any extension code during or before Django's bootstrap procedure. This
may result in conflict override behavior that does not honor INSTALLED_APPS order.
may result in conflict override behavior that does not honor :setting:`INSTALLED_APPS` order.

.. _configure-rich-exception-tracebacks:

Configure rich_ Stack Traces
----------------------------
Configure :doc:`rich <rich:index>` Stack Traces
-----------------------------------------------

When rich_ is installed it may be `configured to display rendered stack traces
<https://rich.readthedocs.io/en/stable/traceback.html>`_ for unhandled exceptions.
When :doc:`rich <rich:index>` is installed it may be
:doc:`configured to display rendered stack traces <rich:traceback>` for unhandled exceptions.
These stack traces are information dense and can be very helpful for debugging. By default, if
rich_ is installed django-typer_ will configure it to render stack traces. You can disable
this behavior by setting the ``DT_RICH_TRACEBACK_CONFIG`` config to ``False``. You may also
set ``DT_RICH_TRACEBACK_CONFIG`` to a dictionary holding the parameters to pass to
`rich.traceback.install`.
:doc:`rich <rich:index>` is installed django-typer_ will configure it to render stack traces. You
can disable this behavior by setting the ``DT_RICH_TRACEBACK_CONFIG`` config to ``False``. You may
also set ``DT_RICH_TRACEBACK_CONFIG`` to a dictionary holding the parameters to pass to
:func:`rich.traceback.install`.

This provides a common hook for configuring rich_ that you can control on a per-deployment basis:
This provides a common hook for configuring :doc:`rich <rich:index>` that you can control on a
per-deployment basis:

.. code-block::
:caption: settings.py
Expand Down Expand Up @@ -819,22 +824,21 @@ print, self.stdout and typer.echo
---------------------------------

There are no unbreakable rules about how you should print output from your commands.
You could use loggers, normal print statements or the BaseCommand_ stdout and
stderr output wrappers. Django advises the use of ``self.stdout.write`` because the
stdout and stderr streams can be configured by calls to call_command_ or
:func:`~django_typer.management.get_command` which allows you to easily grab output from your
commands for testing. Using the command's configured stdout and stderr
output wrappers also means output will respect the ``--force-color`` and ``--no-color``
parameters.

Typer_ and click_ provide `echo and secho <https://typer.tiangolo.com/tutorial/printing/>`_
functions that automatically handle byte to string conversions and offer simple styling
support. :class:`~django_typer.management.TyperCommand` provides
:meth:`~django_typer.management.TyperCommand.echo` and
You could use loggers, normal print statements or the :class:`~django.core.management.BaseCommand`
stdout and stderr output wrappers. Django advises the use of ``self.stdout.write`` because the
stdout and stderr streams can be configured by calls to :func:`~django.core.management.call_command`
or :func:`~django_typer.management.get_command` which allows you to easily grab output from your
commands for testing. Using the command's configured stdout and stderr output wrappers also means
output will respect the :option:`--force-color` and :option:`--no-color` parameters.

Typer_ and :doc:`click <click:index>` provide `echo and secho
<https://typer.tiangolo.com/tutorial/printing/>`_ functions that automatically handle byte to string
conversions and offer simple styling support. :class:`~django_typer.management.TyperCommand`
provides :meth:`~django_typer.management.TyperCommand.echo` and
:meth:`~django_typer.management.TyperCommand.secho` wrapper functions for the Typer_ echo/secho
functions. If you wish to use Typer_'s echo you should use these wrapper functions because they
honor the command's ``--force-color`` and ``--no-color`` flags and the configured stdout/stderr
streams:
honor the command's :option:`--force-color` and :option:`--no-color` flags and the configured
stdout/stderr streams:

.. tabs::

Expand All @@ -854,9 +858,10 @@ streams:
Toggle on/off result printing
-----------------------------

Django's BaseCommand_ will print any truthy values returned from the handle() method. This may not
always be desired behavior. By default :class:`~django_typer.management.TyperCommand` will do the
same, but you may toggle this behavior off by setting the class field ``print_result`` to False.
Django's :class:`~django.core.management.BaseCommand` will print any truthy values returned from the
:meth:`~django.core.management.BaseCommand.handle` method. This may not always be desired behavior.
By default :class:`~django_typer.management.TyperCommand` will do the same, but you may toggle this
behavior off by setting the class field ``print_result`` to False.


.. tabs::
Expand Down
20 changes: 11 additions & 9 deletions doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ Django Typer

Use static typing to define the CLI for your Django_ management commands with Typer_. Optionally
use the provided :class:`~django_typer.management.TyperCommand` class that inherits from
BaseCommand_. This class maps the Typer_ interface onto a class based interface that Django
developers will be familiar with. All of the BaseCommand_ functionality is inherited, so that
:class:`~django.core.management.BaseCommand`. This class maps the Typer_ interface onto a class
based interface that Django developers will be familiar with. All of the
:class:`~django.core.management.BaseCommand` functionality is inherited, so that
:class:`~django_typer.management.TyperCommand` can be a drop in replacement.

**django-typer makes it easy to:**
Expand All @@ -17,13 +18,13 @@ developers will be familiar with. All of the BaseCommand_ functionality is inher
* Create subcommands and hierarchical groups of commands.
* Use the full power of Typer_'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_.
* Configure the rendering of exception stack traces using :doc:`rich <rich:index>`.
* :ref:`Install shell tab-completion support <shellcompletions>` for bash_, zsh_, fish_ and
powershell_.
* :ref:`Create custom and portable shell tab-completions for your CLI parameters.
<define-shellcompletions>`
* Port existing commands (:class:`~django_typer.management.TyperCommand` is interface compatible
with BaseCommand_).
with :class:`~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.
Expand All @@ -49,15 +50,15 @@ developers will be familiar with. All of the BaseCommand_ functionality is inher

pip install django-typer

rich_ is a powerful library for rich text and beautiful formatting in the terminal.
:doc:`rich <rich:index>` is a powerful library for rich text and beautiful formatting in the terminal.
It is not required, but highly recommended for the best experience:

.. code:: bash

pip install "django-typer[rich]"


2. Optionally add ``django_typer`` to your ``INSTALLED_APPS`` setting:
2. Optionally add ``django_typer`` to your :setting:`INSTALLED_APPS` setting:

.. code:: python

Expand All @@ -82,9 +83,10 @@ developers will be familiar with. All of the BaseCommand_ functionality is inher

:big:`Basic Example`

:class:`~django_typer.management.TyperCommand` is a drop in extension to BaseCommand_. All of the
documented features of BaseCommand_ work the same way! Or, you may also use an interface identical
to Typer_'s. Simply import Typer_ from django_typer instead of typer.
:class:`~django_typer.management.TyperCommand` is a drop in extension to
:class:`~django.core.management.BaseCommand`. All of the documented features of
:class:`~django.core.management.BaseCommand` work the same way! Or, you may also use an interface
identical to Typer_'s. Simply import Typer_ from django_typer instead of typer.

.. tabs::

Expand Down
13 changes: 10 additions & 3 deletions doc/source/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@ django_typer
:exclude-members: Typer, CommandGroup, TyperCommand, Context, CommandNode
:show-inheritance:

.. autoclass:: django_typer.management.Context
:members:

.. autoclass:: django_typer.management.Typer
:members: callback, initialize, finalize, command, group, add_typer
:members:

.. autoclass:: django_typer.management.TyperCommand
:members: initialize, callback, finalize, command, group, echo, secho, print_help, get_subcommand
:members:
:special-members: __call__

.. autoclass:: django_typer.management.TyperParser
:members:

.. autoclass:: django_typer.management.CommandNode
:members: name, click_command, context, children, get_command, print_help
:members:

.. autoclass:: django_typer.management.TyperCommandMeta

Expand Down
23 changes: 9 additions & 14 deletions doc/source/refs.rst
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@

.. _Django: https://www.djangoproject.com/
.. _Django: https://www.djangoproject.com
.. _GitHub: https://github.com/django-commons/django-typer
.. _PyPI: https://pypi.python.org/pypi/django-typer
.. _Typer: https://typer.tiangolo.com/
.. _click: https://click.palletsprojects.com/
.. _rich: https://rich.readthedocs.io/
.. _Typer: https://typer.tiangolo.com
.. _DRY: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
.. _BaseCommand: https://docs.djangoproject.com/en/stable/howto/custom-management-commands/#command-objects
.. _argparse: https://docs.python.org/3/library/argparse.html
.. _django-typer: https://pypi.python.org/pypi/django-typer
.. _powershell: https://learn.microsoft.com/en-us/powershell/scripting/overview
.. _fish: https://fishshell.com/
.. _zsh: https://www.zsh.org/
.. _bash: https://www.gnu.org/software/bash/
.. _Arguments: https://typer.tiangolo.com/tutorial/arguments/
.. _Options: https://typer.tiangolo.com/tutorial/options/
.. _call_command: https://docs.djangoproject.com/en/5.0/ref/django-admin/#running-management-commands-from-your-code
.. _sphinxcontrib-typer: https://pypi.org/project/sphinxcontrib-typer/
.. _pluggy: https://pluggy.readthedocs.io/
.. _fish: https://fishshell.com
.. _zsh: https://www.zsh.org
.. _bash: https://www.gnu.org/software/bash
.. _Arguments: https://typer.tiangolo.com/tutorial/arguments
.. _Options: https://typer.tiangolo.com/tutorial/options
.. _sphinxcontrib-typer: https://pypi.org/project/sphinxcontrib-typer
.. _pluggy: https://pluggy.readthedocs.io
.. _CLI: https://en.wikipedia.org/wiki/Command-line_interface
Loading
Loading