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=[],