Move ruff to 0.12.2 and fix new complaints

This commit is contained in:
boxydog 2025-07-04 14:44:04 -05:00
commit 4dedf17958
21 changed files with 64 additions and 102 deletions

View file

@ -16,14 +16,14 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# ruff version should match the one in pyproject.toml
rev: v0.7.2
rev: v0.12.2
hooks:
- id: ruff
- id: ruff-check
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
- repo: https://github.com/rtts/djhtml
rev: '3.0.7'
rev: '3.0.8'
hooks:
- id: djhtml
- id: djcss

View file

@ -41,7 +41,7 @@ version = ".".join(release.split(".")[:1])
last_stable = project_data.get("version")
rst_prolog = f"""
.. |last_stable| replace:: :pelican-doc:`{last_stable}`
.. |min_python| replace:: {project_data.get('requires-python').split(",")[0]}
.. |min_python| replace:: {project_data.get("requires-python").split(",")[0]}
"""
extlinks = {"pelican-doc": ("https://docs.getpelican.com/en/latest/%s.html", "%s")}

View file

@ -402,8 +402,7 @@ def parse_arguments(argv=None):
"--autoreload",
dest="autoreload",
action="store_true",
help="Relaunch pelican each time a modification occurs"
" on the content files.",
help="Relaunch pelican each time a modification occurs on the content files.",
)
parser.add_argument(
@ -446,8 +445,7 @@ def parse_arguments(argv=None):
choices=("errors", "warnings"),
default="",
help=(
"Exit the program with non-zero status if any "
"errors/warnings encountered."
"Exit the program with non-zero status if any errors/warnings encountered."
),
)

View file

@ -1,3 +1,4 @@
import gzip
import hashlib
import logging
import os
@ -22,8 +23,6 @@ class FileDataCacher:
self._cache_path = os.path.join(self.settings["CACHE_PATH"], cache_name)
self._cache_data_policy = caching_policy
if self.settings["GZIP_CACHE"]:
import gzip
self._cache_open = gzip.open
else:
self._cache_open = open

View file

@ -342,8 +342,7 @@ class Content:
value.geturl(),
extra={
"limit_msg": (
"Other resources were not found "
"and their urls not replaced"
"Other resources were not found and their urls not replaced"
)
},
)

View file

@ -1,4 +1,5 @@
import logging
import warnings
from collections import defaultdict
from rich.console import Console
@ -156,8 +157,6 @@ def init(
def log_warnings():
import warnings
logging.captureWarnings(True)
warnings.simplefilter("default", DeprecationWarning)
init(logging.DEBUG, name="py.warnings")

View file

@ -53,7 +53,7 @@ class Paginator:
"Returns the total number of pages."
if self._num_pages is None:
hits = max(1, self.count - self.orphans)
self._num_pages = int(ceil(hits / (float(self.per_page) or 1)))
self._num_pages = ceil(hits / (float(self.per_page) or 1))
return self._num_pages
num_pages = property(_get_num_pages)

View file

@ -19,7 +19,7 @@ def iter_namespace(ns_pkg):
def get_namespace_plugins(ns_pkg=None):
if ns_pkg is None:
import pelican.plugins as ns_pkg
import pelican.plugins as ns_pkg # noqa: PLC0415
return {
name: importlib.import_module(name)
@ -29,7 +29,7 @@ def get_namespace_plugins(ns_pkg=None):
def list_plugins(ns_pkg=None):
from pelican.log import init as init_logging
from pelican.log import init as init_logging # noqa: PLC0415
init_logging(logging.INFO)
ns_plugins = get_namespace_plugins(ns_pkg)

View file

@ -630,8 +630,9 @@ class Readers(FileStampDataCacher):
# eventually filter the content with typogrify if asked so
if self.settings["TYPOGRIFY"]:
import smartypants
from typogrify.filters import typogrify
# typogrify is an optional feature, user may not have it installed
import smartypants # noqa: PLC0415
from typogrify.filters import typogrify # noqa: PLC0415
typogrify_dashes = self.settings["TYPOGRIFY_DASHES"]
if typogrify_dashes == "oldschool":
@ -657,7 +658,7 @@ class Readers(FileStampDataCacher):
return typogrify(
text,
self.settings["TYPOGRIFY_IGNORE_TAGS"],
**{f: False for f in self.settings["TYPOGRIFY_OMIT_FILTERS"]},
**dict.fromkeys(self.settings["TYPOGRIFY_OMIT_FILTERS"], False),
)
except TypeError:
try:

View file

@ -12,6 +12,7 @@ from types import ModuleType
from typing import Any, Optional
from pelican.log import LimitFilter
from pelican.paginator import PaginationRule
def load_source(name: str, path: str) -> ModuleType:
@ -320,8 +321,7 @@ def handle_deprecated_settings(settings: Settings) -> Settings:
# EXTRA_TEMPLATES_PATHS -> THEME_TEMPLATES_OVERRIDES
if "EXTRA_TEMPLATES_PATHS" in settings:
logger.warning(
"EXTRA_TEMPLATES_PATHS is deprecated use "
"THEME_TEMPLATES_OVERRIDES instead."
"EXTRA_TEMPLATES_PATHS is deprecated use THEME_TEMPLATES_OVERRIDES instead."
)
if settings.get("THEME_TEMPLATES_OVERRIDES"):
raise Exception(
@ -453,8 +453,7 @@ def handle_deprecated_settings(settings: Settings) -> Settings:
settings[key] = _printf_s_to_format_field(settings[key], "lang")
except ValueError:
logger.warning(
"Failed to convert %%s to {lang} for %s. "
"Falling back to default.",
"Failed to convert %%s to {lang} for %s. Falling back to default.",
key,
)
settings[key] = DEFAULT_CONFIG[key]
@ -476,8 +475,7 @@ def handle_deprecated_settings(settings: Settings) -> Settings:
settings[key] = _printf_s_to_format_field(settings[key], "slug")
except ValueError:
logger.warning(
"Failed to convert %%s to {slug} for %s. "
"Falling back to default.",
"Failed to convert %%s to {slug} for %s. Falling back to default.",
key,
)
settings[key] = DEFAULT_CONFIG[key]
@ -689,8 +687,6 @@ def configure_settings(settings: Settings) -> Settings:
)
# fix up pagination rules
from pelican.paginator import PaginationRule
pagination_rules = [
PaginationRule(*r)
for r in settings.get(

View file

@ -7,7 +7,7 @@ from sys import platform
from jinja2.utils import generate_lorem_ipsum
from pelican.contents import Article, Author, Category, Page, Static
from pelican.contents import Article, Author, Category, Page, Static, logger
from pelican.plugins.signals import content_object_init
from pelican.settings import DEFAULT_CONFIG
from pelican.tests.support import LoggedTestCase, get_context, get_settings, unittest
@ -49,24 +49,18 @@ class TestBase(LoggedTestCase):
self._enable_limit_filter()
def _disable_limit_filter(self):
from pelican.contents import logger
logger.disable_filter()
def _enable_limit_filter(self):
from pelican.contents import logger
logger.enable_filter()
def _copy_page_kwargs(self):
# make a deep copy of page_kwargs
page_kwargs = {key: self.page_kwargs[key] for key in self.page_kwargs}
for key in page_kwargs:
if not isinstance(page_kwargs[key], dict):
# copy page_kwargs
page_kwargs = dict(self.page_kwargs)
for key, val in page_kwargs.items():
if not isinstance(val, dict):
break
page_kwargs[key] = {
subkey: page_kwargs[key][subkey] for subkey in page_kwargs[key]
}
page_kwargs[key] = {subkey: val[subkey] for subkey in val}
return page_kwargs
@ -310,18 +304,16 @@ class TestPage(TestBase):
# I doubt this can work on all platforms ...
if platform == "win32":
locale = "jpn"
the_locale = "jpn"
else:
locale = "ja_JP.utf8"
page_kwargs["settings"]["DATE_FORMATS"] = {"jp": (locale, "%Y-%m-%d(%a)")}
the_locale = "ja_JP.utf8"
page_kwargs["settings"]["DATE_FORMATS"] = {"jp": (the_locale, "%Y-%m-%d(%a)")}
page_kwargs["metadata"]["lang"] = "jp"
import locale as locale_module
try:
page = Page(**page_kwargs)
self.assertEqual(page.locale_date, "2015-09-13(\u65e5)")
except locale_module.Error:
except locale.Error:
# The constructor of ``Page`` will try to set the locale to
# ``ja_JP.utf8``. But this attempt will failed when there is no
# such locale in the system. You can see which locales there are
@ -329,7 +321,7 @@ class TestPage(TestBase):
#
# Until we find some other method to test this functionality, we
# will simply skip this test.
unittest.skip(f"There is no locale {locale} in this system.")
unittest.skip(f"There is no locale {the_locale} in this system.")
def test_template(self):
# Pages default to page, metadata overwrites
@ -406,8 +398,7 @@ class TestPage(TestBase):
# fragment
args["content"] = (
"A simple test, with a "
'<a href="|filename|article.rst#section-2">link</a>'
'A simple test, with a <a href="|filename|article.rst#section-2">link</a>'
)
content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(
@ -687,8 +678,7 @@ class TestPage(TestBase):
}
args["content"] = (
"A simple test, with a link to a"
'<a href="{filename}poster.jpg">poster</a>'
'A simple test, with a link to a<a href="{filename}poster.jpg">poster</a>'
)
content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(

View file

@ -916,10 +916,7 @@ class TestArticlesGenerator(unittest.TestCase):
"This is a super article !",
"This is a super article !",
"This is an article with category !",
(
"This is an article with multiple authors in lastname, "
"firstname format!"
),
("This is an article with multiple authors in lastname, firstname format!"),
"This is an article with multiple authors in list format!",
"This is an article with multiple authors!",
"This is an article with multiple authors!",

View file

@ -3,7 +3,7 @@ import locale
from jinja2.utils import generate_lorem_ipsum
from pelican.contents import Article, Author
from pelican.paginator import Paginator
from pelican.paginator import PaginationRule, Paginator
from pelican.settings import DEFAULT_CONFIG
from pelican.tests.support import get_settings, unittest
@ -35,8 +35,6 @@ class TestPage(unittest.TestCase):
def test_save_as_preservation(self):
settings = get_settings()
# fix up pagination rules
from pelican.paginator import PaginationRule
pagination_rules = [
PaginationRule(*r)
for r in settings.get(
@ -56,8 +54,6 @@ class TestPage(unittest.TestCase):
self.assertEqual(page.save_as, "foobar.foo")
def test_custom_pagination_pattern(self):
from pelican.paginator import PaginationRule
settings = get_settings()
settings["PAGINATION_PATTERNS"] = [
PaginationRule(*r)
@ -81,8 +77,6 @@ class TestPage(unittest.TestCase):
self.assertEqual(page2.url, "//blog.my.site/2/")
def test_custom_pagination_pattern_last_page(self):
from pelican.paginator import PaginationRule
settings = get_settings()
settings["PAGINATION_PATTERNS"] = [
PaginationRule(*r)

View file

@ -23,7 +23,7 @@ def tmp_namespace_path(path):
"""
# This avoids calls to internal `pelican.plugins.__path__._recalculate()`
# as it should not be necessary
import pelican
import pelican # noqa: PLC0415
old_path = pelican.__path__[:]
try:
@ -41,8 +41,8 @@ class PluginTest(unittest.TestCase):
_NORMAL_PLUGIN_FOLDER = os.path.join(_PLUGIN_FOLDER, "normal_plugin")
def test_namespace_path_modification(self):
import pelican
import pelican.plugins
import pelican # noqa: PLC0415
import pelican.plugins # noqa: PLC0415
old_path = pelican.__path__[:]

View file

@ -1,12 +1,11 @@
from unittest.mock import Mock
from pelican.rstdirectives import abbr_role
from pelican.tests.support import unittest
class Test_abbr_role(unittest.TestCase):
def call_it(self, text):
from pelican.rstdirectives import abbr_role
rawtext = text
lineno = 42
inliner = Mock(name="inliner")

View file

@ -2,6 +2,7 @@
import argparse
import datetime
import json
import logging
import os
import re
@ -9,6 +10,7 @@ import subprocess
import sys
import tempfile
import time
import urllib.request as urllib_request
from collections import defaultdict
from html import unescape
from urllib.error import URLError
@ -16,6 +18,7 @@ from urllib.parse import quote, urlparse, urlsplit, urlunsplit
from urllib.request import urlretrieve
import dateutil.parser
from docutils.utils import column_width
# because logging.setLoggerClass has to be called before logging.getLogger
from pelican.log import init
@ -118,7 +121,7 @@ def decode_wp_content(content, br=True):
def _import_bs4():
"""Import and return bs4, otherwise sys.exit."""
try:
import bs4
import bs4 # noqa: PLC0415
except ImportError:
error = (
'Missing dependency "BeautifulSoup4" and "lxml" required to '
@ -272,7 +275,7 @@ def blogger2fields(xml):
def dc2fields(file):
"""Opens a Dotclear export file, and yield pelican fields"""
try:
from bs4 import BeautifulSoup
from bs4 import BeautifulSoup # noqa: PLC0415
except ImportError:
error = (
"Missing dependency "
@ -311,7 +314,7 @@ def dc2fields(file):
else:
posts.append(line)
print("%i posts read." % len(posts))
print(f"{len(posts)} posts read.")
subs = DEFAULT_CONFIG["SLUG_REGEX_SUBSTITUTIONS"]
for post in posts:
@ -367,7 +370,7 @@ def dc2fields(file):
.replace("a:0:", "")
)
if len(tag) > 1:
if int(len(tag[:1])) == 1:
if len(tag[:1]) == 1:
newtag = tag.split('"')[1]
tags.append(
BeautifulSoup(newtag, "xml")
@ -418,13 +421,10 @@ def dc2fields(file):
def _get_tumblr_posts(api_key, blogname, offset=0):
import json
import urllib.request as urllib_request
url = (
"https://api.tumblr.com/v2/blog/%s.tumblr.com/"
"posts?api_key=%s&offset=%d&filter=raw"
) % (blogname, api_key, offset)
f"https://api.tumblr.com/v2/blog/{blogname}.tumblr.com/"
f"posts?api_key={api_key}&offset={offset}&filter=raw"
)
request = urllib_request.Request(url)
handle = urllib_request.urlopen(request)
posts = json.loads(handle.read().decode("utf-8"))
@ -673,7 +673,7 @@ def mediumposts2fields(medium_export_dir: str):
def feed2fields(file):
"""Read a feed and yield pelican fields"""
import feedparser
import feedparser # noqa: PLC0415
d = feedparser.parse(file)
subs = DEFAULT_CONFIG["SLUG_REGEX_SUBSTITUTIONS"]
@ -707,8 +707,6 @@ def build_header(
):
"""Build a header from a list of fields"""
from docutils.utils import column_width
header = "{}\n{}\n".format(title, "#" * column_width(title))
if date:
header += f":date: {date}\n"
@ -971,10 +969,10 @@ def fields2pelican(
if is_pandoc_needed(in_markup) and not pandoc_version:
posts_require_pandoc.append(filename)
slug = not disable_slugs and filename or None
assert slug is None or filename == os.path.basename(
filename
), f"filename is not a basename: {filename}"
slug = (not disable_slugs and filename) or None
assert slug is None or filename == os.path.basename(filename), (
f"filename is not a basename: {filename}"
)
if wp_attach and attachments:
try:
@ -1047,8 +1045,7 @@ def fields2pelican(
"--wrap=none" if pandoc_version >= (1, 16) else "--no-wrap"
)
cmd = (
"pandoc --normalize {0} --from=html"
' --to={1} {2} -o "{3}" "{4}"'
'pandoc --normalize {0} --from=html --to={1} {2} -o "{3}" "{4}"'
)
cmd = cmd.format(
parse_raw,
@ -1070,7 +1067,7 @@ def fields2pelican(
try:
rc = subprocess.call(cmd, shell=True)
if rc < 0:
error = "Child was terminated by signal %d" % -rc
error = f"Child was terminated by signal {-rc}"
sys.exit(error)
elif rc > 0:

View file

@ -17,8 +17,7 @@ try:
import pelican
except ImportError:
err(
"Cannot import pelican.\nYou must "
"install Pelican in order to run this script.",
"Cannot import pelican.\nYou must install Pelican in order to run this script.",
-1,
)

View file

@ -10,6 +10,7 @@ import re
import shutil
import sys
import traceback
import unicodedata
import urllib
from collections.abc import Collection, Generator, Hashable, Iterable, Sequence
from contextlib import contextmanager
@ -25,6 +26,7 @@ from typing import (
)
import dateutil.parser
import unidecode
from watchfiles import Change
try:
@ -260,10 +262,6 @@ def slugify(
look into pelican.settings.DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS'].
"""
import unicodedata
import unidecode
def normalize_unicode(text: str) -> str:
# normalize text by compatibility composition
# see: https://en.wikipedia.org/wiki/Unicode_equivalence
@ -796,8 +794,7 @@ def order_content(
content.get_relative_source_path(),
extra={
"limit_msg": (
"More files are missing "
"the needed attribute."
"More files are missing the needed attribute."
)
},
)

View file

@ -261,8 +261,7 @@ class Writer:
# generated pages, and write
for page_num in range(next(iter(paginators.values())).num_pages):
paginated_kwargs = kwargs.copy()
for key in paginators.keys():
paginator = paginators[key]
for key, paginator in paginators.items():
previous_page = paginator.page(page_num) if page_num > 0 else None
page = paginator.page(page_num + 1)
next_page = (

View file

@ -96,7 +96,7 @@ dev = [
"tox>=4.11.3",
"invoke>=2.2.0",
# ruff version should match the one in .pre-commit-config.yaml
"ruff==0.7.2",
"ruff==0.12.2",
"tomli>=2.0.1; python_version < \"3.11\"",
]
@ -112,7 +112,6 @@ source-includes = [
requires = ["pdm-backend"]
build-backend = "pdm.backend"
[tool.ruff.lint]
# see https://docs.astral.sh/ruff/configuration/#using-pyprojecttoml
# "F" contains autoflake, see https://github.com/astral-sh/ruff/issues/1647

View file

@ -3,10 +3,11 @@ from pathlib import Path
from shutil import which
from invoke import task
from livereload import Server
PKG_NAME = "pelican"
PKG_PATH = Path(PKG_NAME)
DOCS_PORT = os.environ.get("DOCS_PORT", 8000)
DOCS_PORT = int(os.environ.get("DOCS_PORT", "8000"))
BIN_DIR = "bin" if os.name != "nt" else "Scripts"
PTY = os.name != "nt"
ACTIVE_VENV = os.environ.get("VIRTUAL_ENV", None)
@ -29,8 +30,6 @@ def docbuild(c):
@task(docbuild)
def docserve(c):
"""Serve docs at http://localhost:$DOCS_PORT/ (default port is 8000)"""
from livereload import Server
server = Server()
server.watch("docs/conf.py", lambda: docbuild(c))
server.watch("CONTRIBUTING.rst", lambda: docbuild(c))