diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3974b83..ba3ea1d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -48,6 +48,7 @@ jobs: - name: Upload to Codecov uses: codecov/codecov-action@v3 with: + token: ${{ secrets.CODECOV_TOKEN }} name: mdit-py-plugins-pytests flags: pytests file: ./coverage.xml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 50bfd2d..41c0fc9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,30 +12,22 @@ exclude: > repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-json - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - - repo: https://github.com/PyCQA/isort - rev: 5.13.2 - hooks: - - id: isort - - - repo: https://github.com/psf/black - rev: 23.12.1 - hooks: - - id: black - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.13 + rev: v0.4.4 hooks: - - id: ruff + - id: ruff + args: [--fix] + - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.8.0 + rev: v1.10.0 hooks: - id: mypy additional_dependencies: [markdown-it-py~=3.0] diff --git a/mdit_py_plugins/admon/index.py b/mdit_py_plugins/admon/index.py index c39a4f7..cb24181 100644 --- a/mdit_py_plugins/admon/index.py +++ b/mdit_py_plugins/admon/index.py @@ -4,7 +4,7 @@ from contextlib import suppress import re -from typing import TYPE_CHECKING, Callable, List, Sequence, Tuple +from typing import TYPE_CHECKING, Callable, Sequence from markdown_it import MarkdownIt from markdown_it.rules_block import StateBlock @@ -17,7 +17,7 @@ from markdown_it.utils import EnvType, OptionsDict -def _get_multiple_tags(params: str) -> Tuple[List[str], str]: +def _get_multiple_tags(params: str) -> tuple[list[str], str]: """Check for multiple tags when the title is double quoted.""" re_tags = re.compile(r'^\s*(?P[^"]+)\s+"(?P.*)"\S*$') match = re_tags.match(params) @@ -27,7 +27,7 @@ def _get_multiple_tags(params: str) -> Tuple[List[str], str]: raise ValueError("No match found for parameters") -def _get_tag(_params: str) -> Tuple[List[str], str]: +def _get_tag(_params: str) -> tuple[list[str], str]: """Separate the tag name from the admonition title.""" params = _params.strip() if not params: @@ -212,7 +212,7 @@ def renderDefault( _options: OptionsDict, env: EnvType, ) -> str: - return self.renderToken(tokens, idx, _options, env) # type: ignore + return self.renderToken(tokens, idx, _options, env) # type: ignore[attr-defined,no-any-return] render = render or renderDefault diff --git a/mdit_py_plugins/amsmath/__init__.py b/mdit_py_plugins/amsmath/__init__.py index d2f71ca..0aed5c1 100644 --- a/mdit_py_plugins/amsmath/__init__.py +++ b/mdit_py_plugins/amsmath/__init__.py @@ -1,8 +1,9 @@ """An extension to capture amsmath latex environments.""" + from __future__ import annotations import re -from typing import TYPE_CHECKING, Callable, Optional, Sequence +from typing import TYPE_CHECKING, Callable, Sequence from markdown_it import MarkdownIt from markdown_it.common.utils import escapeHtml @@ -57,7 +58,7 @@ def amsmath_plugin( - md: MarkdownIt, *, renderer: Optional[Callable[[str], str]] = None + md: MarkdownIt, *, renderer: Callable[[str], str] | None = None ) -> None: """Parses TeX math equations, without any surrounding delimiters, only for top-level `amsmath <https://ctan.org/pkg/amsmath>`__ environments: diff --git a/mdit_py_plugins/attrs/index.py b/mdit_py_plugins/attrs/index.py index 9a6875d..11455f8 100644 --- a/mdit_py_plugins/attrs/index.py +++ b/mdit_py_plugins/attrs/index.py @@ -140,7 +140,7 @@ def _span_rule(state: StateInline, silent: bool) -> bool: state.pos = labelStart state.posMax = labelEnd token = state.push("span_open", "span", 1) - token.attrs = attrs # type: ignore + token.attrs = attrs # type: ignore[assignment] state.md.inline.tokenize(state) token = state.push("span_close", "span", -1) @@ -190,7 +190,7 @@ def _attr_block_rule( return True token = state.push("attrs_block", "", 0) - token.attrs = attrs # type: ignore + token.attrs = attrs # type: ignore[assignment] token.map = [startLine, startLine + 1] state.line = startLine + 1 @@ -211,9 +211,9 @@ def _attr_resolve_block_rule(state: StateCore) -> None: # classes are appended if "class" in state.tokens[i].attrs and "class" in next_token.attrs: - state.tokens[i].attrs[ - "class" - ] = f"{state.tokens[i].attrs['class']} {next_token.attrs['class']}" + state.tokens[i].attrs["class"] = ( + f"{state.tokens[i].attrs['class']} {next_token.attrs['class']}" + ) if next_token.type == "attrs_block": # subsequent attribute blocks take precedence, when merging diff --git a/mdit_py_plugins/attrs/parse.py b/mdit_py_plugins/attrs/parse.py index 6b03117..06539c2 100644 --- a/mdit_py_plugins/attrs/parse.py +++ b/mdit_py_plugins/attrs/parse.py @@ -19,6 +19,7 @@ class <- '.' name bareval <- (ASCII_ALPHANUM | ':' | '_' | '-')+ quotedval <- '"' ([^"] | '\"') '"' """ + from __future__ import annotations from enum import Enum diff --git a/mdit_py_plugins/container/index.py b/mdit_py_plugins/container/index.py index 454aa0e..7cc016f 100644 --- a/mdit_py_plugins/container/index.py +++ b/mdit_py_plugins/container/index.py @@ -1,4 +1,5 @@ """Process block-level custom containers.""" + from __future__ import annotations from math import floor @@ -56,7 +57,7 @@ def renderDefault( if tokens[idx].nesting == 1: tokens[idx].attrJoin("class", name) - return self.renderToken(tokens, idx, _options, env) # type: ignore + return self.renderToken(tokens, idx, _options, env) # type: ignore[attr-defined,no-any-return] min_markers = 3 marker_str = marker diff --git a/mdit_py_plugins/deflist/index.py b/mdit_py_plugins/deflist/index.py index dd8a47b..f5d20d1 100644 --- a/mdit_py_plugins/deflist/index.py +++ b/mdit_py_plugins/deflist/index.py @@ -1,4 +1,5 @@ """Process definition lists.""" + from markdown_it import MarkdownIt from markdown_it.rules_block import StateBlock diff --git a/mdit_py_plugins/dollarmath/index.py b/mdit_py_plugins/dollarmath/index.py index aebd3ea..acfd83f 100644 --- a/mdit_py_plugins/dollarmath/index.py +++ b/mdit_py_plugins/dollarmath/index.py @@ -1,7 +1,7 @@ from __future__ import annotations import re -from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Sequence +from typing import TYPE_CHECKING, Any, Callable, Sequence from markdown_it import MarkdownIt from markdown_it.common.utils import escapeHtml, isWhiteSpace @@ -24,9 +24,9 @@ def dollarmath_plugin( allow_digits: bool = True, allow_blank_lines: bool = True, double_inline: bool = False, - label_normalizer: Optional[Callable[[str], str]] = None, - renderer: Optional[Callable[[str, Dict[str, Any]], str]] = None, - label_renderer: Optional[Callable[[str], str]] = None, + label_normalizer: Callable[[str], str] | None = None, + renderer: Callable[[str, dict[str, Any]], str] | None = None, + label_renderer: Callable[[str], str] | None = None, ) -> None: """Plugin for parsing dollar enclosed math, e.g. inline: ``$a=1$``, block: ``$$b=2$$`` @@ -53,7 +53,7 @@ def dollarmath_plugin( """ if label_normalizer is None: - label_normalizer = lambda label: re.sub(r"\s+", "-", label) + label_normalizer = lambda label: re.sub(r"\s+", "-", label) # noqa: E731 md.inline.ruler.before( "escape", @@ -76,7 +76,7 @@ def dollarmath_plugin( _label_renderer: Callable[[str], str] if label_renderer is None: - _label_renderer = ( + _label_renderer = ( # noqa: E731 lambda label: f'<a href="#{label}" class="mathlabel" title="Permalink to this equation">¶</a>' ) else: @@ -286,7 +286,7 @@ def _math_inline_dollar(state: StateInline, silent: bool) -> bool: def math_block_dollar( allow_labels: bool = True, - label_normalizer: Optional[Callable[[str], str]] = None, + label_normalizer: Callable[[str], str] | None = None, allow_blank_lines: bool = False, ) -> Callable[[StateBlock, int, int, bool], bool]: """Generate block dollar rule.""" diff --git a/mdit_py_plugins/field_list/__init__.py b/mdit_py_plugins/field_list/__init__.py index 50bb907..b3d6407 100644 --- a/mdit_py_plugins/field_list/__init__.py +++ b/mdit_py_plugins/field_list/__init__.py @@ -1,4 +1,5 @@ """Field list plugin""" + from contextlib import contextmanager from typing import Iterator, Optional, Tuple @@ -176,7 +177,7 @@ def _fieldlist_rule( has_first_line = contentStart < maximum if block_indent is None: # no body content - if not has_first_line: # noqa SIM108 + if not has_first_line: # noqa: SIM108 # no body or first line, so just use default block_indent = 2 else: diff --git a/mdit_py_plugins/footnote/index.py b/mdit_py_plugins/footnote/index.py index 6dc3602..14f6138 100644 --- a/mdit_py_plugins/footnote/index.py +++ b/mdit_py_plugins/footnote/index.py @@ -1,7 +1,8 @@ """Process footnotes""" + from __future__ import annotations -from typing import TYPE_CHECKING, List, Optional, Sequence +from typing import TYPE_CHECKING, Sequence from markdown_it import MarkdownIt from markdown_it.helpers import parseLinkLabel @@ -184,7 +185,7 @@ def footnote_inline(state: StateInline, silent: bool) -> bool: refs = state.env.setdefault("footnotes", {}).setdefault("list", {}) footnoteId = len(refs) - tokens: List[Token] = [] + tokens: list[Token] = [] state.md.inline.parse( state.src[labelStart:labelEnd], state.md, state.env, tokens ) @@ -270,7 +271,7 @@ def footnote_tail(state: StateCore) -> None: if "footnotes" not in state.env: return - current: List[Token] = [] + current: list[Token] = [] tok_filter = [] for tok in state.tokens: if tok.type == "footnote_reference_open": @@ -290,7 +291,7 @@ def footnote_tail(state: StateCore) -> None: if insideRef: current.append(tok) - tok_filter.append((not insideRef)) + tok_filter.append(not insideRef) state.tokens = [t for t, f in zip(state.tokens, tok_filter) if f] @@ -329,7 +330,7 @@ def footnote_tail(state: StateCore) -> None: state.tokens.extend(tokens) if state.tokens[len(state.tokens) - 1].type == "paragraph_close": - lastParagraph: Optional[Token] = state.tokens.pop() + lastParagraph: Token | None = state.tokens.pop() else: lastParagraph = None @@ -482,4 +483,4 @@ def render_footnote_anchor( ident += ":" + str(tokens[idx].meta["subId"]) # ↩ with escape code to prevent display as Apple Emoji on iOS - return ' <a href="#fnref' + ident + '" class="footnote-backref">\u21a9\uFE0E</a>' + return ' <a href="#fnref' + ident + '" class="footnote-backref">\u21a9\ufe0e</a>' diff --git a/mdit_py_plugins/front_matter/index.py b/mdit_py_plugins/front_matter/index.py index 7ae67a8..1551d76 100644 --- a/mdit_py_plugins/front_matter/index.py +++ b/mdit_py_plugins/front_matter/index.py @@ -1,4 +1,5 @@ """Process front matter.""" + from markdown_it import MarkdownIt from markdown_it.rules_block import StateBlock diff --git a/pyproject.toml b/pyproject.toml index d5d6458..44a2148 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,15 +53,30 @@ exclude = [ "tests/", ] -[tool.isort] -profile = "black" -force_sort_within_sections = true -known_first_party = ["mdit_py_plugins", "tests"] +[tool.ruff.lint] +extend-select = [ + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "I", # isort + "ICN", # flake8-import-conventions + "ISC", # flake8-implicit-str-concat + "N", # pep8-naming + "PERF", # perflint (performance anti-patterns) + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PTH", # flake8-use-pathlib + "RUF", # Ruff-specific rules + "SIM", # flake8-simplify + "UP", # pyupgrade + "T20", # flake8-print +] +extend-ignore = ["ISC001", "N802", "N803", "N806"] + +[tool.ruff.lint.per-file-ignores] +"tests/**.py" = ["T201"] -[tool.ruff] -line-length = 110 -extend-select = ["B0", "C4", "ICN", "ISC", "N", "RUF", "SIM"] -extend-ignore = ["E731", "N802", "N803", "N806"] +[tool.ruff.lint.isort] +force-sort-within-sections = true [tool.mypy] show_error_codes = true diff --git a/tests/test_footnote.py b/tests/test_footnote.py index fb9bf9d..31c6207 100644 --- a/tests/test_footnote.py +++ b/tests/test_footnote.py @@ -188,89 +188,79 @@ def test_footnote_tail(): tokens = [ Token( - **{ - "type": "footnote_reference_open", - "tag": "", - "nesting": 1, - "attrs": None, - "map": None, - "level": 0, - "children": None, - "content": "", - "markup": "", - "info": "", - "meta": {"label": "a"}, - "block": False, - "hidden": False, - } + type="footnote_reference_open", + tag="", + nesting=1, + attrs=None, + map=None, + level=0, + children=None, + content="", + markup="", + info="", + meta={"label": "a"}, + block=False, + hidden=False, ), Token( - **{ - "type": "paragraph_open", - "tag": "p", - "nesting": 1, - "attrs": None, - "map": [0, 1], - "level": 1, - "children": None, - "content": "", - "markup": "", - "info": "", - "meta": {}, - "block": True, - "hidden": False, - } + type="paragraph_open", + tag="p", + nesting=1, + attrs=None, + map=[0, 1], + level=1, + children=None, + content="", + markup="", + info="", + meta={}, + block=True, + hidden=False, ), Token( - **{ - "type": "inline", - "tag": "", - "nesting": 0, - "attrs": None, - "map": [0, 1], - "level": 2, - "children": [], - "content": "xyz", - "markup": "", - "info": "", - "meta": {}, - "block": True, - "hidden": False, - } + type="inline", + tag="", + nesting=0, + attrs=None, + map=[0, 1], + level=2, + children=[], + content="xyz", + markup="", + info="", + meta={}, + block=True, + hidden=False, ), Token( - **{ - "type": "paragraph_close", - "tag": "p", - "nesting": -1, - "attrs": None, - "map": None, - "level": 1, - "children": None, - "content": "", - "markup": "", - "info": "", - "meta": {}, - "block": True, - "hidden": False, - } + type="paragraph_close", + tag="p", + nesting=-1, + attrs=None, + map=None, + level=1, + children=None, + content="", + markup="", + info="", + meta={}, + block=True, + hidden=False, ), Token( - **{ - "type": "footnote_reference_close", - "tag": "", - "nesting": -1, - "attrs": None, - "map": None, - "level": 0, - "children": None, - "content": "", - "markup": "", - "info": "", - "meta": {}, - "block": False, - "hidden": False, - } + type="footnote_reference_close", + tag="", + nesting=-1, + attrs=None, + map=None, + level=0, + children=None, + content="", + markup="", + info="", + meta={}, + block=False, + hidden=False, ), Token("other", "", 0), ]