Skip to content
5 changes: 5 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ repos:
additional_dependencies:
- [email protected]
args: [src]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.790
hooks:
- id: mypy
files: ^(src/pytest_html|testing)
- repo: local
hooks:
- id: rst
Expand Down
17 changes: 17 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,2 +1,19 @@
[bdist_wheel]
universal = 0

[mypy]
check_untyped_defs = True
disallow_any_generics = True
disallow_incomplete_defs = True
disallow_untyped_calls = True
disallow_untyped_decorators = True
disallow_untyped_defs = True
ignore_missing_imports = True
no_implicit_optional = True
no_implicit_reexport = True
show_error_codes = True
strict_equality = True
warn_redundant_casts = True
warn_return_any = True
warn_unreachable = True
warn_unused_configs = True
2 changes: 1 addition & 1 deletion src/pytest_html/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
try:
from . import __version
from . import __version # type: ignore

__version__ = __version.version
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since __version is an optioanl import at the time typecheckers run,
i usggest to typehint __version__ ala

     __version__: str = __version.version 

this makes type checkers aware that __version__ is supposed to be a sting and warn/fail if it is not

except ImportError:
Expand Down
40 changes: 29 additions & 11 deletions src/pytest_html/extras.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from typing import Dict
from typing import Optional

FORMAT_HTML = "html"
FORMAT_IMAGE = "image"
Expand All @@ -10,7 +12,13 @@
FORMAT_VIDEO = "video"


def extra(content, format_type, name=None, mime_type=None, extension=None):
def extra(
content: str,
format_type: str,
name: Optional[str] = None,
mime_type: Optional[str] = None,
extension: Optional[str] = None,
) -> Dict[str, Optional[str]]:
return {
"name": name,
"format_type": format_type,
Expand All @@ -20,41 +28,51 @@ def extra(content, format_type, name=None, mime_type=None, extension=None):
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

html(), image() etc are facades to extra().

to make this more clear, i suggest to define a variable with the return type of extra() as _ExtraResult = Dict[str, Optional[str]]
and use this _ExtraResult where needed for extra(), html() and so on


def html(content):
def html(content: str) -> Dict[str, Optional[str]]:
return extra(content, FORMAT_HTML)


def image(content, name="Image", mime_type="image/png", extension="png"):
def image(
content: str,
name: str = "Image",
mime_type: str = "image/png",
extension: str = "png",
) -> Dict[str, Optional[str]]:
return extra(content, FORMAT_IMAGE, name, mime_type, extension)


def png(content, name="Image"):
def png(content: str, name: str = "Image") -> Dict[str, Optional[str]]:
return image(content, name, mime_type="image/png", extension="png")


def jpg(content, name="Image"):
def jpg(content: str, name: str = "Image") -> Dict[str, Optional[str]]:
return image(content, name, mime_type="image/jpeg", extension="jpg")


def svg(content, name="Image"):
def svg(content: str, name: str = "Image") -> Dict[str, Optional[str]]:
return image(content, name, mime_type="image/svg+xml", extension="svg")


def json(content, name="JSON"):
def json(content: str, name: str = "JSON") -> Dict[str, Optional[str]]:
return extra(content, FORMAT_JSON, name, "application/json", "json")


def text(content, name="Text"):
def text(content: str, name: str = "Text") -> Dict[str, Optional[str]]:
return extra(content, FORMAT_TEXT, name, "text/plain", "txt")


def url(content, name="URL"):
def url(content: str, name: str = "URL") -> Dict[str, Optional[str]]:
return extra(content, FORMAT_URL, name)


def video(content, name="Video", mime_type="video/mp4", extension="mp4"):
def video(
content: str,
name: str = "Video",
mime_type: str = "video/mp4",
extension: str = "mp4",
) -> Dict[str, Optional[str]]:
return extra(content, FORMAT_VIDEO, name, mime_type, extension)


def mp4(content, name="Video"):
def mp4(content: str, name: str = "Video") -> Dict[str, Optional[str]]:
return video(content, name)
1 change: 1 addition & 0 deletions src/pytest_html/hooks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# type: ignore
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a directive for an IDE extension or something? Like, maybe mypy? I am familiar with typing (use it all the time), but not the details of mypy.



def pytest_html_report_title(report):
Expand Down
1 change: 1 addition & 0 deletions src/pytest_html/html_report.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# type: ignore
import bisect
import datetime
import json
Expand Down
15 changes: 12 additions & 3 deletions src/pytest_html/outcome.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
from typing import Optional

from py.xml import html


class Outcome:
def __init__(self, outcome, total=0, label=None, test_result=None, class_html=None):
def __init__(
self,
outcome: str,
total: int = 0,
label: Optional[str] = None,
test_result: Optional[str] = None,
class_html: Optional[str] = None,
) -> None:
self.outcome = outcome
self.label = label or outcome
self.class_html = class_html or outcome
Expand All @@ -12,7 +21,7 @@ def __init__(self, outcome, total=0, label=None, test_result=None, class_html=No
self.generate_checkbox()
self.generate_summary_item()

def generate_checkbox(self):
def generate_checkbox(self) -> None:
checkbox_kwargs = {"data-test-result": self.test_result.lower()}
if self.total == 0:
checkbox_kwargs["disabled"] = "true"
Expand All @@ -27,7 +36,7 @@ def generate_checkbox(self):
**checkbox_kwargs,
)

def generate_summary_item(self):
def generate_summary_item(self) -> None:
self.summary_item = html.span(
f"{self.total} {self.label}", class_=self.class_html
)
1 change: 1 addition & 0 deletions src/pytest_html/plugin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# type: ignore
import os

import pytest
Expand Down
1 change: 1 addition & 0 deletions src/pytest_html/result.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# type: ignore
import json
import os
import re
Expand Down
6 changes: 4 additions & 2 deletions src/pytest_html/util.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import importlib
from functools import lru_cache
from types import ModuleType
from typing import Optional


@lru_cache()
def ansi_support():
def ansi_support() -> Optional[ModuleType]:
try:
# from ansi2html import Ansi2HTMLConverter, style # NOQA
return importlib.import_module("ansi2html")
except ImportError:
# ansi2html is not installed
pass
return None
1 change: 1 addition & 0 deletions testing/test_pytest_html.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# type: ignore
import builtins
import json
import os
Expand Down