βΉοΈ ΠΠ½ΡΠΎ
ΠΠ»Ρ Π²Π·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡΠ²ΠΈΡ Ρ ΠΏΠ»Π°ΡΡΠΎΡΠΌΠΎΠΉ botx ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° pybotx. Π Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡΠΌΠΎΡΡΠ΅ΡΡ ΠΏΡΠΈΠΌΠ΅ΡΡ Π΅Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ. ΠΠ»Ρ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΠΈ SmartApp ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° pybotx-smartapp-rpc. ΠΠ΅ΡΠ΅Π΄ ΠΏΡΠΎΡΡΠ΅Π½ΠΈΠ΅ΠΌ Π΄Π°Π½Π½ΠΎΠ³ΠΎ ΡΡΡΠΎΡΠΈΠ°Π»Π° ΡΠ»Π΅Π΄ΡΠ΅Ρ Ρ Π½Π΅ΠΉ ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΡΡΡΡ.
Π¨Π°Π±Π»ΠΎΠ½ ΡΠ΅ΡΠ°Π΅Ρ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ Π½Π°ΠΏΠΈΡΠ°Π½ΠΈΡ ΠΏΠΎΠ²ΡΠΎΡΡΡΡΠ΅Π³ΠΎ ΠΊΠΎΠ΄Π° Π² ΡΠ°ΠΌΠΎΠΌ Π½Π°ΡΠ°Π»Π΅ ΡΠ°Π±ΠΎΡΡ Π½Π°Π΄ ΠΏΡΠΎΠ΅ΠΊΡΠΎΠΌ. Π£ΠΆΠ΅ ΡΡΡΠ΅ΡΡΠ²ΡΠ΅Ρ ΡΠ°Π±Π»ΠΎΠ½ Π΄Π»Ρ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ Π±ΠΎΡΠΎΠ² bot-template
ΠΠΎ ΡΡΡΡΠΊΡΡΡΠ° Π±ΠΎΡΠ° ΠΈ SmartApp ΠΎΡΠ»ΠΈΡΠ°Π΅ΡΡΡ Π΄ΡΡΠ³ ΠΎΡ Π΄ΡΡΠ³Π°, ΠΏΠΎΡΡΠΎΠΌΡ Π±ΡΠ» ΡΠΎΠ·Π΄Π°Π½ ΡΡΠΎΡ ΡΠ°Π±Π»ΠΎΠ½.
ΠΠ»Ρ ΡΠ°Π·Π²Π΅ΡΡΡΠ²Π°Π½ΠΈΡ ΠΏΡΠΎΠ΅ΠΊΡΠ° Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ 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 - ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ Π»ΠΈΠ½ΡΠ΅ΡΠΎΠ² ΠΈ ΡΠ΅ΡΡΠΎΠ²
- Π£ΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΏΡΠΎΠ΅ΠΊΡΠ° ΡΠ΅ΡΠ΅Π· poetry:
$ poetry install
- ΠΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ Π² ΡΠ°ΠΉΠ»Π΅
.env
. ΠΡΠΈΠΌΠ΅ΡΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ Π½Π°Ρ ΠΎΠ΄ΡΡΡΡ Π² ΡΠ°ΠΉΠ»Π΅example.env
. - ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΠΌ
postges
ΠΈredis
ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ docker-compose:
$ docker-compose -f docker-compose.dev.yml up -d
- ΠΡΠΈΠΌΠ΅Π½ΡΠ΅ΠΌ Π²ΡΠ΅ ΠΌΠΈΠ³ΡΠ°ΡΠΈΠΈ Π΄Π»Ρ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΠΈ ΡΠ°Π±Π»ΠΈΡ Ρ ΠΏΠΎΠΌΠΎΡΡΡ alembic:
$ alembic upgrade head
- ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΠΌ 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
ΠΠ±ΡΠ°Π±ΠΎΡΡΠΈΠΊΠΈ Π½Π°Ρ
ΠΎΠ΄ΡΡΡΡ Π² ΠΏΠ°ΠΊΠ΅ΡΠ΅ 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],
)
ΠΠ·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡΠ²ΠΎΠ²Π°ΡΡ Ρ Π½ΠΎΠ²ΡΠΌΠΈ ΡΠ°Π±Π»ΠΈΡΠ°ΠΌΠΈ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΅ΡΠ΅Π· ΠΌΠΎΠ΄Π΅Π»ΠΈ 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
ΠΡΡ Π±ΠΈΠ·Π½Π΅Ρ-Π»ΠΎΠ³ΠΈΠΊΠ° ΠΏΡΠΎΠ΅ΠΊΡΠ° Π²ΡΠ½ΠΎΡΠΈΡΡΡ Π² ΠΏΠ°ΠΊΠ΅Ρ app.services
. ΠΠΈΠ·Π½Π΅Ρ-Π»ΠΎΠ³ΠΈΠΊΠ° - Π»ΠΎΠ³ΠΈΠΊΠ°, Ρ
Π°ΡΠ°ΠΊΡΠ΅ΡΠ½Π°Ρ ΡΠΎΠ»ΡΠΊΠΎ Π΄Π»Ρ Π΄Π°Π½Π½ΠΎΠ³ΠΎ ΠΏΡΠΎΠ΅ΠΊΡΠ°. Π’ΡΠ΄Π° ΠΆΠ΅ Π²ΡΠ½ΠΎΡΡΡΡΡ Π·Π°ΠΏΡΠΎΡΡ, ΠΊΠ»ΠΈΠ΅Π½ΡΡ Π΄Π»Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ API ΡΡΠΎΡΠΎΠ½Π½ΠΈΡ
ΡΠ΅ΡΠ²ΠΈΡΠΎΠ², ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π΄Π°Π½Π½ΡΡ
ΠΏΠΎ Π·Π°Π΄Π°Π½Π½ΡΠΌ (Π² Π’Π) ΠΏΡΠ°Π²ΠΈΠ»Π°ΠΌ.
Π‘ΡΡΡΠΊΡΡΡΠ° ΡΠ»Π΅Π΄ΡΡΡΠ°Ρ:
βββ app
β βββ services
β β βββ errors.py - ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ, Π²ΡΠ·ΡΠ²Π°Π΅ΠΌΡΠ΅ Π² ΠΊΠ»ΠΈΠ΅Π½ΡΠ΅
β β βββ client.py - ΠΊΠ»ΠΈΠ΅Π½Ρ Π΄Π»Ρ ΠΎΠ±ΡΠ°ΡΠ΅Π½ΠΈΡ ΠΊ ΡΡΠΎΡΠΎΠ½Π½Π΅ΠΌΡ ΡΠ΅ΡΠ²ΠΈΡΡ
ΠΠΎΠ²ΡΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΡΡΠ΅Π΄Ρ ΠΌΠΎΠΆΠ½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡΡ Π² ΠΊΠ»Π°ΡΡ AppSettings
ΠΈΠ· ΡΠ°ΠΉΠ»Π° app/settings.py
. ΠΡΠ»ΠΈ Ρ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΠΎΠΉ Π½Π΅Ρ Π·Π½Π°ΡΠ΅Π½ΠΈΡ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ, ΡΠΎ ΠΎΠ½ΠΎ Π±ΡΠ΄Π΅Ρ Π±ΡΠ°ΡΡΡΡ ΠΈΠ· ΡΠ°ΠΉΠ»Π° .env
.
Π§ΡΠΎΠ±Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΡΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ Π² Π±ΠΎΡΠ΅, Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΠΎ:
from app.settings import settings
...
settings.MY_VAR
βΉοΈ ΠΠ½ΡΠΎ Π§Π΅ΡΠ΅Π· ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΡΡΠ΅Π΄Ρ ΠΌΠΎΠΆΠ½ΠΎ ΡΠΊΠ°Π·ΡΠ²Π°ΡΡ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ, Π² ΠΊΠΎΡΠΎΡΡΡ Π±ΡΠ΄Π΅Ρ Π·Π°ΠΏΡΡΠΊΠ°ΡΡΡΡ smartapp.
test
,dev
ΠΈΠ»ΠΈprod
. ΠΡΠΎΡΡΠΎ Π΄ΠΎΠ±Π°Π²ΡΡΠ΅ Π² ΡΠ°ΠΉΠ».env
ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡAPP_ENV=prod
.
ΠΠ»Ρ Π·Π°ΠΏΡΡΠΊΠ° Π²ΡΠ΅Ρ ΡΠΎΡΠΌΠ°ΡΠ΅ΡΠΎΠ² Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ Π²ΡΠΏΠΎΠ»Π½ΠΈΡΡ ΡΠΊΡΠΈΠΏΡ:
$ ./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,
Π’Π°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΊΠ»ΡΡΠ°ΡΡ ΠΌΠΎΠ΄ΡΠ»ΠΈ ΠΈ ΠΏΠ°ΠΊΠ΅ΡΡ.
ΠΡΠ΅ ΡΠ΅ΡΡΡ ΠΏΠΈΡΡΡΡΡ Ρ ΠΏΠΎΠΌΠΎΡΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ pytest. ΠΠ°ΠΏΡΡΡΠΈΡΡ ΡΠ΅ΡΡΡ ΠΌΠΎΠΆΠ½ΠΎ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:
$ pytest
ΠΠΎ Π²ΡΠ΅ΠΌΡ ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΏΠΎΠ΄Π½ΠΈΠΌΠ°Π΅ΡΡΡ docker-ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ Ρ ΠΠ. ΠΠΎΡΡ Π²ΡΠ±ΠΈΡΠ°Π΅ΡΡΡ ΡΠ²ΠΎΠ±ΠΎΠ΄Π½ΡΠΉ, ΠΏΠΎΡΡΠΎΠΌΡ Π·Π°ΠΏΡΡΠ΅Π½Π½Π°Ρ Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎ ΠΠ Π½Π΅ Π±ΡΠ΄Π΅Ρ ΠΌΠ΅ΡΠ°ΡΡ. ΠΡΠ»ΠΈ Π²Ρ Ρ
ΠΎΡΠΈΡΠ΅ Π·Π°ΠΏΡΡΠΊΠ°ΡΡ ΡΠ΅ΡΡΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ Π²Π°ΡΡ Π»ΠΎΠΊΠ°Π»ΡΠ½ΡΡ ΠΠ, Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡΡ Π² .env
ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ DB=1
, Π»ΠΈΠ±ΠΎ Π²ΡΠΏΠΎΠ»Π½ΠΈΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ:
$ DB=1 pytest
βΉοΈ ΠΠ½ΡΠΎ
ΠΠΎΡΠΊΠΎΠ»ΡΠΊΡ pytest Π½Π΅ ΡΠΌΠ΅Π΅Ρ Π² Π°ΡΠΈΠ½Ρ ΡΠΎΠ½Π½ΡΠ΅ ΡΠ΅ΡΡΡ, Π΄Π»Ρ ΡΠ°Π±ΠΎΡΡ Ρ Π½ΠΈΠΌΠΈ Π΅ΠΌΡ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌ ΠΏΠ»Π°Π³ΠΈΠ½ pytest-asyncio.
ΠΠΎΠΊΡΡΡΠΈΠ΅ ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°Π΅Ρ ΠΏΡΠΎΡΠ΅Π½Ρ ΠΏΡΠΎΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠ³ΠΎ ΠΈΡΡ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π°, ΠΊΠ°ΠΊ Π²ΡΠ΅Π³ΠΎ, ΡΠ°ΠΊ ΠΈ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΡ ΠΌΠΎΠ΄ΡΠ»Π΅ΠΉ. ΠΠΎΠΊΡΡΡΠΈΠ΅ ΠΏΠΎΠΌΠΎΠ³Π°Π΅Ρ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ ΠΊΠ°ΠΊΠΈΠ΅ ΡΡΠ°Π³ΠΌΠ΅Π½ΡΡ ΠΊΠΎΠ΄Π° Π½Π΅ Π·Π°ΠΏΡΡΠΊΠ°Π»ΠΈΡΡ Π² ΡΠ΅ΡΡΠ°Ρ . ΠΠ»Ρ Π³Π΅Π½Π΅ΡΠ°ΡΠΈΠΈ ΠΎΡΡΠ΅ΡΠΎΠ² ΠΏΠΎΠΊΡΡΡΠΈΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ ΠΏΠ»Π°Π³ΠΈΠ½ pytest-cov.
Π§ΡΠΎΠ±Ρ Π½Π΅ ΠΏΡΠΎΠΏΠΈΡΡΠ²Π°ΡΡ Π²ΡΠ΅ ΡΠ»Π°Π³ΠΈ ΠΊΠ°ΠΆΠ΄ΡΠΉ ΡΠ°Π·, ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΡΠΈ ΡΠΊΡΠΈΠΏΡΡ:
$ ./scripts/test
$ ./scripts/html-cov-test
ΠΠ΅ΡΠ²ΡΠΉ Π²ΡΠ²ΠΎΠ΄ΠΈΡ ΠΎΡΡΠ΅Ρ Π² ΡΠ΅ΡΠΌΠΈΠ½Π°Π»Π΅, Π²ΡΠΎΡΠΎΠΉ Π³Π΅Π½Π΅ΡΠΈΡΡΠ΅Ρ ΠΎΡΡΠ΅Ρ Π² Π²ΠΈΠ΄Π΅ html
ΡΡΡΠ°Π½ΠΈΡ Ρ ΠΏΠΎΠ΄ΡΠ²Π΅ΡΠΊΠΎΠΉ Π½Π΅ΠΏΠΎΠΊΡΡΡΡΡ
ΡΡΠ°ΡΡΠΊΠΎΠ² ΠΊΠΎΠ΄Π°.