comparison planemo/lib/python3.7/site-packages/ruamel/yaml/util.py @ 1:56ad4e20f292 draft

"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author guerler
date Fri, 31 Jul 2020 00:32:28 -0400
parents
children
comparison
equal deleted inserted replaced
0:d30785e31577 1:56ad4e20f292
1 # coding: utf-8
2
3 """
4 some helper functions that might be generally useful
5 """
6
7 from __future__ import absolute_import, print_function
8
9 from functools import partial
10 import re
11
12 from .compat import text_type, binary_type
13
14 if False: # MYPY
15 from typing import Any, Dict, Optional, List, Text # NOQA
16 from .compat import StreamTextType # NOQA
17
18
19 class LazyEval(object):
20 """
21 Lightweight wrapper around lazily evaluated func(*args, **kwargs).
22
23 func is only evaluated when any attribute of its return value is accessed.
24 Every attribute access is passed through to the wrapped value.
25 (This only excludes special cases like method-wrappers, e.g., __hash__.)
26 The sole additional attribute is the lazy_self function which holds the
27 return value (or, prior to evaluation, func and arguments), in its closure.
28 """
29
30 def __init__(self, func, *args, **kwargs):
31 # type: (Any, Any, Any) -> None
32 def lazy_self():
33 # type: () -> Any
34 return_value = func(*args, **kwargs)
35 object.__setattr__(self, 'lazy_self', lambda: return_value)
36 return return_value
37
38 object.__setattr__(self, 'lazy_self', lazy_self)
39
40 def __getattribute__(self, name):
41 # type: (Any) -> Any
42 lazy_self = object.__getattribute__(self, 'lazy_self')
43 if name == 'lazy_self':
44 return lazy_self
45 return getattr(lazy_self(), name)
46
47 def __setattr__(self, name, value):
48 # type: (Any, Any) -> None
49 setattr(self.lazy_self(), name, value)
50
51
52 RegExp = partial(LazyEval, re.compile)
53
54
55 # originally as comment
56 # https://github.com/pre-commit/pre-commit/pull/211#issuecomment-186466605
57 # if you use this in your code, I suggest adding a test in your test suite
58 # that check this routines output against a known piece of your YAML
59 # before upgrades to this code break your round-tripped YAML
60 def load_yaml_guess_indent(stream, **kw):
61 # type: (StreamTextType, Any) -> Any
62 """guess the indent and block sequence indent of yaml stream/string
63
64 returns round_trip_loaded stream, indent level, block sequence indent
65 - block sequence indent is the number of spaces before a dash relative to previous indent
66 - if there are no block sequences, indent is taken from nested mappings, block sequence
67 indent is unset (None) in that case
68 """
69 from .main import round_trip_load
70
71 # load a yaml file guess the indentation, if you use TABs ...
72 def leading_spaces(l):
73 # type: (Any) -> int
74 idx = 0
75 while idx < len(l) and l[idx] == ' ':
76 idx += 1
77 return idx
78
79 if isinstance(stream, text_type):
80 yaml_str = stream
81 elif isinstance(stream, binary_type):
82 yaml_str = stream.decode('utf-8') # most likely, but the Reader checks BOM for this
83 else:
84 yaml_str = stream.read()
85 map_indent = None
86 indent = None # default if not found for some reason
87 block_seq_indent = None
88 prev_line_key_only = None
89 key_indent = 0
90 for line in yaml_str.splitlines():
91 rline = line.rstrip()
92 lline = rline.lstrip()
93 if lline.startswith('- '):
94 l_s = leading_spaces(line)
95 block_seq_indent = l_s - key_indent
96 idx = l_s + 1
97 while line[idx] == ' ': # this will end as we rstripped
98 idx += 1
99 if line[idx] == '#': # comment after -
100 continue
101 indent = idx - key_indent
102 break
103 if map_indent is None and prev_line_key_only is not None and rline:
104 idx = 0
105 while line[idx] in ' -':
106 idx += 1
107 if idx > prev_line_key_only:
108 map_indent = idx - prev_line_key_only
109 if rline.endswith(':'):
110 key_indent = leading_spaces(line)
111 idx = 0
112 while line[idx] == ' ': # this will end on ':'
113 idx += 1
114 prev_line_key_only = idx
115 continue
116 prev_line_key_only = None
117 if indent is None and map_indent is not None:
118 indent = map_indent
119 return round_trip_load(yaml_str, **kw), indent, block_seq_indent
120
121
122 def configobj_walker(cfg):
123 # type: (Any) -> Any
124 """
125 walks over a ConfigObj (INI file with comments) generating
126 corresponding YAML output (including comments
127 """
128 from configobj import ConfigObj # type: ignore
129
130 assert isinstance(cfg, ConfigObj)
131 for c in cfg.initial_comment:
132 if c.strip():
133 yield c
134 for s in _walk_section(cfg):
135 if s.strip():
136 yield s
137 for c in cfg.final_comment:
138 if c.strip():
139 yield c
140
141
142 def _walk_section(s, level=0):
143 # type: (Any, int) -> Any
144 from configobj import Section
145
146 assert isinstance(s, Section)
147 indent = u' ' * level
148 for name in s.scalars:
149 for c in s.comments[name]:
150 yield indent + c.strip()
151 x = s[name]
152 if u'\n' in x:
153 i = indent + u' '
154 x = u'|\n' + i + x.strip().replace(u'\n', u'\n' + i)
155 elif ':' in x:
156 x = u"'" + x.replace(u"'", u"''") + u"'"
157 line = u'{0}{1}: {2}'.format(indent, name, x)
158 c = s.inline_comments[name]
159 if c:
160 line += u' ' + c
161 yield line
162 for name in s.sections:
163 for c in s.comments[name]:
164 yield indent + c.strip()
165 line = u'{0}{1}:'.format(indent, name)
166 c = s.inline_comments[name]
167 if c:
168 line += u' ' + c
169 yield line
170 for val in _walk_section(s[name], level=level + 1):
171 yield val
172
173
174 # def config_obj_2_rt_yaml(cfg):
175 # from .comments import CommentedMap, CommentedSeq
176 # from configobj import ConfigObj
177 # assert isinstance(cfg, ConfigObj)
178 # #for c in cfg.initial_comment:
179 # # if c.strip():
180 # # pass
181 # cm = CommentedMap()
182 # for name in s.sections:
183 # cm[name] = d = CommentedMap()
184 #
185 #
186 # #for c in cfg.final_comment:
187 # # if c.strip():
188 # # yield c
189 # return cm