mirror of
https://github.com/Balshgit/gpt_chat_bot.git
synced 2025-09-10 17:20:41 +03:00
add style check by ruff (#34)
* reformat settings * add init tests for timed lru cache * add check style by ruff
This commit is contained in:
parent
9e3fac0b94
commit
7ef8d6e19d
6
Makefile
6
Makefile
@ -37,6 +37,10 @@ lint-typing:
|
|||||||
lint-complexity:
|
lint-complexity:
|
||||||
flake8 $(PY_TARGET_DIRS)
|
flake8 $(PY_TARGET_DIRS)
|
||||||
|
|
||||||
|
## Запустить линтер ruff
|
||||||
|
lint-ruff:
|
||||||
|
ruff $(PY_TARGET_DIRS)
|
||||||
|
|
||||||
## Проверить зависимостей
|
## Проверить зависимостей
|
||||||
lint-deps:
|
lint-deps:
|
||||||
poetry run poetry check
|
poetry run poetry check
|
||||||
@ -45,7 +49,7 @@ lint-deps:
|
|||||||
poetry run pip-audit
|
poetry run pip-audit
|
||||||
|
|
||||||
## Запустить все линтеры
|
## Запустить все линтеры
|
||||||
lint: lint-typing lint-complexity check-import-sorting lint-deps
|
lint: lint-typing lint-complexity check-import-sorting lint-ruff lint-deps
|
||||||
|
|
||||||
## Show help
|
## Show help
|
||||||
help:
|
help:
|
||||||
|
@ -39,7 +39,7 @@ python main.py
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
cd bot_microservice
|
cd bot_microservice
|
||||||
poetry run uvicorn --host 0.0.0.0 --factory main:create_app --port 8000 --reload
|
poetry run uvicorn --factory --host 0.0.0.0 --port 8080 --reload --workers 2 --log-level warning
|
||||||
```
|
```
|
||||||
|
|
||||||
To start on polling mode set `START_WITH_WEBHOOK` to blank
|
To start on polling mode set `START_WITH_WEBHOOK` to blank
|
||||||
@ -79,7 +79,7 @@ gunicorn main:create_app --workers 10 --bind 0.0.0.0:8000 --worker-class uvicorn
|
|||||||
### Run local tests:
|
### Run local tests:
|
||||||
```bash
|
```bash
|
||||||
cd bot_microservice
|
cd bot_microservice
|
||||||
STAGE=runtests poetry run pytest
|
LOCALTEST=1 STAGE=runtests poetry run pytest
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run tests in docker compose:
|
### Run tests in docker compose:
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
|
from datetime import timezone
|
||||||
from enum import StrEnum, unique
|
from enum import StrEnum, unique
|
||||||
|
|
||||||
|
from dateutil import tz
|
||||||
|
|
||||||
AUDIO_SEGMENT_DURATION = 120 * 1000
|
AUDIO_SEGMENT_DURATION = 120 * 1000
|
||||||
|
|
||||||
API_PREFIX = "/api"
|
API_PREFIX = "/api"
|
||||||
CHATGPT_BASE_URI = "/backend-api/v2/conversation"
|
CHATGPT_BASE_URI = "/backend-api/v2/conversation"
|
||||||
INVALID_GPT_REQUEST_MESSAGES = ("Invalid request model", "return unexpected http status code")
|
INVALID_GPT_REQUEST_MESSAGES = ("Invalid request model", "return unexpected http status code")
|
||||||
|
|
||||||
|
MOSCOW_TZ = tz.gettz("Europe/Moscow")
|
||||||
|
UTC_TZ = timezone.utc
|
||||||
|
|
||||||
|
|
||||||
class BotStagesEnum(StrEnum):
|
class BotStagesEnum(StrEnum):
|
||||||
about_me = "about_me"
|
about_me = "about_me"
|
||||||
|
@ -45,7 +45,7 @@ class BotApplication:
|
|||||||
|
|
||||||
async def polling(self) -> None:
|
async def polling(self) -> None:
|
||||||
if self.settings.STAGE == "runtests":
|
if self.settings.STAGE == "runtests":
|
||||||
return None
|
return
|
||||||
await self.application.initialize()
|
await self.application.initialize()
|
||||||
await self.application.start()
|
await self.application.start()
|
||||||
await self.application.updater.start_polling() # type: ignore
|
await self.application.updater.start_polling() # type: ignore
|
||||||
|
@ -23,7 +23,7 @@ async def main_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> st
|
|||||||
|
|
||||||
async def about_me(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def about_me(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
if not update.effective_message:
|
if not update.effective_message:
|
||||||
return None
|
return
|
||||||
await update.effective_message.reply_text(
|
await update.effective_message.reply_text(
|
||||||
"Автор бота: *Дмитрий Афанасьев*\n\nTg nickname: *Balshtg*", parse_mode="MarkdownV2"
|
"Автор бота: *Дмитрий Афанасьев*\n\nTg nickname: *Balshtg*", parse_mode="MarkdownV2"
|
||||||
)
|
)
|
||||||
@ -31,7 +31,7 @@ async def about_me(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|||||||
|
|
||||||
async def about_bot(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def about_bot(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
if not update.effective_message:
|
if not update.effective_message:
|
||||||
return None
|
return
|
||||||
chatgpt_service = ChatGptService.build()
|
chatgpt_service = ChatGptService.build()
|
||||||
model = await chatgpt_service.get_current_chatgpt_model()
|
model = await chatgpt_service.get_current_chatgpt_model()
|
||||||
await update.effective_message.reply_text(
|
await update.effective_message.reply_text(
|
||||||
@ -44,7 +44,7 @@ async def about_bot(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|||||||
|
|
||||||
async def website(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def website(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
if not update.effective_message:
|
if not update.effective_message:
|
||||||
return None
|
return
|
||||||
website = urljoin(settings.DOMAIN, f"{settings.chat_prefix}/")
|
website = urljoin(settings.DOMAIN, f"{settings.chat_prefix}/")
|
||||||
await update.effective_message.reply_text(f"Веб версия: {website}")
|
await update.effective_message.reply_text(f"Веб версия: {website}")
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
|
|||||||
"""Send a message when the command /help is issued."""
|
"""Send a message when the command /help is issued."""
|
||||||
|
|
||||||
if not update.effective_message:
|
if not update.effective_message:
|
||||||
return None
|
return
|
||||||
reply_markup = InlineKeyboardMarkup(main_keyboard)
|
reply_markup = InlineKeyboardMarkup(main_keyboard)
|
||||||
await update.effective_message.reply_text(
|
await update.effective_message.reply_text(
|
||||||
"Help!",
|
"Help!",
|
||||||
@ -65,7 +65,7 @@ async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
|
|||||||
|
|
||||||
async def ask_question(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def ask_question(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
if not update.message:
|
if not update.message:
|
||||||
return None
|
return
|
||||||
|
|
||||||
await update.message.reply_text("Пожалуйста подождите, ответ в среднем занимает 10-15 секунд")
|
await update.message.reply_text("Пожалуйста подождите, ответ в среднем занимает 10-15 секунд")
|
||||||
|
|
||||||
@ -77,10 +77,10 @@ async def ask_question(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
|
|||||||
|
|
||||||
async def voice_recognize(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def voice_recognize(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
if not update.message:
|
if not update.message:
|
||||||
return None
|
return
|
||||||
await update.message.reply_text("Пожалуйста, ожидайте :)\nТрехминутная запись обрабатывается примерно 30 секунд")
|
await update.message.reply_text("Пожалуйста, ожидайте :)\nТрехминутная запись обрабатывается примерно 30 секунд")
|
||||||
if not update.message.voice:
|
if not update.message.voice:
|
||||||
return None
|
return
|
||||||
|
|
||||||
sound_file = await update.message.voice.get_file()
|
sound_file = await update.message.voice.get_file()
|
||||||
sound_bytes = await sound_file.download_as_bytearray()
|
sound_bytes = await sound_file.download_as_bytearray()
|
||||||
|
@ -70,15 +70,16 @@ class ChatGPTRepository:
|
|||||||
status = response.status_code
|
status = response.status_code
|
||||||
for message in INVALID_GPT_REQUEST_MESSAGES:
|
for message in INVALID_GPT_REQUEST_MESSAGES:
|
||||||
if message in response.text:
|
if message in response.text:
|
||||||
message = f"{message}: {chatgpt_model}"
|
invalid_model_message = f"{message}: {chatgpt_model}"
|
||||||
logger.info(message, question=question, chatgpt_model=chatgpt_model)
|
logger.info(invalid_model_message, question=question, chatgpt_model=chatgpt_model)
|
||||||
return message
|
return invalid_model_message
|
||||||
if status != httpx.codes.OK:
|
if status != httpx.codes.OK:
|
||||||
logger.info(f"got response status: {status} from chat api", response.text)
|
logger.info(f"got response status: {status} from chat api", response.text)
|
||||||
return "Что-то пошло не так, попробуйте еще раз или обратитесь к администратору"
|
return "Что-то пошло не так, попробуйте еще раз или обратитесь к администратору"
|
||||||
return response.text
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error("error get data from chat api", error=error)
|
logger.error("error get data from chat api", error=error)
|
||||||
|
else:
|
||||||
|
return response.text
|
||||||
return "Вообще всё сломалось :("
|
return "Вообще всё сломалось :("
|
||||||
|
|
||||||
async def request_to_chatgpt_microservice(self, question: str, chatgpt_model: str) -> Response:
|
async def request_to_chatgpt_microservice(self, question: str, chatgpt_model: str) -> Response:
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
import subprocess # noqa
|
import subprocess # noqa: S404
|
||||||
import tempfile
|
import tempfile
|
||||||
from concurrent.futures.thread import ThreadPoolExecutor
|
from concurrent.futures.thread import ThreadPoolExecutor
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
@ -68,7 +68,7 @@ class SpeechToTextService:
|
|||||||
new_filename = self.filename + ".wav"
|
new_filename = self.filename + ".wav"
|
||||||
cmd = ["ffmpeg", "-loglevel", "quiet", "-i", self.filename, "-vn", new_filename]
|
cmd = ["ffmpeg", "-loglevel", "quiet", "-i", self.filename, "-vn", new_filename]
|
||||||
try:
|
try:
|
||||||
subprocess.run(args=cmd) # noqa: S603
|
subprocess.run(args=cmd, check=True) # noqa: S603
|
||||||
logger.info("file has been converted to wav", filename=new_filename)
|
logger.info("file has been converted to wav", filename=new_filename)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error("cant convert voice", error=error, filename=self.filename)
|
logger.error("cant convert voice", error=error, filename=self.filename)
|
||||||
@ -80,11 +80,10 @@ class SpeechToTextService:
|
|||||||
with AudioFile(tmpfile) as source:
|
with AudioFile(tmpfile) as source:
|
||||||
audio_text = self.recognizer.listen(source)
|
audio_text = self.recognizer.listen(source)
|
||||||
try:
|
try:
|
||||||
text = self.recognizer.recognize_google(audio_text, language="ru-RU")
|
return self.recognizer.recognize_google(audio_text, language="ru-RU")
|
||||||
return text
|
|
||||||
except SpeechRecognizerError as error:
|
except SpeechRecognizerError as error:
|
||||||
logger.error("error recognizing text with google", error=error)
|
logger.error("error recognizing text with google", error=error)
|
||||||
raise error
|
raise
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -3,6 +3,8 @@ from functools import cache, wraps
|
|||||||
from inspect import cleandoc
|
from inspect import cleandoc
|
||||||
from typing import Any, Callable
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
from constants import MOSCOW_TZ
|
||||||
|
|
||||||
|
|
||||||
def timed_lru_cache(
|
def timed_lru_cache(
|
||||||
microseconds: int = 0,
|
microseconds: int = 0,
|
||||||
@ -15,14 +17,14 @@ def timed_lru_cache(
|
|||||||
update_delta = timedelta(
|
update_delta = timedelta(
|
||||||
microseconds=microseconds, milliseconds=milliseconds, seconds=seconds, minutes=minutes, hours=hours
|
microseconds=microseconds, milliseconds=milliseconds, seconds=seconds, minutes=minutes, hours=hours
|
||||||
)
|
)
|
||||||
next_update = datetime.utcnow() + update_delta
|
next_update = datetime.now(tz=MOSCOW_TZ) + update_delta
|
||||||
|
|
||||||
cached_func = cache(func)
|
cached_func = cache(func)
|
||||||
|
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def _wrapped(*args: Any, **kwargs: Any) -> Callable[[Any], Any]:
|
def _wrapped(*args: Any, **kwargs: Any) -> Callable[[Any], Any]:
|
||||||
nonlocal next_update
|
nonlocal next_update
|
||||||
now = datetime.utcnow()
|
now = datetime.now(tz=MOSCOW_TZ)
|
||||||
if now >= next_update:
|
if now >= next_update:
|
||||||
cached_func.cache_clear()
|
cached_func.cache_clear()
|
||||||
next_update = now + update_delta
|
next_update = now + update_delta
|
||||||
|
@ -44,9 +44,9 @@ class Database:
|
|||||||
session: Session = self._sync_session_factory()
|
session: Session = self._sync_session_factory()
|
||||||
try:
|
try:
|
||||||
return session
|
return session
|
||||||
except Exception as err:
|
except Exception:
|
||||||
session.rollback()
|
session.rollback()
|
||||||
raise err
|
raise
|
||||||
finally:
|
finally:
|
||||||
session.commit()
|
session.commit()
|
||||||
session.close()
|
session.close()
|
||||||
@ -71,9 +71,9 @@ class Database:
|
|||||||
async with self._async_session_factory() as session, session.begin():
|
async with self._async_session_factory() as session, session.begin():
|
||||||
try:
|
try:
|
||||||
yield session
|
yield session
|
||||||
except Exception as error:
|
except Exception:
|
||||||
await session.rollback()
|
await session.rollback()
|
||||||
raise error
|
raise
|
||||||
|
|
||||||
async def create_database(self) -> None:
|
async def create_database(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""initial commit
|
"""initial commit
|
||||||
|
|
||||||
Revision ID: eb78565abec7
|
Revision ID: eb78565abec7
|
||||||
Revises:
|
Revises:
|
||||||
Create Date: 2023-10-05 18:28:30.915361
|
Create Date: 2023-10-05 18:28:30.915361
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -30,7 +30,7 @@ def upgrade() -> None:
|
|||||||
models = results.scalars().all()
|
models = results.scalars().all()
|
||||||
|
|
||||||
if models:
|
if models:
|
||||||
return None
|
return
|
||||||
models = []
|
models = []
|
||||||
for model in ChatGptModelsEnum.values():
|
for model in ChatGptModelsEnum.values():
|
||||||
priority = 0 if model != "gpt-3.5-turbo-stream-FreeGpt" else 1
|
priority = 0 if model != "gpt-3.5-turbo-stream-FreeGpt" else 1
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from functools import cached_property, lru_cache
|
from functools import cache, cached_property
|
||||||
from os import environ
|
from os import environ
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@ -71,9 +71,11 @@ class AppSettings(SentrySettings, LoggingSettings, BaseSettings):
|
|||||||
# Enable uvicorn reloading
|
# Enable uvicorn reloading
|
||||||
RELOAD: bool = False
|
RELOAD: bool = False
|
||||||
|
|
||||||
|
# telegram settings
|
||||||
TELEGRAM_API_TOKEN: str = "123456789:AABBCCDDEEFFaabbccddeeff-1234567890"
|
TELEGRAM_API_TOKEN: str = "123456789:AABBCCDDEEFFaabbccddeeff-1234567890"
|
||||||
# webhook settings
|
|
||||||
START_WITH_WEBHOOK: bool = False
|
START_WITH_WEBHOOK: bool = False
|
||||||
|
|
||||||
|
# domain settings
|
||||||
DOMAIN: str = "https://localhost"
|
DOMAIN: str = "https://localhost"
|
||||||
URL_PREFIX: str = ""
|
URL_PREFIX: str = ""
|
||||||
CHAT_PREFIX: str = ""
|
CHAT_PREFIX: str = ""
|
||||||
@ -82,7 +84,6 @@ class AppSettings(SentrySettings, LoggingSettings, BaseSettings):
|
|||||||
DB_ECHO: bool = False
|
DB_ECHO: bool = False
|
||||||
|
|
||||||
# ==== gpt settings ====
|
# ==== gpt settings ====
|
||||||
GPT_MODEL: str = "gpt-3.5-turbo-stream-DeepAi"
|
|
||||||
GPT_BASE_HOST: str = "http://chathpt_chat_service:8858"
|
GPT_BASE_HOST: str = "http://chathpt_chat_service:8858"
|
||||||
|
|
||||||
@model_validator(mode="before") # type: ignore[arg-type]
|
@model_validator(mode="before") # type: ignore[arg-type]
|
||||||
@ -146,7 +147,7 @@ class AppSettings(SentrySettings, LoggingSettings, BaseSettings):
|
|||||||
case_sensitive = True
|
case_sensitive = True
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=None)
|
@cache
|
||||||
def get_settings() -> AppSettings:
|
def get_settings() -> AppSettings:
|
||||||
return AppSettings()
|
return AppSettings()
|
||||||
|
|
||||||
|
@ -97,8 +97,7 @@ class BotMessageFactory(factory.DictFactory):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_instance(cls, **kwargs: Any) -> dict[str, Any]:
|
def create_instance(cls, **kwargs: Any) -> dict[str, Any]:
|
||||||
data = {**cls.build(**kwargs), "from": BotUserFactory()._asdict()}
|
return {**cls.build(**kwargs), "from": BotUserFactory()._asdict()}
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
class BotUpdateFactory(factory.DictFactory):
|
class BotUpdateFactory(factory.DictFactory):
|
||||||
@ -114,8 +113,7 @@ class CallBackFactory(factory.DictFactory):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_instance(cls, **kwargs: Any) -> dict[str, Any]:
|
def create_instance(cls, **kwargs: Any) -> dict[str, Any]:
|
||||||
data = {**cls.build(**kwargs), "from": BotUserFactory()._asdict()}
|
return {**cls.build(**kwargs), "from": BotUserFactory()._asdict()}
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
class BotCallBackQueryFactory(factory.DictFactory):
|
class BotCallBackQueryFactory(factory.DictFactory):
|
||||||
|
0
bot_microservice/tests/unit/__init__.py
Normal file
0
bot_microservice/tests/unit/__init__.py
Normal file
60
bot_microservice/tests/unit/test_system_utils.py
Normal file
60
bot_microservice/tests/unit/test_system_utils.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import time
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
from core.utils import timed_lru_cache
|
||||||
|
|
||||||
|
|
||||||
|
class TestTimedLruCache:
|
||||||
|
call_count: int = 0
|
||||||
|
|
||||||
|
def sum_two_numbers(self, first: int, second: int) -> int:
|
||||||
|
self.call_count += 1
|
||||||
|
return first + second
|
||||||
|
|
||||||
|
def test_timed_lru_cache_cached_for_1_second(self) -> None:
|
||||||
|
self.call_count = 0
|
||||||
|
|
||||||
|
tested_func = timed_lru_cache(seconds=1)(self.sum_two_numbers)
|
||||||
|
|
||||||
|
self._call_function_many_times(call_times=2, func=tested_func, first=2, second=40, result=42)
|
||||||
|
time.sleep(0.5)
|
||||||
|
self._call_function_many_times(call_times=4, func=tested_func, first=2, second=40, result=42)
|
||||||
|
time.sleep(1)
|
||||||
|
self._call_function_many_times(call_times=3, func=tested_func, first=2, second=40, result=42)
|
||||||
|
assert tested_func(2, 2) == 4
|
||||||
|
assert self.call_count == 3
|
||||||
|
|
||||||
|
def test_timed_lru_cache_cached_for_long_time(self) -> None:
|
||||||
|
self.call_count = 0
|
||||||
|
|
||||||
|
tested_func = timed_lru_cache(minutes=5)(self.sum_two_numbers)
|
||||||
|
|
||||||
|
self._call_function_many_times(call_times=3, func=tested_func, first=2, second=40, result=42)
|
||||||
|
time.sleep(0.2)
|
||||||
|
self._call_function_many_times(call_times=4, func=tested_func, first=2, second=40, result=42)
|
||||||
|
time.sleep(0.2)
|
||||||
|
self._call_function_many_times(call_times=2, func=tested_func, first=2, second=40, result=42)
|
||||||
|
assert tested_func(2, 2) == 4
|
||||||
|
assert self.call_count == 2
|
||||||
|
|
||||||
|
def test_timed_lru_cache_cached_for_short_time(self) -> None:
|
||||||
|
self.call_count = 0
|
||||||
|
|
||||||
|
tested_func = timed_lru_cache(milliseconds=200)(self.sum_two_numbers)
|
||||||
|
|
||||||
|
self._call_function_many_times(call_times=2, func=tested_func, first=2, second=40, result=42)
|
||||||
|
time.sleep(0.3)
|
||||||
|
self._call_function_many_times(call_times=5, func=tested_func, first=2, second=40, result=42)
|
||||||
|
time.sleep(0.3)
|
||||||
|
self._call_function_many_times(call_times=7, func=tested_func, first=2, second=40, result=42)
|
||||||
|
time.sleep(0.3)
|
||||||
|
self._call_function_many_times(call_times=3, func=tested_func, first=2, second=40, result=42)
|
||||||
|
assert tested_func(2, 2) == 4
|
||||||
|
assert self.call_count == 5
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _call_function_many_times(
|
||||||
|
call_times: int, func: Callable[[int, int], int], first: int, second: int, result: int
|
||||||
|
) -> None:
|
||||||
|
for _ in range(call_times):
|
||||||
|
assert func(first, second) == result
|
@ -45,6 +45,9 @@ format:
|
|||||||
4_black_check:
|
4_black_check:
|
||||||
glob: "*.py"
|
glob: "*.py"
|
||||||
run: black --check -S {staged_files}
|
run: black --check -S {staged_files}
|
||||||
|
5_ruff:
|
||||||
|
glob: "*.py"
|
||||||
|
run: ruff bot_microservice
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
parallel: true
|
parallel: true
|
||||||
@ -55,6 +58,9 @@ lint:
|
|||||||
flake8:
|
flake8:
|
||||||
glob: "*.py"
|
glob: "*.py"
|
||||||
run: flake8 bot_microservice
|
run: flake8 bot_microservice
|
||||||
|
ruff:
|
||||||
|
glob: "*.py"
|
||||||
|
run: ruff bot_microservice
|
||||||
|
|
||||||
check-format:
|
check-format:
|
||||||
parallel: true
|
parallel: true
|
||||||
|
34
poetry.lock
generated
34
poetry.lock
generated
@ -2839,6 +2839,32 @@ files = [
|
|||||||
{file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"},
|
{file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ruff"
|
||||||
|
version = "0.0.292"
|
||||||
|
description = "An extremely fast Python linter, written in Rust."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "ruff-0.0.292-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:02f29db018c9d474270c704e6c6b13b18ed0ecac82761e4fcf0faa3728430c96"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:69654e564342f507edfa09ee6897883ca76e331d4bbc3676d8a8403838e9fade"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c3c91859a9b845c33778f11902e7b26440d64b9d5110edd4e4fa1726c41e0a4"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4476f1243af2d8c29da5f235c13dca52177117935e1f9393f9d90f9833f69e4"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be8eb50eaf8648070b8e58ece8e69c9322d34afe367eec4210fdee9a555e4ca7"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9889bac18a0c07018aac75ef6c1e6511d8411724d67cb879103b01758e110a81"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6bdfabd4334684a4418b99b3118793f2c13bb67bf1540a769d7816410402a205"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7c77c53bfcd75dbcd4d1f42d6cabf2485d2e1ee0678da850f08e1ab13081a8"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e087b24d0d849c5c81516ec740bf4fd48bf363cfb104545464e0fca749b6af9"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f160b5ec26be32362d0774964e218f3fcf0a7da299f7e220ef45ae9e3e67101a"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ac153eee6dd4444501c4bb92bff866491d4bfb01ce26dd2fff7ca472c8df9ad0"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-musllinux_1_2_i686.whl", hash = "sha256:87616771e72820800b8faea82edd858324b29bb99a920d6aa3d3949dd3f88fb0"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b76deb3bdbea2ef97db286cf953488745dd6424c122d275f05836c53f62d4016"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-win32.whl", hash = "sha256:e854b05408f7a8033a027e4b1c7f9889563dd2aca545d13d06711e5c39c3d003"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-win_amd64.whl", hash = "sha256:f27282bedfd04d4c3492e5c3398360c9d86a295be00eccc63914438b4ac8a83c"},
|
||||||
|
{file = "ruff-0.0.292-py3-none-win_arm64.whl", hash = "sha256:7f67a69c8f12fbc8daf6ae6d36705037bde315abf8b82b6e1f4c9e74eb750f68"},
|
||||||
|
{file = "ruff-0.0.292.tar.gz", hash = "sha256:1093449e37dd1e9b813798f6ad70932b57cf614e5c2b5c51005bf67d55db33ac"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "safety"
|
name = "safety"
|
||||||
version = "2.4.0b1"
|
version = "2.4.0b1"
|
||||||
@ -2867,13 +2893,13 @@ gitlab = ["python-gitlab (>=1.3.0)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sentry-sdk"
|
name = "sentry-sdk"
|
||||||
version = "1.31.0"
|
version = "1.32.0"
|
||||||
description = "Python client for Sentry (https://sentry.io)"
|
description = "Python client for Sentry (https://sentry.io)"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
files = [
|
files = [
|
||||||
{file = "sentry-sdk-1.31.0.tar.gz", hash = "sha256:6de2e88304873484207fed836388e422aeff000609b104c802749fd89d56ba5b"},
|
{file = "sentry-sdk-1.32.0.tar.gz", hash = "sha256:935e8fbd7787a3702457393b74b13d89a5afb67185bc0af85c00cb27cbd42e7c"},
|
||||||
{file = "sentry_sdk-1.31.0-py2.py3-none-any.whl", hash = "sha256:64a7141005fb775b9db298a30de93e3b83e0ddd1232dc6f36eb38aebc1553291"},
|
{file = "sentry_sdk-1.32.0-py2.py3-none-any.whl", hash = "sha256:eeb0b3550536f3bbc05bb1c7e0feb3a78d74acb43b607159a606ed2ec0a33a4d"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -3491,4 +3517,4 @@ multidict = ">=4.0"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.11"
|
python-versions = "^3.11"
|
||||||
content-hash = "3c1791bb01a98ef620fd9be9cd2bfda3537449c4dadd52512e99574804d2cbf7"
|
content-hash = "6680823e54023a1bea0652422167d179651dcdaa63b72ed1d708490605ff5e1a"
|
||||||
|
@ -97,6 +97,8 @@ flake8-comments = "^0.1"
|
|||||||
flake8-newspaper-style = "^0.4"
|
flake8-newspaper-style = "^0.4"
|
||||||
Flake8-pyproject = "^1.2.3"
|
Flake8-pyproject = "^1.2.3"
|
||||||
|
|
||||||
|
ruff = "^0.0.292"
|
||||||
|
|
||||||
[tool.flake8]
|
[tool.flake8]
|
||||||
inline-quotes = "double"
|
inline-quotes = "double"
|
||||||
max-line-length = 120
|
max-line-length = 120
|
||||||
@ -120,10 +122,10 @@ ignore = [
|
|||||||
"B008"
|
"B008"
|
||||||
]
|
]
|
||||||
per-file-ignores = [
|
per-file-ignores = [
|
||||||
# too complex queries
|
|
||||||
"bot_microservice/tests/*: S101",
|
"bot_microservice/tests/*: S101",
|
||||||
"bot_microservice/tests/integration/conftest.py: NEW100",
|
"bot_microservice/tests/integration/conftest.py: NEW100",
|
||||||
"bot_microservice/settings/config.py: S104"
|
"bot_microservice/settings/config.py: S104",
|
||||||
|
"bot_microservice/tests/unit/test_system_utils.py: S101, AAA01"
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.autoflake]
|
[tool.autoflake]
|
||||||
@ -213,4 +215,38 @@ addopts = '''
|
|||||||
--cov-config=.coveragerc
|
--cov-config=.coveragerc
|
||||||
--cov-context=test
|
--cov-context=test
|
||||||
--no-cov
|
--no-cov
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
extend-select = ["F", "I", "PL", "E", "W", "C4", "PT", "B", "T10", "SIM", "TID", "T20", "PGH", "S", "RET", "ERA", "PIE", "UP", "ASYNC", "ISC", "PERF", "DTZ", "TRY", "C90"]
|
||||||
|
ignore = ["S105", "S106", "PGH003", "TRY003", "TRY004", "PT001", "PT023", "I001"]
|
||||||
|
line-length = 120
|
||||||
|
format="grouped"
|
||||||
|
|
||||||
|
[tool.ruff.per-file-ignores]
|
||||||
|
"bot_microservice/tests/*" = ["S101", "PLR2004", "PLR0913"]
|
||||||
|
"bot_microservice/settings/config.py" = ["S104"]
|
||||||
|
|
||||||
|
[tool.ruff.pylint]
|
||||||
|
max-args = 15
|
||||||
|
|
||||||
|
[tool.ruff.flake8-bugbear]
|
||||||
|
# Allow default arguments like, e.g., `data: List[str] = fastapi.Query(None)`.
|
||||||
|
extend-immutable-calls = [
|
||||||
|
"fastapi.Depends", "fastapi.Query", "fastapi.Body", "fastapi.File", "fastapi.Cookie", "fastapi.HTTPBearer",
|
||||||
|
"fastapi.Header", "fastapi.Security", "fastapi.Path", "app.api.versioning.APIVersioning", "app.api.openapi.clean_doc",
|
||||||
|
"fastapi.Form"]
|
||||||
|
|
||||||
|
[tool.ruff.flake8-pytest-style]
|
||||||
|
parametrize-names-type = "csv"
|
||||||
|
|
||||||
|
[tool.ruff.mccabe]
|
||||||
|
max-complexity = 15
|
||||||
|
|
||||||
|
[tool.ruff.isort]
|
||||||
|
force-wrap-aliases = true
|
||||||
|
combine-as-imports = true
|
||||||
|
|
||||||
|
[tool.ruff.flake8-quotes]
|
||||||
|
inline-quotes = "double"
|
Loading…
x
Reference in New Issue
Block a user