Skip to content

ExpressApp/smartapp-template

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

30 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

SmartApp Template Tutorial

Π’Π²Π΅Π΄Π΅Π½ΠΈΠ΅

ℹ️ Π˜Π½Ρ„ΠΎ
Для взаимодСйствия с ΠΏΠ»Π°Ρ‚Ρ„ΠΎΡ€ΠΌΠΎΠΉ botx ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° pybotx. Π’ Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ Π΅Ρ‘ использования. Для Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ SmartApp ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° pybotx-smartapp-rpc. ΠŸΠ΅Ρ€Π΅Π΄ ΠΏΡ€ΠΎΡ‡Ρ‚Π΅Π½ΠΈΠ΅ΠΌ Π΄Π°Π½Π½ΠΎΠ³ΠΎ Ρ‚ΡƒΡ‚ΠΎΡ€ΠΈΠ°Π»Π° слСдуСт с Π½Π΅ΠΉ ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΡ‚ΡŒΡΡ.


Π¨Π°Π±Π»ΠΎΠ½ Ρ€Π΅ΡˆΠ°Π΅Ρ‚ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ написания ΠΏΠΎΠ²Ρ‚ΠΎΡ€ΡΡŽΡ‰Π΅Π³ΠΎ ΠΊΠΎΠ΄Π° Π² самом Π½Π°Ρ‡Π°Π»Π΅ Ρ€Π°Π±ΠΎΡ‚Ρ‹ Π½Π°Π΄ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠΌ. Π£ΠΆΠ΅ сущСствуСт шаблон для Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ Π±ΠΎΡ‚ΠΎΠ² bot-template

Но структура Π±ΠΎΡ‚Π° ΠΈ SmartApp отличаСтся Π΄Ρ€ΡƒΠ³ ΠΎΡ‚ Π΄Ρ€ΡƒΠ³Π°, поэтому Π±Ρ‹Π» создан этот шаблон.


1. Π Π°Π·Π²Π΅Ρ€Ρ‚Ρ‹Π²Π°Π½ΠΈΠ΅ ΠΈΠ· шаблона ΠΈ структура ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°

Для развСртывания ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ copier ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ:

$ copier smartapp-template smartapp-example

Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° шаблонного SmartApp состоит ΠΈΠ· Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΡ… ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ² ΠΈ ΠΌΠΎΠ΄ΡƒΠ»Π΅ΠΉ:

.
β”œβ”€β”€ app
β”‚   β”œβ”€β”€ api            - рСализация http Ρ€ΠΎΡƒΡ‚ΠΎΠ² для прилоТСния, Π²ΠΊΠ»ΡŽΡ‡Π°Ρ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹Π΅ для smartapp
β”‚   β”œβ”€β”€ bot            - ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ Π±ΠΎΡ‚Π° ΠΈ Π²ΡΠΏΠΎΠΌΠΎΠ³Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ для Π½ΠΈΡ…
β”‚   β”œβ”€β”€ caching        - классы ΠΈ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с in-memory Π‘Π”
β”‚   β”œβ”€β”€ db             - ΠΌΠΎΠ΄Π΅Π»ΠΈ, Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с Π‘Π” ΠΈ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ
β”‚   β”œβ”€β”€ schemas        - сСриализаторы, Π΅Π½Π°ΠΌΡ‹, Π΄ΠΎΠΌΠ΅Π½Π½Ρ‹Π΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ
β”‚   β”œβ”€β”€ services       - сСрвисы с Π»ΠΎΠ³ΠΈΠΊΠΎΠΉ (бизнСс-Π»ΠΎΠ³ΠΈΠΊΠ°)
β”‚   β”œβ”€β”€ smartapp       - rpc - ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ ΠΈ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ для smartapp
β”‚   β”œβ”€β”€ smartapp-files  - статичСскиС Ρ„Π°ΠΉΠ»Ρ‹
β”‚   β”œβ”€β”€ constants.py   - константы
β”‚   β”œβ”€β”€ logger.py      - Π»ΠΎΠ³Π³Π΅Ρ€
β”‚   β”œβ”€β”€ main.py        - запуск сСрвСра с ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠ΅ΠΉ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹Ρ… сСрвисов
β”‚   └── settings.py    - настройки прилоТСния
β”œβ”€β”€ extensions         - Π²ΡΠΏΠΎΠΌΠΎΠ³Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΡ для шаблона
β”œβ”€β”€ scripts            - скрипты для запуска тСстов, Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅Ρ€ΠΎΠ², Π»ΠΈΠ½Ρ‚Π΅Ρ€ΠΎΠ²
β”œβ”€β”€ tests              - тСсты, структура ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… соотвСтствуСт структурС ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°, ΠΈ Ρ…Π΅Π»ΠΏΠ΅Ρ€Ρ‹ для Π½ΠΈΡ…
β”œβ”€β”€ poetry.lock        - конфигурация Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΡ… зависимостСй. ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для ΠΈΡ… установки
β”œβ”€β”€ pyproject.toml     - конфигурация зависимостСй, ΠΌΠ΅Ρ‚Π° информация ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° (Π½Π°Π·Π²Π°Π½ΠΈΠ΅, вСрсия, Π°Π²Ρ‚ΠΎΡ€Ρ‹ ΠΈ Ρ‚.ΠΏ.)
└── setup.cfg          - конфигурация Π»ΠΈΠ½Ρ‚Π΅Ρ€ΠΎΠ² ΠΈ тСстов

2. Запуск ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°

Настройка окруТСния

  1. УстанавливаСм зависимости ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° Ρ‡Π΅Ρ€Π΅Π· poetry:
$ poetry install
  1. ΠžΠΏΡ€Π΅Π΄Π΅Π»ΡΠ΅ΠΌ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ окруТСния Π² Ρ„Π°ΠΉΠ»Π΅ .env. ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ… окруТСния находятся Π² Ρ„Π°ΠΉΠ»Π΅ example.env.
  2. ЗапускаСм postges ΠΈ redis ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ docker-compose:
$ docker-compose -f docker-compose.dev.yml up -d
  1. ΠŸΡ€ΠΈΠΌΠ΅Π½ΡΠ΅ΠΌ всС ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ для ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Ρ‚Π°Π±Π»ΠΈΡ† с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ alembic:
$ alembic upgrade head
  1. ЗапускаСм SmartApp ΠΊΠ°ΠΊ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ FastAPI Ρ‡Π΅Ρ€Π΅Π· gunicorn. Π€Π»Π°Π³ --reload ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΡ€ΠΈ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ для автоматичСского пСрСзапуска сСрвСра ΠΏΡ€ΠΈ измСнСниях Π² ΠΊΠΎΠ΄Π΅:
$ gunicorn "app.main:get_application()" --worker-class uvicorn.workers.UvicornWorker

По нСобходимости Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Ρ„Π»Π°Π³ --workers ΠΈ ΠΈΡ… колличСство, Π² Π΄Π°Π½Π½ΠΎΠΌ случаС 4 Ρ€Π°Π±ΠΎΡ‡ΠΈΡ… процСсса:

$ gunicorn "app.main:get_application()" --worker-class uvicorn.workers.UvicornWorker --workers 4

3. Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½ΠΎΠ²ΠΎΠ³ΠΎ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»Π°

3.1. ΠšΠΎΠΌΠ°Π½Π΄Ρ‹ SmartApp

Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° ΠΏΠ°ΠΊΠ΅Ρ‚Π° ΠΊΠΎΠΌΠ°Π½Π΄

ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ находятся Π² ΠΏΠ°ΠΊΠ΅Ρ‚Π΅ app.smartapp.rpc_methods ΠΈ Π³Ρ€ΡƒΠΏΠΏΠΈΡ€ΡƒΡŽΡ‚ΡΡ Π² ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Π΅ ΠΌΠΎΠ΄ΡƒΠ»ΠΈ Π² зависимости ΠΎΡ‚ Π»ΠΎΠ³ΠΈΠΊΠΈ. ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ Π΄ΠΎΠ±Π°Π²Π»ΡΡŽΡ‚ΡΡ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ RPCRouter.

Если Π² ΠΌΠΎΠ΄ΡƒΠ»Π΅ становится слишком ΠΌΠ½ΠΎΠ³ΠΎ ΠΊΠΎΠΌΠ°Π½Π΄, слСдуСт Ρ€Π°Π·Π±ΠΈΡ‚ΡŒ Π΅Π³ΠΎ Π½Π° Π½ΠΎΠ²Ρ‹Π΅ ΠΌΠΎΠ΄ΡƒΠ»ΠΈ ΠΈ ΡΠ»ΠΎΠΆΠΈΡ‚ΡŒ Π² ΠΎΠ΄ΠΈΠ½ ΠΏΠ°ΠΊΠ΅Ρ‚ с Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ старого модуля. НапримСр, Ρ‚Π°ΠΊ:

smartapp
β”œβ”€β”€ rpc_methods
β”‚   β”œβ”€β”€ common.py
β”‚   β”œβ”€β”€ jira
β”‚       β”œβ”€β”€ projects.py
β”‚       β”œβ”€β”€ issues.py

РСгистрация ΠΊΠΎΠΌΠ°Π½Π΄

Для добавлСния модуля с ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°ΠΌΠΈ Π½ΡƒΠΆΠ½ΠΎ ΠΈΠΌΠΏΠΎΡ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π΅Π³ΠΎ ΠΈΠ· модуля Π½ΡƒΠΆΠ½ΠΎΠ³ΠΎ Π²Π°ΠΌ модуля Π² app/smartapp/smartapp.py ΠΈ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π΅Π³ΠΎ Π² routers smartapp:

from pybotx_smartapp_rpc import SmartAppRPC

from app.smartapp.middlewares.smartlogger import smart_logger_middleware
from app.smartapp.rpc_methods import common, jira

smartapp = SmartAppRPC(
    routers=[common.rpc, jira.rpc],
    middlewares=[smart_logger_middleware],
)

3.2. ВзаимодСйствиС с Π‘Π”

Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π½ΠΎΠ²Ρ‹Ρ… ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ

Π’Π·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡ‚Π²ΠΎΠ²Π°Ρ‚ΡŒ с Π½ΠΎΠ²Ρ‹ΠΌΠΈ Ρ‚Π°Π±Π»ΠΈΡ†Π°ΠΌΠΈ ΠΌΠΎΠΆΠ½ΠΎ Ρ‡Π΅Ρ€Π΅Π· ΠΌΠΎΠ΄Π΅Π»ΠΈ sqlalchemy. Π‘ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π°ΠΌΠΈ использования ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΡ‚ΡŒΡΡ Ρ‚ΡƒΡ‚. МодСли Ρ€Π°ΡΠΏΠΎΠ»Π°Π³Π°ΡŽΡ‚ΡΡ Π² ΠΏΠ°ΠΊΠ΅Ρ‚Π΅ app.db.package_name. Π’Π°ΠΌ ΠΆΠ΅ хранятся crud Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ ΠΈ Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ. Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° ΠΏΠ°ΠΊΠ΅Ρ‚Π° выглядит ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ:

β”œβ”€β”€ app
β”‚   β”œβ”€β”€ db 
β”‚       β”œβ”€β”€ migrations
β”‚       β”œβ”€β”€ exampleapp
β”‚           β”œβ”€β”€ repo.py - Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ/crud Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ
β”‚           β”œβ”€β”€ models.py - ΠΌΠΎΠ΄Π΅Π»ΠΈ Ρ‚Π°Π±Π»ΠΈΡ†

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ ΠΌΠΎΠ΄Π΅Π»ΠΈ:

from sqlalchemy import Column, Integer, String
from app.db.sqlalchemy import Base

class ExampleModel(Base):
    __tablename__ = "examples"

    id: int = Column(Integer, primary_key=True, autoincrement=True)
    text: str = Column(String)

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ рСпозитория:

from sqlalchemy import insert
from app.db.sqlalchemy import session
from app.db.example.models import ExampleModel

class ExampleRepo:
    async def create(self, text: str) -> None:
        query = insert(ExampleModel).values(text=text)
        async with session.begin():
            await session.execute(query)

Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π½ΠΎΠ²Ρ‹Ρ… ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΉ

Для Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΉ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ alembic. ВсС Ρ„Π°ΠΉΠ»Ρ‹ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ хранятся Π² Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠΈ app.db.migrations. Для Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ Π½ΠΎΠ²ΠΎΠΉ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ модСль sqlalchemy ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ:

$ alembic revision --autogenerate -m "migration message"

Новый Ρ„Π°ΠΉΠ» ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ Π±ΡƒΠ΄Π΅Ρ‚ создан Π² ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΉ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠΈ:

β”œβ”€β”€ app
β”‚   β”œβ”€β”€ db 
β”‚       β”œβ”€β”€ migrations
β”‚           β”œβ”€β”€ versions
β”‚               β”œβ”€β”€ 0123456789ab_migration_message.py

Π§Ρ‚ΠΎΠ±Ρ‹ ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ всС ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ, слСдуСт Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ:

$ alembic upgrade head

ΠΈΠ»ΠΈ:

$ alembic upgrade 1

для примСнСния Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΎΠ΄Π½ΠΎΠΉ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ.

Для ΠΎΡ‚ΠΌΠ΅Π½Ρ‹ ΠΎΠ΄Π½ΠΎΠΉ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π²Ρ‹ΠΎΠ»Π½ΠΈΡ‚ΡŒ:

$ alembic downgrade -1

3.3. БСрвисы ΠΈ бизнСс-Π»ΠΎΠ³ΠΈΠΊΠ°

Вся бизнСс-Π»ΠΎΠ³ΠΈΠΊΠ° ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° выносится Π² ΠΏΠ°ΠΊΠ΅Ρ‚ app.services. БизнСс-Π»ΠΎΠ³ΠΈΠΊΠ° - Π»ΠΎΠ³ΠΈΠΊΠ°, характСрная Ρ‚ΠΎΠ»ΡŒΠΊΠΎ для Π΄Π°Π½Π½ΠΎΠ³ΠΎ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°. Π’ΡƒΠ΄Π° ΠΆΠ΅ выносятся запросы, ΠΊΠ»ΠΈΠ΅Π½Ρ‚Ρ‹ для использования API сторонних сСрвисов, ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π΄Π°Π½Π½Ρ‹Ρ… ΠΏΠΎ Π·Π°Π΄Π°Π½Π½Ρ‹ΠΌ (Π² Π’Π—) ΠΏΡ€Π°Π²ΠΈΠ»Π°ΠΌ.

Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π°Ρ:

β”œβ”€β”€ app
β”‚   β”œβ”€β”€ services
β”‚   β”‚     β”œβ”€β”€ errors.py - ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ, Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Π΅ Π² ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π΅
β”‚   β”‚     β”œβ”€β”€ client.py - ΠΊΠ»ΠΈΠ΅Π½Ρ‚ для обращСния ΠΊ стороннСму сСрвису 

3.4. ΠšΠΎΠ½Ρ„ΠΈΠ³ΠΈ ΠΈ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ срСды

НовыС ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ срСды ΠΌΠΎΠΆΠ½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π² класс AppSettings ΠΈΠ· Ρ„Π°ΠΉΠ»Π° app/settings.py. Если Ρƒ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ Π½Π΅Ρ‚ значСния ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ, Ρ‚ΠΎ ΠΎΠ½ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ Π±Ρ€Π°Ρ‚ΡŒΡΡ ΠΈΠ· Ρ„Π°ΠΉΠ»Π° .env. Π§Ρ‚ΠΎΠ±Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ эту ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΡƒΡŽ Π² Π±ΠΎΡ‚Π΅, Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ:

from app.settings import settings
...
settings.MY_VAR

ℹ️ Π˜Π½Ρ„ΠΎ Π§Π΅Ρ€Π΅Π· ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ срСды ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ окруТСния, Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… Π±ΡƒΠ΄Π΅Ρ‚ Π·Π°ΠΏΡƒΡΠΊΠ°Ρ‚ΡŒΡΡ smartapp. test, dev ΠΈΠ»ΠΈ prod. ΠŸΡ€ΠΎΡΡ‚ΠΎ Π΄ΠΎΠ±Π°Π²ΡŒΡ‚Π΅ Π² Ρ„Π°ΠΉΠ» .env ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΡƒΡŽ APP_ENV=prod.


4. Π›ΠΈΠ½Ρ‚Π΅Ρ€Ρ‹ ΠΈ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊΠΎΠ΄Π°

Запуск

Для запуска всСх Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅Ρ€ΠΎΠ² Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ скрипт:

$ ./scripts/format

Для запуска всСх Π»ΠΈΠ½Ρ‚Π΅Ρ€ΠΎΠ² Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ скрипт:

$ ./scripts/lint

ОписаниС

Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для форматирования ΠΊΠΎΠ΄Π° ΠΊ Π΅Π΄ΠΈΠ½ΠΎΠΌΡƒ ΡΡ‚ΠΈΠ»ΡŽ: Ρ€Π°Π·Π±ΠΈΠ²Π°Π΅Ρ‚ Π΄Π»ΠΈΠ½Π½Ρ‹Π΅ строки, слСдит Π·Π° отступами ΠΈ ΠΈΠΌΠΏΠΎΡ€Ρ‚Π°ΠΌΠΈ.

⚠️ ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅
Π’ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… ΠΌΠΎΠΌΠ΅Π½Ρ‚Π°Ρ… isort ΠΊΠΎΠ½Ρ„Π»ΠΈΠΊΡ‚ΡƒΠ΅Ρ‚ с black. ΠšΠΎΠ½Ρ„Π»ΠΈΠΊΡ‚ Ρ€Π΅ΡˆΠ°Π΅Ρ‚ΡΡ настройкой Ρ„Π°ΠΉΠ»Π° ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ setup.cfg.

Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для сортировки ΠΈΠΌΠΏΠΎΡ€Ρ‚ΠΎΠ². Π‘Π½Π°Ρ‡Π°Π»Π° ΠΈΠΌΠΏΠΎΡ€Ρ‚Ρ‹ ΠΈΠ· стандартных Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ python, Π·Π°Ρ‚Π΅ΠΌ ΠΈΠ· Π²Π½Π΅ΡˆΠ½ΠΈΡ… Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ ΠΈ Π² ΠΊΠΎΠ½Ρ†Π΅ ΠΈΠ· ΠΌΠΎΠ΄ΡƒΠ»Π΅ΠΉ Π΄Π°Π½Π½ΠΎΠ³ΠΎ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°. ΠœΠ΅ΠΆΠ΄Ρƒ собой ΠΈΠΌΠΏΠΎΡ€Ρ‚Ρ‹ ΡΠΎΡ€Ρ‚ΠΈΡ€ΡƒΡŽΡ‚ΡΡ ΠΏΠΎ Π°Π»Ρ„Π°Π²ΠΈΡ‚Ρƒ.

Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для удалСния Π½Π΅ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡ‹Ρ… ΠΈΠΌΠΏΠΎΡ€Ρ‚ΠΎΠ² ΠΈ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ….

Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ Ρ‚ΠΈΠΏΠΎΠ². ΠŸΠΎΠΌΠΎΠ³Π°Π΅Ρ‚ Π½Π°Ρ…ΠΎΠ΄ΠΈΡ‚ΡŒ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ошибки Π΅Ρ‰Π΅ Π½Π° стадии Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ.

⚠️ ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅
К соТалСнию, Π½Π΅ всС Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‚ Ρ‚ΠΈΠΏΠΈΠ·Π°Ρ†ΠΈΡŽ. Π§Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠ΄ΡΠΊΠ°Π·Π°Ρ‚ΡŒ это mypy Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ строки Π² Ρ„Π°ΠΉΠ» ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ setup.cfg:

[mypy]

# ...

[mypy-your_library_name.*]
ignore_missing_imports = True

НСкоторыС ΠΆΠ΅ Π½Π°ΠΎΠ±ΠΎΡ€ΠΎΡ‚ ΠΈΠΌΠ΅ΡŽΡ‚ ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Ρ‹Π΅ ΠΏΠ»Π°Π³ΠΈΠ½Ρ‹ для mypy, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ pydantic:

[mypy]
plugins = pydantic.mypy

...

[pydantic-mypy]
init_forbid_extra = True
init_typed = True
warn_required_dynamic_aliases = True
warn_untyped_fields = True

Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для комплСксной ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ. АнализируСт допустимыС ΠΈΠΌΠ΅Π½Π° ΠΏΠ΅Ρ€ΠΌΠ΅Π½Π½Ρ‹Ρ… ΠΈ ΠΈΡ… Π΄Π»ΠΈΠ½Ρƒ, ΡΠ»ΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ Π²Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Ρ… конструкций, ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΡƒΡŽ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ ΠΈ ΠΌΠ½ΠΎΠ³ΠΎΠ΅ Π΄Ρ€ΡƒΠ³ΠΎΠ΅. Для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Ρ‚ΠΈΠΏΠ° ошибок Π΅ΡΡ‚ΡŒ свой ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ Π½ΠΎΠΌΠ΅Ρ€, объяснСниС, ΠΏΠΎΡ‡Π΅ΠΌΡƒ Ρ‚Π°ΠΊ Π΄Π΅Π»Π°Ρ‚ΡŒ Π½Π΅ стоит, ΠΈ объяснСниС, ΠΊΠ°ΠΊ Π΄Π΅Π»Π°Ρ‚ΡŒ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ. Бписок ошибок ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Ρ‚ΡƒΡ‚.

ℹ️ Π˜Π½Ρ„ΠΎ
Π’ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… Ρ€Π΅Π΄ΠΊΠΈΡ… случаях ΠΌΠΎΠΆΠ½ΠΎ ΠΈΠ³Π½ΠΎΡ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΡ€Π°Π²ΠΈΠ»Π° Π»ΠΈΠ½Ρ‚Π΅Ρ€Π°. Для этого Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π»ΠΈΠ±ΠΎ ΠΏΡ€ΠΎΠΏΠΈΡΠ°Ρ‚ΡŒ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ с ΠΌΠ΅Ρ‚ΠΊΠΎΠΉ noqa Π½Π° ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ½ΠΎΠΉ строкС:

var = problem_function()  # noqa: WPS999 

Π»ΠΈΠ±ΠΎ ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ ignore ошибки Π² setup.cfg:

[flake8]
# ...
ignore =
    # f-strings are useful
    WPS305,

Π’Π°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΊΠ»ΡŽΡ‡Π°Ρ‚ΡŒ ΠΌΠΎΠ΄ΡƒΠ»ΠΈ ΠΈ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹.


5. ВСстированиС

5.1. Запуск ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ тСстов

ВсС тСсты ΠΏΠΈΡˆΡƒΡ‚ΡΡ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ pytest. Π—Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ тСсты ΠΌΠΎΠΆΠ½ΠΎ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:

$ pytest

Π’ΠΎ врСмя тСстирования поднимаСтся docker-ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ с Π‘Π”. ΠŸΠΎΡ€Ρ‚ выбираСтся свободный, поэтому запущСнная локально Π‘Π” Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΌΠ΅ΡˆΠ°Ρ‚ΡŒ. Если Π²Ρ‹ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ Π·Π°ΠΏΡƒΡΠΊΠ°Ρ‚ΡŒ тСсты ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ Π²Π°ΡˆΡƒ Π»ΠΎΠΊΠ°Π»ΡŒΠ½ΡƒΡŽ Π‘Π”, Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π² .env ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΡƒΡŽ DB=1, Π»ΠΈΠ±ΠΎ Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ:

$ DB=1 pytest

ℹ️ Π˜Π½Ρ„ΠΎ
ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ pytest Π½Π΅ ΡƒΠΌΠ΅Π΅Ρ‚ Π² асинхронныС тСсты, для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с Π½ΠΈΠΌΠΈ Π΅ΠΌΡƒ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌ ΠΏΠ»Π°Π³ΠΈΠ½ pytest-asyncio.

5.2. ΠŸΠΎΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅

ΠŸΠΎΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ ΠΏΡ€ΠΎΡ†Π΅Π½Ρ‚ протСстированного исходного ΠΊΠΎΠ΄Π°, ΠΊΠ°ΠΊ всСго, Ρ‚Π°ΠΊ ΠΈ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Ρ… ΠΌΠΎΠ΄ΡƒΠ»Π΅ΠΉ. ΠŸΠΎΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ ΠΏΠΎΠΌΠΎΠ³Π°Π΅Ρ‚ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ ΠΊΠ°ΠΊΠΈΠ΅ Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚Ρ‹ ΠΊΠΎΠ΄Π° Π½Π΅ Π·Π°ΠΏΡƒΡΠΊΠ°Π»ΠΈΡΡŒ Π² тСстах. Для Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ ΠΎΡ‚Ρ‡Π΅Ρ‚ΠΎΠ² покрытия ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΏΠ»Π°Π³ΠΈΠ½ pytest-cov.

Π§Ρ‚ΠΎΠ±Ρ‹ Π½Π΅ ΠΏΡ€ΠΎΠΏΠΈΡΡ‹Π²Π°Ρ‚ΡŒ всС Ρ„Π»Π°Π³ΠΈ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Ρ€Π°Π·, ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ эти скрипты:

$ ./scripts/test
$ ./scripts/html-cov-test

ΠŸΠ΅Ρ€Π²Ρ‹ΠΉ Π²Ρ‹Π²ΠΎΠ΄ΠΈΡ‚ ΠΎΡ‚Ρ‡Π΅Ρ‚ Π² Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Π»Π΅, Π²Ρ‚ΠΎΡ€ΠΎΠΉ Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚ ΠΎΡ‚Ρ‡Π΅Ρ‚ Π² Π²ΠΈΠ΄Π΅ htmlстраниц с подсвСткой Π½Π΅ΠΏΠΎΠΊΡ€Ρ‹Ρ‚Ρ‹Ρ… участков ΠΊΠΎΠ΄Π°.