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..653c8ada44 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
@@ -52,7 +53,9 @@ def generate_resource_post(resource):
if resource["type"] == "video" and resource["embeddable"]:
video_url = dedent(f"""\
-
+
""")
content.append(video_url)
@@ -87,7 +90,7 @@ def generate_resource_post(resource):
elif resource["type"] == "video" and resource["embeddable"]:
content.append(
- f"\n\nAs seen at [{resource['event_name']}]({resource['event_url']}).\n\n"
+ f"\n\nAs seen at [{resource['event_name']}]({resource['event_url']}).\n\n" # noqa: E501
)
content.append("")
@@ -110,26 +113,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
- """)
+ """) # noqa: E501
)
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
- """))
+ """) # noqa: E501
+ )
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"""\
@@ -144,19 +151,22 @@ def generate_event_post(authors, event, involvement, team):
{attendees(authors, team)} will be attending [{event.name}]({event.url}) {event_timeframe}!
\n\n
- """)
+ """) # noqa: E501
)
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:
@@ -165,24 +175,26 @@ def generate_event_post(authors, event, involvement, team):
team_members = first_pronoun.capitalize()
if inv["type"] == "talk":
- talk = dedent(f"""
+ talk = dedent(
+ f"""
- {team_members} will be giving a talk entitled [{talk_title_punctuation(inv["title"])}]({inv["url"]})
- """)
+ """ # noqa: E501
+ )
content.append(talk)
elif inv["type"] == "tutorial":
tutorial = dedent(f"""
- {team_members} will be hosting a tutorial entitled [{talk_title_punctuation(inv["title"])}]({inv["url"]})
- """)
+ """) # noqa: E501
content.append(tutorial)
elif inv["type"] == "workshop":
tutorial = dedent(f"""
- {team_members} will be hosting a workshop entitled [{talk_title_punctuation(inv["title"])}]({inv["url"]})
- """)
+ """) # noqa: E501
content.append(tutorial)
elif inv["type"] == "sprint":
@@ -203,7 +215,7 @@ def generate_event_post(authors, event, involvement, team):
tutorial = dedent(f"""
- {team_members} will be hosting the [{talk_title_punctuation(inv["title"])}]({inv["url"]})
- """)
+ """) # noqa: E501
content.append(tutorial)
if len(authors) > 1:
@@ -216,11 +228,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)
@@ -240,14 +252,16 @@ def generate_team_members(team, page, current):
try:
mastodon = member_details["mastodon"].split("@")
- member_image_details_mastodon = f"""{fa("mastodon", "lg", "brands")} [{member_details["mastodon"]}](https://{mastodon[2]}/@{mastodon[1]})
"""
+ member_image_details_mastodon = f"""{fa("mastodon", "lg", "brands")} [{member_details["mastodon"]}](https://{mastodon[2]}/@{mastodon[1]})
""" # noqa: E501
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"]}>
"""
+ member_email_details = f"""{fa("envelope", "lg", "solid")} <{member_details["email"]}>
""" # noqa: E501
except KeyError:
member_email_details = ""
@@ -266,14 +280,19 @@ def generate_team_members(team, page, current):
- """
+ """ # noqa: E501
)
member_bio = (
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..0cc635e009 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -31,6 +31,35 @@ 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
+ # The SIM rules are *very* opinionated, and don't necessarily make for better code.
+ # They may be worth occasionally turning on just to see if something could actually
+ # use improvement.
+ # "SIM", # flake8-simplify
+]
+# Ignores needed to pass the Ruff 0.16 update.
+ignore = [
+ "BLE001",
+ "DTZ001",
+ "DTZ005",
+ "DTZ006",
+ "DTZ011",
+ "DTZ012",
+ "RUF009",
+ # The SIM rules are *very* opinionated, and don't necessarily make for better code.
+ "SIM102", # collapsible-if makes code (and testing paths) harder to verify.
+]
+
[tool.rumdl]
flavor = "mkdocs"
include = ["**/*.md"]
diff --git a/scripts/new_post.py b/scripts/new_post.py
index acb51c6f8a..9bd20def04 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
@@ -62,7 +62,11 @@ def input_date(prompt: str, default: datetime.date | None = None) -> datetime.da
if not date and default:
return default
try:
- return datetime.datetime.strptime(date, "%Y-%m-%d").date()
+ return (
+ datetime.datetime.strptime(date, "%Y-%m-%d")
+ .replace(tzinfo=datetime.UTC)
+ .date()
+ )
except ValueError:
print("Invalid date format. Must be YYYY-DD-MM format.")
@@ -147,27 +151,52 @@ 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
)
involvement_metadata["date"] = input_date(
- f"Start date of {involvement_type} at {event_name} (e.g. 2026-01-01, leave blank if same as {event_name} start date): ",
+ (
+ f"Start date of {involvement_type} at {event_name} "
+ f"(e.g. 2026-01-01, leave blank if same as {event_name} start date): "
+ ),
event_start_date,
)
involvement_metadata["end_date"] = input_date(
- f"End date of {involvement_type} (e.g. 2026-01-01; leave blank if same as {involvement_type} start date): ",
+ (
+ f"End date of {involvement_type} (e.g. 2026-01-01; "
+ f"leave blank if same as {involvement_type} start date): "
+ ),
involvement_metadata["date"],
)
- 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"""\
+ 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.
Description should begin on the line below 'description: |-' with that line left intact.
- """)
+ """ # noqa: E501
+ )
involvements.append(involvement_metadata)
@@ -177,17 +206,20 @@ 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."""),
+ Description should begin on the line below 'description: |-' with that line left intact.
+ """ # noqa: E501
+ ),
},
"involvement": involvements,
}
@@ -213,10 +245,13 @@ 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.""")
+ Description should begin on the line below 'description: |-' with that line left intact.
+ """ # noqa: E501
+ )
authors = set()
resource_authors = input(
@@ -293,13 +328,16 @@ def generate_entry(metadata, payload):
)
if post_type == "blog":
metadata = request_blog_metadata()
- payload = dedent("""\
+ payload = dedent(
+ """\
Add blog post introduction here. Leave newline between frontmatter and content.
- Add blog post content here.""")
+ Add blog post content here.
+ """ # noqa: E501
+ )
elif post_type == "event":
metadata = request_event_metadata()
payload = "\n{{ generate_event_post(authors, event, involvement, team) }}"