Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 0 additions & 3 deletions api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from pydantic_settings import BaseSettings


# pylint: disable=too-few-public-methods
class AuthSettings(BaseSettings):
"""Authentication settings"""

Expand All @@ -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"""

Expand All @@ -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"""

Expand Down
2 changes: 1 addition & 1 deletion api/email_sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
12 changes: 5 additions & 7 deletions api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
# Author: Jeny Sadadia <jeny.sadadia@collabora.com>
# Author: Denys Fedoryshchenko <denys.f@collabora.com>

# pylint: disable=unused-argument,global-statement,too-many-lines

"""KernelCI API main module"""

import asyncio
Expand Down Expand Up @@ -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()
Expand All @@ -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](
Expand All @@ -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()


Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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

Expand Down
22 changes: 8 additions & 14 deletions api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
# Copyright (C) 2023 Collabora Limited
# Author: Jeny Sadadia <jeny.sadadia@collabora.com>

# 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
Expand Down Expand Up @@ -120,7 +114,7 @@ class UserGroupCreateRequest(BaseModel):

class User(
BeanieBaseUser,
Document, # pylint: disable=too-many-ancestors
Document,
DatabaseModel,
):
"""API User model"""
Expand All @@ -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):
Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand Down
5 changes: 1 addition & 4 deletions api/pubsub_mongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
# Copyright (C) 2025 Collabora Limited
# Author: Denys Fedoryshchenko <denys.f@collabora.com>

# pylint: disable=duplicate-code
# Note: This module intentionally shares interface code with pubsub.py
# as both implement the same PubSub API contract

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -410,7 +408,6 @@ async def subscribe(

return sub

# pylint: disable=too-many-arguments
async def _setup_durable_subscription(
self,
sub_id: int,
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e_tests/listen_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}",
},
)
)
Expand Down
10 changes: 5 additions & 5 deletions tests/e2e_tests/test_node_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
)
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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),
)
Expand All @@ -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),
)
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e_tests/test_password_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"}),
)
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e_tests/test_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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"})
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e_tests/test_pubsub_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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
)
Expand Down
12 changes: 3 additions & 9 deletions tests/e2e_tests/test_subscribe_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
12 changes: 4 additions & 8 deletions tests/e2e_tests/test_unsubscribe_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
2 changes: 0 additions & 2 deletions tests/e2e_tests/test_user_invite.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions tests/unit_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
# Copyright (C) 2022, 2023 Collabora Limited
# Author: Jeny Sadadia <jeny.sadadia@collabora.com>

# pylint: disable=protected-access

"""pytest fixtures for KernelCI API"""

from unittest.mock import AsyncMock
Expand Down
Loading
Loading