diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9f959c6b..96a55cfc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -78,11 +78,13 @@ jobs: - name: Check out source code uses: actions/checkout@v4 - - name: Install ruff - run: pip install ruff + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' - - name: Ruff lint - run: ruff check . + - name: Install pre-commit + run: pip install pre-commit - - name: Ruff format - run: ruff format --check --diff . + - name: Run pre-commit hooks + run: pre-commit run --all-files --show-diff-on-failure diff --git a/api/config.py b/api/config.py index 111357a0..1bf9ed8e 100644 --- a/api/config.py +++ b/api/config.py @@ -9,7 +9,6 @@ from pydantic_settings import BaseSettings -# pylint: disable=too-few-public-methods class AuthSettings(BaseSettings): """Authentication settings""" @@ -21,7 +20,6 @@ class AuthSettings(BaseSettings): public_base_url: str | None = None -# pylint: disable=too-few-public-methods class PubSubSettings(BaseSettings): """Pub/Sub settings loaded from the environment""" @@ -35,7 +33,6 @@ class PubSubSettings(BaseSettings): subscriber_state_ttl_days: int = 30 # Cleanup unused subscriber states -# pylint: disable=too-few-public-methods class EmailSettings(BaseSettings): """Email settings""" diff --git a/api/email_sender.py b/api/email_sender.py index 1afb851d..5318c159 100644 --- a/api/email_sender.py +++ b/api/email_sender.py @@ -17,7 +17,7 @@ from .config import EmailSettings -class EmailSender: # pylint: disable=too-few-public-methods +class EmailSender: """Class to send email report using SMTP""" def __init__(self): diff --git a/api/main.py b/api/main.py index efd39c75..27a39548 100644 --- a/api/main.py +++ b/api/main.py @@ -5,8 +5,6 @@ # Author: Jeny Sadadia # Author: Denys Fedoryshchenko -# pylint: disable=unused-argument,global-statement,too-many-lines - """KernelCI API main module""" import asyncio @@ -120,7 +118,7 @@ def _validate_startup_environment(): @asynccontextmanager -async def lifespan(app: FastAPI): # pylint: disable=redefined-outer-name +async def lifespan(app: FastAPI): """Lifespan functions for startup and shutdown events""" await pubsub_startup() await create_indexes() @@ -139,7 +137,7 @@ async def lifespan(app: FastAPI): # pylint: disable=redefined-outer-name app = FastAPI(lifespan=lifespan, debug=True, docs_url=None, redoc_url=None) db = Database(service=os.getenv("MONGO_SERVICE", DEFAULT_MONGO_SERVICE)) auth = Authentication(token_url="user/login") -pubsub = None # pylint: disable=invalid-name +pubsub = None auth_backend = auth.get_user_authentication_backend() fastapi_users_instance = FastAPIUsers[User, PydanticObjectId]( @@ -151,7 +149,7 @@ async def lifespan(app: FastAPI): # pylint: disable=redefined-outer-name async def pubsub_startup(): """Startup event handler to create Pub/Sub object""" - global pubsub # pylint: disable=invalid-name + global pubsub pubsub = await PubSub.create() @@ -557,7 +555,7 @@ async def invite_user( invite_url, ) email_sent = True - except Exception as exc: # pylint: disable=broad-exception-caught + except Exception as exc: print(f"Failed to send invite email: {exc}") return UserInviteResponse( @@ -640,7 +638,7 @@ async def accept_invite(accept: InviteAcceptRequest): try: await user_manager.send_invite_accepted_email(updated_user) - except Exception as exc: # pylint: disable=broad-exception-caught + except Exception as exc: print(f"Failed to send invite accepted email: {exc}") return updated_user diff --git a/api/models.py b/api/models.py index 37390837..65ab0afb 100644 --- a/api/models.py +++ b/api/models.py @@ -3,12 +3,6 @@ # Copyright (C) 2023 Collabora Limited # Author: Jeny Sadadia -# Disable flag as user models don't require any public methods -# at the moment -# pylint: disable=too-few-public-methods - -# pylint: disable=no-name-in-module - """Server-side model definitions""" from datetime import datetime @@ -120,7 +114,7 @@ class UserGroupCreateRequest(BaseModel): class User( BeanieBaseUser, - Document, # pylint: disable=too-many-ancestors + Document, DatabaseModel, ): """API User model""" @@ -131,7 +125,7 @@ class User( ) @field_validator("groups") - def validate_groups(cls, groups): # pylint: disable=no-self-argument + def validate_groups(cls, groups): """Unique group constraint""" unique_names = {group.name for group in groups} if len(unique_names) != len(groups): @@ -159,7 +153,7 @@ class UserRead(schemas.BaseUser[PydanticObjectId], ModelId): groups: List[UserGroup] = Field(default=[]) @field_validator("groups") - def validate_groups(cls, groups): # pylint: disable=no-self-argument + def validate_groups(cls, groups): """Unique group constraint""" unique_names = {group.name for group in groups} if len(unique_names) != len(groups): @@ -174,7 +168,7 @@ class UserCreateRequest(schemas.BaseUserCreate): groups: List[str] = Field(default=[]) @field_validator("groups") - def validate_groups(cls, groups): # pylint: disable=no-self-argument + def validate_groups(cls, groups): """Unique group constraint""" unique_names = set(groups) if len(unique_names) != len(groups): @@ -189,7 +183,7 @@ class UserCreate(schemas.BaseUserCreate): groups: List[UserGroup] = Field(default=[]) @field_validator("groups") - def validate_groups(cls, groups): # pylint: disable=no-self-argument + def validate_groups(cls, groups): """Unique group constraint""" unique_names = {group.name for group in groups} if len(unique_names) != len(groups): @@ -206,7 +200,7 @@ class UserUpdateRequest(schemas.BaseUserUpdate): groups: List[str] = Field(default=[]) @field_validator("groups") - def validate_groups(cls, groups): # pylint: disable=no-self-argument + def validate_groups(cls, groups): """Unique group constraint""" unique_names = set(groups) if len(unique_names) != len(groups): @@ -223,7 +217,7 @@ class UserUpdate(schemas.BaseUserUpdate): groups: List[UserGroup] = Field(default=[]) @field_validator("groups") - def validate_groups(cls, groups): # pylint: disable=no-self-argument + def validate_groups(cls, groups): """Unique group constraint""" unique_names = {group.name for group in groups} if len(unique_names) != len(groups): @@ -246,7 +240,7 @@ class UserInviteRequest(BaseModel): resend_if_exists: bool = False @field_validator("groups") - def validate_groups(cls, groups): # pylint: disable=no-self-argument + def validate_groups(cls, groups): """Unique group constraint""" unique_names = set(groups) if len(unique_names) != len(groups): diff --git a/api/pubsub_mongo.py b/api/pubsub_mongo.py index 308a086b..dafc0954 100644 --- a/api/pubsub_mongo.py +++ b/api/pubsub_mongo.py @@ -3,7 +3,6 @@ # Copyright (C) 2025 Collabora Limited # Author: Denys Fedoryshchenko -# pylint: disable=duplicate-code # Note: This module intentionally shares interface code with pubsub.py # as both implement the same PubSub API contract @@ -35,7 +34,7 @@ logger = logging.getLogger(__name__) -class PubSub: # pylint: disable=too-many-instance-attributes +class PubSub: """Hybrid Pub/Sub implementation with MongoDB durability Supports two modes: @@ -328,7 +327,6 @@ def _eventhistory_to_cloudevent(self, event: Dict) -> str: ce = CloudEvent(attributes=attributes, data=event.get("data", {})) return to_json(ce).decode("utf-8") - # pylint: disable=too-many-arguments async def _get_missed_events( self, channel: str, @@ -410,7 +408,6 @@ async def subscribe( return sub - # pylint: disable=too-many-arguments async def _setup_durable_subscription( self, sub_id: int, diff --git a/tests/e2e_tests/listen_handler.py b/tests/e2e_tests/listen_handler.py index f32e410d..e65d35a4 100644 --- a/tests/e2e_tests/listen_handler.py +++ b/tests/e2e_tests/listen_handler.py @@ -23,7 +23,7 @@ def create_listen_task(test_async_client, subscription_id): listen_path, headers={ "Accept": "application/json", - "Authorization": f"Bearer {pytest.BEARER_TOKEN}", # pylint: disable=no-member + "Authorization": f"Bearer {pytest.BEARER_TOKEN}", }, ) ) diff --git a/tests/e2e_tests/test_node_handler.py b/tests/e2e_tests/test_node_handler.py index f331a343..c192151e 100644 --- a/tests/e2e_tests/test_node_handler.py +++ b/tests/e2e_tests/test_node_handler.py @@ -24,7 +24,7 @@ async def create_node(test_async_client, node): "node", headers={ "Accept": "application/json", - "Authorization": f"Bearer {pytest.BEARER_TOKEN}", # pylint: disable=no-member + "Authorization": f"Bearer {pytest.BEARER_TOKEN}", }, data=json.dumps(node), ) @@ -44,7 +44,7 @@ async def get_node_by_id(test_async_client, node_id): f"node/{node_id}", headers={ "Accept": "application/json", - "Authorization": f"Bearer {pytest.BEARER_TOKEN}", # pylint: disable=no-member + "Authorization": f"Bearer {pytest.BEARER_TOKEN}", }, ) assert response.status_code == 200 @@ -65,7 +65,7 @@ async def get_node_by_attribute(test_async_client, params): params=params, headers={ "Accept": "application/json", - "Authorization": f"Bearer {pytest.BEARER_TOKEN}", # pylint: disable=no-member + "Authorization": f"Bearer {pytest.BEARER_TOKEN}", }, ) assert response.status_code == 200 @@ -85,7 +85,7 @@ async def update_node(test_async_client, node): f"node/{node['id']}", headers={ "Accept": "application/json", - "Authorization": f"Bearer {pytest.BEARER_TOKEN}", # pylint: disable=no-member + "Authorization": f"Bearer {pytest.BEARER_TOKEN}", }, data=json.dumps(node), ) @@ -104,7 +104,7 @@ async def patch_node(test_async_client, node_id, patch_data): f"node/{node_id}", headers={ "Accept": "application/json", - "Authorization": f"Bearer {pytest.BEARER_TOKEN}", # pylint: disable=no-member + "Authorization": f"Bearer {pytest.BEARER_TOKEN}", }, data=json.dumps(patch_data), ) diff --git a/tests/e2e_tests/test_password_handler.py b/tests/e2e_tests/test_password_handler.py index 67bb8bda..eec5d608 100644 --- a/tests/e2e_tests/test_password_handler.py +++ b/tests/e2e_tests/test_password_handler.py @@ -28,7 +28,7 @@ async def test_password_endpoint(test_async_client): "user/me", headers={ "Accept": "application/json", - "Authorization": f"Bearer {pytest.BEARER_TOKEN}", # pylint: disable=no-member + "Authorization": f"Bearer {pytest.BEARER_TOKEN}", }, data=json.dumps({"password": "foo"}), ) diff --git a/tests/e2e_tests/test_pipeline.py b/tests/e2e_tests/test_pipeline.py index 567d5098..55262539 100644 --- a/tests/e2e_tests/test_pipeline.py +++ b/tests/e2e_tests/test_pipeline.py @@ -45,7 +45,7 @@ async def test_node_pipeline(test_async_client): # Create Task to listen pubsub event on 'node' channel task_listen = create_listen_task( test_async_client, pytest.node_channel_subscription_id - ) # pylint: disable=no-member + ) # Create a node node = { @@ -94,7 +94,7 @@ async def test_node_pipeline(test_async_client): # Create Task to listen 'updated' event on 'node' channel task_listen = create_listen_task( test_async_client, pytest.node_channel_subscription_id - ) # pylint: disable=no-member + ) # Update node.state node.update({"state": "done"}) diff --git a/tests/e2e_tests/test_pubsub_handler.py b/tests/e2e_tests/test_pubsub_handler.py index 36ed772c..3015b7ac 100644 --- a/tests/e2e_tests/test_pubsub_handler.py +++ b/tests/e2e_tests/test_pubsub_handler.py @@ -28,7 +28,7 @@ async def test_pubsub_handler(test_async_client): # Create Task to listen pubsub event on 'test_channel' channel task_listen = create_listen_task( test_async_client, pytest.test_channel_subscription_id - ) # pylint: disable=no-member + ) # Created and publish CloudEvent attributes = { @@ -38,7 +38,7 @@ async def test_pubsub_handler(test_async_client): data = {"message": "Test message"} event = CloudEvent(attributes, data) headers, body = to_structured(event) - headers["Authorization"] = f"Bearer {pytest.BEARER_TOKEN}" # pylint: disable=no-member + headers["Authorization"] = f"Bearer {pytest.BEARER_TOKEN}" response = await test_async_client.post( "publish/test_channel", headers=headers, data=body ) diff --git a/tests/e2e_tests/test_subscribe_handler.py b/tests/e2e_tests/test_subscribe_handler.py index 50012068..446896ea 100644 --- a/tests/e2e_tests/test_subscribe_handler.py +++ b/tests/e2e_tests/test_subscribe_handler.py @@ -24,9 +24,7 @@ async def test_subscribe_node_channel(test_async_client): """ response = await test_async_client.post( "subscribe/node", - headers={ - "Authorization": f"Bearer {pytest.BEARER_TOKEN}" # pylint: disable=no-member - }, + headers={"Authorization": f"Bearer {pytest.BEARER_TOKEN}"}, ) pytest.node_channel_subscription_id = response.json()["id"] assert response.status_code == 200 @@ -52,9 +50,7 @@ async def test_subscribe_test_channel(test_async_client): """ response = await test_async_client.post( "subscribe/test_channel", - headers={ - "Authorization": f"Bearer {pytest.BEARER_TOKEN}" # pylint: disable=no-member - }, + headers={"Authorization": f"Bearer {pytest.BEARER_TOKEN}"}, ) pytest.test_channel_subscription_id = response.json()["id"] assert response.status_code == 200 @@ -81,9 +77,7 @@ async def test_subscribe_user_group_channel(test_async_client): """ response = await test_async_client.post( "subscribe/user_group", - headers={ - "Authorization": f"Bearer {pytest.BEARER_TOKEN}" # pylint: disable=no-member - }, + headers={"Authorization": f"Bearer {pytest.BEARER_TOKEN}"}, ) pytest.user_group_channel_subscription_id = response.json()["id"] assert response.status_code == 200 diff --git a/tests/e2e_tests/test_unsubscribe_handler.py b/tests/e2e_tests/test_unsubscribe_handler.py index dff85f65..16f613f2 100644 --- a/tests/e2e_tests/test_unsubscribe_handler.py +++ b/tests/e2e_tests/test_unsubscribe_handler.py @@ -24,10 +24,8 @@ async def test_unsubscribe_node_channel(test_async_client): HTTP Response Code 200 OK """ response = await test_async_client.post( - f"unsubscribe/{pytest.node_channel_subscription_id}", # pylint: disable=no-member - headers={ - "Authorization": f"Bearer {pytest.BEARER_TOKEN}" # pylint: disable=no-member - }, + f"unsubscribe/{pytest.node_channel_subscription_id}", + headers={"Authorization": f"Bearer {pytest.BEARER_TOKEN}"}, ) assert response.status_code == 200 @@ -47,9 +45,7 @@ async def test_unsubscribe_test_channel(test_async_client): HTTP Response Code 200 OK """ response = await test_async_client.post( - f"unsubscribe/{pytest.test_channel_subscription_id}", # pylint: disable=no-member - headers={ - "Authorization": f"Bearer {pytest.BEARER_TOKEN}" # pylint: disable=no-member - }, + f"unsubscribe/{pytest.test_channel_subscription_id}", + headers={"Authorization": f"Bearer {pytest.BEARER_TOKEN}"}, ) assert response.status_code == 200 diff --git a/tests/e2e_tests/test_user_invite.py b/tests/e2e_tests/test_user_invite.py index 4932f611..e9c388d4 100644 --- a/tests/e2e_tests/test_user_invite.py +++ b/tests/e2e_tests/test_user_invite.py @@ -2,8 +2,6 @@ # # Copyright (C) 2025 Collabora Limited # -# pylint: disable=unused-argument - """End-to-end test functions for KernelCI API invite flow""" import json diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index ad214462..9b168128 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -6,8 +6,6 @@ # Copyright (C) 2022, 2023 Collabora Limited # Author: Jeny Sadadia -# pylint: disable=protected-access - """pytest fixtures for KernelCI API""" from unittest.mock import AsyncMock diff --git a/tests/unit_tests/test_authz_handler.py b/tests/unit_tests/test_authz_handler.py index 0475f1e4..a6b92b2e 100644 --- a/tests/unit_tests/test_authz_handler.py +++ b/tests/unit_tests/test_authz_handler.py @@ -4,8 +4,6 @@ """Unit tests for authorization helpers and user self-update.""" -# pylint: disable=duplicate-code - import json from kernelci.api.models import Node, Revision diff --git a/tests/unit_tests/test_count_handler.py b/tests/unit_tests/test_count_handler.py index 5a70c339..a3cfd2c8 100644 --- a/tests/unit_tests/test_count_handler.py +++ b/tests/unit_tests/test_count_handler.py @@ -3,8 +3,6 @@ # Copyright (C) 2022 Collabora Limited # Author: Jeny Sadadia -# pylint: disable=unused-argument - """Unit test functions for KernelCI API count handler""" diff --git a/tests/unit_tests/test_listen_handler.py b/tests/unit_tests/test_listen_handler.py index 43f727ce..30be9c9c 100644 --- a/tests/unit_tests/test_listen_handler.py +++ b/tests/unit_tests/test_listen_handler.py @@ -6,8 +6,6 @@ # Copyright (C) 2022 Jeny Sadadia # Author: Jeny Sadadia -# pylint: disable=unused-argument - """Unit test functions for KernelCI API listen handler""" from tests.unit_tests.conftest import BEARER_TOKEN diff --git a/tests/unit_tests/test_node_handler.py b/tests/unit_tests/test_node_handler.py index fcd37a51..88cd1de1 100644 --- a/tests/unit_tests/test_node_handler.py +++ b/tests/unit_tests/test_node_handler.py @@ -6,8 +6,6 @@ # Copyright (C) 2022 Collabora Limited # Author: Jeny Sadadia -# pylint: disable=unused-argument - """Unit test functions for KernelCI API node handler""" import json diff --git a/tests/unit_tests/test_pubsub.py b/tests/unit_tests/test_pubsub.py index 385ae5a1..bc28d3a3 100644 --- a/tests/unit_tests/test_pubsub.py +++ b/tests/unit_tests/test_pubsub.py @@ -4,8 +4,6 @@ # Author: Michal Galka # Author: Alexandra Pereira -# pylint: disable=protected-access - """Unit test functions for KernelCI API Pub/Sub""" import json diff --git a/tests/unit_tests/test_root_handler.py b/tests/unit_tests/test_root_handler.py index 9d0efa5b..f74955c5 100644 --- a/tests/unit_tests/test_root_handler.py +++ b/tests/unit_tests/test_root_handler.py @@ -3,8 +3,6 @@ # Copyright (C) 2022 Jeny Sadadia # Author: Jeny Sadadia -# pylint: disable=unused-argument - """Unit test function for KernelCI API root handler""" diff --git a/tests/unit_tests/test_subscribe_handler.py b/tests/unit_tests/test_subscribe_handler.py index 808d396a..9d97bc85 100644 --- a/tests/unit_tests/test_subscribe_handler.py +++ b/tests/unit_tests/test_subscribe_handler.py @@ -3,8 +3,6 @@ # Copyright (C) 2022 Jeny Sadadia # Author: Jeny Sadadia -# pylint: disable=unused-argument - """Unit test function for KernelCI API subscribe handler""" from api.pubsub import Subscription diff --git a/tests/unit_tests/test_token_handler.py b/tests/unit_tests/test_token_handler.py index 34405820..0b57c863 100644 --- a/tests/unit_tests/test_token_handler.py +++ b/tests/unit_tests/test_token_handler.py @@ -6,8 +6,6 @@ # Copyright (C) 2022, 2023 Collabora Limited # Author: Jeny Sadadia -# pylint: disable=unused-argument - """Unit test function for KernelCI API token handler""" import pytest diff --git a/tests/unit_tests/test_unsubscribe_handler.py b/tests/unit_tests/test_unsubscribe_handler.py index afa1a982..8ac10f2b 100644 --- a/tests/unit_tests/test_unsubscribe_handler.py +++ b/tests/unit_tests/test_unsubscribe_handler.py @@ -3,8 +3,6 @@ # Copyright (C) 2022 Jeny Sadadia # Author: Jeny Sadadia -# pylint: disable=unused-argument - """Unit test functions for KernelCI API unsubscribe handler""" from tests.unit_tests.conftest import BEARER_TOKEN diff --git a/tests/unit_tests/test_user_handler.py b/tests/unit_tests/test_user_handler.py index c85a899a..7c13770f 100644 --- a/tests/unit_tests/test_user_handler.py +++ b/tests/unit_tests/test_user_handler.py @@ -3,8 +3,6 @@ # Copyright (C) 2022, 2023 Collabora Limited # Author: Jeny Sadadia -# pylint: disable=unused-argument - """Unit test function for KernelCI API user handler""" import json diff --git a/tests/unit_tests/test_whoami_handler.py b/tests/unit_tests/test_whoami_handler.py index 0b579008..0c901252 100644 --- a/tests/unit_tests/test_whoami_handler.py +++ b/tests/unit_tests/test_whoami_handler.py @@ -6,8 +6,6 @@ # Copyright (C) 2023 Collabora Limited # Author: Jeny Sadadia -# pylint: disable=unused-argument - """Unit test function for KernelCI API whoami handler""" import pytest