diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index f98a3dcac..ac9f882e2 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -15,3 +15,5 @@ db241feaa445375dc05e189e69287000ffe5fa8e 7577dd7603f7cb3a09922d1edb65b6eafb6e2ac7 # Indent Jinja templates, HTML, CSS, and JS via DjHTML 4af40e80772a58eac8969360e5caeb99e3e26e78 +# Ruff UP031: Use F-string format specifiers instead of percent format +30bde3823f50b9ba8ac5996c1c46bb72031aa6b8 diff --git a/pelican/contents.py b/pelican/contents.py index 21c662963..cf13dabcb 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -250,7 +250,7 @@ def _expand_settings(self, key: str, klass: Optional[str] = None) -> str: def get_url_setting(self, key: str) -> str: if hasattr(self, "override_" + key): return getattr(self, "override_" + key) - key = key if self.in_default_lang else "lang_%s" % key + key = key if self.in_default_lang else f"lang_{key}" return self._expand_settings(key) def _link_replacer(self, siteurl: str, m: re.Match) -> str: diff --git a/pelican/generators.py b/pelican/generators.py index a2bf2f448..73b517132 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -537,9 +537,9 @@ def generate_direct_templates(self, write): """Generate direct templates pages""" for template in self.settings["DIRECT_TEMPLATES"]: save_as = self.settings.get( - "%s_SAVE_AS" % template.upper(), "%s.html" % template + f"{template.upper()}_SAVE_AS", f"{template}.html" ) - url = self.settings.get("%s_URL" % template.upper(), "%s.html" % template) + url = self.settings.get(f"{template.upper()}_URL", f"{template}.html") if not save_as: continue diff --git a/pelican/settings.py b/pelican/settings.py index 214d88a3c..46b761a51 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -591,7 +591,7 @@ def configure_settings(settings: Settings) -> Settings: if os.path.exists(theme_path): settings["THEME"] = theme_path else: - raise Exception("Could not find the theme %s" % settings["THEME"]) + raise Exception("Could not find the theme {}".format(settings["THEME"])) # standardize strings to lowercase strings for key in ["DEFAULT_LANG"]: diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index d248c3bb6..81f8907c7 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -282,7 +282,7 @@ def test_datetime(self): # # Until we find some other method to test this functionality, we # will simply skip this test. - unittest.skip("There is no locale %s in this system." % locale) + unittest.skip(f"There is no locale {locale} in this system.") def test_template(self): # Pages default to page, metadata overwrites diff --git a/pelican/tests/test_importer.py b/pelican/tests/test_importer.py index 0349a175e..10b0f7f69 100644 --- a/pelican/tests/test_importer.py +++ b/pelican/tests/test_importer.py @@ -148,7 +148,7 @@ def test_dirpage_directive_for_page_kind(self): test_post = filter(lambda p: p[0].startswith("Empty Page"), self.posts) with temporary_folder() as temp: fname = next(iter(silent_f2p(test_post, "markdown", temp, dirpage=True))) - self.assertTrue(fname.endswith("pages%sempty.md" % os.path.sep)) + self.assertTrue(fname.endswith(f"pages{os.path.sep}empty.md")) def test_dircat(self): silent_f2p = mute(True)(fields2pelican) diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index 075f55ebe..ba03b529d 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -71,8 +71,7 @@ def assertDirsEqual(self, left_path, right_path, msg=None): if proc.returncode != 0: msg = self._formatMessage( msg, - "%s and %s differ:\nstdout:\n%s\nstderr\n%s" - % (left_path, right_path, out, err), + f"{left_path} and {right_path} differ:\nstdout:\n{out}\nstderr\n{err}", ) raise self.failureException(msg) diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index 928eb2635..ec366fa84 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -27,8 +27,7 @@ def assertDictHasSubset(self, dictionary, subset): self.assertEqual( value, real_value, - "Expected %s to have value %s, but was %s" - % (key, value, real_value), + f"Expected {key} to have value {value}, but was {real_value}", ) else: self.fail(f"Expected {key} to have value {value}, but was not in Dict") diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index beda71406..f7f11ffbf 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -721,11 +721,11 @@ def _create_dir(self, *path): def _exist_file(self, *path): path = os.path.join(self.root_dir, *path) - self.assertTrue(os.path.isfile(path), "File does not exist: %s" % path) + self.assertTrue(os.path.isfile(path), f"File does not exist: {path}") def _exist_dir(self, *path): path = os.path.join(self.root_dir, *path) - self.assertTrue(os.path.exists(path), "Directory does not exist: %s" % path) + self.assertTrue(os.path.exists(path), f"Directory does not exist: {path}") def test_copy_file_same_path(self): self._create_file("a.txt") diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 39504edbe..65517f42a 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -155,7 +155,7 @@ def wp2fields(xml, wp_custpost=False): # Use HTMLParser due to issues with BeautifulSoup 3 title = unescape(item.title.contents[0]) except IndexError: - title = "No title [%s]" % item.find("post_name").string + title = "No title [{}]".format(item.find("post_name").string) logger.warning('Post "%s" is lacking a proper title', title) post_name = item.find("post_name").string @@ -510,11 +510,10 @@ def tumblr2fields(api_key, blogname): title = post.get("question") content = ( "

" - '%s' - ": %s" + '{}' + ": {}" "

\n" - " %s" - % ( + " {}".format( post.get("asking_name"), post.get("asking_url"), post.get("question"), @@ -710,19 +709,19 @@ def build_header( header = "{}\n{}\n".format(title, "#" * column_width(title)) if date: - header += ":date: %s\n" % date + header += f":date: {date}\n" if author: - header += ":author: %s\n" % author + header += f":author: {author}\n" if categories: - header += ":category: %s\n" % ", ".join(categories) + header += ":category: {}\n".format(", ".join(categories)) if tags: - header += ":tags: %s\n" % ", ".join(tags) + header += ":tags: {}\n".format(", ".join(tags)) if slug: - header += ":slug: %s\n" % slug + header += f":slug: {slug}\n" if status: - header += ":status: %s\n" % status + header += f":status: {status}\n" if attachments: - header += ":attachments: %s\n" % ", ".join(attachments) + header += ":attachments: {}\n".format(", ".join(attachments)) header += "\n" return header @@ -732,21 +731,21 @@ def build_asciidoc_header( ): """Build a header from a list of fields""" - header = "= %s\n" % title + header = f"= {title}\n" if author: - header += "%s\n" % author + header += f"{author}\n" if date: - header += "%s\n" % date + header += f"{date}\n" if categories: - header += ":category: %s\n" % ", ".join(categories) + header += ":category: {}\n".format(", ".join(categories)) if tags: - header += ":tags: %s\n" % ", ".join(tags) + header += ":tags: {}\n".format(", ".join(tags)) if slug: - header += ":slug: %s\n" % slug + header += f":slug: {slug}\n" if status: - header += ":status: %s\n" % status + header += f":status: {status}\n" if attachments: - header += ":attachments: %s\n" % ", ".join(attachments) + header += ":attachments: {}\n".format(", ".join(attachments)) header += "\n" return header @@ -755,21 +754,21 @@ def build_markdown_header( title, date, author, categories, tags, slug, status=None, attachments=None ): """Build a header from a list of fields""" - header = "Title: %s\n" % title + header = f"Title: {title}\n" if date: - header += "Date: %s\n" % date + header += f"Date: {date}\n" if author: - header += "Author: %s\n" % author + header += f"Author: {author}\n" if categories: - header += "Category: %s\n" % ", ".join(categories) + header += "Category: {}\n".format(", ".join(categories)) if tags: - header += "Tags: %s\n" % ", ".join(tags) + header += "Tags: {}\n".format(", ".join(tags)) if slug: - header += "Slug: %s\n" % slug + header += f"Slug: {slug}\n" if status: - header += "Status: %s\n" % status + header += f"Status: {status}\n" if attachments: - header += "Attachments: %s\n" % ", ".join(attachments) + header += "Attachments: {}\n".format(", ".join(attachments)) header += "\n" return header @@ -1076,7 +1075,7 @@ def fields2pelican( error = "Please, check your Pandoc installation." sys.exit(error) except OSError as e: - error = "Pandoc execution failed: %s" % e + error = f"Pandoc execution failed: {e}" sys.exit(error) with open(out_filename, encoding="utf-8") as fs: diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index 9be107d08..e278e3bc4 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -220,7 +220,7 @@ def main(): CONF["basedir"] = open(project).read().rstrip("\n") print( "Using project associated with current virtual environment. " - "Will save to:\n%s\n" % CONF["basedir"] + "Will save to:\n{}\n".format(CONF["basedir"]) ) else: CONF["basedir"] = os.path.abspath( @@ -394,7 +394,7 @@ def main(): render_jinja_template("tasks.py.jinja2", CONF, "tasks.py") render_jinja_template("Makefile.jinja2", CONF, "Makefile") - print("Done. Your new project is available at %s" % CONF["basedir"]) + print("Done. Your new project is available at {}".format(CONF["basedir"])) if __name__ == "__main__": diff --git a/pelican/utils.py b/pelican/utils.py index 6dc4825e0..a29fdf815 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -626,7 +626,7 @@ def truncate_html_words(s: str, num: int, end_text: str = "…") -> str: out += " " + end_text # Close any tags still open for tag in truncator.open_tags: - out += "" % tag + out += f"" # Return string return out diff --git a/pelican/writers.py b/pelican/writers.py index 91b1caebe..1a2cf7b04 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -107,14 +107,14 @@ def _open_w(self, filename, encoding, override=False): """ if filename in self._overridden_files: if override: - raise RuntimeError("File %s is set to be overridden twice" % filename) + raise RuntimeError(f"File {filename} is set to be overridden twice") logger.info("Skipping %s", filename) filename = os.devnull elif filename in self._written_files: if override: logger.info("Overwriting %s", filename) else: - raise RuntimeError("File %s is to be overwritten" % filename) + raise RuntimeError(f"File {filename} is to be overwritten") if override: self._overridden_files.add(filename) self._written_files.add(filename) @@ -272,10 +272,10 @@ def _get_localcontext(context, name, kwargs, relative_urls): ) paginated_kwargs.update( { - "%s_paginator" % key: paginator, - "%s_page" % key: page, - "%s_previous_page" % key: previous_page, - "%s_next_page" % key: next_page, + f"{key}_paginator": paginator, + f"{key}_page": page, + f"{key}_previous_page": previous_page, + f"{key}_next_page": next_page, } ) diff --git a/pyproject.toml b/pyproject.toml index ccd75f9f2..dd256a44b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -190,7 +190,6 @@ ignore = [ # either by removing them from the `select` or `extend-select` configuration, # or adding them to the `ignore` configuration." "ISC001", # single-line-implicit-string-concatenation - "UP031", # printf-string-formatting # PERF203 has minimal performance impact, and you have to catch the exception # inside the loop if you want to ignore it, so let's ignore PERF203. "PERF203", # try-except-in-loop