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

chore: add types #225

Merged
merged 18 commits into from
Mar 11, 2022
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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ jobs:
run: python -m pip install tox
- name: Run linting
run: python -m tox -e pep8
- name: Run mypy
run: python -m tox -e mypy
packaging:
name: Packaging
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
include LICENSE README.rst CHANGES.rst
include tox.ini .coveragerc pytest.ini
include readme_renderer/py.typed

recursive-include tests *.html
recursive-include tests *.py
Expand Down
10 changes: 10 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
[build-system]
requires = ["setuptools>=40.8.0", "wheel", "bleach>=2.1.0", "docutils>=0.13.1", "Pygments>=2.5.1"]
build-backend = "setuptools.build_meta:__legacy__"

[tool.mypy]
strict = true
miketheman marked this conversation as resolved.
Show resolved Hide resolved
warn_unused_configs = true
[[tool.mypy.overrides]]
# These modules do not yet have types available.
module = [
"cmarkgfm.*"
]
ignore_missing_imports = true
miketheman marked this conversation as resolved.
Show resolved Hide resolved
21 changes: 11 additions & 10 deletions readme_renderer/clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from __future__ import absolute_import, division, print_function

import functools
from typing import Any, Dict, Iterator, List, Optional

import bleach
import bleach.callbacks
Expand Down Expand Up @@ -59,15 +60,14 @@
"input": ["type", "checked", "disabled"],
}

ALLOWED_STYLES = [
]


class DisabledCheckboxInputsFilter:
def __init__(self, source):
# The typeshed for bleach (html5lib) filters is incomplete, use `typing.Any`
# See https://github.com/python/typeshed/blob/505ea726415016e53638c8b584b8fdc9c722cac1/stubs/bleach/bleach/html5lib_shim.pyi#L7-L8 # noqa E501
def __init__(self, source: Any) -> None:
self.source = source

def __iter__(self):
def __iter__(self) -> Iterator[Dict[str, Optional[str]]]:
for token in self.source:
if token.get("name") == "input":
# only allow disabled checkbox inputs
Expand All @@ -85,23 +85,24 @@ def __iter__(self):
else:
yield token

def __getattr__(self, name):
def __getattr__(self, name: str) -> Any:
return getattr(self.source, name)


def clean(html, tags=None, attributes=None, styles=None):
def clean(
html: str,
tags: Optional[List[str]] = None,
attributes: Optional[Dict[str, List[str]]] = None
) -> Optional[str]:
if tags is None:
tags = ALLOWED_TAGS
if attributes is None:
attributes = ALLOWED_ATTRIBUTES
if styles is None:
styles = ALLOWED_STYLES

# Clean the output using Bleach
cleaner = bleach.sanitizer.Cleaner(
tags=tags,
attributes=attributes,
styles=styles,
filters=[
# Bleach Linkify makes it easy to modify links, however, we will
# not be using it to create additional links.
Expand Down
7 changes: 7 additions & 0 deletions readme_renderer/integration/distutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# The `distutils` integration is going to need to get updated to `setuptools``
# soon enough, as `distutils` is deprecated and will be removed in Python 3.12.
# There's currently some pass-through imports that allow this to work, but is
# challenging for `mypy` in `strict` mode, so let's skip this file for now.
# See https://peps.python.org/pep-0632/
# mypy: ignore-errors
from __future__ import absolute_import, division, print_function

import cgi
Expand Down
16 changes: 11 additions & 5 deletions readme_renderer/markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@

import re
import warnings
from typing import Any, Match, Optional
miketheman marked this conversation as resolved.
Show resolved Hide resolved

from html.parser import unescape
from html import unescape

import pygments
import pygments.lexers
Expand Down Expand Up @@ -51,7 +52,11 @@
}


def render(raw, variant="GFM", **kwargs):
def render(
raw: str,
variant: str = "GFM",
**kwargs: Any
) -> Optional[str]:
if not variants:
warnings.warn(_EXTRA_WARNING)
return None
Expand All @@ -61,7 +66,8 @@ def render(raw, variant="GFM", **kwargs):
if not renderer:
return None

rendered = renderer(raw)
# The renderer is a lambda function, and mypy fails lambdas right now.
rendered = renderer(raw) # type: ignore
Comment on lines +69 to +70
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you try to work around this? I wonder if Callable and/or cast would help. Or, maybe variants could be updated to use explicit private functions with annotations, instead of lambdas.

Copy link
Member Author

Choose a reason for hiding this comment

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

I did not try to modify the code to make the type annotations work - that felt counter to the effort at hand. mypy struggles with lambdas - see open issue: python/mypy#4226

It'd be awesome if you wanted to take on that as a follow-up once this is merged to remove the ignore!


if not rendered:
return None
Expand All @@ -71,7 +77,7 @@ def render(raw, variant="GFM", **kwargs):
return cleaned


def _highlight(html):
def _highlight(html: str) -> str:
"""Syntax-highlights HTML-rendered Markdown.

Plucks sections to highlight that conform the the GitHub fenced code info
Expand All @@ -94,7 +100,7 @@ def _highlight(html):
'(?(in_code)|<code>)(?P<code>.+?)'
r'</code></pre>', re.DOTALL)

def replacer(match):
def replacer(match: Match[Any]) -> str:
try:
lang = match.group('lang')
lang = _LANG_ALIASES.get(lang, lang)
Expand Down
Empty file added readme_renderer/py.typed
Empty file.
22 changes: 17 additions & 5 deletions readme_renderer/rst.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,28 @@
from __future__ import absolute_import, division, print_function

import io
from typing import Any, Dict, IO, Optional, Union

from docutils.core import publish_parts
from docutils.nodes import colspec, image
from docutils.writers.html4css1 import HTMLTranslator, Writer
from docutils.utils import SystemMessage

from .clean import clean


class ReadMeHTMLTranslator(HTMLTranslator):
class ReadMeHTMLTranslator(HTMLTranslator): # type: ignore[misc] # docutils is incomplete, returns `Any` python/typeshed#7256 # noqa E501

# Overrides base class not to output `<object>` tag for SVG images.
object_image_types = {}

def emptytag(self, node, tagname, suffix="\n", **attributes):
object_image_types: Dict[str, str] = {}

def emptytag(
self,
node: Union[colspec, image],
tagname: str,
suffix: str = "\n",
**attributes: Any
) -> Any:
"""Override this to add back the width/height attributes."""
if tagname == "img":
if "width" in node:
Expand Down Expand Up @@ -95,7 +103,11 @@ def emptytag(self, node, tagname, suffix="\n", **attributes):
}


def render(raw, stream=None, **kwargs):
def render(
raw: str,
stream: Optional[IO[str]] = None,
**kwargs: Any
) -> Optional[str]:
if stream is None:
# Use a io.StringIO as the warning stream to prevent warnings from
# being printed to sys.stderr.
Expand Down
3 changes: 2 additions & 1 deletion readme_renderer/txt.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from __future__ import absolute_import, division, print_function

import sys
from typing import Any, Optional

from .clean import clean

Expand All @@ -26,6 +27,6 @@ def html_escape(s):
return escape(s, quote=True).replace("'", '&#x27;')


def render(raw, **kwargs):
def render(raw: str, **kwargs: Any) -> Optional[str]:
rendered = html_escape(raw).replace("\n", "<br>")
return clean(rendered, tags=["br"])
13 changes: 11 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
[tox]
envlist = py36,py37,py38,py39,py310,pep8,packaging,noextra
envlist = py36,py37,py38,py39,py310,pep8,packaging,noextra,mypy

[testenv]
deps =
pytest
commands =
pytest --strict {posargs}
pytest --strict-markers {posargs}
extras = md

[testenv:mypy]
basepython = python3
deps =
mypy
types-bleach
types-docutils
types-Pygments
commands = mypy readme_renderer

[testenv:pep8]
basepython = python3
deps =
Expand Down