diff options
author | Anthon van der Neut <anthon@mnt.org> | 2017-03-16 08:50:30 +0100 |
---|---|---|
committer | Anthon van der Neut <anthon@mnt.org> | 2017-03-16 08:50:30 +0100 |
commit | f75754aa90ea4d53f87aba5fce692185a25514c3 (patch) | |
tree | 678c37afebe1efd786d513e222a053a3cfe76a07 | |
parent | 65537ac45d2fe35a98322aea5e6056baa1aefd8a (diff) | |
download | ruamel.yaml-f75754aa90ea4d53f87aba5fce692185a25514c3.tar.gz |
some changes for mypy --strict
-rw-r--r-- | .hgignore | 1 | ||||
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | __init__.py | 8 | ||||
-rw-r--r-- | comments.py | 152 | ||||
-rw-r--r-- | compat.py | 28 | ||||
-rw-r--r-- | composer.py | 14 | ||||
-rw-r--r-- | constructor.py | 145 | ||||
-rw-r--r-- | emitter.py | 2 | ||||
-rw-r--r-- | error.py | 5 | ||||
-rw-r--r-- | main.py | 1 | ||||
-rw-r--r-- | tokens.py | 1 | ||||
-rw-r--r-- | util.py | 9 |
12 files changed, 300 insertions, 72 deletions
@@ -26,3 +26,4 @@ cmd TODO.rst _doc/_build .dcw_alt.yml +tags_for_scalarstrings @@ -24,6 +24,12 @@ ext/_yaml.c: ext/_yaml.pyx ls-l: ls -l dist/*$(VERSION)* +mypy: + cd ..; mypy --strict --no-warn-unused-ignores yaml/*.py + +mypy2: + cd ..; mypy --py2 --strict --no-strict-boolean --no-warn-unused-ignores yaml/*.py + #tstvenv: testvenv testsetup testtest # #testvenv: diff --git a/__init__.py b/__init__.py index 78f694e..64b2f5e 100644 --- a/__init__.py +++ b/__init__.py @@ -6,10 +6,12 @@ from __future__ import print_function, absolute_import, division, unicode_litera # ruamel.base installed __init__.py, and thus a new version should # be installed at some point +from typing import Dict, Any # NOQA + _package_data = dict( full_package_name='ruamel.yaml', - version_info=(0, 13, 14), - __version__='0.13.14', + version_info=(0, 13, 15), + __version__='0.13.15', author='Anthon van der Neut', author_email='a.van.der.neut@ruamel.eu', description='ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order', # NOQA @@ -49,7 +51,7 @@ _package_data = dict( read_the_docs='yaml', many_linux='libyaml-devel', supported=[(2, 7), (3, 3)], # minimum -) +) # type: Dict[Any, Any] version_info = _package_data['version_info'] diff --git a/comments.py b/comments.py index e549a33..d77d72a 100644 --- a/comments.py +++ b/comments.py @@ -8,7 +8,9 @@ these are not really related, formatting could be factored out as a separate base """ +from typing import Any, Dict, Optional, List, Union # NOQA import copy + from collections import MutableSet, Sized, Set # type: ignore from ruamel.yaml.compat import ordereddict, PY2 @@ -32,15 +34,17 @@ class Comment(object): attrib = comment_attrib def __init__(self): + # type: () -> None self.comment = None # [post, [pre]] # map key (mapping/omap/dict) or index (sequence/list) to a list of # dict: post_key, pre_key, post_value, pre_value # list: pre item, post item - self._items = {} + self._items = {} # type: Dict[Any, Any] # self._start = [] # should not put these on first item - self._end = [] # end of document comments + self._end = [] # type: List[Any] # end of document comments def __str__(self): + # type: () -> str if self._end: end = ',\n end=' + str(self._end) else: @@ -50,27 +54,33 @@ class Comment(object): @property def items(self): + # type: () -> Any return self._items @property def end(self): + # type: () -> Any return self._end @end.setter def end(self, value): + # type: (Any) -> None self._end = value @property def start(self): + # type: () -> Any return self._start @start.setter def start(self, value): + # type: (Any) -> None self._start = value # to distinguish key from None def NoComment(): + # type: () -> None pass @@ -79,15 +89,19 @@ class Format(object): attrib = format_attrib def __init__(self): - self._flow_style = None + # type: () -> None + self._flow_style = None # type: Any def set_flow_style(self): + # type: () -> None self._flow_style = True def set_block_style(self): + # type: () -> None self._flow_style = False def flow_style(self, default=None): + # type: (Optional[Any]) -> Any """if default (the flow_style) is None, the flow style tacked on to the object explicitly will be taken. If that is None as well the default flow style rules the format down the line, or the type @@ -101,35 +115,42 @@ class LineCol(object): attrib = line_col_attrib def __init__(self): + # type: () -> None self.line = None self.col = None - self.data = None + self.data = None # type: Union[None, Dict[Any, Any]] def add_kv_line_col(self, key, data): + # type: (Any, Any) -> None if self.data is None: self.data = {} self.data[key] = data def key(self, k): + # type: (Any) -> Any return self._kv(k, 0, 1) def value(self, k): + # type: (Any) -> Any return self._kv(k, 2, 3) def _kv(self, k, x0, x1): + # type: (Any, Any, Any) -> Any if self.data is None: return None data = self.data[k] return data[x0], data[x1] def item(self, idx): + # type: (Any) -> Any if self.data is None: return None return self.data[idx][0], self.data[idx][1] def add_idx_line_col(self, key, data): + # type: (Any, Any) -> None if self.data is None: - self.data = {} + self.data = {} # type: Dict[Any, Any] self.data[key] = data @@ -138,6 +159,7 @@ class Anchor(object): attrib = anchor_attrib def __init__(self): + # type: () -> None self.value = None self.always_dump = False @@ -148,17 +170,20 @@ class Tag(object): attrib = tag_attrib def __init__(self): + # type: () -> None self.value = None class CommentedBase(object): @property def ca(self): + # type: () -> Any if not hasattr(self, Comment.attrib): setattr(self, Comment.attrib, Comment()) return getattr(self, Comment.attrib) def yaml_end_comment_extend(self, comment, clear=False): + # type: (Any, bool) -> None if comment is None: return if clear or self.ca.end is None: @@ -166,6 +191,7 @@ class CommentedBase(object): self.ca.end.extend(comment) def yaml_key_comment_extend(self, key, comment, clear=False): + # type: (Any, Any, bool) -> None l = self.ca._items.setdefault(key, [None, None, None, None]) if clear or l[1] is None: if comment[1] is not None: @@ -176,6 +202,7 @@ class CommentedBase(object): l[0] = comment[0] def yaml_value_comment_extend(self, key, comment, clear=False): + # type: (Any, Any, bool) -> None l = self.ca._items.setdefault(key, [None, None, None, None]) if clear or l[3] is None: if comment[1] is not None: @@ -186,6 +213,7 @@ class CommentedBase(object): l[2] = comment[0] def yaml_set_start_comment(self, comment, indent=0): + # type: (Any, Any) -> None """overwrites any preceding comment lines on an object expects comment to be without `#` and possible have multiple lines """ @@ -200,6 +228,7 @@ class CommentedBase(object): def yaml_set_comment_before_after_key(self, key, before=None, indent=0, after=None, after_indent=None): + # type: (Any, Any, Any, Any, Any) -> None """ expects comment (before/after) to be without `#` and possible have multiple lines """ @@ -207,6 +236,7 @@ class CommentedBase(object): from ruamel.yaml.tokens import CommentToken def comment_token(s, mark): + # type: (Any) -> Any # handle empty lines as having no comment return CommentToken(('# ' if s else '') + s + '\n', mark, None) @@ -230,6 +260,7 @@ class CommentedBase(object): @property def fa(self): + # type: () -> Any """format attribute set_flow_style()/set_block_style()""" @@ -238,6 +269,7 @@ class CommentedBase(object): return getattr(self, Format.attrib) def yaml_add_eol_comment(self, comment, key=NoComment, column=None): + # type: (Any, Optional[Any], Optional[Any]) -> None """ there is a problem as eol comments should start with ' #' (but at the beginning of the line the space doesn't have to be before @@ -259,45 +291,55 @@ class CommentedBase(object): @property def lc(self): + # type: () -> Any if not hasattr(self, LineCol.attrib): setattr(self, LineCol.attrib, LineCol()) return getattr(self, LineCol.attrib) def _yaml_set_line_col(self, line, col): + # type: (Any, Any) -> None self.lc.line = line self.lc.col = col def _yaml_set_kv_line_col(self, key, data): + # type: (Any, Any) -> None self.lc.add_kv_line_col(key, data) def _yaml_set_idx_line_col(self, key, data): + # type: (Any, Any) -> None self.lc.add_idx_line_col(key, data) @property def anchor(self): + # type: () -> Any if not hasattr(self, Anchor.attrib): setattr(self, Anchor.attrib, Anchor()) return getattr(self, Anchor.attrib) def yaml_anchor(self): + # type: () -> Any if not hasattr(self, Anchor.attrib): return None return self.anchor def yaml_set_anchor(self, value, always_dump=False): + # type: (Any, bool) -> None self.anchor.value = value self.anchor.always_dump = always_dump @property def tag(self): + # type: () -> Any if not hasattr(self, Tag.attrib): setattr(self, Tag.attrib, Tag()) return getattr(self, Tag.attrib) def yaml_set_tag(self, value): + # type: (Any) -> None self.tag.value = value def copy_attributes(self, t, deep=False): + # type: (Any, bool) -> None for a in [Comment.attrib, Format.attrib, LineCol.attrib, Anchor.attrib, Tag.attrib, merge_attrib]: if hasattr(self, a): @@ -306,23 +348,35 @@ class CommentedBase(object): else: setattr(t, a, getattr(self, a)) + def _yaml_add_eol_comment(self, comment, key): + # type: (Any, Any) -> None + raise NotImplementedError + + def _yaml_get_pre_comment(self): + # type: () -> Any + raise NotImplementedError + class CommentedSeq(list, CommentedBase): __slots__ = Comment.attrib, def _yaml_add_comment(self, comment, key=NoComment): + # type: (Any, Optional[Any]) -> None if key is not NoComment: self.yaml_key_comment_extend(key, comment) else: self.ca.comment = comment def _yaml_add_eol_comment(self, comment, key): + # type: (Any, Any) -> None self._yaml_add_comment(comment, key=key) def _yaml_get_columnX(self, key): + # type: (Any) -> Any return self.ca.items[key][0].start_mark.column def insert(self, idx, val): + # type: (Any, Any) -> None """the comments after the insertion have to move forward""" list.insert(self, idx, val) for list_index in sorted(self.ca.items, reverse=True): @@ -330,7 +384,8 @@ class CommentedSeq(list, CommentedBase): break self.ca.items[list_index+1] = self.ca.items.pop(list_index) - def pop(self, idx): + def pop(self, idx=None): + # type: (Any) -> Any res = list.pop(self, idx) self.ca.items.pop(idx, None) # might not be there -> default value for list_index in sorted(self.ca.items): @@ -340,6 +395,7 @@ class CommentedSeq(list, CommentedBase): return res def _yaml_get_column(self, key): + # type: (Any) -> Any column = None sel_idx = None pre, post = key-1, key+1 @@ -360,14 +416,16 @@ class CommentedSeq(list, CommentedBase): return column def _yaml_get_pre_comment(self): + # type: () -> Any + pre_comments = [] # type: List[Any] if self.ca.comment is None: - pre_comments = [] self.ca.comment = [None, pre_comments] else: - pre_comments = self.ca.comment[1] = [] + self.ca.comment[1] = pre_comments return pre_comments def __deepcopy__(self, memo): + # type: (Any) -> Any res = CommentedSeq() memo[id(self)] = res for k in self: @@ -379,18 +437,22 @@ class CommentedSeq(list, CommentedBase): class CommentedKeySeq(tuple, CommentedBase): """This primarily exists to be able to roundtrip keys that are sequences""" def _yaml_add_comment(self, comment, key=NoComment): + # type: (Any, Optional[Any]) -> None if key is not NoComment: self.yaml_key_comment_extend(key, comment) else: self.ca.comment = comment def _yaml_add_eol_comment(self, comment, key): + # type: (Any, Any) -> None self._yaml_add_comment(comment, key=key) def _yaml_get_columnX(self, key): + # type: (Any) -> Any return self.ca.items[key][0].start_mark.column def _yaml_get_column(self, key): + # type: (Any) -> Any column = None sel_idx = None pre, post = key-1, key+1 @@ -411,11 +473,12 @@ class CommentedKeySeq(tuple, CommentedBase): return column def _yaml_get_pre_comment(self): + # type: () -> Any + pre_comments = [] # type: List[Any] if self.ca.comment is None: - pre_comments = [] self.ca.comment = [None, pre_comments] else: - pre_comments = self.ca.comment[1] = [] + self.ca.comment[1] = pre_comments return pre_comments @@ -423,11 +486,13 @@ class CommentedMapView(Sized): __slots__ = '_mapping', def __init__(self, mapping): + # type: (Any) -> None self._mapping = mapping def __len__(self): + # type: () -> int count = len(self._mapping) - done = [] # list of processed merge items, kept for masking + done = [] # type: List[Any] # list of processed merge items, kept for masking for merged in getattr(self._mapping, merge_attrib, []): for x in merged[1]: if self._mapping._unmerged_contains(x): @@ -441,6 +506,7 @@ class CommentedMapView(Sized): return count def __repr__(self): + # type: () -> str return '{0.__class__.__name__}({0._mapping!r})'.format(self) @@ -449,13 +515,15 @@ class CommentedMapKeysView(CommentedMapView, Set): @classmethod def _from_iterable(self, it): + # type: (Any) -> Any return set(it) def __contains__(self, key): + # type: (Any) -> Any return key in self._mapping def __iter__(self): - # yield from self._mapping # not in py27, pypy + # type: () -> Any # yield from self._mapping # not in py27, pypy for x in self._mapping: yield x @@ -465,9 +533,11 @@ class CommentedMapItemsView(CommentedMapView, Set): @classmethod def _from_iterable(self, it): + # type: (Any) -> Any return set(it) def __contains__(self, item): + # type: (Any) -> Any key, value = item try: v = self._mapping[key] @@ -477,6 +547,7 @@ class CommentedMapItemsView(CommentedMapView, Set): return v == value def __iter__(self): + # type: () -> Any for key in self._mapping._keys(): yield (key, self._mapping[key]) @@ -485,12 +556,14 @@ class CommentedMapValuesView(CommentedMapView): __slots__ = () def __contains__(self, value): + # type: (Any) -> Any for key in self._mapping: if value == self._mapping[key]: return True return False def __iter__(self): + # type: () -> Any for key in self._mapping: yield self._mapping[key] @@ -499,6 +572,7 @@ class CommentedMap(ordereddict, CommentedBase): __slots__ = Comment.attrib, def _yaml_add_comment(self, comment, key=NoComment, value=NoComment): + # type: (Any, Optional[Any], Optional[Any]) -> None """values is set to key to indicate a value attachment of comment""" if key is not NoComment: self.yaml_key_comment_extend(key, comment) @@ -509,13 +583,16 @@ class CommentedMap(ordereddict, CommentedBase): self.ca.comment = comment def _yaml_add_eol_comment(self, comment, key): + # type: (Any, Any) -> None """add on the value line, with value specified by the key""" self._yaml_add_comment(comment, value=key) def _yaml_get_columnX(self, key): + # type: (Any) -> Any return self.ca.items[key][2].start_mark.column def _yaml_get_column(self, key): + # type: (Any) -> Any column = None sel_idx = None pre, post, last = None, None, None @@ -543,14 +620,16 @@ class CommentedMap(ordereddict, CommentedBase): return column def _yaml_get_pre_comment(self): + # type: () -> Any + pre_comments = [] # type: List[Any] if self.ca.comment is None: - pre_comments = [] self.ca.comment = [None, pre_comments] else: - pre_comments = self.ca.comment[1] = [] + self.ca.comment[1] = pre_comments return pre_comments def update(self, vals): + # type: (Any) -> None try: ordereddict.update(self, vals) except TypeError: @@ -559,6 +638,7 @@ class CommentedMap(ordereddict, CommentedBase): self[x] = vals[x] def insert(self, pos, key, value, comment=None): + # type: (Any, Any, Any, Optional[Any]) -> None """insert key value into given position attach comment if provided """ @@ -567,12 +647,14 @@ class CommentedMap(ordereddict, CommentedBase): self.yaml_add_eol_comment(comment, key=key) def mlget(self, key, default=None, list_ok=False): + # type: (Any, Any, Any) -> Any """multi-level get that expects dicts within dicts""" if not isinstance(key, list): return self.get(key, default) # assume that the key is a list of recursively accessible dicts def get_one_level(key_list, level, d): + # type: (Any, Any, Any) -> Any if not list_ok: assert isinstance(d, dict) if level >= len(key_list): @@ -591,6 +673,7 @@ class CommentedMap(ordereddict, CommentedBase): return default def __getitem__(self, key): + # type: (Any) -> Any try: return ordereddict.__getitem__(self, key) except KeyError: @@ -600,10 +683,13 @@ class CommentedMap(ordereddict, CommentedBase): raise def _unmerged_contains(self, key): + # type: (Any) -> Any if ordereddict.__contains__(self, key): return True + return None def __contains__(self, key): + # type: (Any) -> bool if ordereddict.__contains__(self, key): return True # this will only work once the mapping/dict is built to completion @@ -613,21 +699,25 @@ class CommentedMap(ordereddict, CommentedBase): return False def get(self, key, default=None): + # type: (Any, Any) -> Any try: return self.__getitem__(key) except: return default def __repr__(self): + # type: () -> Any if not hasattr(self, merge_attrib): return ordereddict.__repr__(self) return 'ordereddict(' + repr(list(self._items())) + ')' def non_merged_items(self): + # type: () -> Any for x in ordereddict.__iter__(self): yield x, ordereddict.__getitem__(self, x) def __delitem__(self, key): + # type: (Any) -> None found = True for merged in getattr(self, merge_attrib, []): try: @@ -642,9 +732,10 @@ class CommentedMap(ordereddict, CommentedBase): raise def __iter__(self): + # type: () -> Any for x in ordereddict.__iter__(self): yield x - done = [] # list of processed merge items, kept for masking + done = [] # type: List[Any] # list of processed merge items, kept for masking for merged in getattr(self, merge_attrib, []): for x in merged[1]: if ordereddict.__contains__(self, x): @@ -657,9 +748,10 @@ class CommentedMap(ordereddict, CommentedBase): done.append(merged[1]) def _keys(self): + # type: () -> Any for x in ordereddict.__iter__(self): yield x - done = [] # list of processed merge items, kept for masking + done = [] # type: List[Any] # list of processed merge items, kept for masking for merged in getattr(self, merge_attrib, []): for x in merged[1]: if ordereddict.__contains__(self, x): @@ -673,21 +765,26 @@ class CommentedMap(ordereddict, CommentedBase): if PY2: def keys(self): + # type: () -> Any return list(self._keys()) def iterkeys(self): + # type: () -> Any return self._keys() def viewkeys(self): + # type: () -> Any return CommentedMapKeysView(self) else: def keys(self): + # type: () -> Any return CommentedMapKeysView(self) def _values(self): + # type: () -> Any for x in ordereddict.__iter__(self): yield ordereddict.__getitem__(self, x) - done = [] # list of processed merge items, kept for masking + done = [] # type: List[Any] # list of processed merge items, kept for masking for merged in getattr(self, merge_attrib, []): for x in merged[1]: if ordereddict.__contains__(self, x): @@ -701,21 +798,26 @@ class CommentedMap(ordereddict, CommentedBase): if PY2: def values(self): + # type: () -> Any return list(self._values()) def itervalues(self): + # type: () -> Any return self._values() def viewvalues(self): + # type: () -> Any return CommentedMapValuesView(self) else: def values(self): + # type: () -> Any return CommentedMapValuesView(self) def _items(self): + # type: () -> Any for x in ordereddict.__iter__(self): yield x, ordereddict.__getitem__(self, x) - done = [] # list of processed merge items, kept for masking + done = [] # type: List[Any] # list of processed merge items, kept for masking for merged in getattr(self, merge_attrib, []): for x in merged[1]: if ordereddict.__contains__(self, x): @@ -729,27 +831,34 @@ class CommentedMap(ordereddict, CommentedBase): if PY2: def items(self): + # type: () -> Any return list(self._items()) def iteritems(self): + # type: () -> Any return self._items() def viewitems(self): + # type: () -> Any return CommentedMapItemsView(self) else: def items(self): + # type: () -> Any return CommentedMapItemsView(self) @property def merge(self): + # type: () -> Any if not hasattr(self, merge_attrib): setattr(self, merge_attrib, []) return getattr(self, merge_attrib) def add_yaml_merge(self, value): + # type: (Any) -> None self.merge.extend(value) def __deepcopy__(self, memo): + # type: (Any) -> Any res = CommentedMap() memo[id(self)] = res for k in self: @@ -766,28 +875,35 @@ class CommentedSet(MutableSet, CommentedMap): __slots__ = Comment.attrib, 'odict', def __init__(self, values=None): + # type: (Optional[Any]) -> None self.odict = ordereddict() MutableSet.__init__(self) if values is not None: self |= values def add(self, value): + # type: (Any) -> None """Add an element.""" self.odict[value] = None def discard(self, value): + # type: (Any) -> None """Remove an element. Do not raise an exception if absent.""" del self.odict[value] def __contains__(self, x): + # type: (Any) -> Any return x in self.odict def __iter__(self): + # type: () -> Any for x in self.odict: yield x def __len__(self): + # type: () -> int return len(self.odict) def __repr__(self): + # type: () -> str return 'set({0!r})'.format(self.odict.keys()) @@ -8,6 +8,9 @@ import sys import os import types +from typing import Any, Dict, Optional, List, Union # NOQA + + try: from ruamel.ordereddict import ordereddict # type: ignore except: @@ -20,6 +23,7 @@ except: class ordereddict(OrderedDict): # type: ignore if not hasattr(OrderedDict, 'insert'): def insert(self, pos, key, value): + # type: (int, Any, Any) -> None if pos >= len(self): self[key] = value return @@ -39,22 +43,28 @@ PY3 = sys.version_info[0] == 3 if PY3: def utf8(s): + # type: (str) -> str return s def to_str(s): + # type: (str) -> str return s def to_unicode(s): + # type: (str) -> str return s else: def utf8(s): + # type: (unicode) -> str return s.encode('utf-8') def to_str(s): + # type: (str) -> str return str(s) def to_unicode(s): + # type: (str) -> unicode return unicode(s) if PY3: @@ -91,6 +101,7 @@ else: def with_metaclass(meta, *bases): + # type: (Any, Any) -> Any """Create a base class with a metaclass.""" return meta("NewBase", bases, {}) @@ -99,17 +110,20 @@ DBG_EVENT = 2 DBG_NODE = 4 -_debug = None +_debug = None # type: Union[None, int] if _debug: class ObjectCounter(object): def __init__(self): - self.map = {} + # type: () -> None + self.map = {} # type: Dict[Any, Any] def __call__(self, k): + # type: (Any) -> None self.map[k] = self.map.get(k, 0) + 1 def dump(self): + # type: () -> None for k in sorted(self.map): print(k, '->', self.map[k]) @@ -118,20 +132,22 @@ if _debug: # used from yaml util when testing def dbg(val=None): + # type: (Any) -> Any global _debug if _debug is None: # set to true or false - _debug = os.environ.get('YAMLDEBUG') - if _debug is None: + _debugx = os.environ.get('YAMLDEBUG') + if _debugx is None: _debug = 0 else: - _debug = int(_debug) + _debug = int(_debugx) if val is None: return _debug return _debug & val def nprint(*args, **kw): + # type: (Any, Any) -> None if dbg: print(*args, **kw) @@ -139,6 +155,7 @@ def nprint(*args, **kw): def check_namespace_char(ch): + # type: (Any) -> bool if u'\x21' <= ch <= u'\x7E': # ! to ~ return True if u'\xA0' <= ch <= u'\xD7FF': @@ -151,6 +168,7 @@ def check_namespace_char(ch): def check_anchorname_char(ch): + # type: (Any) -> bool if ch in u',[]{}': return False return check_namespace_char(ch) diff --git a/composer.py b/composer.py index 791bf50..bbaa62f 100644 --- a/composer.py +++ b/composer.py @@ -5,6 +5,8 @@ from __future__ import print_function import warnings +from typing import Any, Dict, Optional, List # NOQA + from ruamel.yaml.error import MarkedYAMLError, ReusedAnchorWarning from ruamel.yaml.compat import utf8 @@ -25,9 +27,11 @@ class ComposerError(MarkedYAMLError): class Composer(object): def __init__(self): - self.anchors = {} + # type: () -> None + self.anchors = {} # type: Dict[Any, Any] def check_node(self): + # type: () -> Any # Drop the STREAM-START event. if self.check_event(StreamStartEvent): self.get_event() @@ -36,11 +40,13 @@ class Composer(object): return not self.check_event(StreamEndEvent) def get_node(self): + # type: () -> Any # Get the root node of the next document. if not self.check_event(StreamEndEvent): return self.compose_document() def get_single_node(self): + # type: () -> Any # Drop the STREAM-START event. self.get_event() @@ -63,6 +69,7 @@ class Composer(object): return document def compose_document(self): + # type: (Any) -> Any # Drop the DOCUMENT-START event. self.get_event() @@ -76,6 +83,7 @@ class Composer(object): return node def compose_node(self, parent, index): + # type: (Any, Any) -> Any if self.check_event(AliasEvent): event = self.get_event() alias = event.anchor @@ -107,6 +115,7 @@ class Composer(object): return node def compose_scalar_node(self, anchor): + # type: (Any) -> Any event = self.get_event() tag = event.tag if tag is None or tag == u'!': @@ -119,6 +128,7 @@ class Composer(object): return node def compose_sequence_node(self, anchor): + # type: (Any) -> Any start_event = self.get_event() tag = start_event.tag if tag is None or tag == u'!': @@ -144,6 +154,7 @@ class Composer(object): return node def compose_mapping_node(self, anchor): + # type: (Any) -> Any start_event = self.get_event() tag = start_event.tag if tag is None or tag == u'!': @@ -172,6 +183,7 @@ class Composer(object): return node def check_end_doc_comment(self, end_event, node): + # type: (Any, Any) -> None if end_event.comment and end_event.comment[1]: # pre comments on an end_event, no following to move to if node.comment is None: diff --git a/constructor.py b/constructor.py index 4d3962d..d425bae 100644 --- a/constructor.py +++ b/constructor.py @@ -14,10 +14,15 @@ from typing import Any, Dict # NOQA from ruamel.yaml.error import * # NOQA from ruamel.yaml.nodes import * # NOQA -from ruamel.yaml.compat import (utf8, builtins_module, to_str, PY2, PY3, - ordereddict, text_type) +from ruamel.yaml.nodes import (SequenceNode, MappingNode, ScalarNode) +from ruamel.yaml.compat import (utf8, builtins_module, to_str, PY2, PY3, # NOQA + ordereddict, text_type, nprint) from ruamel.yaml.comments import * # NOQA +from ruamel.yaml.comments import (CommentedMap, CommentedOrderedMap, CommentedSet, + CommentedKeySeq, CommentedSeq) from ruamel.yaml.scalarstring import * # NOQA +from ruamel.yaml.scalarstring import (PreservedScalarString, SingleQuotedScalarString, + DoubleQuotedScalarString, ScalarString) from ruamel.yaml.timestamp import TimeStamp __all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor', @@ -34,6 +39,7 @@ class BaseConstructor(object): yaml_multi_constructors = {} # type: Dict[Any, Any] def __init__(self, preserve_quotes=None): + # type: (Any) -> None self.constructed_objects = {} self.recursive_objects = {} self.state_generators = [] @@ -41,15 +47,18 @@ class BaseConstructor(object): self._preserve_quotes = preserve_quotes def check_data(self): + # type: () -> Any # If there are more documents available? return self.check_node() def get_data(self): + # type: () -> Any # Construct and return the next document. if self.check_node(): return self.construct_document(self.get_node()) def get_single_data(self): + # type: () -> Any # Ensure that the stream contains a single document and construct it. node = self.get_single_node() if node is not None: @@ -57,6 +66,7 @@ class BaseConstructor(object): return None def construct_document(self, node): + # type: (Any) -> Any data = self.construct_object(node) while self.state_generators: state_generators = self.state_generators @@ -70,6 +80,7 @@ class BaseConstructor(object): return data def construct_object(self, node, deep=False): + # type: (Any, bool) -> Any """deep is True when creating an object/mapping recursively, in that case want the underlying elements available during construction """ @@ -124,6 +135,7 @@ class BaseConstructor(object): return data def construct_scalar(self, node): + # type: (Any) -> Any if not isinstance(node, ScalarNode): raise ConstructorError( None, None, @@ -132,6 +144,7 @@ class BaseConstructor(object): return node.value def construct_sequence(self, node, deep=False): + # type: (Any, bool) -> Any """deep is True when creating an object/mapping recursively, in that case want the underlying elements available during construction """ @@ -144,6 +157,7 @@ class BaseConstructor(object): for child in node.value] def construct_mapping(self, node, deep=False): + # type: (Any, bool) -> Any """deep is True when creating an object/mapping recursively, in that case want the underlying elements available during construction """ @@ -179,6 +193,7 @@ class BaseConstructor(object): return mapping def construct_pairs(self, node, deep=False): + # type: (Any, bool) -> Any if not isinstance(node, MappingNode): raise ConstructorError( None, None, @@ -208,6 +223,7 @@ class BaseConstructor(object): class SafeConstructor(BaseConstructor): def construct_scalar(self, node): + # type: (Any) -> Any if isinstance(node, MappingNode): for key_node, value_node in node.value: if key_node.tag == u'tag:yaml.org,2002:value': @@ -215,6 +231,7 @@ class SafeConstructor(BaseConstructor): return BaseConstructor.construct_scalar(self, node) def flatten_mapping(self, node): + # type: (Any) -> Any """ This implements the merge key feature http://yaml.org/type/merge.html by inserting keys from the merge dict/list of dicts if not yet @@ -258,6 +275,7 @@ class SafeConstructor(BaseConstructor): node.value = merge + node.value def construct_mapping(self, node, deep=False): + # type: (Any, bool) -> Any """deep is True when creating an object/mapping recursively, in that case want the underlying elements available during construction """ @@ -266,6 +284,7 @@ class SafeConstructor(BaseConstructor): return BaseConstructor.construct_mapping(self, node, deep=deep) def construct_yaml_null(self, node): + # type: (Any) -> Any self.construct_scalar(node) return None @@ -280,29 +299,31 @@ class SafeConstructor(BaseConstructor): } def construct_yaml_bool(self, node): + # type: (Any) -> bool value = self.construct_scalar(node) return self.bool_values[value.lower()] def construct_yaml_int(self, node): - value = to_str(self.construct_scalar(node)) - value = value.replace('_', '') + # type: (Any) -> int + value_s = to_str(self.construct_scalar(node)) + value_s = value_s.replace('_', '') sign = +1 - if value[0] == '-': + if value_s[0] == '-': sign = -1 - if value[0] in '+-': - value = value[1:] - if value == '0': + if value_s[0] in '+-': + value_s = value_s[1:] + if value_s == '0': return 0 - elif value.startswith('0b'): - return sign*int(value[2:], 2) - elif value.startswith('0x'): - return sign*int(value[2:], 16) - elif value.startswith('0o'): - return sign*int(value[2:], 8) - elif self.processing_version != (1, 2) and value[0] == '0': - return sign*int(value, 8) - elif self.processing_version != (1, 2) and ':' in value: - digits = [int(part) for part in value.split(':')] + elif value_s.startswith('0b'): + return sign*int(value_s[2:], 2) + elif value_s.startswith('0x'): + return sign*int(value_s[2:], 16) + elif value_s.startswith('0o'): + return sign*int(value_s[2:], 8) + elif self.processing_version != (1, 2) and value_s[0] == '0': + return sign*int(value_s, 8) + elif self.processing_version != (1, 2) and ':' in value_s: + digits = [int(part) for part in value_s.split(':')] digits.reverse() base = 1 value = 0 @@ -311,7 +332,7 @@ class SafeConstructor(BaseConstructor): base *= 60 return sign*value else: - return sign*int(value) + return sign*int(value_s) inf_value = 1e300 while inf_value != inf_value*inf_value: @@ -319,19 +340,20 @@ class SafeConstructor(BaseConstructor): nan_value = -inf_value/inf_value # Trying to make a quiet NaN (like C99). def construct_yaml_float(self, node): - value = to_str(self.construct_scalar(node)) - value = value.replace('_', '').lower() + # type: (Any) -> float + value_s = to_str(self.construct_scalar(node)) + value_s = value_s.replace('_', '').lower() sign = +1 - if value[0] == '-': + if value_s[0] == '-': sign = -1 - if value[0] in '+-': - value = value[1:] - if value == '.inf': + if value_s[0] in '+-': + value_s = value_s[1:] + if value_s == '.inf': return sign*self.inf_value - elif value == '.nan': + elif value_s == '.nan': return self.nan_value - elif ':' in value: - digits = [float(part) for part in value.split(':')] + elif ':' in value_s: + digits = [float(part) for part in value_s.split(':')] digits.reverse() base = 1 value = 0.0 @@ -340,10 +362,11 @@ class SafeConstructor(BaseConstructor): base *= 60 return sign*value else: - return sign*float(value) + return sign*float(value_s) if PY3: def construct_yaml_binary(self, node): + # type: (Any) -> Any try: value = self.construct_scalar(node).encode('ascii') except UnicodeEncodeError as exc: @@ -362,6 +385,7 @@ class SafeConstructor(BaseConstructor): "failed to decode base64 data: %s" % exc, node.start_mark) else: def construct_yaml_binary(self, node): + # type: (Any) -> Any value = self.construct_scalar(node) try: return to_str(value).decode('base64') @@ -383,6 +407,7 @@ class SafeConstructor(BaseConstructor): (?::(?P<tz_minute>[0-9][0-9]))?))?)?$''', re.X) def construct_yaml_timestamp(self, node, values=None): + # type: (Any, Any) -> Any if values is None: match = self.timestamp_regexp.match(node.value) values = match.groupdict() @@ -396,10 +421,10 @@ class SafeConstructor(BaseConstructor): second = int(values['second']) fraction = 0 if values['fraction']: - fraction = values['fraction'][:6] - while len(fraction) < 6: - fraction += '0' - fraction = int(fraction) + fraction_s = values['fraction'][:6] + while len(fraction_s) < 6: + fraction_s += '0' + fraction = int(fraction_s) if len(values['fraction']) > 6 and int(values['fraction'][6]) > 4: fraction += 1 delta = None @@ -416,6 +441,7 @@ class SafeConstructor(BaseConstructor): return data def construct_yaml_omap(self, node): + # type: (Any) -> Any # Note: we do now check for duplicate keys omap = ordereddict() yield omap @@ -443,6 +469,7 @@ class SafeConstructor(BaseConstructor): omap[key] = value def construct_yaml_pairs(self, node): + # type: (Any) -> Any # Note: the same code as `construct_yaml_omap`. pairs = [] yield pairs @@ -469,12 +496,14 @@ class SafeConstructor(BaseConstructor): pairs.append((key, value)) def construct_yaml_set(self, node): + # type: (Any) -> Any data = set() yield data value = self.construct_mapping(node) data.update(value) def construct_yaml_str(self, node): + # type: (Any) -> Any value = self.construct_scalar(node) if PY3: return value @@ -484,17 +513,20 @@ class SafeConstructor(BaseConstructor): return value def construct_yaml_seq(self, node): + # type: (Any) -> Any data = [] yield data data.extend(self.construct_sequence(node)) def construct_yaml_map(self, node): + # type: (Any) -> Any data = {} yield data value = self.construct_mapping(node) data.update(value) def construct_yaml_object(self, node, cls): + # type: (Any, Any) -> Any data = cls.__new__(cls) yield data if hasattr(data, '__setstate__'): @@ -505,6 +537,7 @@ class SafeConstructor(BaseConstructor): data.__dict__.update(state) def construct_undefined(self, node): + # type: (Any) -> None raise ConstructorError( None, None, "could not determine a constructor for the tag %r" % @@ -566,13 +599,16 @@ SafeConstructor.add_constructor( class Constructor(SafeConstructor): def construct_python_str(self, node): + # type: (Any) -> Any return utf8(self.construct_scalar(node)) def construct_python_unicode(self, node): + # type: (Any) -> Any return self.construct_scalar(node) if PY3: def construct_python_bytes(self, node): + # type: (Any) -> Any try: value = self.construct_scalar(node).encode('ascii') except UnicodeEncodeError as exc: @@ -591,18 +627,22 @@ class Constructor(SafeConstructor): "failed to decode base64 data: %s" % exc, node.start_mark) def construct_python_long(self, node): + # type: (Any) -> int val = self.construct_yaml_int(node) if PY3: return val return int(val) def construct_python_complex(self, node): + # type: (Any) -> Any return complex(self.construct_scalar(node)) def construct_python_tuple(self, node): + # type: (Any) -> Any return tuple(self.construct_sequence(node)) def find_python_module(self, name, mark): + # type: (Any, Any) -> Any if not name: raise ConstructorError( "while constructing a Python module", mark, @@ -616,6 +656,7 @@ class Constructor(SafeConstructor): return sys.modules[name] def find_python_name(self, name, mark): + # type: (Any, Any) -> Any if not name: raise ConstructorError( "while constructing a Python object", mark, @@ -640,6 +681,7 @@ class Constructor(SafeConstructor): return getattr(module, object_name) def construct_python_name(self, suffix, node): + # type: (Any, Any) -> Any value = self.construct_scalar(node) if value: raise ConstructorError( @@ -649,6 +691,7 @@ class Constructor(SafeConstructor): return self.find_python_name(suffix, node.start_mark) def construct_python_module(self, suffix, node): + # type: (Any, Any) -> Any value = self.construct_scalar(node) if value: raise ConstructorError( @@ -663,6 +706,7 @@ class Constructor(SafeConstructor): def make_python_instance(self, suffix, node, args=None, kwds=None, newobj=False): + # type: (Any, Any, Any, Any, bool) -> Any if not args: args = [] if not kwds: @@ -685,10 +729,11 @@ class Constructor(SafeConstructor): return cls(*args, **kwds) def set_python_instance_state(self, instance, state): + # type: (Any, Any) -> None if hasattr(instance, '__setstate__'): instance.__setstate__(state) else: - slotstate = {} + slotstate = {} # type: Dict[Any, Any] if isinstance(state, tuple) and len(state) == 2: state, slotstate = state if hasattr(instance, '__dict__'): @@ -699,6 +744,7 @@ class Constructor(SafeConstructor): setattr(object, key, value) def construct_python_object(self, suffix, node): + # type: (Any, Any) -> Any # Format: # !!python/object:module.name { ... state ... } instance = self.make_python_instance(suffix, node, newobj=True) @@ -708,6 +754,7 @@ class Constructor(SafeConstructor): self.set_python_instance_state(instance, state) def construct_python_object_apply(self, suffix, node, newobj=False): + # type: (Any, Any, bool) -> Any # Format: # !!python/object/apply # (or !!python/object/new) # args: [ ... arguments ... ] @@ -721,10 +768,10 @@ class Constructor(SafeConstructor): # is how an object is created, check make_python_instance for details. if isinstance(node, SequenceNode): args = self.construct_sequence(node, deep=True) - kwds = {} - state = {} - listitems = [] - dictitems = {} + kwds = {} # type: Dict[Any, Any] + state = {} # type: Dict[Any, Any] + listitems = [] # List[Any] + dictitems = {} # type: Dict[Any, Any] else: value = self.construct_mapping(node, deep=True) args = value.get('args', []) @@ -743,6 +790,7 @@ class Constructor(SafeConstructor): return instance def construct_python_object_new(self, suffix, node): + # type: (Any, Any) -> Any return self.construct_python_object_apply(suffix, node, newobj=True) Constructor.add_constructor( @@ -821,6 +869,7 @@ class RoundTripConstructor(SafeConstructor): """ def construct_scalar(self, node): + # type: (Any) -> Any if not isinstance(node, ScalarNode): raise ConstructorError( None, None, @@ -837,6 +886,7 @@ class RoundTripConstructor(SafeConstructor): return node.value def construct_yaml_str(self, node): + # type: (Any) -> Any value = self.construct_scalar(node) if isinstance(value, ScalarString): return value @@ -851,6 +901,7 @@ class RoundTripConstructor(SafeConstructor): return value def construct_sequence(self, node, seqtyp, deep=False): + # type: (Any, Any, bool) -> Any if not isinstance(node, SequenceNode): raise ConstructorError( None, None, @@ -874,6 +925,7 @@ class RoundTripConstructor(SafeConstructor): return ret_val def flatten_mapping(self, node): + # type: (Any) -> Any """ This implements the merge key feature http://yaml.org/type/merge.html by inserting keys from the merge dict/list of dicts if not yet @@ -935,9 +987,11 @@ class RoundTripConstructor(SafeConstructor): # node.value = merge + node.value def _sentinel(self): + # type: () -> None pass def construct_mapping(self, node, maptyp, deep=False): + # type: (Any, Any, bool) -> Any if not isinstance(node, MappingNode): raise ConstructorError( None, None, @@ -1004,6 +1058,7 @@ class RoundTripConstructor(SafeConstructor): maptyp.add_yaml_merge(merge_map) def construct_setting(self, node, typ, deep=False): + # type: (Any, Any, bool) -> Any if not isinstance(node, MappingNode): raise ConstructorError( None, None, @@ -1045,6 +1100,7 @@ class RoundTripConstructor(SafeConstructor): typ.add(key) def construct_yaml_seq(self, node): + # type: (Any) -> Any data = CommentedSeq() data._yaml_set_line_col(node.start_mark.line, node.start_mark.column) if node.flow_style is True: @@ -1057,6 +1113,7 @@ class RoundTripConstructor(SafeConstructor): data.extend(self.construct_sequence(node, data)) def construct_yaml_map(self, node): + # type: (Any) -> Any data = CommentedMap() data._yaml_set_line_col(node.start_mark.line, node.start_mark.column) if node.flow_style is True: @@ -1067,6 +1124,7 @@ class RoundTripConstructor(SafeConstructor): self.construct_mapping(node, data) def construct_yaml_omap(self, node): + # type: (Any) -> Any # Note: we do now check for duplicate keys omap = CommentedOrderedMap() omap._yaml_set_line_col(node.start_mark.line, node.start_mark.column) @@ -1109,12 +1167,14 @@ class RoundTripConstructor(SafeConstructor): omap[key] = value def construct_yaml_set(self, node): + # type: (Any) -> Any data = CommentedSet() data._yaml_set_line_col(node.start_mark.line, node.start_mark.column) yield data self.construct_setting(node, data) def construct_undefined(self, node): + # type: (Any) -> Any try: data = CommentedMap() data._yaml_set_line_col(node.start_mark.line, node.start_mark.column) @@ -1133,6 +1193,7 @@ class RoundTripConstructor(SafeConstructor): node.start_mark) def construct_yaml_timestamp(self, node): + # type: (Any) -> Any match = self.timestamp_regexp.match(node.value) values = match.groupdict() if not values['hour']: @@ -1150,10 +1211,10 @@ class RoundTripConstructor(SafeConstructor): second = int(values['second']) fraction = 0 if values['fraction']: - fraction = values['fraction'][:6] - while len(fraction) < 6: - fraction += '0' - fraction = int(fraction) + fraction_s = values['fraction'][:6] + while len(fraction_s) < 6: + fraction_s += '0' + fraction = int(fraction_s) if len(values['fraction']) > 6 and int(values['fraction'][6]) > 4: fraction += 1 delta = None @@ -1229,7 +1229,7 @@ class Emitter(object): def write_comment(self, comment): value = comment.value - # print('{:02d} {:02d} {}'.format(self.column, comment.start_mark.column, value)) + # print('{:02d} {:02d} {!r}'.format(self.column, comment.start_mark.column, value)) if value[-1] == '\n': value = value[:-1] try: @@ -4,6 +4,8 @@ from __future__ import absolute_import import warnings +from typing import Any, Dict, Optional, List # NOQA + from ruamel.yaml.compat import utf8 __all__ = ['FileMark', 'StringMark', 'CommentMark', @@ -78,6 +80,7 @@ class CommentMark(object): __slots__ = 'column', def __init__(self, column): + # type: (Any) -> None self.column = column @@ -88,6 +91,7 @@ class YAMLError(Exception): class MarkedYAMLError(YAMLError): def __init__(self, context=None, context_mark=None, problem=None, problem_mark=None, note=None): + # type: (Any, Any, Any, Any, Any) -> None self.context = context self.context_mark = context_mark self.problem = problem @@ -95,6 +99,7 @@ class MarkedYAMLError(YAMLError): self.note = note def __str__(self): + # type: () -> str lines = [] if self.context is not None: lines.append(self.context) @@ -125,6 +125,7 @@ def safe_load_all(stream, version=None): def round_trip_load(stream, version=None, preserve_quotes=None): + # type: (Any, Any, bool) -> Any """ Parse the first YAML document in a stream and produce the corresponding Python object. @@ -216,6 +216,7 @@ class CommentToken(Token): id = '<comment>' def __init__(self, value, start_mark, end_mark): + # type: (Any, Any, Any) -> None Token.__init__(self, start_mark, end_mark) self.value = value @@ -4,8 +4,9 @@ some helper functions that might be generally useful """ -from __future__ import print_function -from __future__ import absolute_import +from __future__ import absolute_import, print_function + +from typing import Any, Dict, Optional, List # NOQA from .compat import text_type, binary_type @@ -16,6 +17,7 @@ from .compat import text_type, binary_type # that check this routines output against a known piece of your YAML # before upgrades to this code break your round-tripped YAML def load_yaml_guess_indent(stream, **kw): + # type: (Any, Any) -> Any """guess the indent and block sequence indent of yaml stream/string returns round_trip_loaded stream, indent level, block sequence indent @@ -27,6 +29,7 @@ def load_yaml_guess_indent(stream, **kw): # load a yaml file guess the indentation, if you use TABs ... def leading_spaces(l): + # type: (Any) -> int idx = 0 while idx < len(l) and l[idx] == ' ': idx += 1 @@ -76,6 +79,7 @@ def load_yaml_guess_indent(stream, **kw): def configobj_walker(cfg): + # type: (Any) -> Any """ walks over a ConfigObj (INI file with comments) generating corresponding YAML output (including comments @@ -94,6 +98,7 @@ def configobj_walker(cfg): def _walk_section(s, level=0): + # type: (Any, int) -> Any from configobj import Section assert isinstance(s, Section) indent = u' ' * level |