Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strict typing, py.typed and link issues #25

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
17 changes: 10 additions & 7 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,6 @@
nitpicky = True
nitpick_ignore: list[tuple[str, str]] = []

nitpick_ignore = [
('py:class', '_pytest.nodes.Item'),
('py:class', 'Config'),
('py:class', 'Path'),
('py:class', 'Session'),
]

# Include Python intersphinx mapping to prevent failures
# jaraco/skeleton#51
extensions += ['sphinx.ext.intersphinx']
Expand All @@ -65,3 +58,13 @@
intersphinx_mapping.update(
pytest=('https://docs.pytest.org/en/latest/', None),
)

# local

nitpick_ignore += [
('py:class', 'Config'),
('py:class', 'Session'),
# jaraco/pytest-checkdocs#25
('py:class', 'PackageMetadata'),
('py:class', 'Node'),
]
6 changes: 5 additions & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[mypy]
# Is the project well-typed?
strict = False
strict = True

# Early opt-in even when strict = False
warn_unused_ignores = True
Expand All @@ -13,3 +13,7 @@ explicit_package_bases = True
disable_error_code =
# Disable due to many false positives
overload-overlap,

# jaraco/jaraco.packaging#20
[mypy-jaraco.packaging.*]
ignore_missing_imports = True
1 change: 1 addition & 0 deletions newsfragments/25.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Complete annotations and add ``py.typed`` marker -- by :user:`Avasam`
6 changes: 2 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ type = [

# local
"types-docutils", # pytest-checkdocs#13
"importlib_metadata; python_version < '3.12'", # Only used for annotations, not at runtime
"pytest>=8.3.3" # Typing fixes not necessary at runtime
]


Expand All @@ -73,7 +75,3 @@ pytest11 = {checkdocs = "pytest_checkdocs"}


[tool.setuptools_scm]


[tool.pytest-enabler.mypy]
# Disabled due to jaraco/skeleton#143
58 changes: 39 additions & 19 deletions pytest_checkdocs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,72 @@
from __future__ import annotations

import contextlib
import pathlib
import re
import sys
from collections.abc import Iterator
from typing import TYPE_CHECKING, Any

import pytest
import docutils.core
import docutils.nodes
import docutils.utils
import pytest
from jaraco.packaging import metadata

if TYPE_CHECKING:
if sys.version_info >= (3, 12):
from importlib.metadata import PackageMetadata
else:
from importlib_metadata import PackageMetadata

from _pytest.nodes import Node
from typing_extensions import Self

project_files = 'setup.py', 'setup.cfg', 'pyproject.toml'


def pytest_collect_file(file_path: pathlib.Path, parent):
def pytest_collect_file(file_path: pathlib.Path, parent: Node) -> CheckdocsItem | None:
if file_path.name not in project_files:
return
return None
return CheckdocsItem.from_parent(parent, name='project')


class Description(str):
content_type: str = ""

@classmethod
def from_md(cls, md):
def from_md(cls, md: PackageMetadata) -> Self:
desc = cls(md.get('Description'))
desc.content_type = md.get('Description-Content-Type', 'text/x-rst')
return desc


class CheckdocsItem(pytest.Item):
def runtest(self):
def runtest(self) -> None:
desc = self.get_long_description()
method_name = f"run_{re.sub('[-/]', '_', desc.content_type)}"
getattr(self, method_name)(desc)

def run_text_markdown(self, desc):
def run_text_markdown(self, desc: str) -> None:
"stubbed"

def run_text_x_rst(self, desc):
def run_text_x_rst(self, desc: str) -> None:
with self.monkey_patch_system_message() as reports:
self.rst2html(desc)
assert not reports

@contextlib.contextmanager
def monkey_patch_system_message(self):
reports = []
def monkey_patch_system_message(self) -> Iterator[list[str | Exception]]:
reports: list[str | Exception] = []
orig = docutils.utils.Reporter.system_message

def system_message(reporter, level, message, *children, **kwargs):
def system_message(
reporter: docutils.utils.Reporter,
level: int,
message: str | Exception,
*children: docutils.nodes.Node,
**kwargs: Any,
) -> docutils.nodes.system_message:
result = orig(reporter, level, message, *children, **kwargs)
if level >= reporter.WARNING_LEVEL:
# All reST failures preventing doc publishing go to reports
Expand All @@ -52,17 +75,14 @@ def system_message(reporter, level, message, *children, **kwargs):

return result

docutils.utils.Reporter.system_message = system_message
docutils.utils.Reporter.system_message = system_message # type: ignore[assignment] # type-stubs expands the kwargs
yield reports
docutils.utils.Reporter.system_message = orig
docutils.utils.Reporter.system_message = orig # type: ignore[method-assign]

def get_long_description(self):
def get_long_description(self) -> Description:
return Description.from_md(metadata.load('.'))

@staticmethod
def rst2html(value):
docutils_settings = {}
parts = docutils.core.publish_parts(
source=value, writer_name="html4css1", settings_overrides=docutils_settings
)
return parts['whole']
def rst2html(value: str) -> str:
parts = docutils.core.publish_parts(source=value, writer_name="html4css1")
return parts['whole'] # type: ignore[no-any-return] # python/typeshed#12595
Empty file added pytest_checkdocs/py.typed
Empty file.
Loading