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.""")