From 5c210bf2556fd462cadf5050301c6a1b8dcfe34e Mon Sep 17 00:00:00 2001 From: Tecquo <46904988+Tecquo@users.noreply.github.com> Date: Sat, 27 Sep 2025 23:51:49 +0300 Subject: [PATCH 1/7] =?UTF-8?q?GetMods=20(=D0=B1=D0=B5=D0=B7=20=D1=85?= =?UTF-8?q?=D0=B5=D0=BD=D0=B4=D0=BB=D0=B5=D1=80=D0=B0=20=D0=B8=20=D0=BF?= =?UTF-8?q?=D0=B0=D1=80=D1=8B=20=D0=BF=D0=BE=D1=82=D0=B5=D0=BD=D1=86=D0=B8?= =?UTF-8?q?=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE=20=D0=BD=D1=83=D0=B6=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D1=81=D1=82=D1=80=D0=BE=D1=87=D0=B5=D0=BA=20=D0=B2=20?= =?UTF-8?q?=D0=91=D0=94,=20=D0=B6=D0=B4=D1=83=20=D0=A2=D0=B8=D0=BC=D0=BE?= =?UTF-8?q?=D1=85=D1=83),=20=D1=83=D0=B1=D1=80=D0=B0=D0=BB=20=D1=80=D1=83?= =?UTF-8?q?=D0=B3=D0=B0=D0=BD=D1=8C=20=D0=BD=D0=B0=20whitespace,=20=D1=87?= =?UTF-8?q?=D1=91=D1=82=20=D0=BE=D0=BD=20=D0=BD=D0=B0=20SQL=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=81=20=D1=82=D1=80=D0=B8=D0=B3=D0=B3=D0=B5?= =?UTF-8?q?=D1=80=D0=B8=D1=82=D1=81=D1=8F=20=D1=81=20=D0=BD=D0=B8=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 2 +- src/modservice/handler/get_mods.py | 33 ++++++++++++++++ src/modservice/handler/handler.py | 11 ++++++ src/modservice/repository/get_mods.py | 52 +++++++++++++++++++++++++ src/modservice/repository/repository.py | 8 ++++ src/modservice/service/get_mods.py | 9 +++++ src/modservice/service/service.py | 6 +++ 7 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 src/modservice/handler/get_mods.py create mode 100644 src/modservice/repository/get_mods.py create mode 100644 src/modservice/service/get_mods.py diff --git a/pyproject.toml b/pyproject.toml index c2d9236..6d8f28a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ skip = [".venv", "src/modservice/grpc"] [tool.flake8] max-line-length = 120 -extend-ignore = ["E203", "W503"] +extend-ignore = ["E203", "W503", "W291"] per-file-ignores = [ "__init__.py:F401", "tests/*:T20,ARG001" diff --git a/src/modservice/handler/get_mods.py b/src/modservice/handler/get_mods.py new file mode 100644 index 0000000..9400ba8 --- /dev/null +++ b/src/modservice/handler/get_mods.py @@ -0,0 +1,33 @@ +# TODO: Раскомментировать когда Тимоха добавит GetMods в proto файл +# from modservice.grpc import mod_pb2 + +# TODO: Раскомментировать когда Тимоха добавит GetMods в proto файл +# def GetMods( +# service: ModService, +# request: mod_pb2.GetModsRequest, +# context: grpc.ServicerContext, # noqa: ARG001 +# ) -> mod_pb2.GetModsResponse: +# mods_data = service.get_mods() +# +# mods = [] +# for mod_data in mods_data: +# mod = mod_pb2.ModInfo( +# id=mod_data["id"], +# author_id=mod_data["author_id"], +# title=mod_data["title"], +# description=mod_data["description"], +# version=mod_data["version"], +# s3_key=mod_data["s3_key"], +# status=mod_data["status"], +# created_at=mod_data["created_at"].isoformat() if mod_data.get("created_at") else "", +# +# # TODO: Раскомментировать когда добавятся в БД и proto: +# # avatar_url=mod_data.get("avatar_url", ""), +# # download_count=mod_data.get("download_count", 0), +# # rating=mod_data.get("rating", 0.0), +# # tags=mod_data.get("tags", []), +# # updated_at=mod_data["updated_at"].isoformat() if mod_data.get("updated_at") else "", +# ) +# mods.append(mod) +# +# return mod_pb2.GetModsResponse(mods=mods) diff --git a/src/modservice/handler/handler.py b/src/modservice/handler/handler.py index 0fb4edd..338904f 100644 --- a/src/modservice/handler/handler.py +++ b/src/modservice/handler/handler.py @@ -5,6 +5,9 @@ from modservice.handler.get_mod_download_link import ( GetDownloadLink as _get_mod_download_link, ) + +# TODO: Раскомментировать когда Тимоха добавит GetMods в proto файл +# from modservice.handler.get_mods import GetMods as _get_mods from modservice.handler.set_status import SetStatus as _set_status from modservice.service.service import ModService @@ -33,3 +36,11 @@ def SetStatus( context: grpc.ServicerContext, ) -> mod_pb2.SetStatusResponse: return _set_status(self._service, request, context) + + # TODO: Раскомментировать когда Тимоха добавит GetMods в proto файл + # def GetMods( + # self, + # request: mod_pb2.GetModsRequest, + # context: grpc.ServicerContext, + # ) -> mod_pb2.GetModsResponse: + # return _get_mods(self._service, request, context) diff --git a/src/modservice/repository/get_mods.py b/src/modservice/repository/get_mods.py new file mode 100644 index 0000000..15e69e2 --- /dev/null +++ b/src/modservice/repository/get_mods.py @@ -0,0 +1,52 @@ +from typing import Any + +from psycopg2.pool import ThreadedConnectionPool + + +def get_mods( + db_pool: ThreadedConnectionPool, +) -> list[dict[str, Any]]: + conn = db_pool.getconn() + try: + with conn.cursor() as cursor: + # TODO: Когда добавятся поля в БД, добавить: + # - avatar_url + # - download_count + # - rating + # - tags + # - updated_at + cursor.execute( + """ + SELECT + id, + author_id, + title, + description, + version, + s3_key, + status, + created_at + FROM mods + ORDER BY created_at DESC + """ + ) + + results = cursor.fetchall() + + mods = [] + for row in results: + mod = { + "id": row[0], + "author_id": row[1], + "title": row[2], + "description": row[3], + "version": row[4], + "s3_key": row[5], + "status": row[6], + "created_at": row[7], + } + mods.append(mod) + + return mods + finally: + db_pool.putconn(conn) diff --git a/src/modservice/repository/repository.py b/src/modservice/repository/repository.py index 2cdf449..4246aa5 100644 --- a/src/modservice/repository/repository.py +++ b/src/modservice/repository/repository.py @@ -1,9 +1,12 @@ +from typing import Any + from psycopg2.pool import ThreadedConnectionPool from modservice.repository.create_mod import create_mod as _create_mod from modservice.repository.get_mod_s3_key import ( get_mod_s3_key as _get_mod_s3_key, ) +from modservice.repository.get_mods import get_mods as _get_mods from modservice.repository.insert_s3_key import insert_s3_key as _insert_s3_key from modservice.repository.set_status import set_status as _set_status @@ -32,3 +35,8 @@ def get_mod_s3_key(self, mod_id: int) -> str: def set_status(self, mod_id: int, status: str) -> bool: return _set_status(self._db_pool, mod_id, status) + + def get_mods( + self, + ) -> list[dict[str, Any]]: + return _get_mods(self._db_pool) diff --git a/src/modservice/service/get_mods.py b/src/modservice/service/get_mods.py new file mode 100644 index 0000000..b6264d3 --- /dev/null +++ b/src/modservice/service/get_mods.py @@ -0,0 +1,9 @@ +from typing import Any + +from modservice.repository.repository import ModRepository + + +def get_mods( + repo: ModRepository, +) -> list[dict[str, Any]]: + return repo.get_mods() diff --git a/src/modservice/service/service.py b/src/modservice/service/service.py index e972081..207aff0 100644 --- a/src/modservice/service/service.py +++ b/src/modservice/service/service.py @@ -2,6 +2,7 @@ from modservice.repository.repository import ModRepository from modservice.service.create_mod import create_mod as _create_mod +from modservice.service.get_mods import get_mods as _get_mods from modservice.service.s3_service import S3Service from modservice.service.set_status import set_status as _set_status @@ -70,3 +71,8 @@ def get_mod_download_link( def set_status(self, mod_id: int, status: str) -> bool: return _set_status(self._repo, mod_id, status) + + def get_mods( + self, + ) -> list[dict[str, Any]]: + return _get_mods(self._repo) From 7e1884eb8533a9bceeab33182bb9d051cdc3536e Mon Sep 17 00:00:00 2001 From: Tecquo <46904988+Tecquo@users.noreply.github.com> Date: Sun, 28 Sep 2025 23:39:38 +0300 Subject: [PATCH 2/7] =?UTF-8?q?=D0=97=D0=B0=D0=BC=D0=B5=D0=BD=D0=B0=20mod?= =?UTF-8?q?=5Ftitle=20=D0=BD=D0=B0=20title,=20=D0=BA=D0=B0=D0=BA=20=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=BB=D0=BE=20=D0=B2=20protos,=20=D1=80=D0=B0?= =?UTF-8?q?=D1=81=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=B9=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D0=A2=D0=B8=D0=BC?= =?UTF-8?q?=D0=BE=D1=85=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modservice/grpc/mod_pb2.py | 41 ++++++++------- src/modservice/grpc/mod_pb2.pyi | 41 +++++++++++++-- src/modservice/grpc/mod_pb2_grpc.py | 43 ++++++++++++++++ src/modservice/handler/create_mod.py | 2 +- src/modservice/handler/get_mods.py | 66 +++++++++++++------------ src/modservice/handler/handler.py | 17 +++---- src/modservice/repository/create_mod.py | 4 +- src/modservice/repository/get_mods.py | 2 +- src/modservice/repository/model.py | 2 +- src/modservice/repository/repository.py | 4 +- src/modservice/service/create_mod.py | 4 +- src/modservice/service/s3_service.py | 10 ++-- src/modservice/service/service.py | 12 ++--- tests/service/test_s3_service.py | 16 +++--- tests/service/test_service.py | 16 +++--- 15 files changed, 180 insertions(+), 100 deletions(-) diff --git a/src/modservice/grpc/mod_pb2.py b/src/modservice/grpc/mod_pb2.py index 7c0415c..bd7edd2 100644 --- a/src/modservice/grpc/mod_pb2.py +++ b/src/modservice/grpc/mod_pb2.py @@ -22,29 +22,36 @@ _sym_db = _symbol_database.Default() +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tmod.proto\x12\x03mod\"B\n\x10SetStatusRequest\x12\x0e\n\x06mod_id\x18\x01 \x01(\x03\x12\x1e\n\x06status\x18\x02 \x01(\x0e\x32\x0e.mod.ModStatus\"$\n\x11SetStatusResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\"_\n\x10\x43reateModRequest\x12\x11\n\tmod_title\x18\x01 \x01(\t\x12\x11\n\tauthor_id\x18\x02 \x01(\x03\x12\x10\n\x08\x66ilename\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\"G\n\x11\x43reateModResponse\x12\x0e\n\x06mod_id\x18\x01 \x01(\x03\x12\x12\n\nupload_url\x18\x02 \x01(\t\x12\x0e\n\x06s3_key\x18\x03 \x01(\t\"+\n\x19GetModDownloadLinkRequest\x12\x0e\n\x06mod_id\x18\x01 \x01(\x03\".\n\x1aGetModDownloadLinkResponse\x12\x10\n\x08link_url\x18\x01 \x01(\t*n\n\tModStatus\x12\x1a\n\x16MOD_STATUS_UNSPECIFIED\x10\x00\x12\x17\n\x13MOD_STATUS_UPLOADED\x10\x01\x12\x15\n\x11MOD_STATUS_BANNED\x10\x02\x12\x15\n\x11MOD_STATUS_HIDDEN\x10\x03\x32\xdb\x01\n\nModService\x12:\n\tCreateMod\x12\x15.mod.CreateModRequest\x1a\x16.mod.CreateModResponse\x12:\n\tSetStatus\x12\x15.mod.SetStatusRequest\x1a\x16.mod.SetStatusResponse\x12U\n\x12GetModDownloadLink\x12\x1e.mod.GetModDownloadLinkRequest\x1a\x1f.mod.GetModDownloadLinkResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tmod.proto\x12\x03mod\x1a\x1fgoogle/protobuf/timestamp.proto\"\x10\n\x0eGetModsRequest\")\n\x0fGetModsResponse\x12\x16\n\x04mods\x18\x01 \x03(\x0b\x32\x08.mod.Mod\"\xa9\x01\n\x03Mod\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x11\n\tauthor_id\x18\x02 \x01(\x03\x12\r\n\x05title\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12\x0f\n\x07version\x18\x05 \x01(\x03\x12\x1e\n\x06status\x18\x06 \x01(\x0e\x32\x0e.mod.ModStatus\x12.\n\ncreated_at\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"B\n\x10SetStatusRequest\x12\x0e\n\x06mod_id\x18\x01 \x01(\x03\x12\x1e\n\x06status\x18\x02 \x01(\x0e\x32\x0e.mod.ModStatus\"$\n\x11SetStatusResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\"[\n\x10\x43reateModRequest\x12\r\n\x05title\x18\x01 \x01(\t\x12\x11\n\tauthor_id\x18\x02 \x01(\x03\x12\x10\n\x08\x66ilename\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\"G\n\x11\x43reateModResponse\x12\x0e\n\x06mod_id\x18\x01 \x01(\x03\x12\x12\n\nupload_url\x18\x02 \x01(\t\x12\x0e\n\x06s3_key\x18\x03 \x01(\t\"+\n\x19GetModDownloadLinkRequest\x12\x0e\n\x06mod_id\x18\x01 \x01(\x03\".\n\x1aGetModDownloadLinkResponse\x12\x10\n\x08link_url\x18\x01 \x01(\t*n\n\tModStatus\x12\x1a\n\x16MOD_STATUS_UNSPECIFIED\x10\x00\x12\x17\n\x13MOD_STATUS_UPLOADED\x10\x01\x12\x15\n\x11MOD_STATUS_BANNED\x10\x02\x12\x15\n\x11MOD_STATUS_HIDDEN\x10\x03\x32\x91\x02\n\nModService\x12:\n\tCreateMod\x12\x15.mod.CreateModRequest\x1a\x16.mod.CreateModResponse\x12:\n\tSetStatus\x12\x15.mod.SetStatusRequest\x1a\x16.mod.SetStatusResponse\x12U\n\x12GetModDownloadLink\x12\x1e.mod.GetModDownloadLinkRequest\x1a\x1f.mod.GetModDownloadLinkResponse\x12\x34\n\x07GetMods\x12\x13.mod.GetModsRequest\x1a\x14.mod.GetModsResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'mod_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: DESCRIPTOR._loaded_options = None - _globals['_MODSTATUS']._serialized_start=387 - _globals['_MODSTATUS']._serialized_end=497 - _globals['_SETSTATUSREQUEST']._serialized_start=18 - _globals['_SETSTATUSREQUEST']._serialized_end=84 - _globals['_SETSTATUSRESPONSE']._serialized_start=86 - _globals['_SETSTATUSRESPONSE']._serialized_end=122 - _globals['_CREATEMODREQUEST']._serialized_start=124 - _globals['_CREATEMODREQUEST']._serialized_end=219 - _globals['_CREATEMODRESPONSE']._serialized_start=221 - _globals['_CREATEMODRESPONSE']._serialized_end=292 - _globals['_GETMODDOWNLOADLINKREQUEST']._serialized_start=294 - _globals['_GETMODDOWNLOADLINKREQUEST']._serialized_end=337 - _globals['_GETMODDOWNLOADLINKRESPONSE']._serialized_start=339 - _globals['_GETMODDOWNLOADLINKRESPONSE']._serialized_end=385 - _globals['_MODSERVICE']._serialized_start=500 - _globals['_MODSERVICE']._serialized_end=719 + _globals['_MODSTATUS']._serialized_start=649 + _globals['_MODSTATUS']._serialized_end=759 + _globals['_GETMODSREQUEST']._serialized_start=51 + _globals['_GETMODSREQUEST']._serialized_end=67 + _globals['_GETMODSRESPONSE']._serialized_start=69 + _globals['_GETMODSRESPONSE']._serialized_end=110 + _globals['_MOD']._serialized_start=113 + _globals['_MOD']._serialized_end=282 + _globals['_SETSTATUSREQUEST']._serialized_start=284 + _globals['_SETSTATUSREQUEST']._serialized_end=350 + _globals['_SETSTATUSRESPONSE']._serialized_start=352 + _globals['_SETSTATUSRESPONSE']._serialized_end=388 + _globals['_CREATEMODREQUEST']._serialized_start=390 + _globals['_CREATEMODREQUEST']._serialized_end=481 + _globals['_CREATEMODRESPONSE']._serialized_start=483 + _globals['_CREATEMODRESPONSE']._serialized_end=554 + _globals['_GETMODDOWNLOADLINKREQUEST']._serialized_start=556 + _globals['_GETMODDOWNLOADLINKREQUEST']._serialized_end=599 + _globals['_GETMODDOWNLOADLINKRESPONSE']._serialized_start=601 + _globals['_GETMODDOWNLOADLINKRESPONSE']._serialized_end=647 + _globals['_MODSERVICE']._serialized_start=762 + _globals['_MODSERVICE']._serialized_end=1035 # @@protoc_insertion_point(module_scope) diff --git a/src/modservice/grpc/mod_pb2.pyi b/src/modservice/grpc/mod_pb2.pyi index 0580a95..40a1cb7 100644 --- a/src/modservice/grpc/mod_pb2.pyi +++ b/src/modservice/grpc/mod_pb2.pyi @@ -1,6 +1,11 @@ +import datetime + +from google.protobuf import timestamp_pb2 as _timestamp_pb2 +from google.protobuf.internal import containers as _containers from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -16,6 +21,34 @@ MOD_STATUS_UPLOADED: ModStatus MOD_STATUS_BANNED: ModStatus MOD_STATUS_HIDDEN: ModStatus +class GetModsRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class GetModsResponse(_message.Message): + __slots__ = ("mods",) + MODS_FIELD_NUMBER: _ClassVar[int] + mods: _containers.RepeatedCompositeFieldContainer[Mod] + def __init__(self, mods: _Optional[_Iterable[_Union[Mod, _Mapping]]] = ...) -> None: ... + +class Mod(_message.Message): + __slots__ = ("id", "author_id", "title", "description", "version", "status", "created_at") + ID_FIELD_NUMBER: _ClassVar[int] + AUTHOR_ID_FIELD_NUMBER: _ClassVar[int] + TITLE_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + VERSION_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + id: int + author_id: int + title: str + description: str + version: int + status: ModStatus + created_at: _timestamp_pb2.Timestamp + def __init__(self, id: _Optional[int] = ..., author_id: _Optional[int] = ..., title: _Optional[str] = ..., description: _Optional[str] = ..., version: _Optional[int] = ..., status: _Optional[_Union[ModStatus, str]] = ..., created_at: _Optional[_Union[datetime.datetime, _timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + class SetStatusRequest(_message.Message): __slots__ = ("mod_id", "status") MOD_ID_FIELD_NUMBER: _ClassVar[int] @@ -31,16 +64,16 @@ class SetStatusResponse(_message.Message): def __init__(self, success: bool = ...) -> None: ... class CreateModRequest(_message.Message): - __slots__ = ("mod_title", "author_id", "filename", "description") - MOD_TITLE_FIELD_NUMBER: _ClassVar[int] + __slots__ = ("title", "author_id", "filename", "description") + TITLE_FIELD_NUMBER: _ClassVar[int] AUTHOR_ID_FIELD_NUMBER: _ClassVar[int] FILENAME_FIELD_NUMBER: _ClassVar[int] DESCRIPTION_FIELD_NUMBER: _ClassVar[int] - mod_title: str + title: str author_id: int filename: str description: str - def __init__(self, mod_title: _Optional[str] = ..., author_id: _Optional[int] = ..., filename: _Optional[str] = ..., description: _Optional[str] = ...) -> None: ... + def __init__(self, title: _Optional[str] = ..., author_id: _Optional[int] = ..., filename: _Optional[str] = ..., description: _Optional[str] = ...) -> None: ... class CreateModResponse(_message.Message): __slots__ = ("mod_id", "upload_url", "s3_key") diff --git a/src/modservice/grpc/mod_pb2_grpc.py b/src/modservice/grpc/mod_pb2_grpc.py index 7aefcbc..008688a 100644 --- a/src/modservice/grpc/mod_pb2_grpc.py +++ b/src/modservice/grpc/mod_pb2_grpc.py @@ -49,6 +49,11 @@ def __init__(self, channel): request_serializer=mod__pb2.GetModDownloadLinkRequest.SerializeToString, response_deserializer=mod__pb2.GetModDownloadLinkResponse.FromString, _registered_method=True) + self.GetMods = channel.unary_unary( + '/mod.ModService/GetMods', + request_serializer=mod__pb2.GetModsRequest.SerializeToString, + response_deserializer=mod__pb2.GetModsResponse.FromString, + _registered_method=True) class ModServiceServicer(object): @@ -72,6 +77,12 @@ def GetModDownloadLink(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def GetMods(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_ModServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -90,6 +101,11 @@ def add_ModServiceServicer_to_server(servicer, server): request_deserializer=mod__pb2.GetModDownloadLinkRequest.FromString, response_serializer=mod__pb2.GetModDownloadLinkResponse.SerializeToString, ), + 'GetMods': grpc.unary_unary_rpc_method_handler( + servicer.GetMods, + request_deserializer=mod__pb2.GetModsRequest.FromString, + response_serializer=mod__pb2.GetModsResponse.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'mod.ModService', rpc_method_handlers) @@ -181,3 +197,30 @@ def GetModDownloadLink(request, timeout, metadata, _registered_method=True) + + @staticmethod + def GetMods(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/mod.ModService/GetMods', + mod__pb2.GetModsRequest.SerializeToString, + mod__pb2.GetModsResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/src/modservice/handler/create_mod.py b/src/modservice/handler/create_mod.py index 2598916..323353d 100644 --- a/src/modservice/handler/create_mod.py +++ b/src/modservice/handler/create_mod.py @@ -11,7 +11,7 @@ def CreateMod( ) -> mod_pb2.CreateModResponse: # Создаем мод mod_id, s3_key, upload_url = service.create_mod( - request.mod_title, + request.title, request.author_id, request.description, ) diff --git a/src/modservice/handler/get_mods.py b/src/modservice/handler/get_mods.py index 9400ba8..3e033ec 100644 --- a/src/modservice/handler/get_mods.py +++ b/src/modservice/handler/get_mods.py @@ -1,33 +1,35 @@ -# TODO: Раскомментировать когда Тимоха добавит GetMods в proto файл -# from modservice.grpc import mod_pb2 +import grpc -# TODO: Раскомментировать когда Тимоха добавит GetMods в proto файл -# def GetMods( -# service: ModService, -# request: mod_pb2.GetModsRequest, -# context: grpc.ServicerContext, # noqa: ARG001 -# ) -> mod_pb2.GetModsResponse: -# mods_data = service.get_mods() -# -# mods = [] -# for mod_data in mods_data: -# mod = mod_pb2.ModInfo( -# id=mod_data["id"], -# author_id=mod_data["author_id"], -# title=mod_data["title"], -# description=mod_data["description"], -# version=mod_data["version"], -# s3_key=mod_data["s3_key"], -# status=mod_data["status"], -# created_at=mod_data["created_at"].isoformat() if mod_data.get("created_at") else "", -# -# # TODO: Раскомментировать когда добавятся в БД и proto: -# # avatar_url=mod_data.get("avatar_url", ""), -# # download_count=mod_data.get("download_count", 0), -# # rating=mod_data.get("rating", 0.0), -# # tags=mod_data.get("tags", []), -# # updated_at=mod_data["updated_at"].isoformat() if mod_data.get("updated_at") else "", -# ) -# mods.append(mod) -# -# return mod_pb2.GetModsResponse(mods=mods) +from modservice.grpc import mod_pb2 +from modservice.service.service import ModService + + +def GetMods( + service: ModService, + request: mod_pb2.GetModsRequest, + context: grpc.ServicerContext, # noqa: ARG001 +) -> mod_pb2.GetModsResponse: + mods_data = service.get_mods() + + mods = [] + for mod_data in mods_data: + mod = mod_pb2.Mod( + id=mod_data["id"], + author_id=mod_data["author_id"], + title=mod_data["title"], + description=mod_data["description"], + version=mod_data["version"], + status=mod_data["status"], + created_at=mod_data["created_at"] + if mod_data.get("created_at") + else None, + # TODO: Раскомментировать когда добавятся в БД и proto: + # avatar_url=mod_data.get("avatar_url", ""), + # download_count=mod_data.get("download_count", 0), + # rating=mod_data.get("rating", 0.0), + # tags=mod_data.get("tags", []), + # updated_at=mod_data["updated_at"] if mod_data.get("updated_at") else None, + ) + mods.append(mod) + + return mod_pb2.GetModsResponse(mods=mods) diff --git a/src/modservice/handler/handler.py b/src/modservice/handler/handler.py index 338904f..a1093c6 100644 --- a/src/modservice/handler/handler.py +++ b/src/modservice/handler/handler.py @@ -5,9 +5,7 @@ from modservice.handler.get_mod_download_link import ( GetDownloadLink as _get_mod_download_link, ) - -# TODO: Раскомментировать когда Тимоха добавит GetMods в proto файл -# from modservice.handler.get_mods import GetMods as _get_mods +from modservice.handler.get_mods import GetMods as _get_mods from modservice.handler.set_status import SetStatus as _set_status from modservice.service.service import ModService @@ -37,10 +35,9 @@ def SetStatus( ) -> mod_pb2.SetStatusResponse: return _set_status(self._service, request, context) - # TODO: Раскомментировать когда Тимоха добавит GetMods в proto файл - # def GetMods( - # self, - # request: mod_pb2.GetModsRequest, - # context: grpc.ServicerContext, - # ) -> mod_pb2.GetModsResponse: - # return _get_mods(self._service, request, context) + def GetMods( + self, + request: mod_pb2.GetModsRequest, + context: grpc.ServicerContext, + ) -> mod_pb2.GetModsResponse: + return _get_mods(self._service, request, context) diff --git a/src/modservice/repository/create_mod.py b/src/modservice/repository/create_mod.py index a8f8c49..c551ea5 100644 --- a/src/modservice/repository/create_mod.py +++ b/src/modservice/repository/create_mod.py @@ -3,7 +3,7 @@ def create_mod( db_pool: ThreadedConnectionPool, - mod_title: str, # noqa: ARG001 + title: str, # noqa: ARG001 author_id: int, # noqa: ARG001 description: str, # noqa: ARG001 ) -> int: @@ -18,7 +18,7 @@ def create_mod( """, ( author_id, - mod_title, + title, description, 1, ), diff --git a/src/modservice/repository/get_mods.py b/src/modservice/repository/get_mods.py index 15e69e2..83a6a07 100644 --- a/src/modservice/repository/get_mods.py +++ b/src/modservice/repository/get_mods.py @@ -9,7 +9,7 @@ def get_mods( conn = db_pool.getconn() try: with conn.cursor() as cursor: - # TODO: Когда добавятся поля в БД, добавить: + # NOTE: Поля для будущего добавления в БД: # - avatar_url # - download_count # - rating diff --git a/src/modservice/repository/model.py b/src/modservice/repository/model.py index 859cf86..1cb555d 100644 --- a/src/modservice/repository/model.py +++ b/src/modservice/repository/model.py @@ -3,7 +3,7 @@ @dataclass class Mod: - mod_title: str + title: str author_id: int filename: str description: str diff --git a/src/modservice/repository/repository.py b/src/modservice/repository/repository.py index 4246aa5..4ae4773 100644 --- a/src/modservice/repository/repository.py +++ b/src/modservice/repository/repository.py @@ -17,11 +17,11 @@ def __init__(self, db_pool: ThreadedConnectionPool): def create_mod( self, - mod_title: str, + title: str, author_id: int, description: str, ) -> int: - return _create_mod(self._db_pool, mod_title, author_id, description) + return _create_mod(self._db_pool, title, author_id, description) def insert_s3_key( self, diff --git a/src/modservice/service/create_mod.py b/src/modservice/service/create_mod.py index aa88bdf..f09977a 100644 --- a/src/modservice/service/create_mod.py +++ b/src/modservice/service/create_mod.py @@ -5,11 +5,11 @@ def create_mod( repo: ModRepository, s3_service: S3Service, - mod_title: str, + title: str, author_id: int, description: str, ) -> tuple[int, str, str]: - mod_id = repo.create_mod(mod_title, author_id, description) + mod_id = repo.create_mod(title, author_id, description) s3_key = repo.insert_s3_key(mod_id, author_id) diff --git a/src/modservice/service/s3_service.py b/src/modservice/service/s3_service.py index 4c36b3d..330e387 100644 --- a/src/modservice/service/s3_service.py +++ b/src/modservice/service/s3_service.py @@ -14,14 +14,14 @@ def __init__(self, s3_client: S3Client) -> None: self._s3_client = s3_client def generate_s3_key( - self, author_id: int, filename: str, mod_title: str | None = None + self, author_id: int, filename: str, title: str | None = None ) -> str: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") safe_filename = self._sanitize_filename(filename) - if mod_title: - safe_title = self._sanitize_title(mod_title) + if title: + safe_title = self._sanitize_title(title) file_ext = os.path.splitext(safe_filename)[1] safe_filename = safe_title + file_ext @@ -85,7 +85,7 @@ def generate_upload_url( self, author_id: int, filename: str, - mod_title: str | None = None, + title: str | None = None, expiration: int = 3600, content_type: str | None = None, ) -> tuple[str, str]: @@ -93,7 +93,7 @@ def generate_upload_url( f"Генерируем Presigned PUT URL для автора {author_id}, файл: {filename}" ) - s3_key = self.generate_s3_key(author_id, filename, mod_title) + s3_key = self.generate_s3_key(author_id, filename, title) if content_type is None: content_type = self._detect_content_type(filename) diff --git a/src/modservice/service/service.py b/src/modservice/service/service.py index 207aff0..06e9013 100644 --- a/src/modservice/service/service.py +++ b/src/modservice/service/service.py @@ -13,31 +13,31 @@ def __init__(self, repo: ModRepository, s3_service: S3Service) -> None: self._s3_service = s3_service def create_mod( - self, mod_title: str, author_id: int, description: str + self, title: str, author_id: int, description: str ) -> tuple[int, str, str]: return _create_mod( self._repo, self._s3_service, - mod_title, + title, author_id, description, ) def generate_s3_key( - self, author_id: int, filename: str, mod_title: str | None = None + self, author_id: int, filename: str, title: str | None = None ) -> str: - return self._s3_service.generate_s3_key(author_id, filename, mod_title) + return self._s3_service.generate_s3_key(author_id, filename, title) def generate_upload_url( self, author_id: int, filename: str, - mod_title: str | None = None, + title: str | None = None, expiration: int = 3600, content_type: str | None = None, ) -> tuple[str, str]: return self._s3_service.generate_upload_url( - author_id, filename, mod_title, expiration, content_type + author_id, filename, title, expiration, content_type ) def get_file_info_from_s3_key(self, s3_key: str) -> dict[str, Any]: diff --git a/tests/service/test_s3_service.py b/tests/service/test_s3_service.py index 72a90c7..8eff9a1 100644 --- a/tests/service/test_s3_service.py +++ b/tests/service/test_s3_service.py @@ -32,15 +32,13 @@ def test_generate_s3_key_basic(self, s3_service: S3Service) -> None: assert s3_key.endswith("_test_mod.zip") assert "_" in s3_key # Должен содержать timestamp - def test_generate_s3_key_with_mod_title( - self, s3_service: S3Service - ) -> None: + def test_generate_s3_key_with_title(self, s3_service: S3Service) -> None: """Тест генерации S3 ключа с заголовком мода""" author_id = 456 filename = "awesome_mod.rar" - mod_title = "Awesome Mod v2.0" + title = "Awesome Mod v2.0" - s3_key = s3_service.generate_s3_key(author_id, filename, mod_title) + s3_key = s3_service.generate_s3_key(author_id, filename, title) assert s3_key.startswith(f"{author_id}/") assert s3_key.endswith("_Awesome_Mod_v2.0.rar") @@ -52,9 +50,9 @@ def test_generate_s3_key_special_characters( """Тест очистки специальных символов в S3 ключе""" author_id = 789 filename = "test@mod#.zip" - mod_title = "Test@Mod# v1.0!" + title = "Test@Mod# v1.0!" - s3_key = s3_service.generate_s3_key(author_id, filename, mod_title) + s3_key = s3_service.generate_s3_key(author_id, filename, title) assert s3_key.startswith(f"{author_id}/") assert s3_key.endswith("_TestMod_v1.0.zip") @@ -82,7 +80,7 @@ def test_generate_upload_url( """Тест генерации URL для загрузки с автогенерацией ключа""" author_id = 123 filename = "test_mod.zip" - mod_title = "Test Mod" + title = "Test Mod" expiration = 7200 content_type = "application/zip" @@ -92,7 +90,7 @@ def test_generate_upload_url( ) s3_key, presigned_url = s3_service.generate_upload_url( - author_id, filename, mod_title, expiration, content_type + author_id, filename, title, expiration, content_type ) assert s3_key.startswith(f"{author_id}/") diff --git a/tests/service/test_service.py b/tests/service/test_service.py index d584b34..2d13cb7 100644 --- a/tests/service/test_service.py +++ b/tests/service/test_service.py @@ -28,16 +28,16 @@ def test_generate_s3_key( ) -> None: author_id = 123 filename = "test_mod.zip" - mod_title = "Test Mod" + title = "Test Mod" expected_key = "mods/123/20231201_120000_Test_Mod.zip" mock_s3_service.generate_s3_key.return_value = expected_key - result = mod_service.generate_s3_key(author_id, filename, mod_title) + result = mod_service.generate_s3_key(author_id, filename, title) assert result == expected_key mock_s3_service.generate_s3_key.assert_called_once_with( - author_id, filename, mod_title + author_id, filename, title ) def test_generate_upload_url( @@ -45,7 +45,7 @@ def test_generate_upload_url( ) -> None: author_id = 456 filename = "awesome_mod.rar" - mod_title = "Awesome Mod" + title = "Awesome Mod" expiration = 7200 content_type = "application/x-rar-compressed" @@ -58,14 +58,14 @@ def test_generate_upload_url( ) s3_key, presigned_url = mod_service.generate_upload_url( - author_id, filename, mod_title, expiration, content_type + author_id, filename, title, expiration, content_type ) assert s3_key == expected_s3_key assert presigned_url == expected_url mock_s3_service.generate_upload_url.assert_called_once_with( - author_id, filename, mod_title, expiration, content_type + author_id, filename, title, expiration, content_type ) def test_get_file_info_from_s3_key( @@ -91,7 +91,7 @@ def test_get_file_info_from_s3_key( def test_create_mod_delegates_to_repository( self, mod_service: ModService ) -> None: - mod_title = "Test Mod" + title = "Test Mod" author_id = 123 description = "Test description" @@ -109,6 +109,6 @@ def test_create_mod_delegates_to_repository( mock_create_mod, ) - result = mod_service.create_mod(mod_title, author_id, description) + result = mod_service.create_mod(title, author_id, description) assert result == expected_result From 0bedd00b44289782599c21aaf0ce1c0c8c0162c7 Mon Sep 17 00:00:00 2001 From: Andrey Kataev Date: Mon, 29 Sep 2025 01:30:04 +0300 Subject: [PATCH 3/7] =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D1=8B=D0=B9=20=D1=82?= =?UTF-8?q?=D0=B5=D0=B3=20=D0=B8=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- justfile | 2 +- pyproject.toml | 2 +- src/modservice/handler/get_mods.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/justfile b/justfile index 4c567c8..01122db 100644 --- a/justfile +++ b/justfile @@ -3,7 +3,7 @@ set dotenv-load := true COMMON_URL := 'https://raw.githubusercontent.com/esclient/tools/refs/heads/main/python/common.just' -PROTO_TAG := 'v0.1.1' +PROTO_TAG := 'v0.1.2' PROTO_NAME := 'mod.proto' TMP_DIR := '.proto' OUT_DIR := 'src/modservice/grpc' diff --git a/pyproject.toml b/pyproject.toml index 6d8f28a..c2d9236 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ skip = [".venv", "src/modservice/grpc"] [tool.flake8] max-line-length = 120 -extend-ignore = ["E203", "W503", "W291"] +extend-ignore = ["E203", "W503"] per-file-ignores = [ "__init__.py:F401", "tests/*:T20,ARG001" diff --git a/src/modservice/handler/get_mods.py b/src/modservice/handler/get_mods.py index 3e033ec..5ca521f 100644 --- a/src/modservice/handler/get_mods.py +++ b/src/modservice/handler/get_mods.py @@ -6,7 +6,7 @@ def GetMods( service: ModService, - request: mod_pb2.GetModsRequest, + request: mod_pb2.GetModsRequest, # noqa: ARG001 context: grpc.ServicerContext, # noqa: ARG001 ) -> mod_pb2.GetModsResponse: mods_data = service.get_mods() From 5499c352321aa7ed9ad80b5e95696ae41939ba5c Mon Sep 17 00:00:00 2001 From: Andrey Kataev Date: Mon, 29 Sep 2025 13:30:09 +0300 Subject: [PATCH 4/7] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BB=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=B1=D0=B5=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modservice/repository/get_mods.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modservice/repository/get_mods.py b/src/modservice/repository/get_mods.py index 83a6a07..96722c6 100644 --- a/src/modservice/repository/get_mods.py +++ b/src/modservice/repository/get_mods.py @@ -17,7 +17,7 @@ def get_mods( # - updated_at cursor.execute( """ - SELECT + SELECT id, author_id, title, From f6eecec23564015133531edbea99c57dc5827a5b Mon Sep 17 00:00:00 2001 From: Tecquo <46904988+Tecquo@users.noreply.github.com> Date: Sat, 27 Sep 2025 23:51:49 +0300 Subject: [PATCH 5/7] =?UTF-8?q?GetMods=20(=D0=B1=D0=B5=D0=B7=20=D1=85?= =?UTF-8?q?=D0=B5=D0=BD=D0=B4=D0=BB=D0=B5=D1=80=D0=B0=20=D0=B8=20=D0=BF?= =?UTF-8?q?=D0=B0=D1=80=D1=8B=20=D0=BF=D0=BE=D1=82=D0=B5=D0=BD=D1=86=D0=B8?= =?UTF-8?q?=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE=20=D0=BD=D1=83=D0=B6=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D1=81=D1=82=D1=80=D0=BE=D1=87=D0=B5=D0=BA=20=D0=B2=20?= =?UTF-8?q?=D0=91=D0=94,=20=D0=B6=D0=B4=D1=83=20=D0=A2=D0=B8=D0=BC=D0=BE?= =?UTF-8?q?=D1=85=D1=83),=20=D1=83=D0=B1=D1=80=D0=B0=D0=BB=20=D1=80=D1=83?= =?UTF-8?q?=D0=B3=D0=B0=D0=BD=D1=8C=20=D0=BD=D0=B0=20whitespace,=20=D1=87?= =?UTF-8?q?=D1=91=D1=82=20=D0=BE=D0=BD=20=D0=BD=D0=B0=20SQL=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=81=20=D1=82=D1=80=D0=B8=D0=B3=D0=B3=D0=B5?= =?UTF-8?q?=D1=80=D0=B8=D1=82=D1=81=D1=8F=20=D1=81=20=D0=BD=D0=B8=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 2 +- src/modservice/handler/handler.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c2d9236..6d8f28a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ skip = [".venv", "src/modservice/grpc"] [tool.flake8] max-line-length = 120 -extend-ignore = ["E203", "W503"] +extend-ignore = ["E203", "W503", "W291"] per-file-ignores = [ "__init__.py:F401", "tests/*:T20,ARG001" diff --git a/src/modservice/handler/handler.py b/src/modservice/handler/handler.py index a1093c6..aef12d9 100644 --- a/src/modservice/handler/handler.py +++ b/src/modservice/handler/handler.py @@ -5,7 +5,13 @@ from modservice.handler.get_mod_download_link import ( GetDownloadLink as _get_mod_download_link, ) +<<<<<<< HEAD from modservice.handler.get_mods import GetMods as _get_mods +======= + +# TODO: Раскомментировать когда Тимоха добавит GetMods в proto файл +# from modservice.handler.get_mods import GetMods as _get_mods +>>>>>>> bb6b9b7 (GetMods (без хендлера и пары потенциально нужных строчек в БД, жду Тимоху), убрал ругань на whitespace, чёт он на SQL запрос триггерится с ним) from modservice.handler.set_status import SetStatus as _set_status from modservice.service.service import ModService From 2392724c41c0536c2da33a64e33d842c8c941be0 Mon Sep 17 00:00:00 2001 From: Tecquo <46904988+Tecquo@users.noreply.github.com> Date: Sun, 28 Sep 2025 23:39:38 +0300 Subject: [PATCH 6/7] =?UTF-8?q?=D0=97=D0=B0=D0=BC=D0=B5=D0=BD=D0=B0=20mod?= =?UTF-8?q?=5Ftitle=20=D0=BD=D0=B0=20title,=20=D0=BA=D0=B0=D0=BA=20=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=BB=D0=BE=20=D0=B2=20protos,=20=D1=80=D0=B0?= =?UTF-8?q?=D1=81=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=B9=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D0=A2=D0=B8=D0=BC?= =?UTF-8?q?=D0=BE=D1=85=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modservice/handler/handler.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/modservice/handler/handler.py b/src/modservice/handler/handler.py index aef12d9..a1093c6 100644 --- a/src/modservice/handler/handler.py +++ b/src/modservice/handler/handler.py @@ -5,13 +5,7 @@ from modservice.handler.get_mod_download_link import ( GetDownloadLink as _get_mod_download_link, ) -<<<<<<< HEAD from modservice.handler.get_mods import GetMods as _get_mods -======= - -# TODO: Раскомментировать когда Тимоха добавит GetMods в proto файл -# from modservice.handler.get_mods import GetMods as _get_mods ->>>>>>> bb6b9b7 (GetMods (без хендлера и пары потенциально нужных строчек в БД, жду Тимоху), убрал ругань на whitespace, чёт он на SQL запрос триггерится с ним) from modservice.handler.set_status import SetStatus as _set_status from modservice.service.service import ModService From 33454515281def7d51f125cb9f6935c37e15f0e3 Mon Sep 17 00:00:00 2001 From: Andrey Kataev Date: Mon, 29 Sep 2025 01:30:04 +0300 Subject: [PATCH 7/7] =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D1=8B=D0=B9=20=D1=82?= =?UTF-8?q?=D0=B5=D0=B3=20=D0=B8=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6d8f28a..c2d9236 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ skip = [".venv", "src/modservice/grpc"] [tool.flake8] max-line-length = 120 -extend-ignore = ["E203", "W503", "W291"] +extend-ignore = ["E203", "W503"] per-file-ignores = [ "__init__.py:F401", "tests/*:T20,ARG001"