From ff13b1cc4d5c0682f349f473b54ad022a6e5fa2c Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 14 Jun 2024 09:03:56 +0200 Subject: [PATCH 1/5] Adjust test for changed arrow Unicode specifier --- pelican/plugins/simple_footnotes/test_simple_footnotes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/plugins/simple_footnotes/test_simple_footnotes.py b/pelican/plugins/simple_footnotes/test_simple_footnotes.py index d70292a..ff7f02b 100644 --- a/pelican/plugins/simple_footnotes/test_simple_footnotes.py +++ b/pelican/plugins/simple_footnotes/test_simple_footnotes.py @@ -27,7 +27,7 @@ def test_simple(self): ( 'words1end' '
    ' - '
  1. footnote \u21a9
  2. ' + '
  3. footnote \u21a9\ufe0e
  4. ' "
" ), ) From 6ea494eb9f226cafb9faa75dbf0ef0e7ad075576 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 13 Jun 2024 15:11:40 +0200 Subject: [PATCH 2/5] Tweak README with minor improvements --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a1c7066..0f00a66 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,9 @@ Simple Footnotes ================ [![Build Status](https://img.shields.io/github/actions/workflow/status/pelican-plugins/simple-footnotes/main.yml?branch=main)](https://github.com/pelican-plugins/simple-footnotes/actions) - [![PyPI Version](https://img.shields.io/pypi/v/pelican-simple-footnotes)](https://pypi.org/project/pelican-simple-footnotes/) +[![Downloads](https://img.shields.io/pypi/dm/pelican-simple-footnotes)](https://pypi.org/project/pelican-simple-footnotes/) +![License](https://img.shields.io/pypi/l/pelican-simple-footnotes?color=blue) Simple Footnotes is a Pelican plugin for adding footnotes to articles and pages. @@ -14,6 +15,8 @@ This plugin, and its dependent package `html5lib`, can be installed via: python -m pip install pelican-simple-footnotes +As long as you have not explicitly added a `PLUGINS` setting to your Pelican settings file, then the newly-installed plugin should be automatically detected and enabled. Otherwise, you must add `simple_footnotes` to your existing `PLUGINS` list. For more information, please see the [How to Use Plugins](https://docs.getpelican.com/en/latest/plugins.html#how-to-use-plugins) documentation. + Usage ----- @@ -23,12 +26,11 @@ When writing an article or page, add a footnote like this: This will appear as, roughly: -Here’s my written text1 +Here is my written text1 1. and here is a footnote ↩︎ -This should work with any content format (reST, Markdown, et cetera), because -it looks for `[ref]` and `[/ref]` once the conversion to HTML has happened. +This should work with any content format (Markdown, reStructuredText, et cetera), because the plugin looks for `[ref]` and `[/ref]` once the conversion to HTML has happened. Contributing ------------ @@ -40,10 +42,7 @@ To start contributing to this plugin, review the [Contributing to Pelican][] doc Credits ------- -Originally authored by [Stuart Langridge](https://kryogenix.org/), February 2014, -and subsequently enhanced by members of the Pelican community, including -[Justin Mayer](https://justinmayer.com/), who re-packaged it for publication to -PyPI. +Originally authored by [Stuart Langridge](https://kryogenix.org/), February 2014, and subsequently enhanced by members of the Pelican community, including [Justin Mayer](https://justinmayer.com/), who re-packaged it for publication to PyPI. Inspired by Andrew Nacin’s [Simple Footnotes WordPress plugin](https://wordpress.org/plugins/simple-footnotes/). From c29c2eb99c51c26de7bceb2fc5a64f1b45cf2a60 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 14 Jun 2024 08:39:43 +0200 Subject: [PATCH 3/5] Replace Poetry with PDM, Flake8/isort with Ruff --- .github/workflows/main.yml | 95 +++++++++++++++++++------- .gitignore | 3 +- .pre-commit-config.yaml | 33 ++------- pyproject.toml | 134 ++++++++++++++++++++++++------------- tasks.py | 76 ++++++++++----------- tox.ini | 3 - 6 files changed, 206 insertions(+), 138 deletions(-) delete mode 100644 tox.ini diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c9bb31c..d8a7b38 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,41 +6,90 @@ on: pull_request: branches: +env: + PYTEST_ADDOPTS: "--color=yes" + +permissions: + contents: read + jobs: test: + name: Test - Python ${{ matrix.python-version }} runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} & PDM + uses: pdm-project/setup-pdm@v4 with: - python-version: "3.9" - - name: Install Poetry - run: python -m pip install poetry + python-version: ${{ matrix.python-version }} + cache: true + cache-dependency-path: ./pyproject.toml + - name: Install dependencies - run: poetry install + run: pdm install + - name: Run tests - run: poetry run invoke tests + run: pdm run invoke tests + + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python & PDM + uses: pdm-project/setup-pdm@v4 + with: + python-version: "3.10" + + - name: Install dependencies + run: pdm install + - name: Run linters - run: poetry run invoke lint --diff + run: pdm run invoke lint --diff + + deploy: + name: Deploy + environment: Deployment + needs: [test, lint] + runs-on: ubuntu-latest + if: github.ref=='refs/heads/main' && github.event_name!='pull_request' + + permissions: + contents: write + id-token: write + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + - name: Check release - if: github.ref == 'refs/heads/main' - run: | - python -m pip install githubrelease - python -m pip install autopub - echo "##[set-output name=release;]$(autopub check)" id: check_release - - name: Deploy - if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' && steps.check_release.outputs.release == '' run: | - git checkout ${GITHUB_REF##*/} - git remote set-url origin https://$GITHUB_TOKEN@github.com/${GITHUB_REPOSITORY} + python -m pip install autopub httpx + python -m pip install https://github.com/scikit-build/github-release/archive/master.zip + autopub check + + - name: Publish + if: ${{ steps.check_release.outputs.autopub_release=='true' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | autopub prepare - poetry build autopub commit + autopub build autopub githubrelease - poetry publish -u __token__ -p $PYPI_PASSWORD - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + + - name: Upload package to PyPI + if: ${{ steps.check_release.outputs.autopub_release=='true' }} + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore index c04bc49..12568d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -poetry.lock +.pdm-python +pdm.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3e1f045..8621642 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ ci: # See https://pre-commit.com/hooks.html for info on hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.6.0 hooks: - id: check-added-large-files - id: check-ast @@ -20,30 +20,9 @@ repos: - id: forbid-new-submodules - id: trailing-whitespace - - repo: https://github.com/psf/black - rev: 23.3.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.8 hooks: - - id: black - - - repo: https://github.com/pycqa/flake8 - rev: 3.9.2 - hooks: - - id: flake8 - args: [--max-line-length=88] - - - repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort - - - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 - hooks: - - id: pyupgrade - args: [--py38-plus] - - - repo: https://github.com/hakancelikdev/unimport - rev: 0.16.0 - hooks: - - id: unimport - args: [--remove, --include-star-import] + - id: ruff + - id: ruff-format + args: ["--check"] diff --git a/pyproject.toml b/pyproject.toml index 054909f..8cc8f85 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,51 +1,64 @@ -[tool.poetry] -name = "pelican-simple-footnotes" -version = "1.0.2" +[project] +name = "pelican-photos" +version = "1.6.0" description = "Pelican plugin to add footnotes to articles and pages" -authors = ["Stuart Langridge ", "Justin Mayer "] -license = "AGPL-3.0" +authors = [{name = "Stuart Langridge", email = "sil@kryogenix.org"}, {name = "Justin Mayer", email = "entroP@gmail.com"}] +license = {text = "AGPL-3.0"} readme = "README.md" -keywords = ["footnotes", "pelican", "plugin"] -repository = "https://github.com/pelican-plugins/simple-footnotes" -documentation = "https://docs.getpelican.com" -packages = [ - { include = "pelican" }, -] - +keywords = ["pelican", "plugin", "footnotes"] classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Framework :: Pelican", "Framework :: Pelican :: Plugins", "Intended Audience :: End Users/Desktop", + "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)", "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Internet :: WWW/HTTP", "Topic :: Software Development :: Libraries :: Python Modules", ] -[tool.poetry.urls] -"Funding" = "https://donate.getpelican.com/" -"Issue Tracker" = "https://github.com/pelican-plugins/sitemap/issues" +requires-python = ">=3.8.1,<4.0" +dependencies = [ + "pelican>=4.5", + "html5lib>=1.1", +] + +[project.urls] +Homepage = "https://github.com/pelican-plugins/simple-footnotes" +"Issue Tracker" = "https://github.com/pelican-plugins/simple-footnotes/issues" +Funding = "https://donate.getpelican.com/" + +[project.optional-dependencies] +markdown = ["markdown>=3.4"] -[tool.poetry.dependencies] -python = ">=3.8.1,<4.0" -pelican = "^4.5" -html5lib = "^1.1" -markdown = {version = ">=3.2", optional = true} +[tool.pdm] -[tool.poetry.group.dev.dependencies] -black = "^23" -flake8 = "^3.9" -flake8-black = "^0.3" -invoke = "^2.1" -isort = "^5.12" -markdown = "^3.4" -pytest = "^7.0" -pytest-cov = "^4.0" -pytest-sugar = "^0.9.7" +[tool.pdm.dev-dependencies] +lint = [ + "invoke>=2.2.0", + "ruff>=0.4.5,<0.5.0" +] +test = [ + "markdown>=3.4", + "pytest>=7.0", + "pytest-cov>=4.0", + "pytest-sugar>=1.0.0", +] -[tool.poetry.extras] -markdown = ["markdown"] +[tool.pdm.build] +source-includes = [ + "CHANGELOG.md", + "CONTRIBUTING.md", +] +includes = ["pelican/"] +excludes = ["**/.DS_Store", "tasks.py"] [tool.autopub] project-name = "Simple Footnotes" @@ -53,20 +66,51 @@ git-username = "botpub" git-email = "52496925+botpub@users.noreply.github.com" append-github-contributor = true -[tool.isort] -profile = "black" -multi_line_output = 3 - -# Sort imports within their section independent of the import type -force_sort_within_sections = true +[tool.ruff.lint] +select = [ + "B", # flake8-bugbear + "BLE", # flake8-blind-except + "C4", # flake8-comprehensions + "D", # pydocstyle + "E", # pycodestyle + "F", # pyflakes + "I", # isort + "ICN", # flake8-import-conventions + "ISC", # flake8-implicit-str-concat + "PGH", # pygrep-hooks + "PL", # pylint + "RET", # flake8-return + "RUF", # ruff-specific rules + "SIM", # flake8-simplify + "T10", # flake8-debugger + "T20", # flake8-print + "TID", # flake8-tidy-imports + "TRY", # tryceratops + "UP", # pyupgrade + "W", # pycodestyle + "YTT", # flake8-2020 +] -# Designate "pelican" as separate import section -known_pelican = "pelican" -known_third_party = [ - "html5lib", +ignore = [ + "D100", # missing docstring in public module + "D101", # missing docstring in public class + "D102", # missing docstring in public method + "D103", # missing docstring in public function + "D203", # blank line before class docstring + "D213", # multi-line docstring summary should start at the second line + "ISC001", # disabled so that format works without warning + "PGH004", # use specific rule codes when using `NOQA` + "RUF012", # mutable class attributes should be annotated with `typing.ClassVar` ] -sections = "FUTURE,STDLIB,THIRDPARTY,PELICAN,FIRSTPARTY,LOCALFOLDER" + +[tool.ruff.lint.per-file-ignores] +"pelican/plugins/simple_footnotes/test_*" = ["E501"] + +[tool.ruff.lint.isort] +combine-as-imports = true +force-sort-within-sections = true +known-first-party = ["pelican"] [build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" +requires = ["pdm-backend"] +build-backend = "pdm.backend" diff --git a/tasks.py b/tasks.py index 803e13f..5113339 100644 --- a/tasks.py +++ b/tasks.py @@ -1,13 +1,16 @@ from inspect import cleandoc +import logging import os from pathlib import Path from shutil import which -import sys from invoke import task +logger = logging.getLogger(__name__) + PKG_NAME = "simple_footnotes" PKG_PATH = Path(f"pelican/plugins/{PKG_NAME}") + ACTIVE_VENV = os.environ.get("VIRTUAL_ENV", None) VENV_HOME = Path(os.environ.get("WORKON_HOME", "~/.local/share/virtualenvs")) VENV_PATH = Path(ACTIVE_VENV) if ACTIVE_VENV else (VENV_HOME.expanduser() / PKG_NAME) @@ -15,54 +18,49 @@ BIN_DIR = "bin" if os.name != "nt" else "Scripts" VENV_BIN = Path(VENV) / Path(BIN_DIR) -TOOLS = ("poetry", "pre-commit") -POETRY = which("poetry") if which("poetry") else (VENV_BIN / "poetry") -CMD_PREFIX = f"{VENV_BIN}/" if ACTIVE_VENV else f"{POETRY} run " +TOOLS = ("pdm", "pre-commit") +PDM = which("pdm") if which("pdm") else (VENV_BIN / "pdm") +CMD_PREFIX = f"{VENV_BIN}/" if ACTIVE_VENV else f"{PDM} run " PRECOMMIT = which("pre-commit") if which("pre-commit") else f"{CMD_PREFIX}pre-commit" -PTY = True if os.name != "nt" else False +PTY = os.name != "nt" @task -def tests(c): - """Run the test suite.""" - PTY = True if os.name != "nt" else False - c.run(f"{CMD_PREFIX}pytest", pty=PTY) +def tests(c, deprecations=False): + """Run the test suite, optionally with `--deprecations`.""" + deprecations_flag = "" if deprecations else "-W ignore::DeprecationWarning" + c.run(f"{CMD_PREFIX}pytest {deprecations_flag}", pty=PTY) @task -def black(c, check=False, diff=False): - """Run Black auto-formatter, optionally with `--check` or `--diff`.""" +def format(c, check=False, diff=False): + """Run Ruff's auto-formatter, optionally with `--check` or `--diff`.""" check_flag, diff_flag = "", "" if check: check_flag = "--check" if diff: diff_flag = "--diff" - c.run(f"{CMD_PREFIX}black {check_flag} {diff_flag} {PKG_PATH} tasks.py") + c.run( + f"{CMD_PREFIX}ruff format {check_flag} {diff_flag} {PKG_PATH} tasks.py", pty=PTY + ) @task -def isort(c, check=False, diff=False): - """Ensure imports are sorted according to project standards.""" - check_flag, diff_flag = "", "" - if check: - check_flag = "-c" +def ruff(c, fix=False, diff=False): + """Run Ruff to ensure code meets project standards.""" + diff_flag, fix_flag = "", "" + if fix: + fix_flag = "--fix" if diff: diff_flag = "--diff" - c.run(f"{CMD_PREFIX}isort {check_flag} {diff_flag} .") - - -@task -def flake8(c): - """Check code for PEP8 compliance via Flake8.""" - c.run(f"{CMD_PREFIX}flake8 {PKG_PATH} tasks.py") + c.run(f"{CMD_PREFIX}ruff check {diff_flag} {fix_flag} .", pty=PTY) @task -def lint(c, diff=False): +def lint(c, fix=False, diff=False): """Check code style via linting tools.""" - isort(c, check=True, diff=diff) - black(c, check=True, diff=diff) - flake8(c) + ruff(c, fix=fix, diff=diff) + format(c, check=(not fix), diff=diff) @task @@ -70,34 +68,34 @@ def tools(c): """Install development tools in the virtual environment if not already on PATH.""" for tool in TOOLS: if not which(tool): - print(f"** Installing {tool}.") + logger.info(f"** Installing {tool} **") c.run(f"{CMD_PREFIX}pip install {tool}") @task def precommit(c): - """Install pre-commit hooks to `.git/hooks/pre-commit`.""" - print("** Installing pre-commit hooks.") + """Install pre-commit hooks to .git/hooks/pre-commit.""" + logger.info("** Installing pre-commit hooks **") c.run(f"{PRECOMMIT} install") @task def setup(c): """Set up the development environment.""" - if which("poetry") or ACTIVE_VENV: + if which("pdm") or ACTIVE_VENV: tools(c) - c.run(f"{CMD_PREFIX}python -m pip install --upgrade pip") - c.run(f"{POETRY} install") + c.run(f"{CMD_PREFIX}python -m pip install --upgrade pip", pty=PTY) + c.run(f"{PDM} update --dev", pty=PTY) precommit(c) - print("\nDevelopment environment should now be set up and ready!\n") + logger.info("\nDevelopment environment should now be set up and ready!\n") else: error_message = """ - Poetry is not installed, and there is no active virtual environment available. + PDM is not installed, and there is no active virtual environment available. You can either manually create and activate a virtual environment, or you can - install Poetry via: + install PDM via: - curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - + curl -sSL https://raw.githubusercontent.com/pdm-project/pdm/main/install-pdm.py | python3 - Once you have taken one of the above two steps, run `invoke setup` again. """ # noqa: E501 - sys.exit(cleandoc(error_message)) + raise SystemExit(cleandoc(error_message)) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 91e7c69..0000000 --- a/tox.ini +++ /dev/null @@ -1,3 +0,0 @@ -[flake8] -max-line-length = 88 -ignore = E203, E266, E501, W503 From e8effe80bfe6829cdd9c92595b2870c8a3b92168 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 14 Jun 2024 08:46:45 +0200 Subject: [PATCH 4/5] Comply with new Ruff code style standards --- .../plugins/simple_footnotes/simple_footnotes.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pelican/plugins/simple_footnotes/simple_footnotes.py b/pelican/plugins/simple_footnotes/simple_footnotes.py index a3f0984..05dc4c5 100644 --- a/pelican/plugins/simple_footnotes/simple_footnotes.py +++ b/pelican/plugins/simple_footnotes/simple_footnotes.py @@ -9,14 +9,15 @@ def getText(node, recursive=False): """Get all the text associated with this node. - With recursive == True, all text from child nodes is retrieved.""" + + With recursive == True, all text from child nodes is retrieved. + """ L = [""] for n in node.childNodes: if n.nodeType in (node.TEXT_NODE, node.CDATA_SECTION_NODE): L.append(n.data) - else: - if not recursive: - return None + elif not recursive: + return None L.append(getText(n)) return "".join(L) @@ -68,7 +69,7 @@ def parse_for_footnotes(article_or_page_generator): number = dom.createElement("sup") number.setAttribute("id", fnbackid) numbera = dom.createElement("a") - numbera.setAttribute("href", "#%s" % fnid) + numbera.setAttribute("href", f"#{fnid}") numbera.setAttribute("class", "simple-footnote") numbera.appendChild(dom.createTextNode(str(count))) txt = getText(footnote, recursive=True).replace("\n", " ") @@ -84,7 +85,7 @@ def parse_for_footnotes(article_or_page_generator): while e.firstChild: li.appendChild(e.firstChild) backlink = dom.createElement("a") - backlink.setAttribute("href", "#%s" % fnbackid) + backlink.setAttribute("href", f"#{fnbackid}") backlink.setAttribute("class", "simple-footnote-back") backlink.appendChild(dom.createTextNode("\u21a9\ufe0e")) li.appendChild(dom.createTextNode(" ")) From d37fbcd0ff0aa173c9f741dfbb86d6ea006a3c40 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 14 Jun 2024 09:17:45 +0200 Subject: [PATCH 5/5] Prepare release --- RELEASE.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..f886cf2 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,6 @@ +Release type: patch + +- Enable footnote generation on hidden pages and other page types +- Render arrow Unicode character as text instead of emoji on iOS +- Replace Poetry with PDM, Flake8/isort with Ruff +- Drop official support for Python 3.6 & 3.7