Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 8 additions & 12 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.9, "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- uses: actions/checkout@v1
Expand All @@ -43,14 +43,14 @@ jobs:
cd dart-sass && chmod +x ./sass
./sass --no-source-map ../all.scss ../dist/neoteroi-mkdocs.css
./sass --no-source-map --style compressed ../all.scss ../dist/neoteroi-mkdocs.min.css
if: matrix.python-version == '3.10'
if: matrix.python-version == '3.13'

- name: Publish CSS files
uses: actions/upload-artifact@v4
with:
name: arts-package-css
path: styles/dist
if: matrix.python-version == '3.10'
if: matrix.python-version == '3.13'

- name: Use Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
Expand Down Expand Up @@ -80,24 +80,20 @@ jobs:
path: junit/pytest-results-${{ matrix.python-version }}.xml
if: always()

- name: Codecov
run: |
bash <(curl -s https://codecov.io/bash)

- name: Install distribution dependencies
run: pip install build
if: matrix.python-version == '3.12'
if: matrix.python-version == '3.13'

- name: Create distribution package
run: python -m build
if: matrix.python-version == '3.12'
if: matrix.python-version == '3.13'

- name: Upload distribution package
uses: actions/upload-artifact@v4
with:
name: dist-${{ matrix.os }}-${{ matrix.python-version }}
path: dist
if: matrix.python-version == '3.12'
if: matrix.python-version == '3.13'

publish:
runs-on: ubuntu-latest
Expand All @@ -111,10 +107,10 @@ jobs:
merge-multiple: true
path: dist

- name: Use Python 3.12
- name: Use Python 3.13
uses: actions/setup-python@v1
with:
python-version: '3.12'
python-version: '3.13'

- name: Install dependencies
run: |
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.2.0] 2025-11-23

- Add support for custom templates, by @sindrehan.
- Remove support for Python 3.9.
- Add Python 3.14 to the build matrix.
- Remove Codecov from build and README.
- Update type annotations to Python >= 3.10.

## [1.1.3] 2025-08-02

- Improve `read_from_source()` to support an optional CWD parameter used to
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
[![pypi](https://img.shields.io/pypi/v/neoteroi-mkdocs.svg)](https://pypi.python.org/pypi/neoteroi-mkdocs)
[![versions](https://img.shields.io/pypi/pyversions/neoteroi-mkdocs.svg)](https://github.com/neoteroi/mkdocs-plugins)
[![license](https://img.shields.io/github/license/neoteroi/mkdocs-plugins.svg)](https://github.com/neoteroi/mkdocs-plugins/blob/main/LICENSE)
[![codecov](https://codecov.io/gh/Neoteroi/mkdocs-plugins/branch/main/graph/badge.svg)](https://codecov.io/gh/Neoteroi/mkdocs-plugins)
[![documentation](https://img.shields.io/badge/📖-docs-purple)](https://www.neoteroi.dev/mkdocs-plugins/)


Expand Down
13 changes: 6 additions & 7 deletions neoteroi/mkdocs/cards/domain.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
from dataclasses import dataclass
from typing import List, Optional

from neoteroi.mkdocs.markdown.images import Image


@dataclass
class CardItem:
title: str
url: Optional[str] = None
content: Optional[str] = None
icon: Optional[str] = None
key: Optional[str] = None
image: Optional[Image] = None
url: str | None = None
content: str | None = None
icon: str | None = None
key: str | None = None
image: Image | None = None

def __post_init__(self):
if self.image and not self.image.alt:
Expand All @@ -20,4 +19,4 @@ def __post_init__(self):

@dataclass
class Cards:
items: List[CardItem]
items: list[CardItem]
7 changes: 3 additions & 4 deletions neoteroi/mkdocs/contribs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
from fnmatch import fnmatch
from pathlib import Path
from subprocess import CalledProcessError
from typing import List

from mkdocs.config import config_options as c
from mkdocs.plugins import BasePlugin
Expand All @@ -38,7 +37,7 @@ def __init__(self) -> None:
self._git_reader = GitContributionsReader()
self._txt_reader = TXTContributionsReader()

def get_contributors(self, file_path: Path) -> List[Contributor]:
def get_contributors(self, file_path: Path) -> list[Contributor]:
git_history_contributors = self._git_reader.get_contributors(file_path)
configured_contributors = self._txt_reader.get_contributors(file_path)
return list(
Expand Down Expand Up @@ -71,7 +70,7 @@ def __init__(self) -> None:

def _merge_contributor_by_email(
self,
contributors: List[Contributor],
contributors: list[Contributor],
contributor: Contributor,
contributor_info: dict,
) -> bool:
Expand All @@ -97,7 +96,7 @@ def _merge_contributor_by_email(

return False

def _get_contributors(self, page_file: File) -> List[Contributor]:
def _get_contributors(self, page_file: File) -> list[Contributor]:
results = []
contributors = self._contribs_reader.get_contributors(
Path("docs") / page_file.src_path
Expand Down
7 changes: 3 additions & 4 deletions neoteroi/mkdocs/contribs/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import List, Optional


@dataclass
class Contributor:
name: str
email: str
count: int = -1
image: Optional[str] = None
key: Optional[str] = None
image: str | None = None
key: str | None = None


class ContributionsReader(ABC):
@abstractmethod
def get_contributors(self, file_path: Path) -> List[Contributor]:
def get_contributors(self, file_path: Path) -> list[Contributor]:
"""Obtains the list of contributors for a file with the given path."""

@abstractmethod
Expand Down
6 changes: 3 additions & 3 deletions neoteroi/mkdocs/contribs/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import subprocess
from datetime import datetime
from pathlib import Path
from typing import Iterable, List, Tuple
from typing import Iterable

from dateutil.parser import ParserError
from dateutil.parser import parse as parse_date
Expand All @@ -26,7 +26,7 @@ def _decode(self, value: bytes) -> str:
except UnicodeDecodeError:
return value.decode("ISO-8859-1")

def _parse_name_and_email(self, name_and_email) -> Tuple[str, str]:
def _parse_name_and_email(self, name_and_email) -> tuple[str, str]:
match = self._name_email_rx.search(name_and_email)
if match:
name = match.groupdict()["name"].strip()
Expand All @@ -41,7 +41,7 @@ def parse_committers(self, output: str) -> Iterable[Contributor]:
name, email = self._parse_name_and_email(name_and_email)
yield Contributor(name, email, int(count))

def get_contributors(self, file_path: Path) -> List[Contributor]:
def get_contributors(self, file_path: Path) -> list[Contributor]:
"""
Obtains the list of contributors for a file with the given path,
using the Git CLI.
Expand Down
5 changes: 2 additions & 3 deletions neoteroi/mkdocs/contribs/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import xml.etree.ElementTree as etree
from dataclasses import dataclass
from datetime import datetime
from typing import List
from xml.etree.ElementTree import tostring as xml_to_str

from neoteroi.mkdocs.contribs.domain import Contributor
Expand All @@ -25,7 +24,7 @@ class ContribsViewOptions:


def contribution_stats_to_element(
contributors: List[Contributor],
contributors: list[Contributor],
last_commit_date: datetime,
options: ContribsViewOptions,
) -> etree.Element:
Expand Down Expand Up @@ -85,7 +84,7 @@ def contribution_stats_to_element(


def render_contribution_stats(
contributors: List[Contributor],
contributors: list[Contributor],
last_commit_date: datetime,
options: ContribsViewOptions,
) -> str:
Expand Down
6 changes: 3 additions & 3 deletions neoteroi/mkdocs/contribs/txt.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import re
from datetime import datetime
from pathlib import Path
from typing import Iterable, List, Tuple
from typing import Iterable

from dateutil.parser import parse

Expand Down Expand Up @@ -30,7 +30,7 @@ class TXTContributionsReader(ContributionsReader):
r"^\s*Last\smodified\stime:\s(?P<value>.+)$", re.IGNORECASE | re.MULTILINE
)

def _parse_value(self, value: str) -> Tuple[str, str, int]:
def _parse_value(self, value: str) -> tuple[str, str, int]:
match = self._contrib_rx.search(value)
if match:
values = match.groupdict()
Expand All @@ -51,7 +51,7 @@ def _get_contributors_from_txt_file(self, file_path: Path) -> Iterable[Contribut
if name and email:
yield Contributor(name, email, count)

def get_contributors(self, file_path: Path) -> List[Contributor]:
def get_contributors(self, file_path: Path) -> list[Contributor]:
"""
Obtains the list of contributors from a txt file with the given path.
The file contents should look like:
Expand Down
5 changes: 2 additions & 3 deletions neoteroi/mkdocs/markdown/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"""

import re
from typing import Dict, Tuple

_PROPS_RE = re.compile(
r"""\s?((?P<name>[^\s\=]+)=(?P<quot>"|')(?P<value>[^\"\']+)(?P=quot))""",
Expand All @@ -14,7 +13,7 @@

def parse_props(
line: str, prefix: str = "", bool_attrs: bool = False
) -> Dict[str, str]:
) -> dict[str, str]:
"""
Parses a line describing properties, in this form:

Expand Down Expand Up @@ -49,7 +48,7 @@ def parse_props(
return props


def extract_props(line: str, prefix: str = "") -> Tuple[str, Dict[str, str]]:
def extract_props(line: str, prefix: str = "") -> tuple[str, dict[str, str]]:
"""
Extracts properties like the `parse_props` function, but
also returns the original line with properties removed.
Expand Down
7 changes: 3 additions & 4 deletions neoteroi/mkdocs/markdown/images.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import xml.etree.ElementTree as etree
from dataclasses import dataclass
from typing import Optional

from .utils import create_instance


@dataclass
class Image:
url: str
height: Optional[int] = None
width: Optional[int] = None
alt: Optional[str] = None
height: int | None = None
width: int | None = None
alt: str | None = None

@classmethod
def from_obj(cls, obj):
Expand Down
12 changes: 6 additions & 6 deletions neoteroi/mkdocs/markdown/processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import textwrap
import xml.etree.ElementTree as etree
from abc import ABC, abstractmethod
from typing import Iterable, List, Optional
from typing import Iterable

from markdown.blockprocessors import BlockProcessor

Expand All @@ -34,7 +34,7 @@
logger = logging.getLogger("MARKDOWN")


def find_closing_fragment_index(pattern: re.Pattern, blocks: List[str]) -> int:
def find_closing_fragment_index(pattern: re.Pattern, blocks: list[str]) -> int:
for index, block in enumerate(blocks):
if pattern.search(block):
return index
Expand Down Expand Up @@ -128,7 +128,7 @@ def render(self, parent, data, props):
f"Could not render a {self.name} block. Please correct the input.",
)

def get_match(self, pattern, blocks) -> Optional[re.Match]:
def get_match(self, pattern, blocks) -> re.Match | None:
first_block = blocks.pop(0)
new_lines = []
match = None
Expand Down Expand Up @@ -159,7 +159,7 @@ class SourceBlockProcessor(BlockProcessor, BaseProcessor):
[timeline(https://.../example.json)]
"""

_pattern: Optional[re.Pattern] = None
_pattern: re.Pattern | None = None

@property
def pattern(self) -> re.Pattern:
Expand Down Expand Up @@ -233,8 +233,8 @@ class EmbeddedBlockProcessor(BlockProcessor, BaseProcessor):
The source of data in this case is always coming as a string.
"""

_start_pattern: Optional[re.Pattern] = None
_end_pattern: Optional[re.Pattern] = None
_start_pattern: re.Pattern | None = None
_end_pattern: re.Pattern | None = None

@property
def start_pattern(self) -> re.Pattern:
Expand Down
16 changes: 8 additions & 8 deletions neoteroi/mkdocs/markdown/tables/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import re
from typing import Any, Dict, Iterable, List, Optional, Tuple, Type, TypeVar
from typing import Any, Iterable, Type, TypeVar

_TABLE_LINE_PATTERN = re.compile(r"\s?\|([^\|]+)")
_TABLE_SEPARATOR_LINE_PATTERN = re.compile(r"^[-:\s\|]*$")
Expand All @@ -20,7 +20,7 @@ def __init__(
)

@property
def rows(self) -> Tuple[List[Any], ...]:
def rows(self) -> tuple[list[Any], ...]:
return self._rows

def __str__(self) -> str:
Expand Down Expand Up @@ -71,14 +71,14 @@ def __repr__(self) -> str:
)

@property
def headers(self) -> Tuple[str, ...]:
def headers(self) -> tuple[str, ...]:
return self._headers

@property
def records(self) -> Tuple[Tuple[str, ...], ...]:
def records(self) -> tuple[tuple[str, ...], ...]:
return self._records

def items(self) -> Iterable[Dict[str, str]]:
def items(self) -> Iterable[dict[str, str]]:
properties = {index: header for index, header in enumerate(self.headers)}

for record in self.records:
Expand Down Expand Up @@ -110,9 +110,9 @@ def __getitem__(self, key):
T = TypeVar("T", bound=Table)


def read_table(markdown: str, cls: Type[T] = Table) -> Optional[T]:
headers: List[str] = []
records: List[List[str]] = []
def read_table(markdown: str, cls: Type[T] = Table) -> T | None:
headers: list[str] = []
records: list[list[str]] = []

for line in markdown.splitlines():
if _TABLE_SEPARATOR_LINE_PATTERN.match(line):
Expand Down
Loading