Skip to content

Commit

Permalink
Python 3.9 Modernization (#437)
Browse files Browse the repository at this point in the history
Python 3.8 was dropped in #439.
  • Loading branch information
tony authored Dec 21, 2024
2 parents ffeaac0 + 5a58174 commit 9eb6ea0
Show file tree
Hide file tree
Showing 33 changed files with 87 additions and 80 deletions.
10 changes: 10 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@

Python 3.8 reached end-of-life status on October 7th, 2024 (see PEP 569).

### Development

- Aggressive automated lint fixes via `ruff` (#437)

via ruff v0.8.4, all automated lint fixes, including unsafe and previews were applied for Python 3.9:

```sh
ruff check --select ALL . --fix --unsafe-fixes --preview --show-fixes; ruff format .
```

## django-docutils 0.27.0 (2024-11-26)

_Maintenance release only, no bug fixes or new features_
Expand Down
8 changes: 4 additions & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
django.setup()

# package data
about: t.Dict[str, str] = {}
about: dict[str, str] = {}
with (src_root / "django_docutils" / "__about__.py").open() as fp:
exec(fp.read(), about)

Expand Down Expand Up @@ -76,8 +76,8 @@
html_css_files = ["css/custom.css"]
html_extra_path = ["manifest.json"]
html_theme = "furo"
html_theme_path: t.List[str] = []
html_theme_options: t.Dict[str, t.Union[str, t.List[t.Dict[str, str]]]] = {
html_theme_path: list[str] = []
html_theme_options: dict[str, t.Union[str, list[dict[str, str]]]] = {
"light_logo": "img/icons/logo.svg",
"dark_logo": "img/icons/logo-dark.svg",
"footer_icons": [
Expand Down Expand Up @@ -147,7 +147,7 @@
}


def linkcode_resolve(domain: str, info: t.Dict[str, str]) -> t.Union[None, str]:
def linkcode_resolve(domain: str, info: dict[str, str]) -> t.Union[None, str]:
"""
Determine the URL corresponding to Python object.
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ build-backend = "hatchling.build"
[tool.mypy]
strict = true
plugins = ["mypy_django_plugin.main"]
python_version = "3.9"
files = [
"src/",
"tests/",
Expand Down Expand Up @@ -174,7 +175,7 @@ disable_error_code = [
]

[tool.ruff]
target-version = "py38"
target-version = "py39"

[tool.ruff.lint]
select = [
Expand Down
6 changes: 3 additions & 3 deletions src/django_docutils/lib/directives/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ def patch_bash_session_lexer() -> None:
DEFAULT = HtmlFormatter(cssclass="highlight code-block", noclasses=INLINESTYLES)

#: Add name -> formatter pairs for every variant you want to use
VARIANTS: t.Dict[str, "Formatter[str]"] = {
VARIANTS: dict[str, "Formatter[str]"] = {
# 'linenos': HtmlFormatter(noclasses=INLINESTYLES, linenos=True),
}

DEFAULT_OPTION_SPEC: t.Dict[str, t.Callable[[str], t.Any]] = dict.fromkeys(
DEFAULT_OPTION_SPEC: dict[str, t.Callable[[str], t.Any]] = dict.fromkeys(
VARIANTS,
directives.flag,
)
Expand All @@ -90,7 +90,7 @@ class CodeBlock(Directive):
option_spec = DEFAULT_OPTION_SPEC
has_content = True

def run(self) -> t.List[nodes.Node]:
def run(self) -> list[nodes.Node]:
"""Directive run method for CodeBlock."""
self.assert_has_content()
try:
Expand Down
2 changes: 1 addition & 1 deletion src/django_docutils/lib/metadata/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def extract_title(document: nodes.document) -> t.Optional[str]:
return None


def extract_metadata(document: nodes.document) -> t.Dict[str, str]:
def extract_metadata(document: nodes.document) -> dict[str, str]:
"""Return the dict containing document metadata.
Parameters
Expand Down
4 changes: 1 addition & 3 deletions src/django_docutils/lib/metadata/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,12 @@ def process_datetime(metadata):
See *processors.py* for more examples.
"""

import typing as t

from django.utils.module_loading import import_string

from django_docutils.lib.settings import DJANGO_DOCUTILS_LIB_RST


def process_metadata(metadata: t.Dict[str, str]) -> t.Dict[str, str]:
def process_metadata(metadata: dict[str, str]) -> dict[str, str]:
"""Return objects from RST metadata pulled from document source.
This will turn things like string dates into time-zone'd dateutil objects.
Expand Down
4 changes: 2 additions & 2 deletions src/django_docutils/lib/metadata/processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
pass


def process_datetime(metadata: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]:
def process_datetime(metadata: dict[str, t.Any]) -> dict[str, t.Any]:
"""Optionally supports localizing times via pytz."""
timezone_formats = [ # timezone formats to try, most detailed to least
"%Y-%m-%d %I:%M%p",
Expand All @@ -41,7 +41,7 @@ def process_datetime(metadata: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]:
return metadata


def process_anonymous_user(metadata: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]:
def process_anonymous_user(metadata: dict[str, t.Any]) -> dict[str, t.Any]:
"""Corrects name of author "anonymous" to django's anonymous username."""
if metadata.get("author") == "anonymous" and hasattr(
settings,
Expand Down
2 changes: 1 addition & 1 deletion src/django_docutils/lib/publisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def publish_parts_from_doctree(
settings_overrides: t.Optional[t.Any] = None,
config_section: t.Optional[str] = None,
enable_exit_status: bool = False,
) -> t.Dict[str, str]:
) -> dict[str, str]:
"""Render docutils doctree into docutils parts."""
reader = Reader(parser_name="null")
pub = Publisher(
Expand Down
6 changes: 3 additions & 3 deletions src/django_docutils/lib/roles/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def generic_url_role(
name: str,
text: str,
url_handler_fn: "UrlHandlerFn",
innernodeclass: t.Type[t.Union[nodes.Text, nodes.TextElement]] = nodes.Text,
innernodeclass: type[t.Union[nodes.Text, nodes.TextElement]] = nodes.Text,
) -> "RoleFnReturnValue":
"""Docutils Role for Django Docutils.
Expand Down Expand Up @@ -79,8 +79,8 @@ def generic_remote_url_role(
name: str,
text: str,
url_handler_fn: "RemoteUrlHandlerFn",
innernodeclass: t.Type[t.Union[nodes.Text, nodes.TextElement]] = nodes.Text,
) -> t.Tuple[t.List[nodes.reference], t.List[t.Any]]:
innernodeclass: type[t.Union[nodes.Text, nodes.TextElement]] = nodes.Text,
) -> tuple[list[nodes.reference], list[t.Any]]:
"""Docutils Role that can call an external data source for title and URL.
Same as generic_url_role, but can return url and title via external data source.
Expand Down
2 changes: 1 addition & 1 deletion src/django_docutils/lib/roles/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def email_role(
text: str,
lineno: int,
inliner: Inliner,
options: t.Optional[t.Dict[str, t.Any]] = None,
options: t.Optional[dict[str, t.Any]] = None,
content: t.Optional[str] = None,
) -> RoleFnReturnValue:
"""Role for linking to email articles.
Expand Down
12 changes: 6 additions & 6 deletions src/django_docutils/lib/roles/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ def file_role(
text: str,
lineno: int,
inliner: Inliner,
options: t.Optional[t.Dict[str, t.Any]] = None,
options: t.Optional[dict[str, t.Any]] = None,
content: t.Optional[str] = None,
) -> t.Tuple[t.List[nodes.emphasis], t.List[t.Any]]:
) -> tuple[list[nodes.emphasis], list[t.Any]]:
"""Role for files.
Examples
Expand Down Expand Up @@ -66,9 +66,9 @@ def manifest_role(
text: str,
lineno: int,
inliner: Inliner,
options: t.Optional[t.Dict[str, t.Any]] = None,
options: t.Optional[dict[str, t.Any]] = None,
content: t.Optional[str] = None,
) -> t.Tuple[t.List[nodes.emphasis], t.List[t.Any]]:
) -> tuple[list[nodes.emphasis], list[t.Any]]:
"""Role for manifests (package.json, file outputs).
Examples
Expand Down Expand Up @@ -103,9 +103,9 @@ def exe_role(
text: str,
lineno: int,
inliner: Inliner,
options: t.Optional[t.Dict[str, t.Any]] = None,
options: t.Optional[dict[str, t.Any]] = None,
content: t.Optional[str] = None,
) -> t.Tuple[t.List[nodes.emphasis], t.List[t.Any]]:
) -> tuple[list[nodes.emphasis], list[t.Any]]:
"""Role for executables.
Examples
Expand Down
2 changes: 1 addition & 1 deletion src/django_docutils/lib/roles/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def github_role(
text: str,
lineno: int,
inliner: Inliner,
options: t.Optional[t.Dict[str, t.Any]] = None,
options: t.Optional[dict[str, t.Any]] = None,
content: t.Optional[str] = None,
) -> RoleFnReturnValue:
"""Role for linking to GitHub repos and issues.
Expand Down
2 changes: 1 addition & 1 deletion src/django_docutils/lib/roles/hackernews.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def hackernews_role(
text: str,
lineno: int,
inliner: Inliner,
options: t.Optional[t.Dict[str, t.Any]] = None,
options: t.Optional[dict[str, t.Any]] = None,
content: t.Optional[str] = None,
) -> RoleFnReturnValue:
"""Role for linking to hackernews articles.
Expand Down
4 changes: 2 additions & 2 deletions src/django_docutils/lib/roles/kbd.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ def kbd_role(
text: str,
lineno: int,
inliner: Inliner,
options: t.Optional[t.Dict[str, t.Any]] = None,
options: t.Optional[dict[str, t.Any]] = None,
content: t.Optional[str] = None,
) -> t.Tuple[t.List[nodes.raw], t.List[t.Any]]:
) -> tuple[list[nodes.raw], list[t.Any]]:
"""Role for ``<kbd>``, the keyboard input element.
Examples
Expand Down
2 changes: 1 addition & 1 deletion src/django_docutils/lib/roles/leanpub.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def leanpub_role(
text: str,
lineno: int,
inliner: Inliner,
options: t.Optional[t.Dict[str, t.Any]] = None,
options: t.Optional[dict[str, t.Any]] = None,
content: t.Optional[str] = None,
) -> RoleFnReturnValue:
"""Role for linking to leanpub page.
Expand Down
2 changes: 1 addition & 1 deletion src/django_docutils/lib/roles/pypi.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def pypi_role(
text: str,
lineno: int,
inliner: Inliner,
options: t.Optional[t.Dict[str, t.Any]] = None,
options: t.Optional[dict[str, t.Any]] = None,
content: t.Optional[str] = None,
) -> RoleFnReturnValue:
"""Role for linking to PyPI (Python Package Index) page.
Expand Down
2 changes: 1 addition & 1 deletion src/django_docutils/lib/roles/readthedocs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def readthedocs_role(
text: str,
lineno: int,
inliner: Inliner,
options: t.Optional[t.Dict[str, t.Any]] = None,
options: t.Optional[dict[str, t.Any]] = None,
content: t.Optional[str] = None,
) -> RoleFnReturnValue:
"""Role for linking to readthedocs.org page.
Expand Down
2 changes: 1 addition & 1 deletion src/django_docutils/lib/roles/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def register_django_docutils_roles() -> None:
return


def register_role_mapping(role_mapping: t.Dict[str, t.Any]) -> None:
def register_role_mapping(role_mapping: dict[str, t.Any]) -> None:
"""Register a dict mapping of roles.
An item consists of a role name, import string to a callable, and an
Expand Down
2 changes: 1 addition & 1 deletion src/django_docutils/lib/roles/twitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def twitter_role(
text: str,
lineno: int,
inliner: Inliner,
options: t.Optional[t.Dict[str, t.Any]] = None,
options: t.Optional[dict[str, t.Any]] = None,
content: t.Optional[str] = None,
) -> RoleFnReturnValue:
"""Role for linking to twitter articles.
Expand Down
4 changes: 2 additions & 2 deletions src/django_docutils/lib/roles/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ def __call__(self, target: str) -> str:
class RemoteUrlHandlerFn(Protocol):
"""Protocol for role handler callback that retrieve from external data sources."""

def __call__(self, target: str) -> t.Tuple[str, str]:
def __call__(self, target: str) -> tuple[str, str]:
"""Role function that can query an external source for its title."""
...


RoleFnReturnValue = t.Tuple[t.List[nodes.reference], t.List[t.Any]]
RoleFnReturnValue = tuple[list[nodes.reference], list[t.Any]]
"""Role function return value.
See also
Expand Down
2 changes: 1 addition & 1 deletion src/django_docutils/lib/roles/url.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def url_role(
text: str,
lineno: int,
inliner: Inliner,
options: t.Optional[t.Dict[str, t.Any]] = None,
options: t.Optional[dict[str, t.Any]] = None,
content: t.Optional[str] = None,
) -> RoleFnReturnValue:
"""Role for linking to url articles.
Expand Down
2 changes: 1 addition & 1 deletion src/django_docutils/lib/roles/wikipedia.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def wikipedia_role(
text: str,
lineno: int,
inliner: Inliner,
options: t.Optional[t.Dict[str, t.Any]] = None,
options: t.Optional[dict[str, t.Any]] = None,
content: t.Optional[str] = None,
) -> RoleFnReturnValue:
"""Role for linking to Wikipedia articles.
Expand Down
4 changes: 2 additions & 2 deletions src/django_docutils/lib/transforms/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

from pygments.token import _TokenType

TokenStream = Iterator[t.Tuple[_TokenType, str]]
TokenGenerator = Iterator[t.Tuple[t.Union[str, int], str]]
TokenStream = Iterator[tuple[_TokenType, str]]
TokenGenerator = Iterator[tuple[t.Union[str, int], str]]


class InlineHtmlFormatter(HtmlFormatter): # type:ignore
Expand Down
6 changes: 3 additions & 3 deletions src/django_docutils/lib/transforms/toc.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ def build_contents(
self,
node: nodes.Node,
level: int = 0,
) -> t.Union[nodes.bullet_list, t.List[t.Any]]:
) -> t.Union[nodes.bullet_list, list[t.Any]]:
"""Build nested bullet list from doctree content."""
assert isinstance(node, nodes.Element)
level += 1
sections: t.List[nodes.section] = [
sections: list[nodes.section] = [
sect for sect in node.children if isinstance(sect, nodes.section)
]
assert self.startnode is not None
entries: t.List[nodes.Node] = []
entries: list[nodes.Node] = []

depth = (
self.startnode.details.get("depth", sys.maxsize)
Expand Down
12 changes: 5 additions & 7 deletions src/django_docutils/lib/types.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
"""Typings for Django Docutils settings for django."""

import typing as t

from typing_extensions import NotRequired, TypedDict


class DjangoDocutilsLibRSTRolesSettings(TypedDict):
"""Docutils role mappings."""

local: NotRequired[t.Dict[str, str]]
local: NotRequired[dict[str, str]]


class DjangoDocutilsLibRSTDocutilsSettings(TypedDict):
Expand All @@ -22,14 +20,14 @@ class DjangoDocutilsLibRSTDocutilsSettings(TypedDict):
class DjangoDocutilsLibRSTSettings(TypedDict):
"""Core settings object for ``DJANGO_DOCUTILS_LIB_RST``."""

metadata_processors: NotRequired[t.List[str]]
transforms: NotRequired[t.List[str]]
metadata_processors: NotRequired[list[str]]
transforms: NotRequired[list[str]]
docutils: NotRequired[DjangoDocutilsLibRSTDocutilsSettings]
directives: NotRequired[t.Dict[str, str]]
directives: NotRequired[dict[str, str]]
roles: NotRequired[DjangoDocutilsLibRSTRolesSettings]


class DjangoDocutilsLibTextSettings(TypedDict):
"""Core settings object for ``DJANGO_DOCUTILS_TEXT_RST``."""

uncapitalized_word_filters: t.List[str]
uncapitalized_word_filters: list[str]
2 changes: 1 addition & 1 deletion src/django_docutils/lib/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
ws_re: "re.Pattern[str]" = re.compile(r"\s+")


def split_explicit_title(text: str) -> t.Tuple[bool, str, str]:
def split_explicit_title(text: str) -> tuple[bool, str, str]:
"""Split role content into title and target, if given (from sphinx)."""
match = explicit_title_re.match(text)
if match:
Expand Down
Loading

0 comments on commit 9eb6ea0

Please sign in to comment.