This is an automated email from the ASF dual-hosted git repository. sbp pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tooling-atr-experiments.git
The following commit(s) were added to refs/heads/main by this push: new 352776e Add some lints to ruff 352776e is described below commit 352776eccf5815956d58dc40bbc660257d022222 Author: Sean B. Palmer <s...@miscoranda.com> AuthorDate: Mon Feb 17 19:14:07 2025 +0200 Add some lints to ruff --- atr/models.py | 80 +++++++++++----------- atr/routes.py | 41 +++++------ atr/server.py | 11 +-- atr/templates/add-release-candidate.html | 2 +- migrations/env.py | 7 +- migrations/versions/15182b58521a_initial_schema.py | 9 ++- pyproject.toml | 2 +- scripts/generate-certificates | 8 +-- 8 files changed, 80 insertions(+), 80 deletions(-) diff --git a/atr/models.py b/atr/models.py index 61c9dd4..ea56aa6 100644 --- a/atr/models.py +++ b/atr/models.py @@ -18,12 +18,12 @@ "models.py" import datetime -from typing import List, Optional from enum import Enum +from typing import Optional -from sqlmodel import SQLModel, Field, Relationship -from sqlalchemy import Column, JSON from pydantic import BaseModel +from sqlalchemy import JSON, Column +from sqlmodel import Field, Relationship, SQLModel class UserRole(str, Enum): @@ -45,70 +45,70 @@ class PublicSigningKey(SQLModel, table=True): public_key: str key_type: str expiration: datetime.datetime - pmcs: List["PMC"] = Relationship(back_populates="public_signing_keys", link_model=PMCKeyLink) + pmcs: list["PMC"] = Relationship(back_populates="public_signing_keys", link_model=PMCKeyLink) class VotePolicy(SQLModel, table=True): - id: Optional[int] = Field(default=None, primary_key=True) - mailto_addresses: List[str] = Field(default_factory=list, sa_column=Column(JSON)) + id: int | None = Field(default=None, primary_key=True) + mailto_addresses: list[str] = Field(default_factory=list, sa_column=Column(JSON)) manual_vote: bool = Field(default=False) min_hours: int = Field(default=0) release_checklist: str = Field(default="") pause_for_rm: bool = Field(default=False) # One-to-many: A vote policy can be used by multiple PMCs - pmcs: List["PMC"] = Relationship(back_populates="vote_policy") + pmcs: list["PMC"] = Relationship(back_populates="vote_policy") # One-to-many: A vote policy can be used by multiple product lines - product_lines: List["ProductLine"] = Relationship(back_populates="vote_policy") + product_lines: list["ProductLine"] = Relationship(back_populates="vote_policy") # One-to-many: A vote policy can be used by multiple releases - releases: List["Release"] = Relationship(back_populates="vote_policy") + releases: list["Release"] = Relationship(back_populates="vote_policy") class PMC(SQLModel, table=True): - id: Optional[int] = Field(default=None, primary_key=True) + id: int | None = Field(default=None, primary_key=True) project_name: str = Field(unique=True) # One-to-many: A PMC can have multiple product lines, each product line belongs to one PMC - product_lines: List["ProductLine"] = Relationship(back_populates="pmc") + product_lines: list["ProductLine"] = Relationship(back_populates="pmc") - pmc_members: List[str] = Field(default_factory=list, sa_column=Column(JSON)) - committers: List[str] = Field(default_factory=list, sa_column=Column(JSON)) - release_managers: List[str] = Field(default_factory=list, sa_column=Column(JSON)) + pmc_members: list[str] = Field(default_factory=list, sa_column=Column(JSON)) + committers: list[str] = Field(default_factory=list, sa_column=Column(JSON)) + release_managers: list[str] = Field(default_factory=list, sa_column=Column(JSON)) # Many-to-many: A PMC can have multiple signing keys, and a signing key can belong to multiple PMCs - public_signing_keys: List[PublicSigningKey] = Relationship(back_populates="pmcs", link_model=PMCKeyLink) + public_signing_keys: list[PublicSigningKey] = Relationship(back_populates="pmcs", link_model=PMCKeyLink) # Many-to-one: A PMC can have one vote policy, a vote policy can be used by multiple entities - vote_policy_id: Optional[int] = Field(default=None, foreign_key="votepolicy.id") - vote_policy: Optional[VotePolicy] = Relationship(back_populates="pmcs") + vote_policy_id: int | None = Field(default=None, foreign_key="votepolicy.id") + vote_policy: VotePolicy | None = Relationship(back_populates="pmcs") # One-to-many: A PMC can have multiple releases - releases: List["Release"] = Relationship(back_populates="pmc") + releases: list["Release"] = Relationship(back_populates="pmc") class ProductLine(SQLModel, table=True): - id: Optional[int] = Field(default=None, primary_key=True) + id: int | None = Field(default=None, primary_key=True) # Many-to-one: A product line belongs to one PMC, a PMC can have multiple product lines - pmc_id: Optional[int] = Field(default=None, foreign_key="pmc.id") - pmc: Optional[PMC] = Relationship(back_populates="product_lines") + pmc_id: int | None = Field(default=None, foreign_key="pmc.id") + pmc: PMC | None = Relationship(back_populates="product_lines") product_name: str latest_version: str # One-to-many: A product line can have multiple distribution channels, each channel belongs to one product line - distribution_channels: List["DistributionChannel"] = Relationship(back_populates="product_line") + distribution_channels: list["DistributionChannel"] = Relationship(back_populates="product_line") # Many-to-one: A product line can have one vote policy, a vote policy can be used by multiple entities - vote_policy_id: Optional[int] = Field(default=None, foreign_key="votepolicy.id") - vote_policy: Optional[VotePolicy] = Relationship(back_populates="product_lines") + vote_policy_id: int | None = Field(default=None, foreign_key="votepolicy.id") + vote_policy: VotePolicy | None = Relationship(back_populates="product_lines") # One-to-many: A product line can have multiple releases, each release belongs to one product line - releases: List["Release"] = Relationship(back_populates="product_line") + releases: list["Release"] = Relationship(back_populates="product_line") class DistributionChannel(SQLModel, table=True): - id: Optional[int] = Field(default=None, primary_key=True) + id: int | None = Field(default=None, primary_key=True) name: str = Field(index=True, unique=True) url: str credentials: str @@ -116,18 +116,18 @@ class DistributionChannel(SQLModel, table=True): automation_endpoint: str # Many-to-one: A distribution channel belongs to one product line, a product line can have multiple channels - product_line_id: Optional[int] = Field(default=None, foreign_key="productline.id") - product_line: Optional[ProductLine] = Relationship(back_populates="distribution_channels") + product_line_id: int | None = Field(default=None, foreign_key="productline.id") + product_line: ProductLine | None = Relationship(back_populates="distribution_channels") class Package(SQLModel, table=True): - id: Optional[int] = Field(default=None, primary_key=True) + id: int | None = Field(default=None, primary_key=True) file: str signature: str checksum: str # Many-to-one: A package belongs to one release - release_key: Optional[str] = Field(default=None, foreign_key="release.storage_key") + release_key: str | None = Field(default=None, foreign_key="release.storage_key") release: Optional["Release"] = Relationship(back_populates="packages") @@ -168,21 +168,21 @@ class Release(SQLModel, table=True): phase: ReleasePhase # Many-to-one: A release belongs to one PMC, a PMC can have multiple releases - pmc_id: Optional[int] = Field(default=None, foreign_key="pmc.id") - pmc: Optional[PMC] = Relationship(back_populates="releases") + pmc_id: int | None = Field(default=None, foreign_key="pmc.id") + pmc: PMC | None = Relationship(back_populates="releases") # Many-to-one: A release belongs to one product line, a product line can have multiple releases - product_line_id: Optional[int] = Field(default=None, foreign_key="productline.id") - product_line: Optional[ProductLine] = Relationship(back_populates="releases") + product_line_id: int | None = Field(default=None, foreign_key="productline.id") + product_line: ProductLine | None = Relationship(back_populates="releases") - package_managers: List[str] = Field(default_factory=list, sa_column=Column(JSON)) + package_managers: list[str] = Field(default_factory=list, sa_column=Column(JSON)) version: str # One-to-many: A release can have multiple packages - packages: List[Package] = Relationship(back_populates="release") - sboms: List[str] = Field(default_factory=list, sa_column=Column(JSON)) + packages: list[Package] = Relationship(back_populates="release") + sboms: list[str] = Field(default_factory=list, sa_column=Column(JSON)) # Many-to-one: A release can have one vote policy, a vote policy can be used by multiple releases - vote_policy_id: Optional[int] = Field(default=None, foreign_key="votepolicy.id") - vote_policy: Optional[VotePolicy] = Relationship(back_populates="releases") + vote_policy_id: int | None = Field(default=None, foreign_key="votepolicy.id") + vote_policy: VotePolicy | None = Relationship(back_populates="releases") - votes: List[VoteEntry] = Field(default_factory=list, sa_column=Column(JSON)) + votes: list[VoteEntry] = Field(default_factory=list, sa_column=Column(JSON)) diff --git a/atr/routes.py b/atr/routes.py index c5690fc..a1be503 100644 --- a/atr/routes.py +++ b/atr/routes.py @@ -25,33 +25,34 @@ import pprint import secrets import shutil import tempfile - from contextlib import asynccontextmanager from io import BufferedReader from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple, cast +from typing import Any, cast import aiofiles import aiofiles.os import gnupg import httpx - -from asfquart import APP -from asfquart.auth import Requirements as R, require -from asfquart.base import ASFQuartException -from asfquart.session import read as session_read, ClientSession -from quart import current_app, render_template, request, Request -from sqlmodel import select +from quart import Request, current_app, render_template, request from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload from sqlalchemy.orm.attributes import InstrumentedAttribute +from sqlmodel import select from werkzeug.datastructures import FileStorage +from asfquart import APP +from asfquart.auth import Requirements as R +from asfquart.auth import require +from asfquart.base import ASFQuartException +from asfquart.session import ClientSession +from asfquart.session import read as session_read + from .models import ( - DistributionChannel, PMC, - PMCKeyLink, + DistributionChannel, Package, + PMCKeyLink, ProductLine, PublicSigningKey, Release, @@ -348,10 +349,10 @@ async def root_release_signatures_verify(release_key: str) -> str: async_session = current_app.config["async_session"] async with async_session() as db_session: # Get the release and its packages, and PMC with its keys - release_packages = selectinload(cast(InstrumentedAttribute[List[Package]], Release.packages)) + release_packages = selectinload(cast(InstrumentedAttribute[list[Package]], Release.packages)) release_pmc = selectinload(cast(InstrumentedAttribute[PMC], Release.pmc)) pmc_keys_loader = selectinload(cast(InstrumentedAttribute[PMC], Release.pmc)).selectinload( - cast(InstrumentedAttribute[List[PublicSigningKey]], PMC.public_signing_keys) + cast(InstrumentedAttribute[list[PublicSigningKey]], PMC.public_signing_keys) ) # For now, for debugging, we'll just get all keys in the database @@ -465,7 +466,7 @@ async def root_pmc_directory() -> str: @APP.route("/pmc/list") -async def root_pmc_list() -> List[dict]: +async def root_pmc_list() -> list[dict]: "List all PMCs in the database." async_session = current_app.config["async_session"] async with async_session() as db_session: @@ -565,7 +566,7 @@ async def root_user_uploads() -> str: # TODO: We don't actually record who uploaded the release candidate # We should probably add that information! release_pmc = selectinload(cast(InstrumentedAttribute[PMC], Release.pmc)) - release_packages = selectinload(cast(InstrumentedAttribute[List[Package]], Release.packages)) + release_packages = selectinload(cast(InstrumentedAttribute[list[Package]], Release.packages)) statement = ( select(Release) .options(release_pmc, release_packages) @@ -623,7 +624,7 @@ async def save_file_by_hash(base_dir: Path, file: FileStorage) -> str: raise e -async def user_keys_add(session: ClientSession, public_key: str) -> Tuple[str, Optional[dict]]: +async def user_keys_add(session: ClientSession, public_key: str) -> tuple[str, dict | None]: if not public_key: return ("Public key is required", None) @@ -661,7 +662,7 @@ async def user_keys_add(session: ClientSession, public_key: str) -> Tuple[str, O async def user_keys_add_session( session: ClientSession, public_key: str, key: dict, db_session: AsyncSession -) -> Tuple[str, Optional[dict]]: +) -> tuple[str, dict | None]: # Check if key already exists statement = select(PublicSigningKey).where(PublicSigningKey.user_id == session.uid) @@ -709,7 +710,7 @@ async def user_keys_add_session( ) -async def verify_gpg_signature(artifact_path: Path, signature_path: Path, public_keys: List[str]) -> Dict[str, Any]: +async def verify_gpg_signature(artifact_path: Path, signature_path: Path, public_keys: list[str]) -> dict[str, Any]: """ Verify a GPG signature for a release artifact. Returns a dictionary with verification results and debug information. @@ -727,8 +728,8 @@ async def verify_gpg_signature(artifact_path: Path, signature_path: Path, public async def verify_gpg_signature_file( - sig_file: BufferedReader, artifact_path: Path, public_keys: List[str] -) -> Dict[str, Any]: + sig_file: BufferedReader, artifact_path: Path, public_keys: list[str] +) -> dict[str, Any]: # Run the blocking GPG verification in a thread async with ephemeral_gpg_home() as gpg_home: gpg = gnupg.GPG(gnupghome=gpg_home) diff --git a/atr/server.py b/atr/server.py index e3e80d0..83c938e 100644 --- a/atr/server.py +++ b/atr/server.py @@ -19,15 +19,16 @@ import os +from alembic import command +from alembic.config import Config +from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine +from sqlalchemy.sql import text +from sqlmodel import SQLModel + import asfquart import asfquart.generics import asfquart.session from asfquart.base import QuartApp -from sqlmodel import SQLModel -from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker -from alembic import command -from alembic.config import Config -from sqlalchemy.sql import text from .models import __file__ as data_models_file diff --git a/atr/templates/add-release-candidate.html b/atr/templates/add-release-candidate.html index c3f3497..84c1883 100644 --- a/atr/templates/add-release-candidate.html +++ b/atr/templates/add-release-candidate.html @@ -1,7 +1,7 @@ {% extends "layouts/base.html" %} {% block title %} - Add Release Candidate ~ ATR + Add release candidate ~ ATR {% endblock title %} {% block description %} diff --git a/migrations/env.py b/migrations/env.py index efdaa9d..7ea3ab1 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -1,9 +1,8 @@ from logging.config import fileConfig -from typing import Any, Dict, cast +from typing import Any, cast -from sqlalchemy import engine_from_config, pool from alembic import context - +from sqlalchemy import engine_from_config, pool from sqlmodel import SQLModel # this is the Alembic Config object, which provides @@ -47,7 +46,7 @@ def run_migrations_online() -> None: configuration = config.get_section(config.config_ini_section) if configuration is None: configuration = {} - configuration = cast(Dict[str, Any], configuration) + configuration = cast(dict[str, Any], configuration) configuration["sqlalchemy.url"] = sync_url connectable = engine_from_config( diff --git a/migrations/versions/15182b58521a_initial_schema.py b/migrations/versions/15182b58521a_initial_schema.py index d893727..6db694f 100644 --- a/migrations/versions/15182b58521a_initial_schema.py +++ b/migrations/versions/15182b58521a_initial_schema.py @@ -6,14 +6,13 @@ Create Date: 2025-02-11 20:09:20.011634 """ -from typing import Sequence, Union - +from collections.abc import Sequence # revision identifiers, used by Alembic. revision: str = "15182b58521a" -down_revision: Union[str, None] = None -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None +down_revision: str | None = None +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None def upgrade() -> None: diff --git a/pyproject.toml b/pyproject.toml index 5b17146..68774e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ executionEnvironments = [ ] [tool.ruff] -lint.select = ["E", "W", "F", "C90"] +lint.select = ["I", "E", "W", "F", "C90", "UP"] lint.ignore = [] line-length = 120 exclude = ["asfquart"] diff --git a/scripts/generate-certificates b/scripts/generate-certificates index 951c646..7a5fa72 100755 --- a/scripts/generate-certificates +++ b/scripts/generate-certificates @@ -1,12 +1,12 @@ #!/usr/bin/env python3 -import os import datetime import ipaddress +import os from cryptography import x509 -from cryptography.x509.oid import NameOID -from cryptography.hazmat.primitives import serialization, hashes +from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.x509.oid import NameOID # All state is placed in this directory STATE_DIR = "./state" @@ -31,7 +31,7 @@ def generate_self_signed_cert() -> None: ) # Use timezone-aware datetime objects for UTC - now = datetime.datetime.now(datetime.timezone.utc) + now = datetime.datetime.now(datetime.UTC) # Build a certificate with a SAN for 127.0.0.1 cert = ( --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tooling.apache.org For additional commands, e-mail: dev-h...@tooling.apache.org