Skip to content

Commit b322bd2

Browse files
authored
Merge pull request #99 from bckohan/v2.x.x
V2.x.x
2 parents fc439fb + cd78308 commit b322bd2

File tree

8 files changed

+88
-79
lines changed

8 files changed

+88
-79
lines changed

.github/workflows/test.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ jobs:
2323
- '3.2' # LTS April 2024
2424
- '4.2' # LTS April 2026
2525
- '5.0' # April 2025
26+
- '5.1b1' # December 2025
2627
exclude:
2728
- python-version: '3.8'
2829
django-version: '5.0'
@@ -32,6 +33,10 @@ jobs:
3233
django-version: '3.2'
3334
- python-version: '3.12'
3435
django-version: '3.2'
36+
- python-version: '3.8'
37+
django-version: '5.1b1'
38+
- python-version: '3.9'
39+
django-version: '5.1b1'
3540

3641
steps:
3742
- uses: actions/checkout@v4

django_typer/management/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2678,7 +2678,7 @@ def make_command(func: t.Callable[P, R]) -> t.Callable[P, R]:
26782678
# Rich settings
26792679
rich_help_panel=rich_help_panel,
26802680
**kwargs,
2681-
)(_strip_static(func))
2681+
)(_strip_static(func)) # pyright: ignore[reportCallIssue, reportArgumentType]
26822682
return func
26832683

26842684
return make_command

doc/source/architecture.rst

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ command callbacks as methods or static functions. Supporting dynamic command/gro
1111
attributes on command instances also requires careful usage of advanced Python features.
1212

1313
The Typer_ app tree defines the layers of groups and commands that define the CLI. Each
14-
:class:`~django_typer.TyperCommand` maintains its own app tree defined by a root
15-
:class:`~django_typer.Typer` node. When other classes inherit from a base command class, that app
16-
tree is copied and the new class can modify it without affecting the base class's tree. We extend
17-
Typer_'s Typer type with our own :class:`~django_typer.Typer` class that adds additional
18-
bookkeeping and attribute resolution features we need.
14+
:class:`~django_typer.management.TyperCommand` maintains its own app tree defined by a root
15+
:class:`~django_typer.management.Typer` node. When other classes inherit from a base command class,
16+
that app tree is copied and the new class can modify it without affecting the base class's tree.
17+
We extend Typer_'s Typer type with our own :class:`~django_typer.management.Typer` class that adds
18+
additional bookkeeping and attribute resolution features we need.
1919

2020
django-typer_ must behave intuitively as expected and therefore it must support all of the
2121
following:
@@ -37,8 +37,9 @@ method and if so, bind it to the correct class and pass the correct self instanc
3737
test is :func:`~django_typer.utils.is_method` and simply checks to see if the function accepts
3838
a first positional argument named `self`.
3939

40-
django-typer_ uses metaclasses to build the typer app tree when :class:`~django_typer.TyperCommand`
41-
classes are instantiated. The logic flow proceeds this way:
40+
django-typer_ uses metaclasses to build the typer app tree when
41+
:class:`~django_typer.management.TyperCommand` classes are instantiated. The logic flow proceeds
42+
this way:
4243

4344
- Class definition is read and @initialize/@callback, @group, @command decorators label and store
4445
typer config and registration logic onto the function objects for processing later once the root
@@ -50,18 +51,18 @@ classes are instantiated. The logic flow proceeds this way:
5051
included during this registration because they do not appear as attributes on the base classes.
5152
This keeps inheritance pure while allowing plugins to not interfere. The exception to this is
5253
when using the Typer-style interface where all commands and groups are registered dynamically.
53-
A :class:`~django_typer.Typer` instance is passed as an argument to the
54-
:class:`~django_typer.Typer` constructor and when this happens, the commands and groups will
55-
be copied.
54+
A :class:`~django_typer.management.Typer` instance is passed as an argument to the
55+
:class:`~django_typer.management.Typer` constructor and when this happens, the commands and
56+
groups will be copied.
5657
- Metaclass __init__ sets the newly created Command class into the typer app tree and determines
5758
if a common initializer needs to be added containing the default unsupressed django options.
5859
- Command __init__ loads any registered plugins (this is a one time opperation that will happen
5960
when the first Command of a given type is instantiated). It also determines if the addition
6061
of any plugins should necessitate the addition of a common initializer and makes some last
6162
attempts to pick the correct help from __doc__ if no help is present.
6263

63-
Below you can see that the backup inhertiance example :class:`~django_typer.Typer` tree. Each
64-
command class has its own completely separate tree.
64+
Below you can see that the backup inhertiance example :class:`~django_typer.management.Typer` tree.
65+
Each command class has its own completely separate tree.
6566

6667
.. image:: /_static/img/inheritance_tree.png
6768
:align: center

doc/source/extensions.rst

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ Tutorial: Inheritance & Plugins
99
You may need to change the behavior of an
1010
`upstream command <https://en.wikipedia.org/wiki/Upstream_(software_development)>`_ or wish
1111
you could add an additional subcommand or group to it. django-typer_ offers two patterns for
12-
changing or extending the behavior of commands. :class:`~django_typer.TyperCommand` classes
13-
:ref:`support inheritance <inheritance>`, even multiple inheritance. This can be a way to override
14-
or add additional commands to a command implemented elsewhere. You can then use Django's built in
15-
command override precedence (INSTALLED_APPS) to ensure your command is used instead of the upstream
16-
command or give it a different name if you would like the upstream command to still be available.
17-
The :ref:`plugin pattern <plugin>` allows commands and groups to be added or overridden
12+
changing or extending the behavior of commands. :class:`~django_typer.management.TyperCommand`
13+
classes :ref:`support inheritance <inheritance>`, even multiple inheritance. This can be a way to
14+
override or add additional commands to a command implemented elsewhere. You can then use Django's
15+
built in command override precedence (INSTALLED_APPS) to ensure your command is used instead of the
16+
upstream command or give it a different name if you would like the upstream command to still be
17+
available. The :ref:`plugin pattern <plugin>` allows commands and groups to be added or overridden
1818
directly on upstream commands without inheritance. This mechanism is useful when you might expect
1919
other apps to also modify the original command. Conflicts are resolved in INSTALLED_APPS order.
2020

@@ -27,8 +27,8 @@ but we anticipate our command being extended so we may also provide default beha
2727
discover and run every backup routine defined on the command if no specific subroutine is invoked.
2828
We can `use the context <https://typer.tiangolo.com/tutorial/commands/context/#getting-the-context>`_
2929
to determine if a subcommand was called in our root initializer callback and we can find
30-
subroutines added by plugins at runtime using :func:`~django_typer.TyperCommand.get_subcommand`.
31-
Our command might look like this:
30+
subroutines added by plugins at runtime using
31+
:func:`~django_typer.management.TyperCommand.get_subcommand`. Our command might look like this:
3232

3333
.. tabs::
3434

@@ -329,8 +329,8 @@ And the command line parameters to database have been removed:
329329
The extension code is lazily loaded. This means plugins are resolved on command classes
330330
the first time an instance of the class is instantiated. This avoids unnecessary code
331331
execution but does mean that if you are working directly with the ``typer_app`` attribute
332-
on a :class:`~django_typer.TyperCommand` you will need to make sure at least one instance
333-
has been instantiated.
332+
on a :class:`~django_typer.management.TyperCommand` you will need to make sure at least one
333+
instance has been instantiated.
334334

335335

336336
Overriding Groups
@@ -432,7 +432,8 @@ Plugins can be used to group like behavior together under a common root command.
432432
thought of as a way to namespace CLI tools or easily share significant code between tools that have
433433
common initialization logic. Moreover it allows you to do this safely and in a way that can be
434434
deterministically controlled in settings. Most use cases are not this complex and even our backup
435-
example could probably better be implemented as a batch of commands.
435+
example could probably better be implemented as a
436+
`batch of commands <https://github.com/bckohan/django-routines>`_.
436437

437438
Django apps are great for forcing separation of concerns on your code base. In large self contained
438439
projects its often a good idea to break your code into apps that are as self contained as possible.

doc/source/howto.rst

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ Define Multiple Subcommands
9191

9292
Commands with a single executable function should simply implement handle(), but if you would
9393
like have multiple subcommands you can define any number of functions decorated with
94-
:func:`~django_typer.command` or :func:`~django_typer.Typer.command`:
94+
:func:`~django_typer.management.command` or :func:`~django_typer.management.Typer.command`:
9595

9696
.. tabs::
9797

@@ -196,8 +196,8 @@ Lets look at the help output:
196196
Define Groups of Commands
197197
-------------------------
198198

199-
Any depth of command tree can be defined. Use the :func:`~django_typer.group` or
200-
:meth:`~django_typer.Typer.add_typer` decorator to define a group of subcommands:
199+
Any depth of command tree can be defined. Use the :func:`~django_typer.management.group` or
200+
:meth:`~django_typer.management.Typer.add_typer` decorator to define a group of subcommands:
201201

202202

203203
.. tabs::
@@ -225,8 +225,8 @@ Define an Initialization Callback
225225
---------------------------------
226226

227227
You can define an initializer function that takes arguments_ and options_ that will be invoked
228-
before your handle() command or subcommands using the :func:`~django_typer.initialize` decorator.
229-
This is like defining a group at the command root and is an extension of the
228+
before your handle() command or subcommands using the :func:`~django_typer.management.initialize`
229+
decorator. This is like defining a group at the command root and is an extension of the
230230
`typer callback mechanism <https://typer.tiangolo.com/tutorial/commands/callback/>`_.
231231

232232

@@ -264,11 +264,11 @@ This is like defining a group at the command root and is an extension of the
264264
Call Commands from Code
265265
-----------------------
266266

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

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

@@ -303,8 +303,8 @@ Say we have this command, called ``mycommand``:
303303
The rule of thumb is this:
304304

305305
- Use call_command_ if your options and arguments need parsing.
306-
- Use :func:`~django_typer.get_command` and invoke the command functions directly if your
307-
options and arguments are already of the correct type.
306+
- Use :func:`~django_typer.management.get_command` and invoke the command functions directly if
307+
your options and arguments are already of the correct type.
308308

309309
If the second argument is a type, static type checking will assume the return value of get_command
310310
to be of that type:
@@ -325,7 +325,7 @@ You may also fetch a subcommand function directly by passing its path:
325325
326326
.. tip::
327327

328-
Also refer to the :func:`~django_typer.get_command` docs and :ref:`here <default_cmd>`
328+
Also refer to the :func:`~django_typer.management.get_command` docs and :ref:`here <default_cmd>`
329329
and :ref:`here <multi_commands>` for the nuances of calling commands when handle() is and is
330330
not implemented.
331331

@@ -335,15 +335,15 @@ You may also fetch a subcommand function directly by passing its path:
335335
Change Default Django Options
336336
-----------------------------
337337

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

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

348348
.. tabs::
349349

@@ -366,9 +366,9 @@ Configure Typer_ Options
366366
------------------------
367367

368368
Typer_ apps can be configured using a number of parameters. These parameters are usually passed
369-
to the :class:`~django_typer.Typer` class constructor when the application is created.
369+
to the :class:`~django_typer.management.Typer` class constructor when the application is created.
370370
django-typer_ provides a way to pass these options upstream to Typer_ by supplying them as keyword
371-
arguments to the :class:`~django_typer.TyperCommand` class inheritance:
371+
arguments to the :class:`~django_typer.management.TyperCommand` class inheritance:
372372

373373
.. tabs::
374374

@@ -398,9 +398,9 @@ arguments to the :class:`~django_typer.TyperCommand` class inheritance:
398398
399399
.. tip::
400400

401-
See :class:`~django_typer.TyperCommandMeta` or :class:`~django_typer.Typer` for a list of
402-
available parameters. Also refer to the `Typer docs <https://typer.tiangolo.com>`_ for more
403-
details.
401+
See :class:`~django_typer.management.TyperCommandMeta` or
402+
:class:`~django_typer.management.Typer` for a list of available parameters. Also refer to the
403+
`Typer docs <https://typer.tiangolo.com>`_ for more details.
404404

405405

406406
Define Shell Tab Completions for Parameters
@@ -658,8 +658,8 @@ Document Commands w/Sphinx
658658
sphinxcontrib-typer_ can be used to render your rich helps to Sphinx docs and is used extensively
659659
in this documentation.
660660

661-
For example, to document a :class:`~django_typer.TyperCommand` with sphinxcontrib-typer, you would
662-
do something like this:
661+
For example, to document a :class:`~django_typer.management.TyperCommand` with sphinxcontrib-typer,
662+
you would do something like this:
663663

664664
.. code-block:: rst
665665
@@ -698,17 +698,18 @@ There are no unbreakable rules about how you should print output from your comma
698698
You could use loggers, normal print statements or the BaseCommand_ stdout and
699699
stderr output wrappers. Django advises the use of ``self.stdout.write`` because the
700700
stdout and stderr streams can be configured by calls to call_command_ or
701-
:func:`~django_tyoer.get_command` which allows you to easily grab output from your
701+
:func:`~django_typer.management.get_command` which allows you to easily grab output from your
702702
commands for testing. Using the command's configured stdout and stderr
703703
output wrappers also means output will respect the ``--force-color`` and ``--no-color``
704704
parameters.
705705

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

714715
.. tabs::

doc/source/index.rst

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ Django Typer
66
============
77

88
Use static typing to define the CLI for your Django_ management commands with Typer_. Optionally
9-
use the provided :class:`~django_typer.TyperCommand` class that inherits from BaseCommand_. This
10-
class maps the Typer_ interface onto a class based interface that Django developers will be
11-
familiar with. All of the BaseCommand_ functionality is preserved, so that
12-
:class:`~django_typer.TyperCommand` can be a drop in replacement.
9+
use the provided :class:`~django_typer.management.TyperCommand` class that inherits from
10+
BaseCommand_. This class maps the Typer_ interface onto a class based interface that Django
11+
developers will be familiar with. All of the BaseCommand_ functionality is preserved, so that
12+
:class:`~django_typer.management.TyperCommand` can be a drop in replacement.
1313

1414
**django-typer makes it easy to:**
1515

@@ -22,7 +22,7 @@ familiar with. All of the BaseCommand_ functionality is preserved, so that
2222
powershell_.
2323
* :ref:`Create custom and portable shell tab-completions for your CLI parameters.
2424
<define-shellcompletions>`
25-
* Port existing commands (:class:`~django_typer.TyperCommand` is interface compatible
25+
* Port existing commands (:class:`~django_typer.management.TyperCommand` is interface compatible
2626
with BaseCommand_).
2727
* Use either a Django-style class-based interface or the Typer-style interface to define
2828
commands.
@@ -76,15 +76,15 @@ familiar with. All of the BaseCommand_ functionality is preserved, so that
7676
This documentation shows all examples using both the function oriented Typer-style interface
7777
and the class based Django-style interface in separate tabs. Each interface is functionally
7878
equivalent so the choice of which to use is a matter of preference and familiarity. All
79-
django-typer commands are instances of :class:`~django_typer.TyperCommand`, including commands
80-
defined in the Typer-style interface. **This means you may always specify a self argument to
81-
receive the instance of the command in your functions.**
79+
django-typer commands are instances of :class:`~django_typer.management.TyperCommand`,
80+
including commands defined in the Typer-style interface. **This means you may always specify a
81+
self argument to receive the instance of the command in your functions.**
8282

8383
:big:`Basic Example`
8484

85-
:class:`~django_typer.TyperCommand` is a very simple drop in replacement for BaseCommand_. All of
86-
the documented features of BaseCommand_ work the same way! Or, you may also use an interface
87-
identical to Typer_'s. Simply import Typer_ from django_typer instead of typer.
85+
:class:`~django_typer.management.TyperCommand` is a very simple drop in replacement for
86+
BaseCommand_. All of the documented features of BaseCommand_ work the same way! Or, you may also
87+
use an interface identical to Typer_'s. Simply import Typer_ from django_typer instead of typer.
8888

8989
.. tabs::
9090

@@ -158,8 +158,8 @@ command like so:
158158
159159
Any number of groups and subcommands and subgroups of other groups can be defined allowing for
160160
arbitrarily complex command hierarchies. The Typer-style interface builds a
161-
:class:`~django_typer.TyperCommand` class for us **that allows you to optionally accept the self
162-
argument in your commands.**
161+
:class:`~django_typer.management.TyperCommand` class for us **that allows you to optionally accept
162+
the self argument in your commands.**
163163

164164
.. tabs::
165165

0 commit comments

Comments
 (0)