fix(lint): restore noqa directives and register custom codes with ruff
Some checks failed
CI / ruff (push) Failing after 7s
CI / pytest (push) Failing after 1s
CI / architecture (push) Failing after 8s
CI / dependency-scanning (push) Successful in 26s
CI / audit (push) Successful in 9s
CI / docs (push) Has been skipped

Reverts the noqa: removal — the architecture validators (SVC-006,
SEC-034, MOD-004, API-007) use these to skip known-safe violations.
Added ruff lint.external config so ruff treats them as valid codes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 23:21:27 +01:00
parent 1b24269ef1
commit 3a264c0a39
27 changed files with 41 additions and 37 deletions

View File

@@ -13,7 +13,7 @@ from sqlalchemy.orm import Session
from app.api.deps import get_current_store_from_cookie_or_header, get_db from app.api.deps import get_current_store_from_cookie_or_header, get_db
from app.modules.core.services.platform_settings_service import ( from app.modules.core.services.platform_settings_service import (
platform_settings_service, # MOD-004 - shared platform service platform_settings_service, # noqa: MOD-004 - shared platform service
) )
from app.modules.tenancy.models import Store, User from app.modules.tenancy.models import Store, User
from app.templates_config import templates from app.templates_config import templates

View File

@@ -27,7 +27,7 @@ from app.modules.checkout.schemas import (
from app.modules.checkout.services import checkout_service from app.modules.checkout.services import checkout_service
from app.modules.customers.schemas import CustomerContext from app.modules.customers.schemas import CustomerContext
from app.modules.messaging.services.email_service import ( from app.modules.messaging.services.email_service import (
EmailService, # MOD-004 - Core email service EmailService, # noqa: MOD-004 - Core email service
) )
from app.modules.orders.schemas import OrderCreate, OrderResponse from app.modules.orders.schemas import OrderCreate, OrderResponse
from app.modules.orders.services import order_service from app.modules.orders.services import order_service

View File

@@ -27,7 +27,7 @@ from app.modules.cms.schemas import (
from app.modules.cms.services import content_page_service from app.modules.cms.services import content_page_service
from app.modules.tenancy.models import User from app.modules.tenancy.models import User
from app.modules.tenancy.services.store_service import ( from app.modules.tenancy.services.store_service import (
StoreService, # MOD-004 - shared platform service StoreService, # noqa: MOD-004 - shared platform service
) )
store_service = StoreService() store_service = StoreService()

View File

@@ -14,7 +14,7 @@ from sqlalchemy.orm import Session
from app.api.deps import get_current_store_from_cookie_or_header, get_db from app.api.deps import get_current_store_from_cookie_or_header, get_db
from app.modules.cms.services import content_page_service from app.modules.cms.services import content_page_service
from app.modules.core.services.platform_settings_service import ( from app.modules.core.services.platform_settings_service import (
platform_settings_service, # MOD-004 - shared platform service platform_settings_service, # noqa: MOD-004 - shared platform service
) )
from app.modules.tenancy.models import Store, User from app.modules.tenancy.models import Store, User
from app.templates_config import templates from app.templates_config import templates

View File

@@ -28,7 +28,7 @@ from app.api.deps import (
get_db, get_db,
) )
from app.modules.core.services.menu_service import MenuItemConfig, menu_service from app.modules.core.services.menu_service import MenuItemConfig, menu_service
from app.modules.enums import FrontendType # API-007 - Enum for type safety from app.modules.enums import FrontendType # noqa: API-007 - Enum for type safety
from app.modules.tenancy.services.platform_service import platform_service from app.modules.tenancy.services.platform_service import platform_service
from app.utils.i18n import DEFAULT_LANGUAGE, translate from app.utils.i18n import DEFAULT_LANGUAGE, translate
from models.schema.auth import UserContext from models.schema.auth import UserContext

View File

@@ -61,7 +61,7 @@ class AuthService:
# Update last_login timestamp # Update last_login timestamp
user.last_login = datetime.now(UTC) user.last_login = datetime.now(UTC)
db.commit() # SVC-006 - Login must persist last_login timestamp db.commit() # noqa: SVC-006 - Login must persist last_login timestamp
token_data = self.auth_manager.create_access_token(user) token_data = self.auth_manager.create_access_token(user)
@@ -176,7 +176,7 @@ class AuthService:
# Update last_login timestamp # Update last_login timestamp
user.last_login = datetime.now(UTC) user.last_login = datetime.now(UTC)
db.commit() # SVC-006 - Login must persist last_login timestamp db.commit() # noqa: SVC-006 - Login must persist last_login timestamp
token_data = self.auth_manager.create_access_token(user) token_data = self.auth_manager.create_access_token(user)

View File

@@ -133,7 +133,7 @@ class PlatformSettingsService:
) )
db.add(admin_setting) db.add(admin_setting)
db.commit() # SVC-006 - Setting change is atomic, commit is intentional db.commit() # noqa: SVC-006 - Setting change is atomic, commit is intentional
db.refresh(admin_setting) db.refresh(admin_setting)
logger.info(f"Platform setting '{key}' set to '{value}' by user {user_id}") logger.info(f"Platform setting '{key}' set to '{value}' by user {user_id}")

View File

@@ -25,7 +25,7 @@ from app.core.database import get_db
from app.core.environment import should_use_secure_cookies from app.core.environment import should_use_secure_cookies
from app.exceptions import ValidationException from app.exceptions import ValidationException
from app.modules.core.services.auth_service import ( from app.modules.core.services.auth_service import (
AuthService, # MOD-004 - Core auth service AuthService, # noqa: MOD-004 - Core auth service
) )
from app.modules.customers.models import PasswordResetToken from app.modules.customers.models import PasswordResetToken
from app.modules.customers.schemas import ( from app.modules.customers.schemas import (
@@ -44,7 +44,7 @@ from app.modules.customers.services import (
customer_service, customer_service,
) )
from app.modules.messaging.services.email_service import ( from app.modules.messaging.services.email_service import (
EmailService, # MOD-004 - Core email service EmailService, # noqa: MOD-004 - Core email service
) )
from app.modules.tenancy.exceptions import StoreNotFoundException from app.modules.tenancy.exceptions import StoreNotFoundException
from models.schema.auth import ( from models.schema.auth import (

View File

@@ -754,7 +754,7 @@ class InventoryService:
self, db: Session self, db: Session
) -> AdminStoresWithInventoryResponse: ) -> AdminStoresWithInventoryResponse:
"""Get list of stores that have inventory entries (admin only).""" """Get list of stores that have inventory entries (admin only)."""
# SVC-005 - Admin function, intentionally cross-store # noqa: SVC-005 - Admin function, intentionally cross-store
# Use subquery to avoid DISTINCT on JSON columns (PostgreSQL can't compare JSON) # Use subquery to avoid DISTINCT on JSON columns (PostgreSQL can't compare JSON)
store_ids_subquery = ( store_ids_subquery = (
db.query(Inventory.store_id) db.query(Inventory.store_id)

View File

@@ -25,8 +25,8 @@ class MarketplaceImportJobRequest(BaseModel):
@field_validator("source_url") @field_validator("source_url")
@classmethod @classmethod
def validate_url(cls, v): def validate_url(cls, v):
if not v.startswith(("http://", "https://")): # SEC-034 if not v.startswith(("http://", "https://")): # noqa: SEC-034
raise ValueError("URL must start with http:// or https://") # SEC-034 raise ValueError("URL must start with http:// or https://") # noqa: SEC-034
return v.strip() return v.strip()
@field_validator("marketplace") @field_validator("marketplace")
@@ -64,8 +64,8 @@ class AdminMarketplaceImportJobRequest(BaseModel):
@field_validator("source_url") @field_validator("source_url")
@classmethod @classmethod
def validate_url(cls, v): def validate_url(cls, v):
if not v.startswith(("http://", "https://")): # SEC-034 if not v.startswith(("http://", "https://")): # noqa: SEC-034
raise ValueError("URL must start with http:// or https://") # SEC-034 raise ValueError("URL must start with http:// or https://") # noqa: SEC-034
return v.strip() return v.strip()
@field_validator("marketplace") @field_validator("marketplace")

View File

@@ -865,7 +865,7 @@ class LetzshopOrderService:
pass pass
if needs_update: if needs_update:
self.db.commit() # SVC-006 - background task needs incremental commits self.db.commit() # noqa: SVC-006 - background task needs incremental commits
stats["updated"] += 1 stats["updated"] += 1
else: else:
stats["skipped"] += 1 stats["skipped"] += 1
@@ -881,7 +881,7 @@ class LetzshopOrderService:
# Create new order using unified service # Create new order using unified service
try: try:
self.create_order(store_id, shipment) self.create_order(store_id, shipment)
self.db.commit() # SVC-006 - background task needs incremental commits self.db.commit() # noqa: SVC-006 - background task needs incremental commits
stats["imported"] += 1 stats["imported"] += 1
# Decrement remaining count for batch efficiency # Decrement remaining count for batch efficiency
@@ -1093,7 +1093,7 @@ class LetzshopOrderService:
status="pending", status="pending",
) )
self.db.add(job) self.db.add(job)
self.db.commit() # SVC-006 - job must be visible immediately before background task starts self.db.commit() # noqa: SVC-006 - job must be visible immediately before background task starts
self.db.refresh(job) self.db.refresh(job)
return job return job
@@ -1134,6 +1134,6 @@ class LetzshopOrderService:
) )
if job: if job:
job.celery_task_id = celery_task_id job.celery_task_id = celery_task_id
self.db.commit() # SVC-006 - Called from API endpoint self.db.commit() # noqa: SVC-006 - Called from API endpoint
return True return True
return False return False

View File

@@ -498,7 +498,7 @@ class LetzshopStoreSyncService:
# Create store # Create store
store = admin_service.create_store(self.db, store_data) store = admin_service.create_store(self.db, store_data)
# Mark the Letzshop store as claimed (commits internally) # SVC-006 # Mark the Letzshop store as claimed (commits internally) # noqa: SVC-006
self.mark_store_claimed(letzshop_slug, store.id) self.mark_store_claimed(letzshop_slug, store.id)
logger.info( logger.info(

View File

@@ -446,7 +446,7 @@ class MarketplaceProductService:
InventorySummaryResponse if inventory found, None otherwise InventorySummaryResponse if inventory found, None otherwise
""" """
try: try:
# SVC-005 - Admin/internal function for inventory lookup by GTIN # noqa: SVC-005 - Admin/internal function for inventory lookup by GTIN
inventory_entries = db.query(Inventory).filter(Inventory.gtin == gtin).all() inventory_entries = db.query(Inventory).filter(Inventory.gtin == gtin).all()
if not inventory_entries: if not inventory_entries:
return None return None

View File

@@ -415,7 +415,7 @@ class PlatformSignupService:
) )
subscription.stripe_customer_id = stripe_customer_id subscription.stripe_customer_id = stripe_customer_id
db.commit() # SVC-006 - Atomic account creation needs commit db.commit() # noqa: SVC-006 - Atomic account creation needs commit
# Update session # Update session
self.update_session(session_id, { self.update_session(session_id, {
@@ -603,7 +603,7 @@ class PlatformSignupService:
if subscription: if subscription:
subscription.card_collected_at = datetime.now(UTC) subscription.card_collected_at = datetime.now(UTC)
subscription.stripe_payment_method_id = payment_method_id subscription.stripe_payment_method_id = payment_method_id
db.commit() # SVC-006 - Finalize signup needs commit db.commit() # noqa: SVC-006 - Finalize signup needs commit
# Get store info # Get store info
store = db.query(Store).filter(Store.id == store_id).first() store = db.query(Store).filter(Store.id == store_id).first()

View File

@@ -1336,7 +1336,7 @@ class EmailService:
related_id=related_id, related_id=related_id,
) )
self.db.add(log) self.db.add(log)
self.db.commit() # SVC-006 - Email logs are side effects, commit immediately self.db.commit() # noqa: SVC-006 - Email logs are side effects, commit immediately
return log return log
# Inject branding variables if requested # Inject branding variables if requested
@@ -1471,7 +1471,7 @@ class EmailService:
if not email_enabled: if not email_enabled:
log.status = EmailStatus.FAILED.value log.status = EmailStatus.FAILED.value
log.error_message = "Email sending is disabled" log.error_message = "Email sending is disabled"
self.db.commit() # SVC-006 - Email logs are side effects, commit immediately self.db.commit() # noqa: SVC-006 - Email logs are side effects, commit immediately
logger.info(f"Email sending disabled, skipping: {to_email}") logger.info(f"Email sending disabled, skipping: {to_email}")
return log return log
@@ -1497,7 +1497,7 @@ class EmailService:
log.mark_failed(error or "Unknown error") log.mark_failed(error or "Unknown error")
logger.error(f"Email failed to {to_email}: {error}") logger.error(f"Email failed to {to_email}: {error}")
self.db.commit() # SVC-006 - Email logs are side effects, commit immediately self.db.commit() # noqa: SVC-006 - Email logs are side effects, commit immediately
return log return log

View File

@@ -44,7 +44,7 @@ class BackgroundTasksService:
def get_running_test_runs(self, db: Session) -> list[TestRun]: def get_running_test_runs(self, db: Session) -> list[TestRun]:
"""Get currently running test runs""" """Get currently running test runs"""
# SVC-005 - Platform-level, TestRuns not store-scoped # noqa: SVC-005 - Platform-level, TestRuns not store-scoped
return db.query(TestRun).filter(TestRun.status == "running").all() return db.query(TestRun).filter(TestRun.status == "running").all()
def get_import_stats(self, db: Session) -> dict: def get_import_stats(self, db: Session) -> dict:

View File

@@ -32,7 +32,7 @@ from app.modules.orders.schemas import (
) )
from app.modules.orders.services import order_service from app.modules.orders.services import order_service
from app.modules.orders.services.invoice_service import ( from app.modules.orders.services.invoice_service import (
invoice_service, # MOD-004 - Core invoice service invoice_service, # noqa: MOD-004 - Core invoice service
) )
from app.modules.tenancy.exceptions import StoreNotFoundException from app.modules.tenancy.exceptions import StoreNotFoundException

View File

@@ -1292,7 +1292,7 @@ class OrderService:
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Get shipping label information for an order (admin only).""" """Get shipping label information for an order (admin only)."""
from app.modules.core.services.admin_settings_service import ( from app.modules.core.services.admin_settings_service import (
admin_settings_service, # MOD-004 admin_settings_service, # noqa: MOD-004
) )
order = db.query(Order).filter(Order.id == order_id).first() order = db.query(Order).filter(Order.id == order_id).first()

View File

@@ -103,7 +103,7 @@ class MerchantDomain(Base, TimestampMixin):
- EXAMPLE.COM -> example.com - EXAMPLE.COM -> example.com
""" """
# Remove protocol # Remove protocol
domain = domain.replace("https://", "").replace("http://", "") # SEC-034 domain = domain.replace("https://", "").replace("http://", "") # noqa: SEC-034
# Remove trailing slash # Remove trailing slash
domain = domain.rstrip("/") domain = domain.rstrip("/")

View File

@@ -92,7 +92,7 @@ class StoreDomain(Base, TimestampMixin):
- EXAMPLE.COM -> example.com - EXAMPLE.COM -> example.com
""" """
# Remove protocol # Remove protocol
domain = domain.replace("https://", "").replace("http://", "") # SEC-034 domain = domain.replace("https://", "").replace("http://", "") # noqa: SEC-034
# Remove trailing slash # Remove trailing slash
domain = domain.rstrip("/") domain = domain.rstrip("/")

View File

@@ -100,7 +100,7 @@ def _build_module_response(
is_enabled: bool, is_enabled: bool,
) -> ModuleResponse: ) -> ModuleResponse:
"""Build ModuleResponse from module code.""" """Build ModuleResponse from module code."""
from app.modules.enums import FrontendType # API-007 - Enum for type safety from app.modules.enums import FrontendType # noqa: API-007 - Enum for type safety
module = MODULES.get(code) module = MODULES.get(code)
if not module: if not module:

View File

@@ -22,7 +22,7 @@ from app.api.deps import get_current_super_admin, get_current_super_admin_api
from app.core.database import get_db from app.core.database import get_db
from app.exceptions import ValidationException from app.exceptions import ValidationException
from app.modules.tenancy.models import ( from app.modules.tenancy.models import (
User, # API-007 - Internal helper uses User model User, # noqa: API-007 - Internal helper uses User model
) )
from app.modules.tenancy.services.admin_platform_service import admin_platform_service from app.modules.tenancy.services.admin_platform_service import admin_platform_service
from models.schema.auth import UserContext from models.schema.auth import UserContext

View File

@@ -18,7 +18,7 @@ from sqlalchemy.orm import Session
from app.core.database import get_db from app.core.database import get_db
from app.modules.tenancy.schemas.store import StoreDetailResponse from app.modules.tenancy.schemas.store import StoreDetailResponse
from app.modules.tenancy.services.store_service import store_service # mod-004 from app.modules.tenancy.services.store_service import store_service # noqa: mod-004
store_router = APIRouter() store_router = APIRouter()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@@ -34,7 +34,7 @@ class MerchantDomainCreate(BaseModel):
def validate_domain(cls, v: str) -> str: def validate_domain(cls, v: str) -> str:
"""Validate and normalize domain.""" """Validate and normalize domain."""
# Remove protocol if present # Remove protocol if present
domain = v.replace("https://", "").replace("http://", "") # SEC-034 domain = v.replace("https://", "").replace("http://", "") # noqa: SEC-034
# Remove trailing slash # Remove trailing slash
domain = domain.rstrip("/") domain = domain.rstrip("/")

View File

@@ -35,7 +35,7 @@ class StoreDomainCreate(BaseModel):
def validate_domain(cls, v: str) -> str: def validate_domain(cls, v: str) -> str:
"""Validate and normalize domain.""" """Validate and normalize domain."""
# Remove protocol if present # Remove protocol if present
domain = v.replace("https://", "").replace("http://", "") # SEC-034 domain = v.replace("https://", "").replace("http://", "") # noqa: SEC-034
# Remove trailing slash # Remove trailing slash
domain = domain.rstrip("/") domain = domain.rstrip("/")

View File

@@ -502,6 +502,6 @@ class AuthManager:
db.refresh(admin_user) db.refresh(admin_user)
# Log creation for audit trail (credentials redacted for security) # Log creation for audit trail (credentials redacted for security)
logger.info("Default admin user created") # sec-001 sec-021 logger.info("Default admin user created") # noqa: sec-001 sec-021
return admin_user return admin_user

View File

@@ -18,6 +18,7 @@ include = ["app*", "models*", "middleware*", "storage*"]
line-length = 88 line-length = 88
target-version = "py311" target-version = "py311"
# Exclude directories # Exclude directories
exclude = [ exclude = [
".git", ".git",
@@ -34,6 +35,9 @@ exclude = [
] ]
[tool.ruff.lint] [tool.ruff.lint]
# Allow custom architecture validator codes in # noqa directives (e.g., SEC-034, SVC-006)
external = ["SEC", "SVC", "MOD", "API", "ARCH", "PERF"]
# Enable comprehensive rule sets # Enable comprehensive rule sets
select = [ select = [
"E", # pycodestyle errors "E", # pycodestyle errors