Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/boltons/namedutils.py @ 0:d30785e31577 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
| author | guerler |
|---|---|
| date | Fri, 31 Jul 2020 00:18:57 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:d30785e31577 |
|---|---|
| 1 # -*- coding: utf-8 -*- | |
| 2 """\ | |
| 3 The ``namedutils`` module defines two lightweight container types: | |
| 4 :class:`namedtuple` and :class:`namedlist`. Both are subtypes of built-in | |
| 5 sequence types, which are very fast and efficient. They simply add | |
| 6 named attribute accessors for specific indexes within themselves. | |
| 7 | |
| 8 The :class:`namedtuple` is identical to the built-in | |
| 9 :class:`collections.namedtuple`, with a couple of enhancements, | |
| 10 including a ``__repr__`` more suitable to inheritance. | |
| 11 | |
| 12 The :class:`namedlist` is the mutable counterpart to the | |
| 13 :class:`namedtuple`, and is much faster and lighter-weight than | |
| 14 full-blown :class:`object`. Consider this if you're implementing nodes | |
| 15 in a tree, graph, or other mutable data structure. If you want an even | |
| 16 skinnier approach, you'll probably have to look to C. | |
| 17 """ | |
| 18 | |
| 19 from __future__ import print_function | |
| 20 | |
| 21 import sys as _sys | |
| 22 try: | |
| 23 from collections import OrderedDict | |
| 24 except ImportError: | |
| 25 # backwards compatibility (2.6 has no OrderedDict) | |
| 26 OrderedDict = dict | |
| 27 from keyword import iskeyword as _iskeyword | |
| 28 from operator import itemgetter as _itemgetter | |
| 29 | |
| 30 try: | |
| 31 basestring | |
| 32 def exec_(code, global_env): | |
| 33 exec("exec code in global_env") | |
| 34 except NameError: | |
| 35 basestring = (str, bytes) # Python 3 compat | |
| 36 def exec_(code, global_env): | |
| 37 exec(code, global_env) | |
| 38 | |
| 39 __all__ = ['namedlist', 'namedtuple'] | |
| 40 | |
| 41 # Tiny templates | |
| 42 | |
| 43 _repr_tmpl = '{name}=%r' | |
| 44 | |
| 45 _imm_field_tmpl = '''\ | |
| 46 {name} = _property(_itemgetter({index:d}), doc='Alias for field {index:d}') | |
| 47 ''' | |
| 48 | |
| 49 _m_field_tmpl = '''\ | |
| 50 {name} = _property(_itemgetter({index:d}), _itemsetter({index:d}), doc='Alias for field {index:d}') | |
| 51 ''' | |
| 52 | |
| 53 ################################################################# | |
| 54 ### namedtuple | |
| 55 ################################################################# | |
| 56 | |
| 57 _namedtuple_tmpl = '''\ | |
| 58 class {typename}(tuple): | |
| 59 '{typename}({arg_list})' | |
| 60 | |
| 61 __slots__ = () | |
| 62 | |
| 63 _fields = {field_names!r} | |
| 64 | |
| 65 def __new__(_cls, {arg_list}): # TODO: tweak sig to make more extensible | |
| 66 'Create new instance of {typename}({arg_list})' | |
| 67 return _tuple.__new__(_cls, ({arg_list})) | |
| 68 | |
| 69 @classmethod | |
| 70 def _make(cls, iterable, new=_tuple.__new__, len=len): | |
| 71 'Make a new {typename} object from a sequence or iterable' | |
| 72 result = new(cls, iterable) | |
| 73 if len(result) != {num_fields:d}: | |
| 74 raise TypeError('Expected {num_fields:d}' | |
| 75 ' arguments, got %d' % len(result)) | |
| 76 return result | |
| 77 | |
| 78 def __repr__(self): | |
| 79 'Return a nicely formatted representation string' | |
| 80 tmpl = self.__class__.__name__ + '({repr_fmt})' | |
| 81 return tmpl % self | |
| 82 | |
| 83 def _asdict(self): | |
| 84 'Return a new OrderedDict which maps field names to their values' | |
| 85 return OrderedDict(zip(self._fields, self)) | |
| 86 | |
| 87 def _replace(_self, **kwds): | |
| 88 'Return a new {typename} object replacing field(s) with new values' | |
| 89 result = _self._make(map(kwds.pop, {field_names!r}, _self)) | |
| 90 if kwds: | |
| 91 raise ValueError('Got unexpected field names: %r' % kwds.keys()) | |
| 92 return result | |
| 93 | |
| 94 def __getnewargs__(self): | |
| 95 'Return self as a plain tuple. Used by copy and pickle.' | |
| 96 return tuple(self) | |
| 97 | |
| 98 __dict__ = _property(_asdict) | |
| 99 | |
| 100 def __getstate__(self): | |
| 101 'Exclude the OrderedDict from pickling' # wat | |
| 102 pass | |
| 103 | |
| 104 {field_defs} | |
| 105 ''' | |
| 106 | |
| 107 def namedtuple(typename, field_names, verbose=False, rename=False): | |
| 108 """Returns a new subclass of tuple with named fields. | |
| 109 | |
| 110 >>> Point = namedtuple('Point', ['x', 'y']) | |
| 111 >>> Point.__doc__ # docstring for the new class | |
| 112 'Point(x, y)' | |
| 113 >>> p = Point(11, y=22) # instantiate with pos args or keywords | |
| 114 >>> p[0] + p[1] # indexable like a plain tuple | |
| 115 33 | |
| 116 >>> x, y = p # unpack like a regular tuple | |
| 117 >>> x, y | |
| 118 (11, 22) | |
| 119 >>> p.x + p.y # fields also accessible by name | |
| 120 33 | |
| 121 >>> d = p._asdict() # convert to a dictionary | |
| 122 >>> d['x'] | |
| 123 11 | |
| 124 >>> Point(**d) # convert from a dictionary | |
| 125 Point(x=11, y=22) | |
| 126 >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields | |
| 127 Point(x=100, y=22) | |
| 128 """ | |
| 129 | |
| 130 # Validate the field names. At the user's option, either generate an error | |
| 131 # message or automatically replace the field name with a valid name. | |
| 132 if isinstance(field_names, basestring): | |
| 133 field_names = field_names.replace(',', ' ').split() | |
| 134 field_names = [str(x) for x in field_names] | |
| 135 if rename: | |
| 136 seen = set() | |
| 137 for index, name in enumerate(field_names): | |
| 138 if (not all(c.isalnum() or c == '_' for c in name) | |
| 139 or _iskeyword(name) | |
| 140 or not name | |
| 141 or name[0].isdigit() | |
| 142 or name.startswith('_') | |
| 143 or name in seen): | |
| 144 field_names[index] = '_%d' % index | |
| 145 seen.add(name) | |
| 146 for name in [typename] + field_names: | |
| 147 if not all(c.isalnum() or c == '_' for c in name): | |
| 148 raise ValueError('Type names and field names can only contain ' | |
| 149 'alphanumeric characters and underscores: %r' | |
| 150 % name) | |
| 151 if _iskeyword(name): | |
| 152 raise ValueError('Type names and field names cannot be a ' | |
| 153 'keyword: %r' % name) | |
| 154 if name[0].isdigit(): | |
| 155 raise ValueError('Type names and field names cannot start with ' | |
| 156 'a number: %r' % name) | |
| 157 seen = set() | |
| 158 for name in field_names: | |
| 159 if name.startswith('_') and not rename: | |
| 160 raise ValueError('Field names cannot start with an underscore: ' | |
| 161 '%r' % name) | |
| 162 if name in seen: | |
| 163 raise ValueError('Encountered duplicate field name: %r' % name) | |
| 164 seen.add(name) | |
| 165 | |
| 166 # Fill-in the class template | |
| 167 fmt_kw = {'typename': typename} | |
| 168 fmt_kw['field_names'] = tuple(field_names) | |
| 169 fmt_kw['num_fields'] = len(field_names) | |
| 170 fmt_kw['arg_list'] = repr(tuple(field_names)).replace("'", "")[1:-1] | |
| 171 fmt_kw['repr_fmt'] = ', '.join(_repr_tmpl.format(name=name) | |
| 172 for name in field_names) | |
| 173 fmt_kw['field_defs'] = '\n'.join(_imm_field_tmpl.format(index=index, name=name) | |
| 174 for index, name in enumerate(field_names)) | |
| 175 class_definition = _namedtuple_tmpl.format(**fmt_kw) | |
| 176 | |
| 177 if verbose: | |
| 178 print(class_definition) | |
| 179 | |
| 180 # Execute the template string in a temporary namespace and support | |
| 181 # tracing utilities by setting a value for frame.f_globals['__name__'] | |
| 182 namespace = dict(_itemgetter=_itemgetter, | |
| 183 __name__='namedtuple_%s' % typename, | |
| 184 OrderedDict=OrderedDict, | |
| 185 _property=property, | |
| 186 _tuple=tuple) | |
| 187 try: | |
| 188 exec_(class_definition, namespace) | |
| 189 except SyntaxError as e: | |
| 190 raise SyntaxError(e.message + ':\n' + class_definition) | |
| 191 result = namespace[typename] | |
| 192 | |
| 193 # For pickling to work, the __module__ variable needs to be set to the frame | |
| 194 # where the named tuple is created. Bypass this step in environments where | |
| 195 # sys._getframe is not defined (Jython for example) or sys._getframe is not | |
| 196 # defined for arguments greater than 0 (IronPython). | |
| 197 try: | |
| 198 frame = _sys._getframe(1) | |
| 199 result.__module__ = frame.f_globals.get('__name__', '__main__') | |
| 200 except (AttributeError, ValueError): | |
| 201 pass | |
| 202 | |
| 203 return result | |
| 204 | |
| 205 | |
| 206 ################################################################# | |
| 207 ### namedlist | |
| 208 ################################################################# | |
| 209 | |
| 210 _namedlist_tmpl = '''\ | |
| 211 class {typename}(list): | |
| 212 '{typename}({arg_list})' | |
| 213 | |
| 214 __slots__ = () | |
| 215 | |
| 216 _fields = {field_names!r} | |
| 217 | |
| 218 def __new__(_cls, {arg_list}): # TODO: tweak sig to make more extensible | |
| 219 'Create new instance of {typename}({arg_list})' | |
| 220 return _list.__new__(_cls, ({arg_list})) | |
| 221 | |
| 222 def __init__(self, {arg_list}): # tuple didn't need this but list does | |
| 223 return _list.__init__(self, ({arg_list})) | |
| 224 | |
| 225 @classmethod | |
| 226 def _make(cls, iterable, new=_list, len=len): | |
| 227 'Make a new {typename} object from a sequence or iterable' | |
| 228 # why did this function exist? why not just star the | |
| 229 # iterable like below? | |
| 230 result = cls(*iterable) | |
| 231 if len(result) != {num_fields:d}: | |
| 232 raise TypeError('Expected {num_fields:d} arguments,' | |
| 233 ' got %d' % len(result)) | |
| 234 return result | |
| 235 | |
| 236 def __repr__(self): | |
| 237 'Return a nicely formatted representation string' | |
| 238 tmpl = self.__class__.__name__ + '({repr_fmt})' | |
| 239 return tmpl % tuple(self) | |
| 240 | |
| 241 def _asdict(self): | |
| 242 'Return a new OrderedDict which maps field names to their values' | |
| 243 return OrderedDict(zip(self._fields, self)) | |
| 244 | |
| 245 def _replace(_self, **kwds): | |
| 246 'Return a new {typename} object replacing field(s) with new values' | |
| 247 result = _self._make(map(kwds.pop, {field_names!r}, _self)) | |
| 248 if kwds: | |
| 249 raise ValueError('Got unexpected field names: %r' % kwds.keys()) | |
| 250 return result | |
| 251 | |
| 252 def __getnewargs__(self): | |
| 253 'Return self as a plain list. Used by copy and pickle.' | |
| 254 return tuple(self) | |
| 255 | |
| 256 __dict__ = _property(_asdict) | |
| 257 | |
| 258 def __getstate__(self): | |
| 259 'Exclude the OrderedDict from pickling' # wat | |
| 260 pass | |
| 261 | |
| 262 {field_defs} | |
| 263 ''' | |
| 264 | |
| 265 | |
| 266 def namedlist(typename, field_names, verbose=False, rename=False): | |
| 267 """Returns a new subclass of list with named fields. | |
| 268 | |
| 269 >>> Point = namedlist('Point', ['x', 'y']) | |
| 270 >>> Point.__doc__ # docstring for the new class | |
| 271 'Point(x, y)' | |
| 272 >>> p = Point(11, y=22) # instantiate with pos args or keywords | |
| 273 >>> p[0] + p[1] # indexable like a plain list | |
| 274 33 | |
| 275 >>> x, y = p # unpack like a regular list | |
| 276 >>> x, y | |
| 277 (11, 22) | |
| 278 >>> p.x + p.y # fields also accessible by name | |
| 279 33 | |
| 280 >>> d = p._asdict() # convert to a dictionary | |
| 281 >>> d['x'] | |
| 282 11 | |
| 283 >>> Point(**d) # convert from a dictionary | |
| 284 Point(x=11, y=22) | |
| 285 >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields | |
| 286 Point(x=100, y=22) | |
| 287 """ | |
| 288 | |
| 289 # Validate the field names. At the user's option, either generate an error | |
| 290 # message or automatically replace the field name with a valid name. | |
| 291 if isinstance(field_names, basestring): | |
| 292 field_names = field_names.replace(',', ' ').split() | |
| 293 field_names = [str(x) for x in field_names] | |
| 294 if rename: | |
| 295 seen = set() | |
| 296 for index, name in enumerate(field_names): | |
| 297 if (not all(c.isalnum() or c == '_' for c in name) | |
| 298 or _iskeyword(name) | |
| 299 or not name | |
| 300 or name[0].isdigit() | |
| 301 or name.startswith('_') | |
| 302 or name in seen): | |
| 303 field_names[index] = '_%d' % index | |
| 304 seen.add(name) | |
| 305 for name in [typename] + field_names: | |
| 306 if not all(c.isalnum() or c == '_' for c in name): | |
| 307 raise ValueError('Type names and field names can only contain ' | |
| 308 'alphanumeric characters and underscores: %r' | |
| 309 % name) | |
| 310 if _iskeyword(name): | |
| 311 raise ValueError('Type names and field names cannot be a ' | |
| 312 'keyword: %r' % name) | |
| 313 if name[0].isdigit(): | |
| 314 raise ValueError('Type names and field names cannot start with ' | |
| 315 'a number: %r' % name) | |
| 316 seen = set() | |
| 317 for name in field_names: | |
| 318 if name.startswith('_') and not rename: | |
| 319 raise ValueError('Field names cannot start with an underscore: ' | |
| 320 '%r' % name) | |
| 321 if name in seen: | |
| 322 raise ValueError('Encountered duplicate field name: %r' % name) | |
| 323 seen.add(name) | |
| 324 | |
| 325 # Fill-in the class template | |
| 326 fmt_kw = {'typename': typename} | |
| 327 fmt_kw['field_names'] = tuple(field_names) | |
| 328 fmt_kw['num_fields'] = len(field_names) | |
| 329 fmt_kw['arg_list'] = repr(tuple(field_names)).replace("'", "")[1:-1] | |
| 330 fmt_kw['repr_fmt'] = ', '.join(_repr_tmpl.format(name=name) | |
| 331 for name in field_names) | |
| 332 fmt_kw['field_defs'] = '\n'.join(_m_field_tmpl.format(index=index, name=name) | |
| 333 for index, name in enumerate(field_names)) | |
| 334 class_definition = _namedlist_tmpl.format(**fmt_kw) | |
| 335 | |
| 336 if verbose: | |
| 337 print(class_definition) | |
| 338 | |
| 339 def _itemsetter(key): | |
| 340 def _itemsetter(obj, value): | |
| 341 obj[key] = value | |
| 342 return _itemsetter | |
| 343 | |
| 344 # Execute the template string in a temporary namespace and support | |
| 345 # tracing utilities by setting a value for frame.f_globals['__name__'] | |
| 346 namespace = dict(_itemgetter=_itemgetter, | |
| 347 _itemsetter=_itemsetter, | |
| 348 __name__='namedlist_%s' % typename, | |
| 349 OrderedDict=OrderedDict, | |
| 350 _property=property, | |
| 351 _list=list) | |
| 352 try: | |
| 353 exec_(class_definition, namespace) | |
| 354 except SyntaxError as e: | |
| 355 raise SyntaxError(e.message + ':\n' + class_definition) | |
| 356 result = namespace[typename] | |
| 357 | |
| 358 # For pickling to work, the __module__ variable needs to be set to | |
| 359 # the frame where the named list is created. Bypass this step in | |
| 360 # environments where sys._getframe is not defined (Jython for | |
| 361 # example) or sys._getframe is not defined for arguments greater | |
| 362 # than 0 (IronPython). | |
| 363 try: | |
| 364 frame = _sys._getframe(1) | |
| 365 result.__module__ = frame.f_globals.get('__name__', '__main__') | |
| 366 except (AttributeError, ValueError): | |
| 367 pass | |
| 368 | |
| 369 return result |
