From 3dbc5f700b59369e74fc7c89f2c4e1e0f2639af4 Mon Sep 17 00:00:00 2001 From: Aruna Selvam Date: Sun, 30 Aug 2026 14:50:34 +1000 Subject: [PATCH] Add ruff for linting and formatting Python scripts --- .pre-commit-config.yaml | 6 ++++++ docs/macros.py | 43 +++++++++++++++++++++++++++-------------- pyproject.toml | 31 +++++++++++++++++++++++++++++ scripts/new_post.py | 30 +++++++++++++++++++++------- 4 files changed, 89 insertions(+), 21 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c41a2c5609..4ba43ec78c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,6 +9,12 @@ repos: # PO files may not have a trailing EOL, as they are tool generated exclude: "locales" - id: trailing-whitespace + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.16.3 + hooks: + - id: ruff-check + args: [ --fix ] + - id: ruff-format - repo: https://github.com/rvben/rumdl-pre-commit rev: v0.2.43 hooks: diff --git a/docs/macros.py b/docs/macros.py index 5783804281..82a6f226ef 100644 --- a/docs/macros.py +++ b/docs/macros.py @@ -1,6 +1,7 @@ import datetime from pathlib import Path from textwrap import dedent + import yaml @@ -110,26 +111,30 @@ def generate_event_post(authors, event, involvement, team): show_participation = False for inv in involvement: if inv["type"] == "organizing": - highlight_authors.update({m for m in inv["team_members"]}) + highlight_authors.update(set(inv["team_members"])) content.append( dedent(f"""\ {attendees(inv["team_members"], team)} will be organizing [{event.name}]({event.url}), which will happen {event_timeframe}!\n\n """) ) elif inv["type"] == "keynote": - highlight_authors.update({m for m in inv["team_members"]}) - content.append(dedent(f""" + highlight_authors.update(set(inv["team_members"])) + content.append( + dedent(f""" {attendees(inv["team_members"], team)} will be keynoting {event.name}, giving a presentation entitled [{talk_title_punctuation(inv["title"])}]({inv["url"]}) \n\n - """)) + """) + ) elif inv["type"] != "attending": show_participation = True inv_types = {inv["type"] for inv in involvement} if "keynote" in inv_types or "organizing" in inv_types: - other_authors = [author for author in authors if author not in highlight_authors] + other_authors = [ + author for author in authors if author not in highlight_authors + ] if other_authors: content.append( dedent(f"""\ @@ -149,14 +154,17 @@ def generate_event_post(authors, event, involvement, team): content.append(f"{event.description}\n\n") - _, first_pronoun, second_pronoun = pronouns(team["authors"][authors[-1]]["pronoun"]) - + _, first_pronoun, second_pronoun = pronouns( + team["authors"][authors[-1]]["pronoun"] + ) if show_participation: if len(authors) > 1: - content.append(f"You can find us throughout the event:\n\n") + content.append("You can find us throughout the event:\n\n") else: - content.append(f"You can find {second_pronoun} throughout the event:\n\n") + content.append( + f"You can find {second_pronoun} throughout the event:\n\n" + ) for inv in involvement: if len(authors) > 1: @@ -216,11 +224,11 @@ def generate_event_post(authors, event, involvement, team): f"Please come say hello, {first_pronoun}'d love to meet you. " ) if first_pronoun == "he": - content.append(f"He's looking forward to seeing you there!") + content.append("He's looking forward to seeing you there!") elif first_pronoun == "she": - content.append(f"She's looking forward to seeing you there!") + content.append("She's looking forward to seeing you there!") elif first_pronoun == "they": - content.append(f"They're looking forward to seeing you there!") + content.append("They're looking forward to seeing you there!") return "".join(content) @@ -244,7 +252,9 @@ def generate_team_members(team, page, current): except KeyError: member_image_details_mastodon = "" - pronoun_logo, first_pronoun, second_pronoun = pronouns(member_details["pronoun"]) + pronoun_logo, first_pronoun, second_pronoun = pronouns( + member_details["pronoun"] + ) try: member_email_details = f"""
{fa("envelope", "lg", "solid")} <{member_details["email"]}>
""" @@ -273,7 +283,12 @@ def generate_team_members(team, page, current): Path(page.file.src_dir) / f"about/team/{github_id}.md" ).read_text() - team_member = member_title + member_image_details + member_bio + "" + team_member = ( + member_title + + member_image_details + + member_bio + + "" + ) if not current and "emeritus_date" in member_details: team_member_content.append( diff --git a/pyproject.toml b/pyproject.toml index 6734a71fad..cb9f1f0f5d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,9 @@ +[tool.uv] +# On Windows, uv's isolated build environments don't include tzdata, but +# beeware-docs-tools' build backend (autocalver) calls zoneinfo.ZoneInfo("UTC"), +# which fails without it. Extend the build environment with tzdata on Windows. +extra-build-dependencies = { "beeware-docs-tools" = ["tzdata; sys_platform == 'win32'"] } + [dependency-groups] pre-commit = [ "pre-commit == 4.6.1", @@ -31,6 +37,31 @@ translate = [ {include-group = "wlc"}, ] +[tool.ruff.lint] +# In addition to the default rules, these additional rules will be used: +extend-select = [ + "E", # pycodestyle + "W", # pycodestyle + "F", # pyflakes + "UP", # pyupgrade + "B", # flake8-bugbear + "ASYNC", # flake8-async + "C4", # flake8-comprehensions + "I", # isort +] +ignore = [ + # Blog/event post metadata is date-only and never needs timezone awareness. + "DTZ005", + "DTZ007", + "DTZ011", +] + +[tool.ruff.lint.per-file-ignores] +# These files build long Markdown/HTML content from f-strings; splitting the +# lines to satisfy the line-length limit would break the generated content. +"docs/macros.py" = ["E501"] +"scripts/new_post.py" = ["E501"] + [tool.rumdl] flavor = "mkdocs" include = ["**/*.md"] diff --git a/scripts/new_post.py b/scripts/new_post.py index acb51c6f8a..634e1b07f9 100644 --- a/scripts/new_post.py +++ b/scripts/new_post.py @@ -1,8 +1,8 @@ +import datetime import re from pathlib import Path -import datetime from textwrap import dedent -from urllib.error import URLError, HTTPError +from urllib.error import HTTPError, URLError from urllib.parse import urlparse from urllib.request import Request, urlopen @@ -147,7 +147,15 @@ def request_event_metadata(): presentation_title = input("Presentation title: ") involvement_metadata["title"] = presentation_title - if involvement_type in ["keynote", "talk", "tutorial", "workshop", "sprint", "booth", "track"]: + if involvement_type in [ + "keynote", + "talk", + "tutorial", + "workshop", + "sprint", + "booth", + "track", + ]: involvement_metadata["url"] = input_url( f"{involvement_type} URL (leave blank if unavailable): ", event_url ) @@ -161,7 +169,15 @@ def request_event_metadata(): involvement_metadata["date"], ) - if involvement_type in ["keynote", "talk", "tutorial", "workshop", "sprint", "booth", "track"]: + if involvement_type in [ + "keynote", + "talk", + "tutorial", + "workshop", + "sprint", + "booth", + "track", + ]: # if statement duplicated for the purposes of preserving desired metadata order involvement_metadata["description"] = dedent(f"""\ TODO: Remove this content and update with {involvement_type} description. @@ -177,14 +193,14 @@ def request_event_metadata(): return { "title": f"We'll be at {event_name}!", "date": datetime.date.today(), - "authors": sorted(list(authors)), + "authors": sorted(authors), "categories": ["Events"], "event": { "name": event_name, "url": event_url, "date": event_start_date, "end_date": event_end_date, - "description": dedent(f"""\ + "description": dedent("""\ TODO: Remove this content and update with event description. Description should begin on the line below 'description: |-' with that line left intact."""), @@ -213,7 +229,7 @@ def request_resource_metadata(): resource_metadata["event_name"] = input("Event name: ") resource_metadata["event_url"] = input_url("Event URL: ") - resource_metadata["description"] = dedent(f"""\ + resource_metadata["description"] = dedent("""\ TODO: Remove this content and update with resource description. Description should begin on the line below 'description: |-' with that line left intact.""")