comparison env/lib/python3.9/site-packages/ruamel/yaml/util.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
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 # type: Any
81 elif isinstance(stream, binary_type):
82 # most likely, but the Reader checks BOM for this
83 yaml_str = stream.decode('utf-8')
84 else:
85 yaml_str = stream.read()
86 map_indent = None
87 indent = None # default if not found for some reason
88 block_seq_indent = None
89 prev_line_key_only = None
90 key_indent = 0
91 for line in yaml_str.splitlines():
92 rline = line.rstrip()
93 lline = rline.lstrip()
94 if lline.startswith('- '):
95 l_s = leading_spaces(line)
96 block_seq_indent = l_s - key_indent
97 idx = l_s + 1
98 while line[idx] == ' ': # this will end as we rstripped
99 idx += 1
100 if line[idx] == '#': # comment after -
101 continue
102 indent = idx - key_indent
103 break
104 if map_indent is None and prev_line_key_only is not None and rline:
105 idx = 0
106 while line[idx] in ' -':
107 idx += 1
108 if idx > prev_line_key_only:
109 map_indent = idx - prev_line_key_only
110 if rline.endswith(':'):
111 key_indent = leading_spaces(line)
112 idx = 0
113 while line[idx] == ' ': # this will end on ':'
114 idx += 1
115 prev_line_key_only = idx
116 continue
117 prev_line_key_only = None
118 if indent is None and map_indent is not None:
119 indent = map_indent
120 return round_trip_load(yaml_str, **kw), indent, block_seq_indent
121
122
123 def configobj_walker(cfg):
124 # type: (Any) -> Any
125 """
126 walks over a ConfigObj (INI file with comments) generating
127 corresponding YAML output (including comments
128 """
129 from configobj import ConfigObj # type: ignore
130
131 assert isinstance(cfg, ConfigObj)
132 for c in cfg.initial_comment:
133 if c.strip():
134 yield c
135 for s in _walk_section(cfg):
136 if s.strip():
137 yield s
138 for c in cfg.final_comment:
139 if c.strip():
140 yield c
141
142
143 def _walk_section(s, level=0):
144 # type: (Any, int) -> Any
145 from configobj import Section
146
147 assert isinstance(s, Section)
148 indent = u' ' * level
149 for name in s.scalars:
150 for c in s.comments[name]:
151 yield indent + c.strip()
152 x = s[name]
153 if u'\n' in x:
154 i = indent + u' '
155 x = u'|\n' + i + x.strip().replace(u'\n', u'\n' + i)
156 elif ':' in x:
157 x = u"'" + x.replace(u"'", u"''") + u"'"
158 line = u'{0}{1}: {2}'.format(indent, name, x)
159 c = s.inline_comments[name]
160 if c:
161 line += u' ' + c
162 yield line
163 for name in s.sections:
164 for c in s.comments[name]:
165 yield indent + c.strip()
166 line = u'{0}{1}:'.format(indent, name)
167 c = s.inline_comments[name]
168 if c:
169 line += u' ' + c
170 yield line
171 for val in _walk_section(s[name], level=level + 1):
172 yield val
173
174
175 # def config_obj_2_rt_yaml(cfg):
176 # from .comments import CommentedMap, CommentedSeq
177 # from configobj import ConfigObj
178 # assert isinstance(cfg, ConfigObj)
179 # #for c in cfg.initial_comment:
180 # # if c.strip():
181 # # pass
182 # cm = CommentedMap()
183 # for name in s.sections:
184 # cm[name] = d = CommentedMap()
185 #
186 #
187 # #for c in cfg.final_comment:
188 # # if c.strip():
189 # # yield c
190 # return cm