This project includes the PEP-484 compatible "type stubs" for Django APIs. Using a compliant checking tool (typically, mypy), it allows you to document and verify more of your code. Your annotated code will look like:
def vote(request: HttpRequest, question_id: str) -> HttpResponse:
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
return render(request, 'polls/detail.html', {'question': question})
else:
selected_choice.votes += 1
selected_choice.save()
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
If you use incorrect annotations, like in the following example
class ResultsView(generic.DetailView):
model = Question
def get_template_names(self) -> str:
if some_condition():
return "template_a.html"
else:
return "template_b.html"
Running mypy will report the problem:
$ mypy --strict-optional -p polls
...
polls/views.py: note: In class "ResultsView":
polls/views.py:41: error: Return type of "get_template_names" incompatible with supertype "SingleObjectTemplateResponseMixin"
polls/views.py:41: error: Return type of "get_template_names" incompatible with supertype "TemplateResponseMixin"
You'll need to install mypy (other PEP-484 checkers might work, but I haven't tested them).
pip install mypy
should do the trick. There are no other requirements.
This is not a python package (no actual executable code), so this is not installed with pip
or
available in PyPI. You can just git clone
the latest version from
https://github.com/machinalis/mypy-django.git or download and unzip https://github.com/machinalis/mypy-django/archive/master.zip
Once you have your copy, set your MYPYPATH
environment variable to point to the files. For example (in Linux/bash):
$ export MYPYPATH=/home/dmoisset/mypy-django/
$ ls $MYPYPATH
django
$ ls $MYPYPATH/django
conf core http __init__.pyi urls utils views
If you don't see the above (the second line might have a few more items in your computer), check that the path exists, and that it points to the correct level in the directory tree.
We are building this as a tool at Machinalis to improve the quality of the Django projects we build for our clients. Feel free to contact me if you want to hear more about how we use it or how it can be applied. I can be found at dmoisset@machinalis.com or at @dmoisset via Twitter.
In a more general perspective, it makes sense to use static typing for Django given the following:
Any
(i.e. unchecked)manage.py check
are actually type checks. So this fits very well with the
framework philosophyI reimplemented most of the standard Django tutorial with annotations, so you can see how it looks. The code (and a README with some details of problems and solutions found when annotating) are available at https://github.com/machinalis/mypy-django-example
str
to
describe arguments/return values that can be text strings, i.e. unicode).# type: ignore
some messages like:
polls/views.py:1: error: No library stub file for module 'django.db.models.query'
--strict-optional
option; many of the stubs assume
that you do, and you might get some warnings inside the stub files if you don't use it.filter
and
get
queries) or Q
and F
objects are beyond the expressive possibilities of mypy as it is now.BSD. See LICENSE file for details