دمج ربط .pre- الالتزام في مشروع Django

يوم جيد!

اسمي أندري سوبوليف واليوم سأخبرك كيف أعددنا خطاف ما قبل الالتزام في مشروعنا.

المقدمة


بادئ ذي بدء ، بضع كلمات حول ماهية الخطافات بشكل عام ولماذا قد تكون هناك حاجة إليها. يوفر Git out of the box أداة يمكنها تشغيل البرامج النصية الخاصة بك عند حدوث حدث (على سبيل المثال ، الدفع إلى خادم ، وما إلى ذلك). يعد التنفيذ

المسبق إضافة ملائمة إلى ربط git المسبق الافتراضي git الذي يقوم بتشغيل البرامج النصية الموضحة في .pre-الالتزام-config.yaml قبل الالتزام. من الناحية النظرية ، يبدو الأمر بسيطًا ، دعنا ننتقل إلى الممارسة.

التركيب


قم بتعيين التبعيات اللازمة:

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

سوف أعبر عن رأيي حول flake-8 و linter بشكل عام. إذا كان لديك بالفعل مشروع كبير يحتوي على مجموعة من التعليمات البرمجية القديمة ، فيمكنك حينئذٍ حذف الوبر. التكاليف التي سيتم إنفاقها على "تحقيق المثل الأعلى" ، لن تقدر السلطات. نضع زبالة لمشاريع جديدة (وصغيرة). أكرر ، هذا رأيي الشخصي ، أنا لا أفرضه على أي شخص.

تكامل البيئة


ننتقل إلى الدليل الجذر لبيئة التطوير وننفذ الأوامر التالية

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

إذا أقسم .pre-الالتزام على sqlite ، فستحتاج إلى تثبيته (على سبيل المثال $ yum install sqlite) وإنشاء python مرة أخرى

إعداد ملف .pre-الالتزام-config.yaml


قم بإنشاء الملف .pre-الالتزام-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


الاختبارات


بالإضافة إلى التحقق من الكود وتنسيقه ، سنقوم بإجراء الاختبارات في مرحلة إنشاء الالتزام. للقيام بذلك ، سوف نستخدم pytest (https://docs.pytest.org/en/latest/) ونقوم بتهيئته لاحتياجاتنا.

في الدليل الجذر للبيئة ، قم بإنشاء مجلد الاختبارات ووضع الملفات التالية
test_example_without_db.py ، test_example_with_db.py من

أجل الراحة ، قم بتكوين الاختبارات بحيث يمكنك استخدام قاعدة البيانات الحالية (على سبيل المثال ، نسخة من قاعدة البيانات من خادم المعركة) ، ولا تنشئ واحدة جديدة في كل مرة .

اختبار بسيط test_example_without_db.py

def inc(x):
    return x + 1

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

في الاختبارات البسيطة ، يمكننا الاتصال على سبيل المثال webbot وتجاوز عقد نظامنا لأتمتة العمل اليدوي للمختبر.

اختبار باستخدام قاعدة بيانات 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

المثال مصطنع تمامًا وتم إنشاؤه حصريًا لهذه الملاحظة ، ولكنه مع ذلك يسمح لنا بالوصول إلى قاعدة البيانات الحالية وإجراء اختبارات معقدة تتجاوز الاختبارات اليدوية.

ندرج الاختبارات في .pre-ارتكاب


لربط الاختبارات ، نحتاج إلى نص برمجي shell في الدليل الجذر للبيئة ، والذي سنطلق عليه الاختبارات. sh:

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

محتواه واضح للغاية ، ولكن قد تلاحظ أن تنشيط البيئة الافتراضية مكتوب بشكل صريح في التعليمات البرمجية. قد يكون هذا غير مريح إذا كان فريقك يعمل على تطوير محطات عمل مختلفة (على سبيل المثال ، من نشر البيئة على جهاز محلي ، وقام شخص ما بالتطوير على خادم).

يمكنك حل هذه المشكلة من خلال المتغيرات في .env تنفيذ

سبيل المثال:

github.com/Sobolev5/starlette-vue-backend/blob/master/.env.example (لاحظ ENV_ACTIVATE متغير)

github.com/Sobolev5/starlette-vue-backend /blob/master/tests.sh (تحليل ENV_ACTIVATE وتنشيط البيئة)

إنشاء التزام


الآن يبقى إنشاء التزام ومعرفة كيف يعمل

$ 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%]

يتم إنشاء الالتزام الآن في خطوتين. في المرحلة الأولى ، تقوم الخطافات بتنسيق الرمز ، لذلك بعد عملهم نحتاج فقط إلى "تكرار" الأوامر.

اتضح التسلسل التالي.

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

هذا كل شيء ، شكرا لاهتمامكم.

روابط أقسام الموقع


القائمة الكاملة للخطافات

All Articles