Integrieren des .pre-commit-Hooks in ein Django-Projekt

Guten Tag!

Mein Name ist Andrey Sobolev und heute werde ich Ihnen erzählen, wie wir den Pre-Commit-Hook für unser Projekt vorbereitet haben.

Einführung


Zunächst ein paar Worte darüber, was Haken im Allgemeinen sind und warum sie möglicherweise benötigt werden. Git out of the box bietet ein Tool, mit dem Sie Ihre Skripte ausführen können, wenn ein Ereignis auftritt (z. B. auf einen Server übertragen usw.).

Pre-Commit ist ein praktisches Add-On zum Standard-Git-Pre-Commit-Hook, der die in beschriebenen Skripte ausführt .pre-commit-config.yaml vor dem Festschreiben. Theoretisch klingt es einfach, lassen Sie uns mit dem Üben fortfahren.

Installation


Stellen Sie die erforderlichen Abhängigkeiten ein:

pre-commit
#   https://pre-commit.com/

autoflake
#     (  )
black
#  
pyupgrade
#     
reorder-python-imports
#   
yesqa
#   noqa  ( )

# 
flake8
flake8-annotations
flake8-annotations-coverage
flake8-bandit
flake8-broken-line
flake8-bugbear
flake8-builtins
flake8-commas
flake8-comprehensions
flake8-debugger
flake8-eradicate
flake8-executable
flake8-fixme
flake8-future-import
flake8-pyi
flake8-pytest
flake8-pytest-style
flake8-mutable
flake8-string-format
flake8-todo
flake8-unused-arguments

# 
pytest

Ich werde meine Meinung zu Flake-8 und Linter im Allgemeinen äußern. Wenn Sie bereits ein großes Projekt mit einer Reihe von Legacy-Code haben, können Sie Linters sicher löschen. Die Kosten, die für die "Idealisierung" aufgewendet werden, werden die Behörden nicht zu schätzen wissen. Wir setzen Linters für neue (und kleine) Projekte. Ich wiederhole, das ist meine persönliche Meinung, ich lege sie niemandem auf.

Umgebungsintegration


Wir gehen in das Stammverzeichnis der Entwicklungsumgebung und führen die folgenden Befehle aus

$ pre-commit install
pre-commit installed at .git/hooks/pre-commit
$ pre-commit --version
pre-commit 2.4.0

Wenn .pre-commit auf sqlite schwört, müssen Sie es installieren (z. B. $ yum install sqlite) und Python erneut kompilieren

Einrichten der Datei .pre-commit-config.yaml


Erstellen Sie im Stammverzeichnis der Umgebung die Datei .pre-commit-config.yaml

- repo: https://github.com/pre-commit/pre-commit-hooks
  rev: "v2.5.0"
  hooks:
    - id: check-merge-conflict
    - id: debug-statements

- repo: local

  hooks:
    - id: black
      name: black
      entry: black
      language: system
      types: [python]
      args: [--line-length=200, --target-version=py37]

    - id: autoflake
      name: autoflake
      entry: autoflake
      language: system
      types: [python]
      args: [--in-place, --remove-all-unused-imports, --remove-duplicate-keys]

    # -   id: flake8
    #     name: flake8
    #     entry: flake8
    #     language: system
    #     types: [python]
    #     args: [
    #         "--ignore=E203,W503,FI10,FI11,FI12,FI13,FI14,FI15,FI16,FI17,FI58,PT013",
    #         # black
    #             # E203 whitespace before ':'
    #             # W503 line break before binary operator
    #         # flake8-future-import
    #             # FI10 __future__ import "division" missing
    #             # FI11 __future__ import "absolute_import" missing
    #             # FI12 __future__ import "with_statement" missing
    #             # FI13 __future__ import "print_function" missing
    #             # FI14 __future__ import "unicode_literals" missing
    #             # FI15 __future__ import "generator_stop" missing
    #             # FI16 __future__ import "nested_scopes" missing
    #             # FI17 __future__ import "generators" missing
    #             # FI58 __future__ import "annotations" present
    #         # flake8-pytest-style
    #             # PT013 found incorrect import of pytest, use simple 'import pytest' instead
    #         "--max-line-length=110",
    #         "--per-file-ignores=tests/*.py:S101"
    #         # S101 Use of assert detected
    #     ]

    - id: pyupgrade
      name: pyupgrade
      entry: pyupgrade
      language: system
      types: [python]
      args: [--py37-plus]

    - id: reorder-python-imports
      name: reorder-python-imports
      entry: reorder-python-imports
      language: system
      types: [python]
      args: [--py37-plus]

    - id: yesqa
      name: yesqa
      entry: yesqa
      language: system
      types: [python]

    - id: tests
      name: Run tests
      entry: "bash tests.sh"
      language: system
      verbose: true


Tests


Zusätzlich zum Überprüfen und Formatieren des Codes führen wir Tests in der Phase der Erstellung des Commits durch. Dazu verwenden wir pytest (https://docs.pytest.org/en/latest/) und konfigurieren es für unsere Bedürfnisse.

Erstellen Sie im Stammverzeichnis der Umgebung den
Testordner und legen Sie die folgenden Dateien test_example_without_db.py, test_example_with_db.py dort ab

. Konfigurieren Sie die Tests so, dass Sie die aktuelle Datenbank verwenden können (z. B. eine Kopie der Datenbank vom Battle Server), und erstellen Sie nicht jedes Mal eine neue .

Einfacher Test test_example_without_db.py

def inc(x):
    return x + 1

def test_answer():
    assert inc(3) == 4

In einfachen Tests können wir zum Beispiel einen Webbot verbinden und die Knoten unseres Systems umgehen, um die manuelle Arbeit des Testers zu automatisieren.

Testen Sie mit der Datenbank test_example_with_db.py

import pytest
from chat.models import ChatRoom
from settings import POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD, \
                    POSTGRES_HOST, POSTGRES_PORT

@pytest.fixture(scope='session')
def django_db_setup():
    settings.DATABASES['default'] = {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': POSTGRES_DB,
        'USER': POSTGRES_USER,
        'PASSWORD': POSTGRES_PASSWORD,
        'HOST': POSTGRES_HOST,
        'PORT': POSTGRES_PORT,
    }   
    
    
@pytest.fixture
def db_access_without_rollback_and_truncate(request, django_db_setup, django_db_blocker):
    django_db_blocker.unblock()
    request.addfinalizer(django_db_blocker.restore)

def chat():
    return ChatRoom.objects.all().count()

@pytest.mark.django_db
def test_chat():
    assert chat() > 0

Das Beispiel ist ziemlich künstlich und wurde exklusiv für diesen Hinweis erstellt. Es ermöglicht uns jedoch, auf die aktuelle Datenbank zuzugreifen und komplexe Tests durchzuführen, die über manuelle Tests hinausgehen.

Wir schließen Tests in .pre-commit ein


Um die Tests zu verbinden, benötigen wir ein Shell-Skript im Stammverzeichnis der Umgebung, das wir tests.sh nennen:

source ../../python38_env/bin/activate && python -m pytest -v tests

Der Inhalt ist sehr offensichtlich, aber Sie können feststellen, dass die Aktivierung der virtuellen Umgebung explizit im Code geschrieben ist. Dies kann unpraktisch sein, wenn Ihr Team auf verschiedenen Workstations entwickelt (z. B. wer die Umgebung auf einem lokalen Computer bereitgestellt hat und jemand auf einem Server entwickelt).

Sie können dieses Problem durch Variablen im Implementierungsbeispiel .env lösen

:

github.com/Sobolev5/starlette-vue-backend/blob/master/.env.example (beachten Sie die Variable ENV_ACTIVATE)

github.com/Sobolev5/starlette-vue-backend /blob/master/tests.sh ( ENV_ACTIVATE analysieren und Umgebung aktivieren)

Erstellen Sie ein Commit


Jetzt bleibt ein Commit zu erstellen und zu sehen, wie es funktioniert

$ git add .
$ git commit -m Sobolev:TestPreCommitHook
Check for merge conflicts................................................Passed
Debug Statements (Python)................................................Passed
black....................................................................Failed
- hook id: black
- files were modified by this hook

reformatted /var/www/file.py
All done!   
1 file reformatted, 2 files left unchanged.

autoflake................................................................Passed
pyupgrade................................................................Passed
reorder-python-imports...................................................Failed
- hook id: reorder-python-imports
- exit code: 1
- files were modified by this hook

Reordering imports in file.py

yesqa....................................................................Passed
Run tests................................................................Passed
- hook id: tests
- duration: 2.85s

tests/test_example_with_db.py::test_chat PASSED                          [ 66%]
tests/test_example_without_db.py::test_answer PASSED                     [100%]

Ein Commit wird jetzt in zwei Schritten erstellt. In der ersten Phase formatieren Hooks den Code. Nachdem sie funktionieren, müssen wir nur die Befehle „wiederholen“.

Es stellt sich die folgende Reihenfolge heraus.

$ git add .
$ git commit -m Sobolev:TestPreCommitHook
$ git add .
$ git commit -m Sobolev:TestPreCommitHook

Das ist alles, danke für Ihre Aufmerksamkeit.

Sitelinks


Vollständige Liste der Haken

All Articles