Skip to content

Commit a270e6e

Browse files
authored
Merge pull request #32 from bckohan/pyright
Pyright
2 parents f36da3c + 97d2695 commit a270e6e

File tree

9 files changed

+179
-123
lines changed

9 files changed

+179
-123
lines changed

.github/workflows/lint.yml

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,31 @@ jobs:
99
strategy:
1010
matrix:
1111
# run static analysis on bleeding and trailing edges
12-
python-version: [ '3.8', '3.12' ]
12+
python-version: [ '3.8', '3.10', '3.12' ]
1313
django-version:
14-
- 'Django~=3.2.0' # LTS April 2024
15-
- 'Django~=4.2.0' # LTS April 2026
14+
- '3.2' # LTS April 2024
15+
- '4.2' # LTS April 2026
16+
- '5.0' # April 2025
1617
exclude:
1718
- python-version: '3.8'
18-
django-version: 'Django~=4.2.0'
19+
django-version: '4.2'
1920
- python-version: '3.12'
20-
django-version: 'Django~=3.2.0'
21+
django-version: '4.2'
22+
- python-version: '3.12'
23+
django-version: '3.2'
24+
- python-version: '3.10'
25+
django-version: '3.2'
26+
- python-version: '3.8'
27+
django-version: '5.0'
28+
- python-version: '3.10'
29+
django-version: '5.0'
2130

2231
steps:
2332
- uses: actions/checkout@v4
2433
- name: Set up Python ${{ matrix.python-version }}
2534
uses: actions/setup-python@v5
2635
with:
2736
python-version: ${{ matrix.python-version }}
28-
2937
- name: Install Poetry
3038
uses: snok/install-poetry@v1
3139
with:
@@ -36,15 +44,20 @@ jobs:
3644
poetry config virtualenvs.in-project true
3745
poetry run pip install --upgrade pip
3846
poetry install
39-
poetry run pip install -U "${{ matrix.django-version }}"
47+
poetry run pip install -U "django~=${{ matrix.django-version }}"
4048
- name: Run Static Analysis
4149
run: |
42-
poetry run isort django_typer --check
43-
poetry run black django_typer --check
44-
poetry run pylint django_typer
45-
poetry run mypy django_typer
50+
source .venv/bin/activate
51+
isort django_typer --check
52+
black django_typer --check
53+
pylint django_typer
54+
mypy django_typer
4655
poetry check
47-
poetry run pip check
48-
poetry run python -m readme_renderer ./README.rst -o /tmp/README.html
56+
pip check
57+
python -m readme_renderer ./README.rst -o /tmp/README.html
4958
cd ./doc
50-
poetry run doc8 --ignore-path build --max-line-length 100
59+
doc8 --ignore-path build --max-line-length 100
60+
echo "$(poetry env info --path)/bin" >> $GITHUB_PATH
61+
62+
- name: Run pyright
63+
uses: jakebailey/pyright-action@v2

CONTRIBUTING.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
.. _readthedocs: https://readthedocs.org/
99
.. _me: https://github.com/bckohan
1010
.. _black: https://black.readthedocs.io/en/stable/
11+
.. _pyright: https://github.com/microsoft/pyright
1112

1213
Contributing
1314
############
@@ -51,7 +52,7 @@ then run Sphinx_:
5152
Static Analysis
5253
---------------
5354

54-
`django-typer` uses Pylint_ for python linting and mypy_ for static type
55+
`django-typer` uses Pylint_ for python linting and mypy_ and pyright_ for static type
5556
checking. Header imports are also standardized using isort_ and formatting is
5657
done with black_. Before any PR is accepted the following must be run, and
5758
static analysis tools should not produce any errors or warnings. Disabling
@@ -63,6 +64,7 @@ certain errors or warnings where justified is acceptable:
6364
poetry run black django_typer
6465
poetry run pylint django_typer
6566
poetry run mypy django_typer
67+
pyright
6668
poetry check
6769
poetry run pip check
6870
poetry run python -m readme_renderer ./README.rst

django_typer/__init__.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
from django.core.management import get_commands
7777
from django.core.management.base import BaseCommand, CommandError
7878
from django.core.management.base import OutputWrapper as BaseOutputWrapper
79+
from django.core.management.color import Style as ColorStyle
7980
from django.db.models import Model
8081
from django.utils.translation import gettext as _
8182

@@ -89,15 +90,21 @@
8990
from typer.core import TyperGroup as CoreTyperGroup
9091
from typer.main import get_command as get_typer_command
9192
from typer.main import get_params_convertors_ctx_param_name_from_function
92-
from typer.models import CommandFunctionType
9393
from typer.models import Context as TyperContext
9494
from typer.models import Default, DefaultPlaceholder
9595

9696
from .completers import ModelObjectCompleter
9797
from .parsers import ModelObjectParser
98-
from .types import ForceColor, NoColor, PythonPath, Settings, SkipChecks
99-
from .types import Style as ColorStyle
100-
from .types import Traceback, Verbosity, Version
98+
from .types import (
99+
ForceColor,
100+
NoColor,
101+
PythonPath,
102+
Settings,
103+
SkipChecks,
104+
Traceback,
105+
Verbosity,
106+
Version,
107+
)
101108
from .utils import _command_context, traceback_config, with_typehint
102109

103110
VERSION = (1, 0, 1)
@@ -713,7 +720,7 @@ def subcommand(self):
713720
this can be used to group commands into panels in the help output.
714721
"""
715722

716-
def create_app(func: CommandFunctionType):
723+
def create_app(func: t.Callable[..., t.Any]):
717724
grp = GroupFunction( # type: ignore
718725
name=name,
719726
cls=cls,
@@ -855,7 +862,7 @@ def divide(
855862
this can be used to group commands into panels in the help output.
856863
"""
857864

858-
def decorator(func: CommandFunctionType):
865+
def decorator(func: t.Callable[..., t.Any]):
859866
setattr(
860867
func,
861868
"_typer_callback_",
@@ -958,7 +965,7 @@ def other_command(self):
958965
this can be used to group commands into panels in the help output.
959966
"""
960967

961-
def decorator(func: CommandFunctionType):
968+
def decorator(func: t.Callable[..., t.Any]):
962969
setattr(
963970
func,
964971
"_typer_command_",
@@ -1071,7 +1078,7 @@ def subcommand(self):
10711078
this can be used to group commands into panels in the help output.
10721079
"""
10731080

1074-
def create_app(func: CommandFunctionType):
1081+
def create_app(func: t.Callable[..., t.Any]):
10751082
grp = GroupFunction( # type: ignore
10761083
name=name,
10771084
cls=cls,
@@ -1199,8 +1206,9 @@ def __new__(
11991206
This method is called when a new class is created.
12001207
"""
12011208
try:
1202-
TyperCommand # pylint: disable=pointless-statement
1203-
is_base_init = False
1209+
# avoid unnecessary work creating the TyperCommand class
1210+
# is there a less weird way to do this?
1211+
is_base_init = not TyperCommand
12041212
except NameError:
12051213
is_base_init = True
12061214
typer_app = None
@@ -1267,7 +1275,7 @@ def __init__(cls, name, bases, attrs, **kwargs):
12671275
for arg in cls.suppressed_base_arguments or []
12681276
} # per django docs - allow these to be specified by either the option or param name
12691277

1270-
def get_ctor(attr: str) -> t.Optional[t.Callable[..., t.Any]]:
1278+
def get_ctor(attr: t.Any) -> t.Optional[t.Callable[..., t.Any]]:
12711279
return getattr(
12721280
attr, "_typer_command_", getattr(attr, "_typer_callback_", None)
12731281
)
@@ -1671,19 +1679,20 @@ def command2(self, option: t.Optional[str] = None):
16711679
style: ColorStyle
16721680
stdout: BaseOutputWrapper
16731681
stderr: BaseOutputWrapper
1674-
requires_system_checks: t.Union[t.Sequence[str], str]
1682+
# requires_system_checks: t.Union[t.List[str], t.Tuple[str, ...], t.Literal['__all__']]
16751683

16761684
# we do not use verbosity because the base command does not do anything with it
16771685
# if users want to use a verbosity flag like the base django command adds
16781686
# they can use the type from django_typer.types.Verbosity
1679-
suppressed_base_arguments: t.Optional[t.Iterable[str]] = {"verbosity"}
1687+
suppressed_base_arguments = {"verbosity"}
16801688

16811689
typer_app: Typer
16821690
no_color: bool = False
16831691
force_color: bool = False
16841692
_num_commands: int = 0
16851693
_has_callback: bool = False
16861694
_root_groups: int = 0
1695+
_handle: t.Callable[..., t.Any]
16871696

16881697
command_tree: CommandNode
16891698

@@ -1701,8 +1710,8 @@ def __exit__(self, exc_type, exc_val, exc_tb):
17011710

17021711
def __init__(
17031712
self,
1704-
stdout: t.Optional[t.IO[str]] = None,
1705-
stderr: t.Optional[t.IO[str]] = None,
1713+
stdout: t.Optional[t.TextIO] = None,
1714+
stderr: t.Optional[t.TextIO] = None,
17061715
no_color: bool = no_color,
17071716
force_color: bool = force_color,
17081717
**kwargs,
@@ -1795,7 +1804,9 @@ def __init_subclass__(cls, **_):
17951804
"""Avoid passing typer arguments up the subclass init chain"""
17961805
return super().__init_subclass__()
17971806

1798-
def create_parser(self, prog_name: str, subcommand: str, **_):
1807+
def create_parser( # pyright: ignore[reportIncompatibleMethodOverride]
1808+
self, prog_name: str, subcommand: str, **_
1809+
):
17991810
"""
18001811
Create a parser for this command. This also sets the command
18011812
context, so any functions below this call on the stack may
@@ -1862,7 +1873,7 @@ def handle(self, option1: bool, option2: bool):
18621873
:param kwargs: the options to directly pass to handle()
18631874
"""
18641875
with self:
1865-
if getattr(self, "_handle", None):
1876+
if getattr(self, "_handle", None) and callable(self._handle):
18661877
return self._handle(*args, **kwargs)
18671878
raise NotImplementedError(
18681879
_(

django_typer/apps.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@
2424

2525
try:
2626
import rich
27+
from rich import traceback
2728

2829
tb_config = traceback_config()
2930
if rich and isinstance(tb_config, dict) and not tb_config.get("no_install", False):
3031
# install rich tracebacks if we've been configured to do so (default)
3132
no_color = "NO_COLOR" in os.environ
3233
force_color = "FORCE_COLOR" in os.environ
33-
rich.traceback.install(
34+
traceback.install(
3435
console=tb_config.pop(
3536
"console",
3637
(
@@ -48,8 +49,7 @@
4849
**{
4950
param: value
5051
for param, value in tb_config.items()
51-
if param
52-
in set(inspect.signature(rich.traceback.install).parameters.keys())
52+
if param in set(inspect.signature(traceback.install).parameters.keys())
5353
},
5454
)
5555
except ImportError:
@@ -62,7 +62,7 @@ def check_traceback_config(app_configs, **kwargs) -> t.List[CheckMessage]:
6262
A system check that validates that the traceback config is valid and
6363
contains only the expected parameters.
6464
"""
65-
warnings = []
65+
warnings: t.List[CheckMessage] = []
6666
tb_cfg = traceback_config()
6767
if isinstance(tb_cfg, dict):
6868
if rich:

django_typer/completers.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,9 @@ def __init__(
255255
distinct: bool = distinct,
256256
):
257257
self.model_cls = model_cls
258-
self.lookup_field = lookup_field or model_cls._meta.pk.name
258+
self.lookup_field = str(
259+
lookup_field or getattr(self.model_cls._meta.pk, "name", "id")
260+
)
259261
self.help_field = help_field
260262
self.limit = limit
261263
self.case_insensitive = case_insensitive
@@ -322,9 +324,9 @@ def __call__(
322324
],
323325
help=getattr(obj, self.help_field, None) if self.help_field else "",
324326
)
325-
for obj in self.model_cls.objects.filter(completion_qry).distinct()[
326-
0 : self.limit
327-
]
327+
for obj in getattr(self.model_cls, "objects")
328+
.filter(completion_qry)
329+
.distinct()[0 : self.limit]
328330
if (
329331
getattr(obj, self.lookup_field) is not None
330332
and str(getattr(obj, self.lookup_field))

0 commit comments

Comments
 (0)