Skip to content

Commit

Permalink
🔧 MAINTAIN: Add mypy type-checking (#64)
Browse files Browse the repository at this point in the history
  • Loading branch information
hukkinj1 authored Dec 14, 2020
1 parent 3a5bdcc commit b16ee04
Show file tree
Hide file tree
Showing 28 changed files with 142 additions and 92 deletions.
5 changes: 5 additions & 0 deletions .mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[mypy]
warn_unused_ignores = True
warn_redundant_casts = True
no_implicit_optional = True
strict_equality = True
12 changes: 9 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,30 @@ exclude: >
repos:

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
rev: v3.3.0
hooks:
- id: check-json
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace

- repo: https://github.com/mgedmin/check-manifest
rev: "0.42"
rev: "0.44"
hooks:
- id: check-manifest

- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.3
rev: 3.8.4
hooks:
- id: flake8

- repo: https://github.com/psf/black
rev: 20.8b1
hooks:
- id: black

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.790
hooks:
- id: mypy
additional_dependencies: [attrs]
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ exclude .flake8
exclude .circleci
exclude .circleci/config.yml
exclude codecov.yml
exclude .mypy.ini

include LICENSE
include LICENSE.markdown-it
Expand Down
2 changes: 1 addition & 1 deletion markdown_it/common/entities.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""HTML5 entities map: { name -> characters }."""
import html
import html.entities


class _Entities:
Expand Down
4 changes: 2 additions & 2 deletions markdown_it/common/normalize_url.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import html
import re
from typing import Callable
from typing import Callable, Optional
from urllib.parse import urlparse, urlunparse, quote, unquote # noqa: F401

from .utils import ESCAPABLE
Expand Down Expand Up @@ -166,7 +166,7 @@ def normalizeLinkText(link):
GOOD_DATA_RE = re.compile(r"^data:image\/(gif|png|jpeg|webp);")


def validateLink(url: str, validator: Callable = None):
def validateLink(url: str, validator: Optional[Callable] = None):
"""Validate URL link is allowed in output.
This validator can prohibit more than really needed to prevent XSS.
Expand Down
9 changes: 5 additions & 4 deletions markdown_it/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def assign(obj):
# })
# })

return obj
# return obj


def arrayReplaceAt(src: list, pos: int, newElements: list):
Expand Down Expand Up @@ -139,9 +139,10 @@ def replaceEntityPattern(match, name):


def unescapeMd(string: str):
if "\\" in string:
return string
return string.replace(UNESCAPE_MD_RE, "$1")
raise NotImplementedError
# if "\\" in string:
# return string
# return string.replace(UNESCAPE_MD_RE, "$1")


def unescapeAll(string: str):
Expand Down
15 changes: 8 additions & 7 deletions markdown_it/extensions/anchors/index.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import re
from typing import Callable, List, Optional
from typing import Callable, List, Optional, Set

from markdown_it import MarkdownIt
from markdown_it.rules_core import StateCore
Expand Down Expand Up @@ -65,19 +65,20 @@ def _make_anchors_func(
permalinkBefore: bool,
permalinkSpace: bool,
):
slugs = set()
slugs: Set[str] = set()

def _anchor_func(state: StateCore):
for (idx, token) in enumerate(state.tokens):
token: Token
if token.type != "heading_open":
continue
level = int(token.tag[1])
if level not in selected_levels:
continue
inline_token = state.tokens[idx + 1]
assert inline_token.children is not None
title = "".join(
child.content
for child in state.tokens[idx + 1].children
for child in inline_token.children
if child.type in ["text", "code_inline"]
)
slug = unique_slug(slug_func(title), slugs)
Expand All @@ -95,17 +96,17 @@ def _anchor_func(state: StateCore):
Token("link_close", "a", -1),
]
if permalinkBefore:
state.tokens[idx + 1].children = (
inline_token.children = (
link_tokens
+ (
[Token("text", "", 0, content=" ")]
if permalinkSpace
else []
)
+ state.tokens[idx + 1].children
+ inline_token.children
)
else:
state.tokens[idx + 1].children.extend(
inline_token.children.extend(
([Token("text", "", 0, content=" ")] if permalinkSpace else [])
+ link_tokens
)
Expand Down
5 changes: 3 additions & 2 deletions markdown_it/extensions/container/index.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Process block-level custom containers."""
from math import floor
from typing import Callable
from typing import Callable, Optional

from markdown_it import MarkdownIt
from markdown_it.common.utils import charCodeAt
Expand All @@ -11,7 +11,7 @@ def container_plugin(
md: MarkdownIt,
name: str,
marker: str = ":",
validate: Callable[[str, str], bool] = None,
validate: Optional[Callable[[str, str], bool]] = None,
render=None,
):
"""Plugin ported from
Expand Down Expand Up @@ -80,6 +80,7 @@ def container_func(state: StateBlock, startLine: int, endLine: int, silent: bool

markup = state.src[start:pos]
params = state.src[pos:maximum]
assert validate is not None
if not validate(params, markup):
return False

Expand Down
2 changes: 1 addition & 1 deletion markdown_it/extensions/deflist/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def deflist_plugin(md: MarkdownIt):
~ Definition 2b
"""
isSpace = md.utils.isSpace
isSpace = md.utils.isSpace # type: ignore

def skipMarker(state: StateBlock, line: int):
"""Search `[:~][\n ]`, returns next pos after marker on success or -1 on fail."""
Expand Down
8 changes: 5 additions & 3 deletions markdown_it/extensions/footnote/index.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Process footnotes
#

from typing import List, Optional

from markdown_it import MarkdownIt
from markdown_it.token import Token
from markdown_it.rules_inline import StateInline
Expand Down Expand Up @@ -174,7 +176,7 @@ def footnote_inline(state: StateInline, silent: bool):
refs = state.env.setdefault("footnotes", {}).setdefault("list", {})
footnoteId = len(refs)

tokens = []
tokens: List[Token] = []
state.md.inline.parse(
state.src[labelStart:labelEnd], state.md, state.env, tokens
)
Expand Down Expand Up @@ -260,7 +262,7 @@ def footnote_tail(state: StateBlock, *args, **kwargs):
if "footnotes" not in state.env:
return

current = []
current: List[Token] = []
tok_filter = []
for tok in state.tokens:

Expand Down Expand Up @@ -320,7 +322,7 @@ def footnote_tail(state: StateBlock, *args, **kwargs):

state.tokens.extend(tokens)
if state.tokens[len(state.tokens) - 1].type == "paragraph_close":
lastParagraph = state.tokens.pop()
lastParagraph: Optional[Token] = state.tokens.pop()
else:
lastParagraph = None

Expand Down
1 change: 1 addition & 0 deletions markdown_it/extensions/tasklists/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def is_todo_item(tokens, index):
)

def todoify(token: Token, token_constructor):
assert token.children is not None
token.children.insert(0, make_checkbox(token, token_constructor))
token.children[1].content = token.children[1].content[3:]
token.content = token.content[3:]
Expand Down
28 changes: 14 additions & 14 deletions markdown_it/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from contextlib import contextmanager
from typing import Any, Callable, Dict, List, Optional, Union
from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Union

from . import helpers, presets # noqa F401
from .common import utils # noqa F401
Expand Down Expand Up @@ -28,7 +28,7 @@

class MarkdownIt:
def __init__(
self, config: Union[str, AttrDict] = "commonmark", renderer_cls=RendererHTML
self, config: Union[str, Mapping] = "commonmark", renderer_cls=RendererHTML
):
"""Main parser class
Expand All @@ -43,7 +43,7 @@ def __init__(

self.utils = utils
self.helpers = helpers
self.options = {}
self.options: Dict[str, Any] = {}
self.configure(config)

self.linkify = linkify_it.LinkifyIt() if linkify_it else None
Expand All @@ -69,7 +69,7 @@ def set(self, options):
"""
self.options = options

def configure(self, presets: Union[str, AttrDict]):
def configure(self, presets: Union[str, Mapping]):
"""Batch load of all options and component settings.
This is an internal method, and you probably will not need it.
But if you will - see available presets and data structure
Expand All @@ -87,13 +87,13 @@ def configure(self, presets: Union[str, AttrDict]):
)
if not presets:
raise ValueError("Wrong `markdown-it` preset, can't be empty")
presets = AttrDict(presets)
config = AttrDict(presets)

if "options" in presets:
self.set(presets.options)
if "options" in config:
self.set(config.options)

if "components" in presets:
for name, component in presets.components.items():
if "components" in config:
for name, component in config.components.items():
rules = component.get("rules", None)
if rules:
self[name].ruler.enableOnly(rules)
Expand Down Expand Up @@ -122,7 +122,7 @@ def get_active_rules(self) -> Dict[str, List[str]]:
return rules

def enable(
self, names: Union[str, List[str]], ignoreInvalid: bool = False
self, names: Union[str, Iterable[str]], ignoreInvalid: bool = False
) -> "MarkdownIt":
"""Enable list or rules. (chainable)
Expand Down Expand Up @@ -155,7 +155,7 @@ def enable(
return self

def disable(
self, names: Union[str, List[str]], ignoreInvalid: bool = False
self, names: Union[str, Iterable[str]], ignoreInvalid: bool = False
) -> "MarkdownIt":
"""The same as [[MarkdownIt.enable]], but turn specified rules off. (chainable)
Expand Down Expand Up @@ -193,7 +193,7 @@ def add_render_rule(self, name: str, function: Callable, fmt="html"):
Only applied when ``renderer.__output__ == fmt``
"""
if self.renderer.__output__ == fmt:
self.renderer.rules[name] = function.__get__(self.renderer)
self.renderer.rules[name] = function.__get__(self.renderer) # type: ignore

def use(self, plugin: Callable, *params, **options) -> "MarkdownIt":
"""Load specified plugin with given params into current parser instance. (chainable)
Expand Down Expand Up @@ -225,7 +225,7 @@ def parse(self, src: str, env: Optional[AttrDict] = None) -> List[Token]:
and then pass updated object to renderer.
"""
env = AttrDict() if env is None else env
if not isinstance(env, AttrDict):
if not isinstance(env, AttrDict): # type: ignore
raise TypeError(f"Input data should be an AttrDict, not {type(env)}")
if not isinstance(src, str):
raise TypeError(f"Input data should be a string, not {type(src)}")
Expand Down Expand Up @@ -259,7 +259,7 @@ def parseInline(self, src: str, env: Optional[AttrDict] = None) -> List[Token]:
tokens in `children` property. Also updates `env` object.
"""
env = AttrDict() if env is None else env
if not isinstance(env, AttrDict):
if not isinstance(env, AttrDict): # type: ignore
raise TypeError(f"Input data should be an AttrDict, not {type(env)}")
if not isinstance(src, str):
raise TypeError(f"Input data should be a string, not {type(src)}")
Expand Down
11 changes: 9 additions & 2 deletions markdown_it/parser_block.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Block-level tokenizer."""
import logging
from typing import List
from typing import List, Optional

from .ruler import Ruler
from .token import Token
Expand Down Expand Up @@ -92,7 +92,14 @@ def tokenize(
line += 1
state.line = line

def parse(self, src: str, md, env, outTokens: List[Token], ords: List[int] = None):
def parse(
self,
src: str,
md,
env,
outTokens: List[Token],
ords: Optional[List[int]] = None,
):
"""Process input string and push block tokens into `outTokens`."""
if not src:
return
Expand Down
4 changes: 3 additions & 1 deletion markdown_it/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def render(self, tokens: List[Token], options, env) -> str:
for i, token in enumerate(tokens):

if token.type == "inline":
assert token.children is not None
result += self.renderInline(token.children, options, env)
elif token.type in self.rules:
result += self.rules[token.type](tokens, i, options, env)
Expand Down Expand Up @@ -124,7 +125,7 @@ def renderToken(
result += self.renderAttrs(token)

# Add a slash for self-closing tags, e.g. `<img src="foo" /`
if token.nesting == 0 and options.xhtmlOut:
if token.nesting == 0 and options["xhtmlOut"]:
result += " /"

# Check if we need to add a newline after this tag
Expand Down Expand Up @@ -184,6 +185,7 @@ def renderInlineAsText(self, tokens: List[Token], options, env) -> str:
if token.type == "text":
result += token.content
elif token.type == "image":
assert token.children is not None
result += self.renderInlineAsText(token.children, options, env)

return result
Expand Down
Loading

0 comments on commit b16ee04

Please sign in to comment.