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 <[email protected]>
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: [email protected]
For additional commands, e-mail: [email protected]