diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2503d65..d350820 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -22,7 +22,7 @@ jobs:
- name: Run tests on Python${{ matrix.python-version }}
run: |
nix-shell --argstr pyVersion ${{ matrix.python-version }} --run \
- "pip install -e . && pip install -r requirements.txt && pip install -r requirements-test.txt && python -m pytest"
+ "pip install -e . && pip install -r requirements.txt && pip install -r requirements-test.txt && make test"
- name: Coveralls
env:
diff --git a/Makefile b/Makefile
index 9a1ee0e..7f796e3 100644
--- a/Makefile
+++ b/Makefile
@@ -17,7 +17,7 @@ test:
.PHONY: typecheck
typecheck:
- mypy --config-file setup.cfg --package $(PROJECT_NAME)
+ mypy --config-file setup.cfg --strict --package $(PROJECT_NAME)
.PHONY: prepare-dist
prepare-dist:
diff --git a/plim/__init__.py b/plim/__init__.py
index 9f5119d..763cc5d 100644
--- a/plim/__init__.py
+++ b/plim/__init__.py
@@ -1,23 +1,25 @@
import functools
+from typing import Mapping, Type, Sequence, Any, Callable
+
+from pyrsistent import v
from .lexer import compile_plim_source
from . import syntax as available_syntax
-def preprocessor_factory(custom_parsers=None, syntax='mako'):
+def preprocessor_factory(custom_parsers: Sequence[Any] = v(), syntax: str = 'mako') -> Callable[[str, bool], str]:
"""
:param custom_parsers: a list of 2-tuples of (parser_regex, parser_callable) or None
:type custom_parsers: list or None
:param syntax: name of the target template engine ('mako' by default)
- :type syntax: str or None
:return: preprocessor instance
"""
- syntax_choices = {
+ syntax_choices: Mapping[str, Type[available_syntax.BaseSyntax]] = {
'mako': available_syntax.Mako,
'django': available_syntax.Django,
}
- selected_syntax = syntax_choices[syntax](custom_parsers)
+ selected_syntax = syntax_choices[syntax](custom_parsers or v())
return functools.partial(compile_plim_source, syntax=selected_syntax)
diff --git a/plim/adapters/pyramid_renderer.py b/plim/adapters/pyramid_renderer.py
index 15e99c8..43ffb3f 100644
--- a/plim/adapters/pyramid_renderer.py
+++ b/plim/adapters/pyramid_renderer.py
@@ -33,7 +33,7 @@ def add_plim_renderer(config, extension, mako_settings_prefix='mako.', preproces
renderer_factory = MakoRendererFactory()
config.add_renderer(extension, renderer_factory)
- def register():
+ def register() -> None:
settings = copy.copy(config.registry.settings)
settings['{prefix}preprocessor'.format(prefix=mako_settings_prefix)] = preprocessor
diff --git a/plim/console.py b/plim/console.py
index 69bf82c..736d05f 100644
--- a/plim/console.py
+++ b/plim/console.py
@@ -43,7 +43,7 @@ def plimc(args=None, stdout=None):
# Add an empty string path, so modules located at the current working dir
# are reachable and considered in the first place (see issue #32).
sys.path.insert(0, '')
- preprocessor = EntryPoint.parse('x={}'.format(preprocessor_path)).load(False)
+ preprocessor = EntryPoint.parse('x={}'.format(preprocessor_path)).resolve()
# Render to html, if requested
# ----------------------------
diff --git a/plim/errors.py b/plim/errors.py
index d828624..5b81c74 100644
--- a/plim/errors.py
+++ b/plim/errors.py
@@ -1,29 +1,23 @@
-# -*- coding: utf-8 -*-
-from .util import u
-
-
-
class PlimError(Exception):
- def __str__(self):
- return self.__unicode__().encode('utf-8')
+ pass
class PlimSyntaxError(PlimError):
- def __init__(self, msg, line):
+ def __init__(self, msg: str, line: str):
super(PlimSyntaxError, self).__init__()
self.msg = msg
self.line = line
- def __unicode__(self):
- return u('{msg} | at line(pos) "{line}"').format(msg=self.msg, line=self.line)
+ def __str__(self) -> str:
+ return '{msg} | at line(pos) "{line}"'.format(msg=self.msg, line=self.line)
class ParserNotFound(PlimError):
- def __init__(self, lineno, line):
+ def __init__(self, lineno: int, line: str):
super(ParserNotFound, self).__init__()
self.lineno = lineno
self.line = line
- def __unicode__(self):
- return u("Invalid syntax at line {lineno}: {line}").format(
+ def __str__(self) -> str:
+ return "Invalid syntax at line {lineno}: {line}".format(
lineno=self.lineno, line=self.line)
diff --git a/plim/extensions.py b/plim/extensions.py
index 530ad7d..920e3d9 100644
--- a/plim/extensions.py
+++ b/plim/extensions.py
@@ -1,28 +1,28 @@
+from typing import Mapping
+
from docutils.core import publish_parts
import coffeescript
from scss import Scss
from stylus import Stylus
-from .util import u
-
-def rst_to_html(source):
+def rst_to_html(source: str) -> str:
# This code was taken from http://wiki.python.org/moin/ReStructuredText
# You may also be interested in http://www.tele3.cz/jbar/rest/about.html
- html = publish_parts(source=source, writer_name='html')
+ html: Mapping[str, str] = publish_parts(source=source, writer_name='html')
return html['html_body']
-def coffee_to_js(source):
- return u('').format(js=coffeescript.compile(source))
+def coffee_to_js(source: str) -> str:
+ return ''.format(js=coffeescript.compile(source))
-def scss_to_css(source):
+def scss_to_css(source: str) -> str:
css = Scss().compile(source).strip()
- return u('').format(css=css)
+ return ''.format(css=css)
-def stylus_to_css(source):
+def stylus_to_css(source: str) -> str:
compiler = Stylus()
- return u('').format(css=compiler.compile(source).strip())
+ return ''.format(css=compiler.compile(source).strip())
diff --git a/plim/lexer.py b/plim/lexer.py
index 36045d9..406d0ad 100644
--- a/plim/lexer.py
+++ b/plim/lexer.py
@@ -2,8 +2,10 @@
"""Plim lexer"""
import functools
import re
+from typing import Optional, Tuple, Any, Mapping, Callable, Iterator, Sequence
import markdown2
+from pyrsistent import v
from . import errors
from .util import StringIO, MAXSIZE, joined, space_separated, u
@@ -61,21 +63,21 @@
INLINE_PYTHON_TERMINATOR = '---'
-CSS_ID_SHORTCUT_TERMINATORS = (
+CSS_ID_SHORTCUT_TERMINATORS = v(
CSS_CLASS_SHORTCUT_DELIMITER,
WHITESPACE,
OPEN_BRACE,
INLINE_TAG_SEPARATOR
)
-CSS_CLASS_SHORTCUT_TERMINATORS = (
+CSS_CLASS_SHORTCUT_TERMINATORS = v(
CSS_CLASS_SHORTCUT_DELIMITER,
WHITESPACE,
OPEN_BRACE,
INLINE_TAG_SEPARATOR
)
-ATTRIBUTE_TERMINATORS = (
+ATTRIBUTE_TERMINATORS = v(
ATTRIBUTE_VALUE_DELIMITER,
ATTRIBUTES_DELIMITER,
INLINE_TAG_SEPARATOR,
@@ -83,13 +85,13 @@
LITERAL_CONTENT_SPACE_PREFIX
)
-ATTRIBUTE_TERMINATORS_WITH_PARENTHESES = (
+ATTRIBUTE_TERMINATORS_WITH_PARENTHESES = v(
ATTRIBUTE_VALUE_DELIMITER,
ATTRIBUTES_DELIMITER,
CLOSE_BRACE
)
-ATTRIBUTE_VALUE_TERMINATORS = (
+ATTRIBUTE_VALUE_TERMINATORS = v(
ATTRIBUTES_DELIMITER,
INLINE_TAG_SEPARATOR,
LITERAL_CONTENT_PREFIX,
@@ -98,7 +100,7 @@
BOOLEAN_ATTRIBUTE_MARKER
)
-ATTRIBUTE_VALUE_TERMINATORS_WITH_PARENTHESES = (
+ATTRIBUTE_VALUE_TERMINATORS_WITH_PARENTHESES = v(
ATTRIBUTES_DELIMITER,
INLINE_TAG_SEPARATOR,
LITERAL_CONTENT_PREFIX,
@@ -173,11 +175,12 @@
# Searchers
# ==================================================================================
-def search_quotes(line, escape_char='\\', quotes_re=QUOTES_RE):
- """
+SourceIter = Iterator[tuple[int, str]]
+Parsed = Tuple[str, int, str, str]
+def search_quotes(line: str, escape_char: str = '\\', quotes_re = QUOTES_RE) -> Optional[int]:
+ """
:param line: may be empty
- :type line: str
:param escape_char:
"""
match = quotes_re.match(line)
@@ -198,7 +201,7 @@ def search_quotes(line, escape_char='\\', quotes_re=QUOTES_RE):
return None
-def search_parser(lineno, line, syntax):
+def search_parser(lineno, line, syntax) -> Tuple[Any, Any]:
"""Finds a proper parser function for a given line or raises an error
:param lineno:
@@ -214,7 +217,7 @@ def search_parser(lineno, line, syntax):
# Extractors
# ==================================================================================
-def extract_embedding_quotes(content):
+def extract_embedding_quotes(content) -> Optional[Tuple[Any, Any, Any]]:
"""
``content`` may be empty
@@ -239,28 +242,28 @@ def extract_embedding_quotes(content):
if tail.startswith(EMBEDDING_QUOTE):
append_seq = EMBEDDING_QUOTE_END if tail.startswith(EMBEDDING_QUOTE_END) else EMBEDDING_QUOTE
original_string.append(append_seq)
- original_string = joined(original_string)
- content = content[len(original_string):]
- embedded_string = joined(embedded_string)
- return embedded_string, original_string, content
+ original_string_str = joined(original_string)
+ content = content[len(original_string_str):]
+ embedded_string_str = joined(embedded_string)
+ return embedded_string_str, original_string_str, content
current_char = tail[0]
original_string.append(current_char)
embedded_string.append(current_char)
tail = tail[1:]
- original_string = joined(original_string)
- pos = len(original_string)
- raise errors.PlimSyntaxError(u('Embedding quote is not closed: "{}"').format(original_string), pos)
+ original_string_str = joined(original_string)
+ pos = len(original_string_str)
+ raise errors.PlimSyntaxError(u('Embedding quote is not closed: "{}"').format(original_string_str), pos)
-def _extract_braces_expression(line, source, starting_braces_re, open_braces_re, closing_braces_re):
+def _extract_braces_expression(
+ line: str, source: SourceIter, starting_braces_re, open_braces_re, closing_braces_re
+) -> Optional[Tuple[Any, Any, Any]]:
"""
:param line: may be empty
- :type line: str
:param source:
- :type source: str
:param starting_braces_re:
:param open_braces_re:
:param closing_braces_re:
@@ -323,16 +326,16 @@ def _extract_braces_expression(line, source, starting_braces_re, open_braces_re,
)
-def extract_identifier(line, source, identifier_start='#', terminators=('.', ' ', CLOSE_BRACE, INLINE_TAG_SEPARATOR)):
+def extract_identifier(line: str, source, identifier_start: str = '#',
+ terminators: Sequence[str] = v('.', ' ', CLOSE_BRACE, INLINE_TAG_SEPARATOR)
+ ) -> Optional[Tuple[str, str, Any]]:
"""
:param line: Current line. It may be empty.
:type line: str or unicode
:param source:
- :type source: str
:param identifier_start:
:param terminators:
- :type terminators: tuple or set
"""
if not line or not line.startswith(identifier_start):
return None
@@ -353,10 +356,10 @@ def extract_identifier(line, source, identifier_start='#', terminators=('.', ' '
continue
# Check for a string object
- result = search_quotes(tail)
- if result is not None:
- buf.append(tail[:result])
- tail = tail[result:]
+ result_pos = search_quotes(tail)
+ if result_pos is not None:
+ buf.append(tail[:result_pos])
+ tail = tail[result_pos:]
continue
# Try to search braces of function calls etc
@@ -420,7 +423,7 @@ def extract_dynamic_attr_value(line, source, terminators, syntax):
return value, tail, source
-def extract_dynamic_tag_attributes(line, source, syntax, inside_parentheses=False):
+def extract_dynamic_tag_attributes(line: str, source: str, syntax, inside_parentheses=False) -> Optional[Tuple[Any, Any, Any]]:
"""
Extract one occurrence of ``**dynamic_attributes``
:param line:
@@ -463,8 +466,7 @@ def extract_dynamic_tag_attributes(line, source, syntax, inside_parentheses=Fals
return attributes, tail, source
-
-def extract_tag_attribute(line, source, syntax, inside_parentheses=False):
+def extract_tag_attribute(line: str, source: str, syntax, inside_parentheses=False):
"""
:param line:
@@ -537,7 +539,7 @@ def extract_tag_attribute(line, source, syntax, inside_parentheses=False):
return None
-def extract_line_break(tail, source):
+def extract_line_break(tail, source: SourceIter):
"""
Checks the first character of the tail.
@@ -559,7 +561,7 @@ def extract_line_break(tail, source):
return found, tail, source
-def extract_statement_expression(tail, source):
+def extract_statement_expression(tail: str, source: SourceIter) -> Tuple[str, str]:
"""
:param tail:
@@ -676,13 +678,13 @@ def extract_tag_line(line, source, syntax):
if css_id:
attributes.append(u('id="{ids}"').format(ids=css_id))
if class_identifiers:
- class_identifiers = space_separated(class_identifiers)
- attributes.append(u('class="{classes}"').format(classes=class_identifiers))
+ class_identifiers_str = space_separated(class_identifiers)
+ attributes.append(u('class="{classes}"').format(classes=class_identifiers_str))
break
- attributes = space_separated(attributes)
- components['attributes'] = attributes
- if attributes:
- tag_composer.extend([' ', attributes])
+ attributes_str = space_separated(attributes)
+ components['attributes'] = attributes_str
+ if attributes_str:
+ tag_composer.extend([' ', attributes_str])
# 3.2 syntax check
if inside_parentheses:
@@ -807,7 +809,7 @@ def parse_doctype(indent_level, current_line, ___, source, syntax):
return DOCTYPES.get(doctype, DOCTYPES['5']), indent_level, '', source
-def parse_handlebars(indent_level, current_line, ___, source, syntax):
+def parse_handlebars(indent_level: int, current_line, ___, source: SourceIter, syntax) -> Parsed:
"""
:param indent_level:
@@ -830,7 +832,7 @@ def parse_handlebars(indent_level, current_line, ___, source, syntax):
return processed_tag, tail_indent, tail_line, source
-def parse_tag_tree(indent_level, current_line, ___, source, syntax):
+def parse_tag_tree(indent_level: int, current_line: str, ___: Any, source: SourceIter, syntax) -> Parsed:
"""
:param indent_level:
@@ -962,7 +964,7 @@ def parse_python_new_style(indent_level, __, matched, source, syntax):
-def parse_mako_text(indent, __, matched, source, syntax):
+def parse_mako_text(indent, __, matched, source, syntax) -> Parsed:
"""
:param indent:
@@ -1061,7 +1063,7 @@ def parse_comment(indent_level, __, ___, source, syntax):
return '', 0, '', source
-def parse_statements(indent_level, __, matched, source, syntax):
+def parse_statements(indent_level: int, __: Any, matched, source: SourceIter, syntax) -> Parsed:
"""
:param indent_level:
@@ -1096,7 +1098,9 @@ def parse_statements(indent_level, __, matched, source, syntax):
else:
tail_indent, tail_line = scan_line(tail_line)
- def complete_statement(buf, tail_indent, tail_line, source, statement, syntax):
+ def complete_statement(
+ buf: list[str], tail_indent: int, tail_line: str, source, statement: str, syntax
+ ) -> Parsed:
buf.extend([
'\n',
syntax.STATEMENT_END_START_SEQUENCE,
@@ -1217,7 +1221,7 @@ def complete_statement(buf, tail_indent, tail_line, source, statement, syntax):
-def parse_foreign_statements(indent_level, __, matched, source, syntax):
+def parse_foreign_statements(indent_level: int, __: Any, matched, source: SourceIter, syntax):
"""
:param indent_level:
@@ -1238,7 +1242,7 @@ def parse_foreign_statements(indent_level, __, matched, source, syntax):
return parse_statements(indent_level, __, matched, source, syntax)
-def parse_explicit_literal(indent_level, current_line, ___, source, syntax, parse_embedded):
+def parse_explicit_literal(indent_level, current_line, ___, source, syntax, parse_embedded) -> Parsed:
"""
Parses lines and blocks started with the "|" (pipe) or "," (comma) character.
@@ -1255,7 +1259,7 @@ def parse_explicit_literal(indent_level, current_line, ___, source, syntax, pars
trailing_space_required = current_line[0] == LITERAL_CONTENT_SPACE_PREFIX
# ---------------------------------
- def prepare_result(buf):
+ def prepare_result(buf: Sequence[str]) -> str:
result = joined(buf).rstrip()
if trailing_space_required:
result = u("{} ").format(result)
@@ -1276,7 +1280,7 @@ def prepare_result(buf):
except StopIteration:
break
indent, line = scan_line(current_line)
- if not line:
+ if not line or indent is None:
buf.append('\n')
continue
if indent <= indent_level:
@@ -1287,8 +1291,8 @@ def prepare_result(buf):
if align > new_align:
align = new_align
# remove preceding spaces
- line = current_line[align:].rstrip()
- buf.extend([line.rstrip(), "\n"])
+ ne_line = current_line[align:].rstrip()
+ buf.extend([ne_line.rstrip(), "\n"])
result = prepare_result(buf)
return result, 0, '', source
@@ -1297,14 +1301,13 @@ def prepare_result(buf):
parse_explicit_literal_no_embedded = functools.partial(parse_explicit_literal, parse_embedded=False)
-def _parse_embedded_markup(content, syntax):
+def _parse_embedded_markup(content: str, syntax) -> str:
"""
:param content:
:param syntax: an instance of one of :class:`plim.syntax.BaseSyntax` children.
:type syntax: :class:`plim.syntax.BaseSyntax`
:return:
- :rtype: str
"""
buf = []
tail = content
@@ -1333,7 +1336,7 @@ def _parse_embedded_markup(content, syntax):
return joined(buf)
-def _inject_n_filter(line):
+def _inject_n_filter(line: str) -> str:
"""
This is a helper function for :func:parse_variable
@@ -1352,7 +1355,7 @@ def _inject_n_filter(line):
return line
-def parse_variable(indent_level, __, matched, source, syntax):
+def parse_variable(indent_level: int, __, matched, source: SourceIter, syntax) -> Parsed:
""" = variable or == variable
:param indent_level:
@@ -1375,22 +1378,22 @@ def parse_variable(indent_level, __, matched, source, syntax):
if not line:
continue
if indent <= indent_level:
- buf = joined(buf)
+ buf_str = joined(buf)
if prevent_escape:
- buf = _inject_n_filter(buf)
+ buf_str = _inject_n_filter(buf_str)
# add a closing brace to complete variable expression syntax ("${}" in case of mako).
- buf += syntax.VARIABLE_PLACEHOLDER_END_SEQUENCE + explicit_space
- return buf, indent, line, source
+ buf_str += syntax.VARIABLE_PLACEHOLDER_END_SEQUENCE + explicit_space
+ return buf_str, indent, line, source
buf.append(line.strip())
- buf = joined(buf)
+ buf_str = joined(buf)
if prevent_escape:
- buf = _inject_n_filter(buf)
- buf += syntax.VARIABLE_PLACEHOLDER_END_SEQUENCE + explicit_space
- return buf, 0, '', source
+ buf_str = _inject_n_filter(buf_str)
+ buf_str += syntax.VARIABLE_PLACEHOLDER_END_SEQUENCE + explicit_space
+ return buf_str, 0, '', source
-def parse_early_return(indent_level, __, matched, source, syntax):
+def parse_early_return(indent_level, __, matched, source, syntax) -> Parsed:
"""
:param indent_level:
@@ -1404,7 +1407,7 @@ def parse_early_return(indent_level, __, matched, source, syntax):
return u('\n<% {keyword} %>\n').format(keyword=matched.group('keyword')), indent_level, '', source
-def parse_implicit_literal(indent_level, __, matched, source, syntax):
+def parse_implicit_literal(indent_level, __, matched, source: SourceIter, syntax) -> Parsed:
"""
:param indent_level:
@@ -1424,7 +1427,7 @@ def parse_implicit_literal(indent_level, __, matched, source, syntax):
)
-def parse_raw_html(indent_level, current_line, ___, source, syntax):
+def parse_raw_html(indent_level, current_line, ___, source, syntax) -> Parsed:
"""
:param indent_level:
@@ -1458,7 +1461,7 @@ def parse_raw_html(indent_level, current_line, ___, source, syntax):
return joined(buf), 0, '', source
-def parse_mako_one_liners(indent_level, __, matched, source, syntax):
+def parse_mako_one_liners(indent_level, __, matched, source, syntax) -> Parsed:
"""
:param indent_level:
@@ -1479,7 +1482,7 @@ def parse_mako_one_liners(indent_level, __, matched, source, syntax):
return joined(buf), indent_level, '', source
-def parse_def_block(indent_level, __, matched, source, syntax):
+def parse_def_block(indent_level: int, __, matched, source: str, syntax) -> Parsed:
"""
:param indent_level:
@@ -1505,7 +1508,7 @@ def parse_def_block(indent_level, __, matched, source, syntax):
except StopIteration:
break
tail_indent, tail_line = scan_line(tail_line)
- if not tail_line:
+ if not tail_line or tail_indent is None:
continue
# Parse a tree
# --------------------------------------------------------
@@ -1523,7 +1526,7 @@ def parse_def_block(indent_level, __, matched, source, syntax):
return joined(buf), 0, '', source
-def parse_plim_tail(lineno, indent_level, tail_line, source, syntax):
+def parse_plim_tail(lineno: int, indent_level, tail_line, source: SourceIter, syntax) -> Parsed:
"""
:param lineno:
@@ -1547,7 +1550,7 @@ def parse_plim_tail(lineno, indent_level, tail_line, source, syntax):
# Miscellaneous utilities
# ==================================================================================
-def enumerate_source(source):
+def enumerate_source(source: str) -> SourceIter:
"""
:param source:
@@ -1556,17 +1559,16 @@ def enumerate_source(source):
return enumerate(StringIO(source), start=1)
-def scan_line(line):
+def scan_line(line: str) -> Tuple[Optional[int], Optional[str]]:
""" Returns a 2-tuple of (length_of_the_indentation, line_without_preceding_indentation)
-
- :param line:
- :type line: str
"""
match = LINE_PARTS_RE.match(line)
- return len(match.group('indent')), match.group('line')
+ if match:
+ return len(match.group('indent')), match.group('line')
+ return None, None
-def compile_plim_source(source, syntax, strip=True):
+def compile_plim_source(source: str, syntax: Any, strip=True) -> str:
"""
:param source:
@@ -1595,10 +1597,10 @@ def compile_plim_source(source, syntax, strip=True):
parsed_data, tail_indent, tail_line, source = parse(tail_indent, tail_line, matched_obj, source, syntax)
result.append(parsed_data)
- result = joined(result)
+ result_str = joined(result)
if strip:
- result = result.strip()
- return result
+ result_str = result_str.strip()
+ return result_str
# Acknowledgements
@@ -1606,7 +1608,7 @@ def compile_plim_source(source, syntax, strip=True):
EMPTY_TAGS = {'meta', 'img', 'link', 'input', 'area', 'base', 'col', 'br', 'hr'}
-MARKUP_LANGUAGES = {
+MARKUP_LANGUAGES: Mapping[str, Callable[[Any], str]] = {
'md': markdown2.markdown,
'markdown': markdown2.markdown,
'rst': rst_to_html,
@@ -1617,8 +1619,8 @@ def compile_plim_source(source, syntax, strip=True):
'stylus': stylus_to_css
}
-DOCTYPES = {
- 'html':'',
+DOCTYPES: Mapping[str, str] = {
+ 'html': '',
'5': '',
'1.1': '',
'strict': '',
diff --git a/plim/syntax.py b/plim/syntax.py
index 32539ab..4307f5a 100644
--- a/plim/syntax.py
+++ b/plim/syntax.py
@@ -1,4 +1,7 @@
import re
+from typing import Sequence, Any
+
+from pyrsistent import v, pvector
from . import lexer as l
@@ -51,13 +54,15 @@ class BaseSyntax(object):
PARSE_ELIF_ELSE_RE = re.compile('-\s*(?Pelif|else)(?P.*)')
PARSE_EXCEPT_ELSE_FINALLY_RE = re.compile('-\s*(?Pexcept|else|finally)(?P.*)')
- def __init__(self, custom_parsers=None):
+ def __init__(self, custom_parsers: Sequence[Any] = v()):
"""
:param custom_parsers: a list of 2-tuples of (parser_regex, parser_callable) or None
:type custom_parsers: list or None
"""
- if custom_parsers is None:
- custom_parsers = []
+ if not custom_parsers:
+ custom_parsers = v()
+ else:
+ custom_parsers = pvector(custom_parsers)
# We initialize standard parsers here rather than in a class' scope, because
# we would like to be able to discard parsers in some syntax implementations by
@@ -83,16 +88,16 @@ def __init__(self, custom_parsers=None):
(self.PARSE_EARLY_RETURN_RE, l.parse_early_return),
(self.PARSE_EXTENSION_LANGUAGES_RE, l.parse_markup_languages)
)
- custom_parsers.extend(standard_parsers)
+ custom_parsers = custom_parsers.extend(standard_parsers)
# discard parsers with None pattern
self.parsers = tuple([p for p in custom_parsers if p[0]])
- def __str__(self):
+ def __str__(self) -> str:
return 'Base Syntax'
class Mako(BaseSyntax):
- def __str__(self):
+ def __str__(self) -> str:
return 'Mako Syntax'
@@ -104,8 +109,8 @@ class Django(BaseSyntax):
STATEMENT_END_START_SEQUENCE = STATEMENT_START_START_SEQUENCE
STATEMENT_END_END_SEQUENCE = STATEMENT_START_END_SEQUENCE
- PARSE_MAKO_ONE_LINERS_RE = None
- PARSE_MAKO_TEXT_RE = None
+ PARSE_MAKO_ONE_LINERS_RE = None # type: ignore
+ PARSE_MAKO_TEXT_RE = None # type: ignore
def __str__(self):
return 'Django Syntax'
diff --git a/plim/util.py b/plim/util.py
index 0f8c845..6746da6 100644
--- a/plim/util.py
+++ b/plim/util.py
@@ -1,10 +1,21 @@
import sys
+from typing import Sequence, Iterable
PY3K = sys.version_info >= (3, 0)
from io import StringIO
-joined = lambda buf: ''.join(buf)
-space_separated = lambda buf: ' '.join(buf)
+
+def joined(buf: Iterable[str], sep: str = '') -> str:
+ """ note: `buf` iterable will be fully consumed, so if you are passing a stream make sure you tee it
+ if you need to use the `buf` again later
+ """
+ return sep.join(buf)
+
+
+def space_separated(buf: Sequence[str]) -> str:
+ return joined(buf, ' ')
+
+
u = str
MAXSIZE = sys.maxsize
diff --git a/requirements-test.txt b/requirements-test.txt
index 78fc9ce..b6c48c9 100644
--- a/requirements-test.txt
+++ b/requirements-test.txt
@@ -1,4 +1,7 @@
pytest
coverage
pytest-cov
+
mypy
+types-docutils
+types-setuptools
diff --git a/requirements.txt b/requirements.txt
index bfec571..2ac49ff 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,5 @@
Mako>=0.9.0
+pyrsistent>=0.18.1
babel>=1.3
# We use reStructuredText (docutils component) for both supporting
# the "-rest" extension and project documenting. So, ensure that the docutils
diff --git a/setup.cfg b/setup.cfg
index 27a5a42..0781b1b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,7 +5,7 @@ show_error_context = true
warn_incomplete_stub = true
ignore_missing_imports = true
check_untyped_defs = true
-cache_dir = ./local/mypy-cache
+cache_dir = ./.local/mypy-cache
warn_redundant_casts = true
warn_unused_configs = true
strict_optional = true
diff --git a/setup.py b/setup.py
index 47043cc..45b269c 100644
--- a/setup.py
+++ b/setup.py
@@ -37,7 +37,7 @@ def read(*filenames, **kwargs):
setup(
name='Plim',
- version='1.0.0',
+ version='1.1.0',
packages=find_packages(exclude=['tests', 'nixpkgs', 'node_modules']),
install_requires=requires,
setup_requires=[],