def namedtuple(typename, field_names, verbose=False, rename=False): if isinstance(field_names, str): field_names = field_names.replace(',', ' ').split() field_names = list(map(str, field_names)) if rename: seen = set() for (index, name) in enumerate(field_names): if not name.isidentifier() or (_iskeyword(name) or name.startswith('_')) or name in seen: field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: if not name.isidentifier(): raise ValueError('Type names and field names must be valid identifiers: %r' % name) while _iskeyword(name): raise ValueError('Type names and field names cannot be a keyword: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: %r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) class_definition = _class_template.format(typename=typename, field_names=tuple(field_names), num_fields=len(field_names), arg_list=repr(tuple(field_names)).replace("'", '')[1:-1], repr_fmt=', '.join(_repr_template.format(name=name) for name in field_names), field_defs='\n'.join(_field_template.format(index=index, name=name) for (index, name) in enumerate(field_names))) namespace = dict(__name__='namedtuple_%s' % typename) exec(class_definition, namespace) result = namespace[typename] result._source = class_definition if verbose: print(result._source) try: result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass return result
def namedtuple(typename, field_names, verbose = False, rename = False): if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() field_names = tuple(map(str, field_names)) if rename: names = list(field_names) seen = set() for i, name in enumerate(names): if not all((c.isalnum() or c == '_' for c in name)) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen: names[i] = '_%d' % i seen.add(name) field_names = tuple(names) for name in (typename,) + field_names: if not all((c.isalnum() or c == '_' for c in name)): raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with a number: %r' % name) seen_names = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: %r' % name) if name in seen_names: raise ValueError('Encountered duplicate field name: %r' % name) seen_names.add(name) numfields = len(field_names) argtxt = repr(field_names).replace("'", '')[1:-1] reprtxt = ', '.join(('%s=%%r' % name for name in field_names)) template = "class %(typename)s(tuple):\n '%(typename)s(%(argtxt)s)' \n\n __slots__ = () \n\n _fields = %(field_names)r \n\n def __new__(_cls, %(argtxt)s):\n 'Create new instance of %(typename)s(%(argtxt)s)'\n return _tuple.__new__(_cls, (%(argtxt)s)) \n\n @classmethod\n def _make(cls, iterable, new=tuple.__new__, len=len):\n 'Make a new %(typename)s object from a sequence or iterable'\n result = new(cls, iterable)\n if len(result) != %(numfields)d:\n raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result))\n return result \n\n def __repr__(self):\n 'Return a nicely formatted representation string'\n return '%(typename)s(%(reprtxt)s)' %% self \n\n def _asdict(self):\n 'Return a new OrderedDict which maps field names to their values'\n return OrderedDict(zip(self._fields, self)) \n\n def _replace(_self, **kwds):\n 'Return a new %(typename)s object replacing specified fields with new values'\n result = _self._make(map(kwds.pop, %(field_names)r, _self))\n if kwds:\n raise ValueError('Got unexpected field names: %%r' %% kwds.keys())\n return result \n\n def __getnewargs__(self):\n 'Return self as a plain tuple. Used by copy and pickle.'\n return tuple(self) \n\n" % locals() for i, name in enumerate(field_names): template += " %s = _property(_itemgetter(%d), doc='Alias for field number %d')\n" % (name, i, i) if verbose: print template namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, OrderedDict=OrderedDict, _property=property, _tuple=tuple) try: exec template in namespace except SyntaxError as e: raise SyntaxError(e.message + ':\n' + template) result = namespace[typename] try: result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass return result
def namedtuple(typename, field_names, *, verbose=False, rename=False, module=None): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessible by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Validate the field names. At the user's option, either generate an error # message or automatically replace the field name with a valid name. if isinstance(field_names, str): field_names = field_names.replace(',', ' ').split() field_names = list(map(str, field_names)) typename = str(typename) if rename: seen = set() for index, name in enumerate(field_names): if (not name.isidentifier() or _iskeyword(name) or name.startswith('_') or name in seen): field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: if type(name) is not str: raise TypeError('Type names and field names must be strings') if not name.isidentifier(): raise ValueError('Type names and field names must be valid ' 'identifiers: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' 'keyword: %r' % name)
def namedtuple(typename, field_names, verbose=False): # Parse and validate the field names. Validation serves two purposes, # generating informative error messages and preventing template injection attacks. if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas field_names = tuple(field_names) for name in (typename,) + field_names: if not all(c.isalnum() or c=='_' for c in name): raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with a number: %r' % name) seen_names = set() for name in field_names: if name.startswith('_'): raise ValueError('Field names cannot start with an underscore: %r' % name) if name in seen_names: raise ValueError('Encountered duplicate field name: %r' % name) seen_names.add(name) # Create and fill-in the class template numfields = len(field_names) argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes reprtxt = ', '.join('%s=%%r' % name for name in field_names) dicttxt = ', '.join('%r: t[%d]' % (name, pos) for pos, name in enumerate(field_names)) template = '''class %(typename)s(tuple): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n def __new__(cls, %(argtxt)s): return tuple.__new__(cls, (%(argtxt)s)) \n @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): 'Make a new %(typename)s object from a sequence or iterable' result = new(cls, iterable) if len(result) != %(numfields)d: raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) return result \n def __repr__(self): return '%(typename)s(%(reprtxt)s)' %% self \n def _asdict(t): 'Return a new dict which maps field names to their values' return {%(dicttxt)s} \n def _replace(self, **kwds): 'Return a new %(typename)s object replacing specified fields with new values' result = self._make(map(kwds.pop, %(field_names)r, self)) if kwds: raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) return result \n\n''' % locals() for i, name in enumerate(field_names): template += ' %s = property(itemgetter(%d))\n' % (name, i) if verbose: print template # Execute the template string in a temporary namespace namespace = dict(itemgetter=_itemgetter) try: exec template in namespace except SyntaxError, e: raise SyntaxError(e.message + ':\n' + template)
def getPymelEnums(self, enumDict): """remove all common prefixes from list of enum values""" if len(enumDict) > 1: enumList = enumDict.keys() splitEnums = [ [ y for y in re.split( '([A-Z0-9][a-z0-9]*)', x ) if y ] for x in enumList ] splitEnumsCopy = splitEnums[:] for partList in zip( *splitEnumsCopy ): if tuple([partList[0]]*len(partList)) == partList: [ x.pop(0) for x in splitEnums ] else: break joinedEnums = [ util.uncapitalize(''.join(x), preserveAcronymns=True ) for x in splitEnums] for i, enum in enumerate(joinedEnums): if _iskeyword(enum): joinedEnums[i] = enum+'_' self.xprint( "bad enum", enum ) elif enum[0].isdigit(): joinedEnums[i] = 'k' + enum self.xprint( "bad enum", enum ) #print joinedEnums #print enumList #break pymelEnumDict = dict( [ (k2,enumDict[k1]) for k1, k2 in zip( enumList, joinedEnums ) ] ) #print "enums", joinedEnums return pymelEnumDict return enumDict
def create_struct_class(field_names): # type: (Iterable[str]) -> Type field_names = tuple(field_names) struct_class = _CLASS_CACHE.get(field_names) if struct_class: return struct_class # Variables used in the methods and docstrings for name in field_names: if not all(c.isalnum() or c == "_" for c in name): raise ValueError( "Field names can only contain alphanumeric characters and underscores: %r" % name ) if _iskeyword(name): raise ValueError("Field names cannot be a keyword: %r" % name) if name[0].isdigit(): raise ValueError("Field names cannot start with a number: %r" % name) arg_list = repr(field_names).replace("'", "")[1:-1] tuple_new = tuple.__new__ # Create all the named tuple methods to be added to the class namespace s = ( "def __new__(_cls, " + arg_list + "): return _tuple_new(_cls, (" + arg_list + "))" ) namespace = {"_tuple_new": tuple_new, "__name__": _TYPENAME} # Note: exec() has the side-effect of interning the field names exec(s, namespace) __new__ = namespace["__new__"] # Build-up the class namespace dictionary # and use type() to build the result class class_namespace = { "__slots__": (), "_fields": field_names, "__new__": __new__, "__repr__": _create_struct_repr(field_names), "_asdict": _asdict, "to_json": _to_json, } cache = _nt_itemgetters for index, name in enumerate(field_names): try: itemgetter_object = cache[index] except KeyError: itemgetter_object = _itemgetter(index) cache[index] = itemgetter_object class_namespace[name] = property(itemgetter_object) struct_class = type(_TYPENAME, (tuple,), class_namespace) _CLASS_CACHE[field_names] = struct_class return struct_class
def _validate_fields(field_names: list[str]) -> list[str]: """ Validate the given field names Args: field_names: a list of strings to be used as attributes. Example ======= .. code:: # Numbers are not valid identifiers # an object cannot have non-unique attributes >>> _validate_fields(["0", "field", "field"]) ['_0', 'field', '_2'] """ names = list(map(str, list(field_names))) seen = set() for i, name in enumerate(names): if (not all(c.isalnum() or c == "_" for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith("_") or name in seen): names[i] = "field%d" % i seen.add(name) return names
def namedtuple(typename, field_names, verbose=False): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', 'x y') >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessable by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Parse and validate the field names. Validation serves two purposes, # generating informative error messages and preventing template injection attacks. if isinstance(field_names, basestring): # names separated by whitespace and/or commas field_names = field_names.replace(',', ' ').split() field_names = list(field_names) seen_names = set() for name in [typename] + field_names: if not all(c.isalnum() or c=='_' for c in name): raise ValueError('Names can only contain alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Encountered duplicate field name: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with a number: %r' % name) seen_names.add(name) name_to_field = dict((v, idx) for idx, v in (enumerate(field_names))) attributes = {'_field_names' : name_to_field, '_fields' : tuple(field_names), # immutable and we need it often '_field_len' : len(field_names), '__slots__' : (), '__doc__': "%s(%s)" % (typename, ', '.join(field_names))} # create properties for attr, idx in name_to_field.iteritems(): attributes[attr] = property(_itemgetter(idx)) if verbose: print attributes # create the new class, deriving from _NamedTuple result = type(typename, (_NamedTuple,), attributes) # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in enviroments where # sys._getframe is not defined (Jython for example). if hasattr(_sys, '_getframe'): result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') return result
def _auto_positive_symbol(tokens, local_dict, global_dict): """ Inserts calls to ``Symbol`` for undefined variables. Passes in positive=True as a keyword argument. Adapted from sympy.sympy.parsing.sympy_parser.auto_symbol """ result = [] prevTok = (None, None) tokens.append((None, None)) # so zip traverses all tokens for tok, nextTok in zip(tokens, tokens[1:]): tokNum, tokVal = tok nextTokNum, nextTokVal = nextTok if tokNum == token.NAME: name = tokVal if (name in ["True", "False", "None"] # special case 'as' for attosecond or (_iskeyword(name) and name != "as") or name in local_dict # Don't convert attribute access or (prevTok[0] == token.OP and prevTok[1] == ".") # Don't convert keyword arguments or (prevTok[0] == token.OP and prevTok[1] in ("(", ",") and nextTokNum == token.OP and nextTokVal == "=")): result.append((token.NAME, name)) continue elif name in global_dict: obj = global_dict[name] if isinstance(obj, (Basic, type)) or callable(obj): result.append((token.NAME, name)) continue # try to resolve known alternative unit name try: used_name = inv_name_alternatives[str(name)] except KeyError: # if we don't know this name it's a user-defined unit name # so we should create a new symbol for it used_name = str(name) result.extend([ (token.NAME, "Symbol"), (token.OP, "("), (token.NAME, repr(used_name)), (token.OP, ","), (token.NAME, "positive"), (token.OP, "="), (token.NAME, "True"), (token.OP, ")"), ]) else: result.append((tokNum, tokVal)) prevTok = (tokNum, tokVal) return result
def is_qualifiable(symbol): """Determines if symbol can be qualified with a module. Can't be ``quote``, ``__import__``, any Python reserved word, an auto-gensym, already qualified, method syntax, or a module literal; and must be a valid identifier or attribute identifier. """ return (symbol not in {"quote", "__import__"} and not _iskeyword(symbol) and not re.match(r".*_QzNo\d+_$", symbol) and all(map(str.isidentifier, symbol.split("."))))
def check_name(name): if not isinstance(name, str): raise TypeError('Type names and field names must be strings') if not _isidentifier(name): raise ValueError('Type names and field names must be valid ' 'identifiers: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' 'keyword: %r' % name) return name
def namedtuple(typename, field_names, verbose=False, rename=False): if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() field_names = map(str, field_names) if rename: seen = set() for index, name in enumerate(field_names): if not all((c.isalnum() or c == '_' for c in name)) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen: field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: if not all((c.isalnum() or c == '_' for c in name)): raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with a number: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: %r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) class_definition = _class_template.format(typename=typename, field_names=tuple(field_names), num_fields=len(field_names), arg_list=repr(tuple(field_names)).replace("'", '')[1:-1], repr_fmt=(', ').join((_repr_template.format(name=name) for name in field_names)), field_defs=('\n').join((_field_template.format(index=index, name=name) for index, name in enumerate(field_names)))) if verbose: print class_definition namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, OrderedDict=OrderedDict, _property=property, _tuple=tuple) try: exec class_definition in namespace except SyntaxError as e: raise SyntaxError(e.message + ':\n' + class_definition) result = namespace[typename] try: result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass return result
def create_struct_class(field_names): # type: (Iterable[str]) -> Type field_names = tuple(field_names) struct_class = _CLASS_CACHE.get(field_names) if struct_class: return struct_class # Variables used in the methods and docstrings for name in field_names: if not all(c.isalnum() or c == "_" for c in name): raise ValueError( "Field names can only contain alphanumeric characters and underscores: %r" % name) if _iskeyword(name): raise ValueError("Field names cannot be a keyword: %r" % name) if name[0].isdigit(): raise ValueError("Field names cannot start with a number: %r" % name) arg_list = repr(field_names).replace("'", "")[1:-1] tuple_new = tuple.__new__ # Create all the named tuple methods to be added to the class namespace s = ("def __new__(_cls, " + arg_list + "): return _tuple_new(_cls, (" + arg_list + "))") namespace = {"_tuple_new": tuple_new, "__name__": _TYPENAME} # Note: exec() has the side-effect of interning the field names exec(s, namespace) __new__ = namespace["__new__"] # Build-up the class namespace dictionary # and use type() to build the result class class_namespace = { "__slots__": (), "_fields": field_names, "__new__": __new__, "__repr__": _create_struct_repr(field_names), "_asdict": _asdict, "to_json": _to_json, } cache = _nt_itemgetters for index, name in enumerate(field_names): try: itemgetter_object = cache[index] except KeyError: itemgetter_object = _itemgetter(index) cache[index] = itemgetter_object class_namespace[name] = property(itemgetter_object) struct_class = type(_TYPENAME, (tuple, ), class_namespace) _CLASS_CACHE[field_names] = struct_class return struct_class
def _validate_name(n): if type(n) != str: raise TypeError("struct/field name is not a str: {!r}".format(n)) if not n.isidentifier(): raise _ValErrF("struct/field name is not a valid identifier: {!r}", n) if _iskeyword(n): raise ValErrF("struct/field name cannot be a keyword: {!r}", n) if n.startswith('__'): raise ValErrF("struct/field name cannot begin with '__': {!r}", n) if n in _reserved_names: raise ValErrF("struct/field name is reserved: {!r}", n)
def getPymelEnums(self, enumDict): """remove all common prefixes from list of enum values""" if len(enumDict) > 1: enumList = enumDict.keys() capitalizedRe = re.compile('([A-Z0-9][a-z0-9]*)') # We first aim to remove all similar 'camel-case-group' prefixes, ie: # if our enums look like: # kFooBar # kFooSomeThing # kFooBunnies # we want to get Bar, SomeThing, Bunnies # {'kFooBar':0, 'kFooSomeThing':1} # => [['k', 'Foo', 'Some', 'Thing'], ['k', 'Foo', 'Bar']] splitEnums = [[y for y in capitalizedRe.split(x) if y] for x in enumList] # [['k', 'Invalid'], ['k', 'Pre', 'Transform']] # => [('k', 'k'), ('Foo', 'Foo'), ('Some', 'Bar')] splitZip = zip(*splitEnums) for partList in splitZip: if tuple([partList[0]] * len(partList)) == partList: [x.pop(0) for x in splitEnums] else: break # splitEnums == [['Some', 'Thing'], ['Bar']] joinedEnums = [ util.uncapitalize(''.join(x), preserveAcronymns=True) for x in splitEnums ] for i, enum in enumerate(joinedEnums): if _iskeyword(enum): joinedEnums[i] = enum + '_' self.xprint("bad enum", enum) elif enum[0].isdigit(): joinedEnums[i] = 'k' + enum self.xprint("bad enum", enum) #print joinedEnums #print enumList #break pymelEnumDict = dict((new, enumDict[orig]) for orig, new in zip(enumList, joinedEnums)) #print "enums", joinedEnums return pymelEnumDict return enumDict
def _generate(name): """ Generate a new variable in the caller's locals. Test if the variable name is valid. """ _locals = _stack()[2][0].f_locals if _iskeyword(name): raise ValueError("cannot name variable '{}' because it is a Python keyword".format(name)) if name in _builtins: raise ValueError("cannot name variable '{}' because it is a Python builtin".format(name)) if name in _locals: raise ValueError("cannot name variable '{}' because that name is already in use".format(name)) if not name.isidentifier(): raise ValueError("cannot name variable '{}' because it is an invalid Python variable name".format(name)) _locals[name] = _df[name]
def _check_name(name): err_id = 'Type names and field names' if not isinstance(name, _six.string_types): raise ValueError('{} must be a string (type: {!r}): ' '{!r}'.format(err_id, type(name), name)) if not name: raise ValueError('{} cannot be empty.'.format(err_id)) if not all(c.isalnum() or c=='_' for c in name): raise ValueError('{} can only contain alphanumerics and underscores: ' '{!r}'.format(err_id, name)) if _iskeyword(name): raise ValueError('{} cannot be a keyword: {!r}'.format(err_id, name)) if name[0].isdigit(): raise ValueError('{} cannot start with a number: ' '{!r}'.format(err_id, name))
def _auto_positive_symbol(tokens, local_dict, global_dict): """ Inserts calls to ``Symbol`` for undefined variables. Passes in positive=True as a keyword argument. Adapted from sympy.sympy.parsing.sympy_parser.auto_symbol """ result = [] prevTok = (None, None) tokens.append((None, None)) # so zip traverses all tokens for tok, nextTok in zip(tokens, tokens[1:]): tokNum, tokVal = tok nextTokNum, nextTokVal = nextTok if tokNum == token.NAME: name = tokVal if (name in ['True', 'False', 'None'] or _iskeyword(name) or name in local_dict # Don't convert attribute access or (prevTok[0] == token.OP and prevTok[1] == '.') # Don't convert keyword arguments or (prevTok[0] == token.OP and prevTok[1] in ('(', ',') and nextTokNum == token.OP and nextTokVal == '=')): result.append((token.NAME, name)) continue elif name in global_dict: obj = global_dict[name] if isinstance(obj, (Basic, type)) or callable(obj): result.append((token.NAME, name)) continue result.extend([ (token.NAME, 'Symbol'), (token.OP, '('), (token.NAME, repr(str(name))), (token.OP, ','), (token.NAME, 'positive'), (token.OP, '='), (token.NAME, 'True'), (token.OP, ')'), ]) else: result.append((tokNum, tokVal)) prevTok = (tokNum, tokVal) return result
def getPymelEnums(self, enumDict): """remove all common prefixes from list of enum values""" if len(enumDict) > 1: enumList = enumDict.keys() capitalizedRe = re.compile('([A-Z0-9][a-z0-9]*)') # We first aim to remove all similar 'camel-case-group' prefixes, ie: # if our enums look like: # kFooBar # kFooSomeThing # kFooBunnies # we want to get Bar, SomeThing, Bunnies # {'kFooBar':0, 'kFooSomeThing':1} # => [['k', 'Foo', 'Some', 'Thing'], ['k', 'Foo', 'Bar']] splitEnums = [ [ y for y in capitalizedRe.split( x ) if y ] for x in enumList ] # [['k', 'Invalid'], ['k', 'Pre', 'Transform']] # => [('k', 'k'), ('Foo', 'Foo'), ('Some', 'Bar')] splitZip = zip( *splitEnums ) for partList in splitZip: if tuple([partList[0]]*len(partList)) == partList: [ x.pop(0) for x in splitEnums ] else: break # splitEnums == [['Some', 'Thing'], ['Bar']] joinedEnums = [ util.uncapitalize(''.join(x), preserveAcronymns=True ) for x in splitEnums] for i, enum in enumerate(joinedEnums): if _iskeyword(enum): joinedEnums[i] = enum+'_' self.xprint( "bad enum", enum ) elif enum[0].isdigit(): joinedEnums[i] = 'k' + enum self.xprint( "bad enum", enum ) #print joinedEnums #print enumList #break pymelEnumDict = dict( (new,enumDict[orig]) for orig, new in zip( enumList, joinedEnums ) ) #print "enums", joinedEnums return pymelEnumDict return enumDict
def nameddict(typename, field_names): """ """ if isinstance(field_names, str): field_names = field_names.replace(',', ' ').split() field_names = list(map(str, field_names)) for name in [typename] + field_names: if not name.isidentifier(): raise ValueError('Type names and field names must be valid ' 'identifiers: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' 'keyword: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: ' '%r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) # Fill-in the class template class_definition = _nameddict_class_tmpl.format( typename=typename, field_names=tuple(field_names), arg_list=','.join(field_names), ) namespace = dict(__name__='nameddict_%s' % typename, _OrderedDict=_OrderedDict, NamedDict=NamedDict) exec(class_definition, namespace) result = namespace[typename] try: result.__module__ = _sys._getframe(1).f_globals.get( '__name__', '__main__') except (AttributeError, ValueError): pass return result
def nameddict(typename, field_names): """ """ if isinstance(field_names, str): field_names = field_names.replace(',', ' ').split() field_names = list(map(str, field_names)) for name in [typename] + field_names: if not name.isidentifier(): raise ValueError('Type names and field names must be valid ' 'identifiers: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' 'keyword: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: ' '%r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) # Fill-in the class template class_definition = _nameddict_class_tmpl.format( typename = typename, field_names = tuple(field_names), arg_list = ','.join(field_names), ) namespace = dict(__name__='nameddict_%s' % typename, _OrderedDict=_OrderedDict, NamedDict=NamedDict) exec(class_definition, namespace) result = namespace[typename] try: result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass return result
def _apiEnumNamesToPymelEnumNames(self, apiEnumNames): """remove all common prefixes from list of enum values""" if isinstance(apiEnumNames, util.Enum): apiEnumNames = apiEnumNames._keys.keys() if len(apiEnumNames) > 1: # We first aim to remove all similar 'camel-case-group' prefixes, ie: # if our enums look like: # kFooBar # kFooSomeThing # kFooBunnies # we want to get Bar, SomeThing, Bunnies # {'kFooBar':0, 'kFooSomeThing':1} # => [['k', 'Foo', 'Some', 'Thing'], ['k', 'Foo', 'Bar']] splitEnums = [ [ y for y in self._capitalizedRe.split( x ) if y ] for x in apiEnumNames ] # [['k', 'Invalid'], ['k', 'Pre', 'Transform']] # => [('k', 'k'), ('Foo', 'Foo'), ('Some', 'Bar')] splitZip = zip( *splitEnums ) for partList in splitZip: if tuple([partList[0]]*len(partList)) == partList: [ x.pop(0) for x in splitEnums ] else: break # splitEnums == [['Some', 'Thing'], ['Bar']] joinedEnums = [ util.uncapitalize(''.join(x), preserveAcronymns=True ) for x in splitEnums] for i, enum in enumerate(joinedEnums): if _iskeyword(enum): joinedEnums[i] = enum+'_' self.xprint( "bad enum", enum ) elif enum[0].isdigit(): joinedEnums[i] = 'k' + enum self.xprint( "bad enum", enum ) #print joinedEnums #print enumList #break return dict(zip(apiEnumNames, joinedEnums)) else: # if only 1 name or less, name is unaltered return dict((name, name) for name in apiEnumNames)
def check_fields(typename, fields): if isinstance(fields, str): fields = fields.split() elif isinstance(fields, tuple): fields = list(fields) if isinstance(fields, list): fields = [fn.strip() for fn in fields] n_fields = len(fields) for name in [typename] + fields: if not isinstance(name, str): raise TypeError('Type names and field names must be strings') if not _isidentifier(name): raise ValueError('Type names and field names must be valid ' 'identifiers: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' 'keyword: %r' % name) return fields
def _check_common(self, name, type_of_name): # tests that are common to both field names and the type name if len(name) == 0: raise ValueError('{0} names cannot be zero ' 'length: {1!r}'.format(type_of_name, name)) if _PY2: if not all(c.isalnum() or c=='_' for c in name): raise ValueError('{0} names can only contain ' 'alphanumeric characters and underscores: ' '{1!r}'.format(type_of_name, name)) if name[0].isdigit(): raise ValueError('{0} names cannot start with a ' 'number: {1!r}'.format(type_of_name, name)) else: if not name.isidentifier(): raise ValueError('{0} names names must be valid ' 'identifiers: {1!r}'.format(type_of_name, name)) if _iskeyword(name): raise ValueError('{0} names cannot be a keyword: ' '{1!r}'.format(type_of_name, name))
def _check_name(name): """Check type or field name is valid. Returns an error message if name is not valid, otherwise the empty string. """ if not name: err = "names must not be empty" elif name[0].isdigit(): err = "name '%s' cannot start with a digit" % name elif name.startswith('_'): err = "name '%s' cannot start with an underscore" % name elif not all(c.isalnum() or c == '_' for c in name): err = ("name '%s' must only contain alphanumeric characters" " and underscores" % name) elif _iskeyword(name): err = "name '%s' is a reserved keyword" % name else: err = "" return err
def _check_name(name, is_type_name=False, seen_names=None): if len(name) == 0: raise ValueError("Type names and field names cannot be zero " "length: {0!r}".format(name)) if not all(c.isalnum() or c == "_" for c in name): raise ValueError( "Type names and field names can only contain " "alphanumeric characters and underscores: " "{0!r}".format(name) ) if _iskeyword(name): raise ValueError("Type names and field names cannot be a keyword: " "{0!r}".format(name)) if name[0].isdigit(): raise ValueError("Type names and field names cannot start with a " "number: {0!r}".format(name)) if not is_type_name: # these tests don't apply for the typename, just the fieldnames if name in seen_names: raise ValueError("Encountered duplicate field name: " "{0!r}".format(name)) if name.startswith("_"): raise ValueError("Field names cannot start with an underscore: " "{0!r}".format(name))
def _validate_names(typename, field_names, extra_field_names): """ Ensure that all the given names are valid Python identifiers that do not start with '_'. Also check that there are no duplicates among field_names + extra_field_names. """ for name in [typename] + field_names + extra_field_names: if type(name) is not str: raise TypeError('typename and all field names must be strings') if not name.isidentifier(): raise ValueError('typename and all field names must be valid ' f'identifiers: {name!r}') if _iskeyword(name): raise ValueError('typename and all field names cannot be a ' f'keyword: {name!r}') seen = set() for name in field_names + extra_field_names: if name.startswith('_'): raise ValueError('Field names cannot start with an underscore: ' f'{name!r}') if name in seen: raise ValueError(f'Duplicate field name: {name!r}') seen.add(name)
def _check_name(name, is_type_name=False, seen_names=None): if len(name) == 0: raise ValueError("Type names and field names cannot be zero " "length: {0!r}".format(name)) if not all(c.isalnum() or c == "_" for c in name): raise ValueError("Type names and field names can only contain " "alphanumeric characters and underscores: " "{0!r}".format(name)) if _iskeyword(name): raise ValueError("Type names and field names cannot be a keyword: " "{0!r}".format(name)) if name[0].isdigit(): raise ValueError("Type names and field names cannot start with a " "number: {0!r}".format(name)) if not is_type_name: # these tests don't apply for the typename, just the fieldnames if name in seen_names: raise ValueError("Encountered duplicate field name: " "{0!r}".format(name)) if name.startswith("_"): raise ValueError("Field names cannot start with an underscore: " "{0!r}".format(name))
def namedtuple(typename, field_names, *, verbose=False, rename=False, module=None): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessible by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Validate the field names. At the user's option, either generate an error # message or automatically replace the field name with a valid name. if isinstance(field_names, str): field_names = field_names.replace(',', ' ').split() field_names = list(map(str, field_names)) typename = str(typename) if rename: seen = set() for index, name in enumerate(field_names): if (not name.isidentifier() or _iskeyword(name) or name.startswith('_') or name in seen): field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: if type(name) is not str: raise TypeError('Type names and field names must be strings') if not name.isidentifier(): raise ValueError('Type names and field names must be valid ' 'identifiers: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' 'keyword: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: ' '%r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) # Fill-in the class template class_definition = _class_template.format( typename=typename, field_names=tuple(field_names), num_fields=len(field_names), arg_list=repr(tuple(field_names)).replace("'", "")[1:-1], repr_fmt=', '.join( _repr_template.format(name=name) for name in field_names), field_defs='\n'.join( _field_template.format(index=index, name=name) for index, name in enumerate(field_names))) # Execute the template string in a temporary namespace and support # tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(__name__='namedtuple_%s' % typename) exec(class_definition, namespace) result = namespace[typename] result._source = class_definition if verbose: print(result._source) # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in environments where # sys._getframe is not defined (Jython for example) or sys._getframe is not # defined for arguments greater than 0 (IronPython), or where the user has # specified a particular module. if module is None: try: module = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass if module is not None: result.__module__ = module return result
def namedtuple(typename, field_names, verbose=False, rename=False): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessible by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() field_names = map(str, field_names) typename = str(typename) if rename: seen = set() for index, name in enumerate(field_names): if not all((c.isalnum() or c == '_' for c in name)) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen: field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: if type(name) != str: raise TypeError('Type names and field names must be strings') if not all((c.isalnum() or c == '_' for c in name)): raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with a number: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: %r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) class_definition = _class_template.format(typename=typename, field_names=tuple(field_names), num_fields=len(field_names), arg_list=repr(tuple(field_names)).replace("'", '')[1:-1], repr_fmt=(', ').join((_repr_template.format(name=name) for name in field_names)), field_defs=('\n').join((_field_template.format(index=index, name=name) for index, name in enumerate(field_names)))) if verbose: print class_definition namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, OrderedDict=OrderedDict, _property=property, _tuple=tuple) try: exec class_definition in namespace except SyntaxError as e: raise SyntaxError(e.message + ':\n' + class_definition) result = namespace[typename] try: result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass return result
def namedtuple(typename, field_names, verbose=False, rename=False): """A collections.namedtuple implementation written in Python to support Python versions < 2.6. Taken from: http://code.activestate.com/recipes/500261/ """ # Parse and validate the field names. Validation serves two # purposes, generating informative error messages and preventing # template injection attacks. if isinstance(field_names, basestring): # names separated by whitespace and/or commas field_names = field_names.replace(',', ' ').split() field_names = tuple(map(str, field_names)) if rename: names = list(field_names) seen = set() for i, name in enumerate(names): if (not min(c.isalnum() or c == '_' for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen): names[i] = '_%d' % i seen.add(name) field_names = tuple(names) for name in (typename, ) + field_names: if not min(c.isalnum() or c == '_' for c in name): raise ValueError('Type names and field names can only contain ' \ 'alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a keyword: %r' \ % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with a ' \ 'number: %r' % name) seen_names = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError( 'Field names cannot start with an underscore: %r' % name) if name in seen_names: raise ValueError('Encountered duplicate field name: %r' % name) seen_names.add(name) # Create and fill-in the class template numfields = len(field_names) # tuple repr without parens or quotes argtxt = repr(field_names).replace("'", "")[1:-1] reprtxt = ', '.join('%s=%%r' % name for name in field_names) template = '''class %(typename)s(tuple): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n def __new__(_cls, %(argtxt)s): return _tuple.__new__(_cls, (%(argtxt)s)) \n @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): 'Make a new %(typename)s object from a sequence or iterable' result = new(cls, iterable) if len(result) != %(numfields)d: raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) return result \n def __repr__(self): return '%(typename)s(%(reprtxt)s)' %% self \n def _asdict(self): 'Return a new dict which maps field names to their values' return dict(zip(self._fields, self)) \n def _replace(_self, **kwds): 'Return a new %(typename)s object replacing specified fields with new values' result = _self._make(map(kwds.pop, %(field_names)r, _self)) if kwds: raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) return result \n def __getnewargs__(self): return tuple(self) \n\n''' % locals() for i, name in enumerate(field_names): template += ' %s = _property(_itemgetter(%d))\n' % (name, i) if verbose: sys.stdout.write(template + '\n') sys.stdout.flush() # Execute the template string in a temporary namespace namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, _property=property, _tuple=tuple) try: exec_(template, namespace) except SyntaxError: e = sys.exc_info()[1] raise SyntaxError(e.message + ':\n' + template) result = namespace[typename] # For pickling to work, the __module__ variable needs to be set # to the frame where the named tuple is created. Bypass this # step in enviroments where sys._getframe is not defined (Jython # for example) or sys._getframe is not defined for arguments # greater than 0 (IronPython). try: result.__module__ = _sys._getframe(1).f_globals.get( '__name__', '__main__') except (AttributeError, ValueError): pass return result
def namedtuple(typename, field_names, *, rename=False, defaults=None, module=None): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessible by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Validate the field names. At the user's option, either generate an error # message or automatically replace the field name with a valid name. if isinstance(field_names, str): field_names = field_names.replace(',', ' ').split() field_names = list(map(str, field_names)) typename = _sys.intern(str(typename)) if rename: seen = set() for index, name in enumerate(field_names): if (not name.isidentifier() or _iskeyword(name) or name.startswith('_') or name in seen): field_names[index] = f'_{index}' seen.add(name) for name in [typename] + field_names: if type(name) is not str: raise TypeError('Type names and field names must be strings') if not name.isidentifier(): raise ValueError('Type names and field names must be valid ' f'identifiers: {name!r}') if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' f'keyword: {name!r}') seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: ' f'{name!r}') if name in seen: raise ValueError(f'Encountered duplicate field name: {name!r}') seen.add(name) field_defaults = {} if defaults is not None: defaults = tuple(defaults) if len(defaults) > len(field_names): raise TypeError('Got more default values than field names') field_defaults = dict(reversed(list(zip(reversed(field_names), reversed(defaults))))) # Variables used in the methods and docstrings field_names = tuple(map(_sys.intern, field_names)) num_fields = len(field_names) arg_list = repr(field_names).replace("'", "")[1:-1] repr_fmt = '(' + ', '.join(f'{name}=%r' for name in field_names) + ')' tuple_new = tuple.__new__ _len = len # Create all the named tuple methods to be added to the class namespace s = f'def __new__(_cls, {arg_list}): return _tuple_new(_cls, ({arg_list}))' namespace = {'_tuple_new': tuple_new, '__name__': f'namedtuple_{typename}'} # Note: exec() has the side-effect of interning the field names exec(s, namespace) __new__ = namespace['__new__'] __new__.__doc__ = f'Create new instance of {typename}({arg_list})' if defaults is not None: __new__.__defaults__ = defaults @classmethod def _make(cls, iterable): result = tuple_new(cls, iterable) if _len(result) != num_fields: raise TypeError(f'Expected {num_fields} arguments, got {len(result)}') return result _make.__func__.__doc__ = (f'Make a new {typename} object from a sequence ' 'or iterable') def _replace(_self, **kwds): result = _self._make(map(kwds.pop, field_names, _self)) if kwds: raise ValueError(f'Got unexpected field names: {list(kwds)!r}') return result _replace.__doc__ = (f'Return a new {typename} object replacing specified ' 'fields with new values') def __repr__(self): 'Return a nicely formatted representation string' return self.__class__.__name__ + repr_fmt % self _dict, _zip = dict, zip def _asdict(self): 'Return a new dict which maps field names to their values.' return _dict(_zip(self._fields, self)) def __getnewargs__(self): 'Return self as a plain tuple. Used by copy and pickle.' return tuple(self) # Modify function metadata to help with introspection and debugging for method in (__new__, _make.__func__, _replace, __repr__, _asdict, __getnewargs__): method.__qualname__ = f'{typename}.{method.__name__}' # Build-up the class namespace dictionary # and use type() to build the result class class_namespace = { '__doc__': f'{typename}({arg_list})', '__slots__': (), '_fields': field_names, '_fields_defaults': field_defaults, '__new__': __new__, '_make': _make, '_replace': _replace, '__repr__': __repr__, '_asdict': _asdict, '__getnewargs__': __getnewargs__, } for index, name in enumerate(field_names): doc = _sys.intern(f'Alias for field number {index}') class_namespace[name] = _tuplegetter(index, doc) result = type(typename, (tuple,), class_namespace) # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in environments where # sys._getframe is not defined (Jython for example) or sys._getframe is not # defined for arguments greater than 0 (IronPython), or where the user has # specified a particular module. if module is None: try: module = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass if module is not None: result.__module__ = module return result
def namedtuple(typename, field_names, *, verbose=False, rename=False, module=None): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessible by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Validate the field names. At the user's option, either generate an error # message or automatically replace the field name with a valid name. if isinstance(field_names, str): field_names = field_names.replace(',', ' ').split() field_names = list(map(str, field_names)) typename = str(typename) if rename: seen = set() for index, name in enumerate(field_names): if (not name.isidentifier() or _iskeyword(name) or name.startswith('_') or name in seen): field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: if type(name) is not str: raise TypeError('Type names and field names must be strings') if not name.isidentifier(): raise ValueError('Type names and field names must be valid ' 'identifiers: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' 'keyword: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: ' '%r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) # Fill-in the class template class_definition = _class_template.format( typename = typename, field_names = tuple(field_names), num_fields = len(field_names), arg_list = repr(tuple(field_names)).replace("'", "")[1:-1], repr_fmt = ', '.join(_repr_template.format(name=name) for name in field_names), field_defs = '\n'.join(_field_template.format(index=index, name=name) for index, name in enumerate(field_names)) ) # Execute the template string in a temporary namespace and support # tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(__name__='namedtuple_%s' % typename) exec(class_definition, namespace) result = namespace[typename] result._source = class_definition if verbose: print(result._source) # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in environments where # sys._getframe is not defined (Jython for example) or sys._getframe is not # defined for arguments greater than 0 (IronPython), or where the user has # specified a particular module. if module is None: try: module = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass if module is not None: result.__module__ = module return result
def rectuple(typename, field_names, verbose=False, rename=False): """ Returns a new subclass of tuple that acts like a record. Used to represent an immutable record with named fields. Compared to namedtuple, it behaves less like a tuple and more like a record. rectuples from different classes are never equal, and they cannot be added or multiplied like tuples. """ # Validate the field names. At the user's option, either generate an error # message or automatically replace the field name with a valid name. if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() field_names = map(str, field_names) if rename: seen = set() for index, name in enumerate(field_names): if (not all(c.isalnum() or c=='_' for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen): field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: if not all(c.isalnum() or c=='_' for c in name): raise ValueError('Type names and field names can only contain ' 'alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' 'keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with ' 'a number: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: ' '%r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) # Fill-in the class template class_definition = _class_template.format( typename = typename, field_names = tuple(field_names), num_fields = len(field_names), arg_list = repr(tuple(field_names)).replace("'", "")[1:-1], repr_fmt = ', '.join(_repr_template.format(name=name) for name in field_names), field_defs = '\n'.join(_field_template.format(index=index, name=name) for index, name in enumerate(field_names)) ) if verbose: print class_definition # Execute the template string in a temporary namespace and support # tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(_itemgetter=_itemgetter, __name__='rectuple_%s' % typename, OrderedDict=OrderedDict, _property=property, _tuple=tuple) try: exec class_definition in namespace except SyntaxError as e: raise SyntaxError(e.message + ':\n' + class_definition) result = namespace[typename] # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in environments where # sys._getframe is not defined (Jython for example) or sys._getframe is not # defined for arguments greater than 0 (IronPython). try: result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass return result
def namedtuple(typename, field_names, verbose=False, rename=False): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', 'x y') >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessable by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Parse and validate the field names. Validation serves two purposes, # generating informative error messages and preventing template injection attacks. if isinstance(field_names, str): field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas field_names = tuple(map(str, field_names)) if rename: names = list(field_names) seen = set() for i, name in enumerate(names): if (not min(c.isalnum() or c=='_' for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen): names[i] = '_%d' % i seen.add(name) field_names = tuple(names) for name in (typename,) + field_names: if not min(c.isalnum() or c=='_' for c in name): raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with a number: %r' % name) seen_names = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: %r' % name) if name in seen_names: raise ValueError('Encountered duplicate field name: %r' % name) seen_names.add(name) # Create and fill-in the class template numfields = len(field_names) argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes reprtxt = ', '.join('%s=%%r' % name for name in field_names) template = '''class %(typename)s(tuple): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n def __new__(_cls, %(argtxt)s): return tuple.__new__(_cls, (%(argtxt)s)) \n @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): 'Make a new %(typename)s object from a sequence or iterable' result = new(cls, iterable) if len(result) != %(numfields)d: raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) return result \n def __repr__(self): return '%(typename)s(%(reprtxt)s)' %% self \n def _asdict(self): 'Return a new dict which maps field names to their values' return dict(zip(self._fields, self)) \n def _replace(_self, **kwds): 'Return a new %(typename)s object replacing specified fields with new values' result = _self._make(map(kwds.pop, %(field_names)r, _self)) if kwds: raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) return result \n def __getnewargs__(self): return tuple(self) \n\n''' % locals() for i, name in enumerate(field_names): template += ' %s = _property(_itemgetter(%d))\n' % (name, i) if verbose: print(template) # Execute the template string in a temporary namespace namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, _property=property, _tuple=tuple) try: exec(template,namespace) except SyntaxError as e: raise SyntaxError(e.message + ':\n' + template) result = namespace[typename] # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in enviroments where # sys._getframe is not defined (Jython for example) or sys._getframe is not # defined for arguments greater than 0 (IronPython). try: result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass return result
def namedtuple(typename, field_names, *, rename=False, defaults=None, module=None): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessible by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Validate the field names. At the user's option, either generate an error # message or automatically replace the field name with a valid name. if isinstance(field_names, str): field_names = field_names.replace(',', ' ').split() field_names = list(map(str, field_names)) typename = _sys.intern(str(typename)) if rename: seen = set() for index, name in enumerate(field_names): if (not name.isidentifier() or _iskeyword(name) or name.startswith('_') or name in seen): field_names[index] = f'_{index}' seen.add(name) for name in [typename] + field_names: if type(name) is not str: raise TypeError('Type names and field names must be strings') if not name.isidentifier(): raise ValueError('Type names and field names must be valid ' f'identifiers: {name!r}') if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' f'keyword: {name!r}') seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: ' f'{name!r}') if name in seen: raise ValueError(f'Encountered duplicate field name: {name!r}') seen.add(name) field_defaults = {} if defaults is not None: defaults = tuple(defaults) if len(defaults) > len(field_names): raise TypeError('Got more default values than field names') field_defaults = dict( reversed(list(zip(reversed(field_names), reversed(defaults))))) # Variables used in the methods and docstrings field_names = tuple(map(_sys.intern, field_names)) num_fields = len(field_names) arg_list = repr(field_names).replace("'", "")[1:-1] repr_fmt = '(' + ', '.join(f'{name}=%r' for name in field_names) + ')' tuple_new = tuple.__new__ _len = len # Create all the named tuple methods to be added to the class namespace s = f'def __new__(_cls, {arg_list}): return _tuple_new(_cls, ({arg_list}))' namespace = {'_tuple_new': tuple_new, '__name__': f'namedtuple_{typename}'} # Note: exec() has the side-effect of interning the field names exec(s, namespace) __new__ = namespace['__new__'] __new__.__doc__ = f'Create new instance of {typename}({arg_list})' if defaults is not None: __new__.__defaults__ = defaults @classmethod def _make(cls, iterable): result = tuple_new(cls, iterable) if _len(result) != num_fields: raise TypeError( f'Expected {num_fields} arguments, got {len(result)}') return result _make.__func__.__doc__ = (f'Make a new {typename} object from a sequence ' 'or iterable') def _replace(_self, **kwds): result = _self._make(map(kwds.pop, field_names, _self)) if kwds: raise ValueError(f'Got unexpected field names: {list(kwds)!r}') return result _replace.__doc__ = (f'Return a new {typename} object replacing specified ' 'fields with new values') def __repr__(self): 'Return a nicely formatted representation string' return self.__class__.__name__ + repr_fmt % self def _asdict(self): 'Return a new OrderedDict which maps field names to their values.' return OrderedDict(zip(self._fields, self)) def __getnewargs__(self): 'Return self as a plain tuple. Used by copy and pickle.' return tuple(self) # Modify function metadata to help with introspection and debugging for method in (__new__, _make.__func__, _replace, __repr__, _asdict, __getnewargs__): method.__qualname__ = f'{typename}.{method.__name__}' # Build-up the class namespace dictionary # and use type() to build the result class class_namespace = { '__doc__': f'{typename}({arg_list})', '__slots__': (), '_fields': field_names, '_fields_defaults': field_defaults, '__new__': __new__, '_make': _make, '_replace': _replace, '__repr__': __repr__, '_asdict': _asdict, '__getnewargs__': __getnewargs__, } cache = _nt_itemgetters for index, name in enumerate(field_names): try: itemgetter_object, doc = cache[index] except KeyError: itemgetter_object = _itemgetter(index) doc = f'Alias for field number {index}' cache[index] = itemgetter_object, doc class_namespace[name] = property(itemgetter_object, doc=doc) result = type(typename, (tuple, ), class_namespace) # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in environments where # sys._getframe is not defined (Jython for example) or sys._getframe is not # defined for arguments greater than 0 (IronPython), or where the user has # specified a particular module. if module is None: try: module = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass if module is not None: result.__module__ = module return result
def sparsenamedtuple(typename, field_names, verbose=False, rename=False): """Returns a new subclass of tuple with named fields. # sparsenamedtuples are defined similar to namedtuples >>> from collections import namedtuple >>> from sparsenamedtuple import sparsenamedtuple >>> Customer = namedtuple('Customer', 'username first middle last city state zip bday') >>> CustomerS = sparsenamedtuple('Customer', 'username first middle last city state zip bday') # __doc__ works the same >>> Customer.__doc__ 'Customer(username, first, middle, last, city, state, zip, bday)' >>> CustomerS.__doc__ 'Customer(username, first, middle, last, city, state, zip, bday)' # unlike namedtuples, sparsenamedtuples are always created by passing kwargs # Note that we don't pass the names of any None values >>> c1 = Customer('jdoe', None, None, None, None, 'NY', None, None) >>> c2 = CustomerS(username='******', state='NY') # repr looks the same >>> c1 Customer(username='******', first=None, middle=None, last=None, city=None, state='NY', zip=None, bday=None) >>> c2 Customer(username='******', first=None, middle=None, last=None, city=None, state='NY', zip=None, bday=None) # but the sparsenamedtuple takes up less space in memory >>> import sys >>> sys.getsizeof(c1) 56 >>> sys.getsizeof(c2) 36 # _asdict works the same >>> c1._asdict() OrderedDict([('username', 'jdoe'), ('first', None), ('middle', None), ('last', None), ('city', None), ('state', 'NY'), ('zip', None), ('bday', None)]) >>> c2._asdict() OrderedDict([('username', 'jdoe'), ('first', None), ('middle', None), ('last', None), ('city', None), ('state', 'NY'), ('zip', None), ('bday', None)]) # indexing works the same as with namedtuple >>> c1[0] 'jdoe' >>> c2[0] 'jdoe' >>> c1[1] >>> c2[1] # accessing values by field name works the same as with namedtuple >>> c1.middle >>> c2.middle >>> c1.state 'NY' >>> c2.state 'NY' # here we can see how it works; in sparsenamedtuple the indexes of the specified values are stashed # in the last element of the tuple. For this reason, unfortunately you can't use tuple unpacking # in the same way that you would with a regular namedtuple >>> tuple(c1) ('jdoe', None, None, None, None, 'NY', None, None) >>> tuple(c2) ('jdoe', 'NY', (0, 5)) # you can use _asnamedtuple to convert a sparsenamedtuple into its namedtuple equivalent tuple; # this allows tuple unpacking similar to namedtuple >>> c2._asnamedtuple() ('jdoe', None, None, None, None, 'NY', None, None) # equality of sparsenamedtuple and namedtuple works... >>> c2 == c1 True # but is not symmetrical; you have to specify the sparsenamedtuple as the lvalue >>> c1 == c2 False """ # Validate the field names. At the user's option, either generate an error # message or automatically replace the field name with a valid name. if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() field_names = map(str, field_names) if rename: seen = set() for index, name in enumerate(field_names): if (not all(c.isalnum() or c=='_' for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen): field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: if not all(c.isalnum() or c=='_' for c in name): raise ValueError('Type names and field names can only contain ' 'alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' 'keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with ' 'a number: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: ' '%r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) # Fill-in the class template class_definition = _class_template.format( typename = typename, field_names = tuple(field_names), num_fields = len(field_names), arg_list = repr(tuple(field_names)).replace("'", "")[1:-1], repr_fmt = ', '.join(_repr_template.format(name=name) for name in field_names), field_defs = '\n'.join(_field_template.format(index=index, name=name) for index, name in enumerate(field_names)) ) if verbose: print class_definition # Execute the template string in a temporary namespace and support # tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(_itemgetter=_itemgetter, __name__='sparsenamedtuple_%s' % typename, OrderedDict=OrderedDict, _property=property, _tuple=tuple) try: exec class_definition in namespace except SyntaxError as e: raise SyntaxError(e.message + ':\n' + class_definition) result = namespace[typename] # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in enviroments where # sys._getframe is not defined (Jython for example) or sys._getframe is not # defined for arguments greater than 0 (IronPython). try: result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass return result
def namedtuple(typename, field_names, *, rename=False, defaults=None, module=None): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessible by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Validate the field names. At the user's option, either generate an error # message or automatically replace the field name with a valid name. if isinstance(field_names, str): field_names = field_names.replace(',', ' ').split() field_names = list(map(str, field_names)) typename = _sys.intern(str(typename)) if rename: seen = set() for index, name in enumerate(field_names): if (not name.isidentifier() or _iskeyword(name) or name.startswith('_') or name in seen): field_names[index] = f'_{index}' seen.add(name) for name in [typename] + field_names: if type(name) is not str: raise TypeError('Type names and field names must be strings') if not name.isidentifier(): raise ValueError('Type names and field names must be valid ' f'identifiers: {name!r}') if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' f'keyword: {name!r}') seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: ' f'{name!r}') if name in seen: raise ValueError(f'Encountered duplicate field name: {name!r}') seen.add(name) field_defaults = {} if defaults is not None: defaults = tuple(defaults) if len(defaults) > len(field_names): raise TypeError('Got more default values than field names') field_defaults = dict( reversed(list(zip(reversed(field_names), reversed(defaults))))) # Variables used in the methods and docstrings field_names = tuple(map(_sys.intern, field_names)) num_fields = len(field_names) arg_list = repr(field_names).replace("'", "")[1:-1] repr_fmt = '(' + ', '.join(f'{name}=%r' for name in field_names) + ')' tuple_new = tuple.__new__ _dict, _tuple, _len, _map, _zip = dict, tuple, len, map, zip # Create all the named tuple methods to be added to the class namespace s = f'def __new__(_cls, {arg_list}): return _tuple_new(_cls, ({arg_list}))' namespace = {'_tuple_new': tuple_new, '__name__': f'namedtuple_{typename}'} # Note: exec() has the side-effect of interning the field names exec(s, namespace) __new__ = namespace['__new__'] __new__.__doc__ = f'Create new instance of {typename}({arg_list})' if defaults is not None: __new__.__defaults__ = defaults @classmethod def _make(cls, iterable): result = tuple_new(cls, iterable) if _len(result) != num_fields: raise TypeError( f'Expected {num_fields} arguments, got {len(result)}') return result _make.__func__.__doc__ = (f'Make a new {typename} object from a sequence ' 'or iterable') def _replace(self, /, **kwds): result = self._make(_map(kwds.pop, field_names, self)) if kwds: raise ValueError(f'Got unexpected field names: {list(kwds)!r}') return result
def namedtuple(typename, field_names, verbose=False): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', 'x y') >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessable by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Parse and validate the field names. Validation serves two purposes, # generating informative error messages and preventing template injection attacks. if isinstance(field_names, basestring): # names separated by whitespace and/or commas field_names = field_names.replace(',', ' ').split() field_names = tuple(map(str, field_names)) for name in (typename,) + field_names: if not all(c.isalnum() or c == '_' for c in name): raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % (name,)) if _iskeyword(name): raise ValueError('Type names and field names cannot be a keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with a number: %r' % name) seen_names = set() for name in field_names: if name.startswith('_'): raise ValueError('Field names cannot start with an underscore: %r' % name) if name in seen_names: raise ValueError('Encountered duplicate field name: %r' % name) seen_names.add(name) # Create and fill-in the class template numfields = len(field_names) argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes reprtxt = ', '.join('%s=%%r' % name for name in field_names) dicttxt = ', '.join('%r: t[%d]' % (name, pos) for pos, name in enumerate(field_names)) template = '''class %(typename)s(tuple): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n def __new__(_cls, %(argtxt)s): return _tuple.__new__(_cls, (%(argtxt)s)) \n def __repr__(self): return '%(typename)s(%(reprtxt)s)' %% self \n def _asdict(t): 'Return a new dict which maps field names to their values' return {%(dicttxt)s} \n def __getnewargs__(self): return tuple(self) \n\n''' % locals() for i, name in enumerate(field_names): template += ' %s = _property(_itemgetter(%d))\n' % (name, i) if verbose: print template # Execute the template string in a temporary namespace and # support tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, _property=property, _tuple=tuple) try: exec template in namespace except SyntaxError, e: raise SyntaxError(e.message + ':\n' + template)
def recordclass(typename, fields, rename=False, defaults=None, readonly=False, hashable=False, gc=False, module=None): """Returns a new subclass of array with named fields. >>> Point = recordclass('Point', 'x y') >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessable by name 33 >>> d = p._asdict() # convert to a dictionary >>> d.x 11 >>> d.x = 33 # assign new value >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ if readonly: baseclass = memoryslotsreadonly else: baseclass = memoryslots # Validate the field names. At the user's option, either generate an error # message or automatically replace the field name with a valid name. if isinstance(fields, str): field_names = fields.replace(',', ' ').split() annotations = None else: msg = "recordclass('Name', [(f0, t0), (f1, t1), ...]); each t must be a type" annotations = {} field_names = [] for fn in fields: if type(fn) is tuple: n, t = fn n = str(n) if _type_check: t = _type_check(t, msg) annotations[n] = t field_names.append(n) else: field_names.append(str(fn)) typename = _intern(str(typename)) if rename: seen = set() for index, name in enumerate(field_names): if (not _isidentifier(name) or _iskeyword(name) or name.startswith('_') or name in seen): field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: if type(name) != str: raise TypeError('Type names and field names must be strings') if not _isidentifier(name): raise ValueError('Type names and field names must be valid ' 'identifiers: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' 'keyword: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: ' '%r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) if defaults is not None: defaults = tuple(defaults) if len(defaults) > len(field_names): raise TypeError('Got more default values than field names') field_defaults = dict(reversed(list(zip(reversed(field_names), reversed(defaults))))) else: field_defaults = {} field_names = tuple(map(_intern, field_names)) n_fields = len(field_names) arg_list = ', '.join(field_names) repr_fmt=', '.join(_repr_template.format(name=name) for name in field_names) if readonly: new_func_template = """\ def __new__(_cls, {1}): 'Create new instance of {0}({1})' return _method_new(_cls, ({1})) """ _method_new = memoryslotsreadonly.__new__ else: new_func_template = """\ def __new__(_cls, {1}): 'Create new instance: {0}({1})' return _method_new(_cls, {1}) """ _method_new = memoryslots.__new__ new_func_def = new_func_template.format(typename, arg_list) # Execute the template string in a temporary namespace and support # tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(_method_new=_method_new) code = compile(new_func_def, "", "exec") eval(code, namespace) __new__ = namespace['__new__'] if defaults is not None: __new__.__defaults__ = defaults if annotations: __new__.__annotations__ = annotations def _make(_cls, iterable): ob = _method_new(_cls, *iterable) if len(ob) != n_fields: raise TypeError('Expected %s arguments, got %s' % (n_fields, len(ob))) return ob _make.__doc__ = 'Make a new %s object from a sequence or iterable' % typename if readonly: def _replace(_self, **kwds): result = _self._make((kwds.pop(name) for name in field_names)) if kwds: raise ValueError('Got unexpected field names: %r' % list(kwds)) return result else: def _replace(_self, **kwds): for name, val in kwds.items(): setattr(_self, name, val) return _self _replace.__doc__ = 'Return a new %s object replacing specified fields with new values' % typename def __repr__(self): 'Return a nicely formatted representation string' return self.__class__.__name__ + "(" + (repr_fmt % tuple(self)) + ")" def _asdict(self): 'Return a new OrderedDict which maps field names to their values.' return OrderedDict(zip(self.__attrs__, self)) def __getnewargs__(self): 'Return self as a plain tuple. Used by copy and pickle.' return tuple(self) def __getstate__(self): 'Exclude the OrderedDict from pickling' return None def __reduce__(self): 'Reduce' return type(self), tuple(self) if not readonly and hashable: def __hash__(self): return hash(tuple(self)) __hash__.__qualname__ = typename + "." + "__hash__" for method in (__new__, _make, _replace, __repr__, _asdict, __getnewargs__, __reduce__, __getstate__): method.__qualname__ = typename + "." + method.__name__ _make = classmethod(_make) if readonly: cache = _itemgeters else: cache = _itemgetseters class_namespace = {} for index, name in enumerate(field_names): try: item_object = cache[index] except KeyError: if readonly: item_object = itemget(index) else: item_object = itemgetset(index) #doc = 'Alias for field number ' + str(index) cache[index] = item_object class_namespace[name] = item_object __options__ = {'hashable':hashable, 'gc':gc} if readonly: __options__['hashable'] = True class_namespace.update({ '__slots__': (), '__doc__': typename+'('+arg_list+')', '__attrs__': field_names, '__new__': __new__, '_make': _make, '_replace': _replace, '__repr__': __repr__, '_asdict': _asdict, '__getnewargs__': __getnewargs__, '__getstate__': __getstate__, '__reduce__': __reduce__, '__dict__': property(_asdict), '__options__': __options__, }) _result = recordclasstype(typename, (baseclass,), class_namespace) # For pickling to work, the __module__ variable needs to be set to the frame # where the class is created. if module is None: try: module = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass if module is not None: _result.__module__ = module if annotations: _result.__annotations__ = annotations return _result
def namedtuple(typename, field_names, verbose=False, rename=False): """Returns a new subclass of tuple with named fields. This is a patched version of collections.namedtuple from the stdlib. Unlike the latter, it accepts non-identifier strings as field names. All values are accessible through dict syntax. Fields whose names are identifiers are also accessible via attribute syntax as in ordinary namedtuples, alongside traditional indexing. This feature is needed as SDMX allows field names to contain '-'. >>> Point = namedtuple('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessable by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ if isinstance(field_names, str): field_names = field_names.replace(",", " ").split() field_names = list(map(str, field_names)) typename = str(typename) for name in [typename] + field_names: if type(name) != str: raise TypeError("Type names and field names must be strings") if _iskeyword(name): raise ValueError("Type names and field names cannot be a " "keyword: %r" % name) if not _isidentifier(typename): raise ValueError("Type names must be valid " "identifiers: %r" % name) seen = set() for name in field_names: if name.startswith("_") and not rename: raise ValueError("Field names cannot start with an underscore: " "%r" % name) if name in seen: raise ValueError("Encountered duplicate field name: %r" % name) seen.add(name) arg_names = ["_" + str(i) for i in range(len(field_names))] # Fill-in the class template class_definition = _class_template.format( typename=typename, field_names=tuple(field_names), num_fields=len(field_names), arg_list=repr(tuple(arg_names)).replace("'", "")[1:-1], repr_fmt=", ".join(_repr_template.format(name=name) for name in field_names), field_defs="\n".join( _field_template.format(index=index, name=name) for index, name in enumerate(field_names) if _isidentifier(name) ), ) # Execute the template string in a temporary namespace and support # tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(__name__="namedtuple_%s" % typename) exec(class_definition, namespace) result = namespace[typename] result._source = class_definition if verbose: print(result._source) # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in environments where # sys._getframe is not defined (Jython for example) or sys._getframe is not # defined for arguments greater than 0 (IronPython). try: result.__module__ = _sys._getframe(1).f_globals.get("__name__", "__main__") except (AttributeError, ValueError): pass return result
def namedtuple(typename, field_names, verbose=False, rename=False): """A collections.namedtuple implementation, see: http://docs.python.org/library/collections.html#namedtuple """ if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() field_names = tuple(map(str, field_names)) if rename: names = list(field_names) seen = set() for i, name in enumerate(names): if ((not min(c.isalnum() or c == '_' for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen)): names[i] = '_%d' % i seen.add(name) field_names = tuple(names) for name in (typename,) + field_names: if not min(c.isalnum() or c == '_' for c in name): raise ValueError('Type names and field names can only contain ' 'alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' 'keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start ' 'with a number: %r' % name) seen_names = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError( 'Field names cannot start with an underscore: %r' % name) if name in seen_names: raise ValueError('Encountered duplicate field name: %r' % name) seen_names.add(name) numfields = len(field_names) argtxt = repr(field_names).replace("'", "")[1:-1] reprtxt = ', '.join('%s=%%r' % name for name in field_names) template = '''class %(typename)s(tuple): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n def __new__(_cls, %(argtxt)s): return _tuple.__new__(_cls, (%(argtxt)s)) \n @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): 'Make a new %(typename)s object from a sequence or iterable' result = new(cls, iterable) if len(result) != %(numfields)d: raise TypeError( 'Expected %(numfields)d arguments, got %%d' %% len(result)) return result \n def __repr__(self): return '%(typename)s(%(reprtxt)s)' %% self \n def _asdict(self): 'Return a new dict which maps field names to their values' return dict(zip(self._fields, self)) \n def _replace(_self, **kwds): result = _self._make(map(kwds.pop, %(field_names)r, _self)) if kwds: raise ValueError( 'Got unexpected field names: %%r' %% kwds.keys()) return result \n def __getnewargs__(self): return tuple(self) \n\n''' % locals() for i, name in enumerate(field_names): template += ' %s = _property(_itemgetter(%d))\n' % (name, i) if verbose: sys.stdout.write(template + '\n') sys.stdout.flush() namespace = dict( _itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, _property=property, _tuple=tuple) try: exec_(template, namespace) except SyntaxError: e = sys.exc_info()[1] raise SyntaxError(e.message + ':\n' + template) result = namespace[typename] try: result.__module__ = _sys._getframe( 1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass return result
def namedtuple(typename, field_names, verbose=False, rename=False): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessable by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Validate the field names. At the user's option, either generate an error # message or automatically replace the field name with a valid name. if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() field_names = map(str, field_names) if rename: seen = set() for index, name in enumerate(field_names): if (not all(c.isalnum() or c=='_' for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen): field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: if not all(c.isalnum() or c=='_' for c in name): raise ValueError('Type names and field names can only contain ' 'alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' 'keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with ' 'a number: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: ' '%r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) # Fill-in the class template class_definition = _class_template.format( typename = typename, field_names = tuple(field_names), num_fields = len(field_names), arg_list = repr(tuple(field_names)).replace("'", "")[1:-1], repr_fmt = ', '.join(_repr_template.format(name=name) for name in field_names), field_defs = '\n'.join(_field_template.format(index=index, name=name) for index, name in enumerate(field_names)) ) if verbose: print class_definition # Execute the template string in a temporary namespace and support # tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, OrderedDict=OrderedDict, _property=property, _tuple=tuple) try: exec class_definition in namespace except SyntaxError as e: raise SyntaxError(e.message + ':\n' + class_definition) result = namespace[typename] # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in environments where # sys._getframe is not defined (Jython for example) or sys._getframe is not # defined for arguments greater than 0 (IronPython). try: result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass return result
def namedtuple(typename, field_names, namespaceaugment={}, verbose=False): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', 'x y') >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessable by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Parse and validate the field names. Validation serves two purposes, # generating informative error messages and preventing template injection attacks. if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas field_names = tuple(map(str, field_names)) for name in (typename,) + field_names: if not all(c.isalnum() or c=='_' for c in name): raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with a number: %r' % name) seen_names = set() for name in field_names: if name.startswith('_'): raise ValueError('Field names cannot start with an underscore: %r' % name) if name in seen_names: raise ValueError('Encountered duplicate field name: %r' % name) seen_names.add(name) # Create and fill-in the class template numfields = len(field_names) argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes reprtxt = ', '.join('%s=%%r' % name for name in field_names) dicttxt = ', '.join('%r: t[%d]' % (name, pos) for pos, name in enumerate(field_names)) template = '''class %(typename)s(tuple): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n def __new__(cls, %(argtxt)s): return tuple.__new__(cls, (%(argtxt)s)) \n @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): 'Make a new %(typename)s object from a sequence or iterable' result = new(cls, iterable) if len(result) != %(numfields)d: raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) return result \n def __repr__(self): return '%(typename)s(%(reprtxt)s)' %% self \n def _asdict(t): 'Return a new dict which maps field names to their values' return {%(dicttxt)s} \n def _replace(self, **kwds): 'Return a new %(typename)s object replacing specified fields with new values' result = self._make(map(kwds.pop, %(field_names)r, self)) if kwds: raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) return result \n def __getnewargs__(self): return tuple(self) \n\n''' % locals() for i, name in enumerate(field_names): template += ' %s = property(itemgetter(%d))\n' % (name, i) for name in namespaceaugment.iterkeys(): template += ' %s = %s\n' % (name, name) if verbose: print template # Execute the template string in a temporary namespace and # support tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(namespaceaugment) namespace.update(itemgetter=_itemgetter, __name__='namedtuple_%s' % typename) try: exec template in namespace except SyntaxError, e: raise SyntaxError(e.message + ':\n' + template)
def marshalable_object(typename, field_names, verbose=False): """Returns a new object with named fields. >>> Point = marshalable_object('Point', 'x y') >>> Point.__doc__ # docstring for the new class 'Point(x, y)' """ # Parse and validate the field names. Validation serves two purposes, # generating informative error messages and preventing template injection attacks. if isinstance(field_names, basestring): field_names = field_names.replace( ',', ' ').split() # names separated by whitespace and/or commas field_names = tuple(map(str, field_names)) for name in (typename, ) + field_names: if not all(c.isalnum() or c == '_' for c in name): raise ValueError( 'Type names and field names can only contain alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError( 'Type names and field names cannot be a keyword: %r' % name) if name[0].isdigit(): raise ValueError( 'Type names and field names cannot start with a number: %r' % name) seen_names = set() for name in field_names: if name.startswith('_'): raise ValueError( 'Field names cannot start with an underscore: %r' % name) if name in seen_names: raise ValueError('Encountered duplicate field name: %r' % name) seen_names.add(name) # Create and fill-in the class template numfields = len(field_names) argtxt = repr(field_names).replace( "'", "")[1:-1] # tuple repr without parens or quotes reprtxt = ', '.join('%s=%%r' % name for name in field_names) dicttxt = ', '.join('%r: t[%d]' % (name, pos) for pos, name in enumerate(field_names)) template = '''class %(typename)s(object): '%(typename)s(%(argtxt)s)' \n __slots__ = %(field_names)r \n #_____________________________________________________________________________________ # # __init__ #_____________________________________________________________________________________ def __init__(self, %(argtxt)s): """ Initialise """ self._do_init(%(argtxt)s) def _as_tuple (self): """ returns data as a list """ return tuple(getattr(self, self.__slots__[i]) for i in range(%(numfields)d)) def _as_dict (self): """ returns data as a list """ return dict(zip(%(field_names)s, tuple(getattr(self, self.__slots__[i]) for i in range(%(numfields)d)))) def _do_init (self, *data): """ delegate from __init__ to handle variable number of arguments """ if len(data) != %(numfields)d: raise TypeError('%(typename)s(%(argtxt)s): Expected %(numfields)d arguments, got %%d' %% len(data)) for i in range(%(numfields)d): setattr(self, self.__slots__[i], data[i]) def __eq__(self, other): if type(other) == %(typename)s: return cmp(self, other) == 0 else: return False def __cmp__(self, other): if type(other) != %(typename)s: return -1 for i in range(%(numfields)d): c = cmp(getattr(self, self.__slots__[i]) != getattr(other, self.__slots__[i])) if c: return c return 0 #_____________________________________________________________________________________ # # dump #_____________________________________________________________________________________ def dump(self, data_file): """ dump """ for i in range(%(numfields)d): dump(getattr(self, self.__slots__[i]), data_file) #_____________________________________________________________________________________ # # load #_____________________________________________________________________________________ @staticmethod def load(data_file): """ load """ data = [] for i in range(%(numfields)d): data.append(load(data_file)) return %(typename)s( *data) #_____________________________________________________________________________________ # # __repr__ #_____________________________________________________________________________________ def __repr__ (self): return ('%(typename)s(%(reprtxt)s)' %% self._as_tuple())\n \n\n''' % locals() if verbose: print template # Execute the template string in a temporary namespace and # support tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(dump=_dump, load=_load, __name__='marshalable_object_%s' % typename) try: exec template in namespace except SyntaxError, e: raise SyntaxError(str(e) + ':\n' + template)
def namedlist(typename, field_names, verbose=False, rename=False): """Returns a new subclass of list with named fields. >>> Point = namedlist('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with pos args or keywords >>> p[0] + p[1] # indexable like a plain list 33 >>> x, y = p # unpack like a regular list >>> x, y (11, 22) >>> p.x + p.y # fields also accessible by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Validate the field names. At the user's option, either generate an error # message or automatically replace the field name with a valid name. if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() field_names = [str(x) for x in field_names] if rename: seen = set() for index, name in enumerate(field_names): if (not all(c.isalnum() or c == '_' for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen): field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: if not all(c.isalnum() or c == '_' for c in name): raise ValueError('Type names and field names can only contain ' 'alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' 'keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with ' 'a number: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: ' '%r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) # Fill-in the class template fmt_kw = {'typename': typename} fmt_kw['field_names'] = tuple(field_names) fmt_kw['num_fields'] = len(field_names) fmt_kw['arg_list'] = repr(tuple(field_names)).replace("'", "")[1:-1] fmt_kw['repr_fmt'] = ', '.join(_repr_tmpl.format(name=name) for name in field_names) fmt_kw['field_defs'] = '\n'.join(_m_field_tmpl.format(index=index, name=name) for index, name in enumerate(field_names)) class_definition = _namedlist_tmpl.format(**fmt_kw) if verbose: print(class_definition) def _itemsetter(key): def _itemsetter(obj, value): obj[key] = value return _itemsetter # Execute the template string in a temporary namespace and support # tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(_itemgetter=_itemgetter, _itemsetter=_itemsetter, __name__='namedlist_%s' % typename, OrderedDict=OrderedDict, _property=property, _list=list) try: exec_(class_definition, namespace) except SyntaxError as e: raise SyntaxError(e.message + ':\n' + class_definition) result = namespace[typename] # For pickling to work, the __module__ variable needs to be set to # the frame where the named list is created. Bypass this step in # environments where sys._getframe is not defined (Jython for # example) or sys._getframe is not defined for arguments greater # than 0 (IronPython). try: frame = _sys._getframe(1) result.__module__ = frame.f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass return result
def namedtuple(typename, field_names, verbose=False, rename=False): """A collections.namedtuple implementation written in Python to support Python versions < 2.6. Taken from: http://code.activestate.com/recipes/500261/ """ # Parse and validate the field names. Validation serves two # purposes, generating informative error messages and preventing # template injection attacks. if isinstance(field_names, basestring): # names separated by whitespace and/or commas field_names = field_names.replace(",", " ").split() field_names = tuple(map(str, field_names)) if rename: names = list(field_names) seen = set() for i, name in enumerate(names): if ( not min(c.isalnum() or c == "_" for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith("_") or name in seen ): names[i] = "_%d" % i seen.add(name) field_names = tuple(names) for name in (typename,) + field_names: if not min(c.isalnum() or c == "_" for c in name): raise ValueError( "Type names and field names can only contain " "alphanumeric characters and underscores: %r" % name ) if _iskeyword(name): raise ValueError("Type names and field names cannot be a keyword: %r" % name) if name[0].isdigit(): raise ValueError("Type names and field names cannot start with a " "number: %r" % name) seen_names = set() for name in field_names: if name.startswith("_") and not rename: raise ValueError("Field names cannot start with an underscore: %r" % name) if name in seen_names: raise ValueError("Encountered duplicate field name: %r" % name) seen_names.add(name) # Create and fill-in the class template numfields = len(field_names) # tuple repr without parens or quotes argtxt = repr(field_names).replace("'", "")[1:-1] reprtxt = ", ".join("%s=%%r" % name for name in field_names) template = ( """class %(typename)s(tuple): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n def __new__(_cls, %(argtxt)s): return _tuple.__new__(_cls, (%(argtxt)s)) \n @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): 'Make a new %(typename)s object from a sequence or iterable' result = new(cls, iterable) if len(result) != %(numfields)d: raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) return result \n def __repr__(self): return '%(typename)s(%(reprtxt)s)' %% self \n def _asdict(self): 'Return a new dict which maps field names to their values' return dict(zip(self._fields, self)) \n def _replace(_self, **kwds): 'Return a new %(typename)s object replacing specified fields with new values' result = _self._make(map(kwds.pop, %(field_names)r, _self)) if kwds: raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) return result \n def __getnewargs__(self): return tuple(self) \n\n""" % locals() ) for i, name in enumerate(field_names): template += " %s = _property(_itemgetter(%d))\n" % (name, i) if verbose: sys.stdout.write(template + "\n") sys.stdout.flush() # Execute the template string in a temporary namespace namespace = dict(_itemgetter=_itemgetter, __name__="namedtuple_%s" % typename, _property=property, _tuple=tuple) try: exec_(template, namespace) except SyntaxError: e = sys.exc_info()[1] raise SyntaxError(e.message + ":\n" + template) result = namespace[typename] # For pickling to work, the __module__ variable needs to be set # to the frame where the named tuple is created. Bypass this # step in enviroments where sys._getframe is not defined (Jython # for example) or sys._getframe is not defined for arguments # greater than 0 (IronPython). try: result.__module__ = _sys._getframe(1).f_globals.get("__name__", "__main__") except (AttributeError, ValueError): pass return result
def namedtuple(typename, field_names, verbose=False): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', 'x y') >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessable by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Parse and validate the field names. Validation serves two purposes, # generating informative error messages and preventing template injection attacks. if isinstance(field_names, basestring): field_names = field_names.replace( ',', ' ').split() # names separated by whitespace and/or commas field_names = tuple(field_names) for name in (typename, ) + field_names: if not min(c.isalnum() or c == '_' for c in name): raise ValueError( 'Type names and field names can only contain alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError( 'Type names and field names cannot be a keyword: %r' % name) if name[0].isdigit(): raise ValueError( 'Type names and field names cannot start with a number: %r' % name) seen_names = set() for name in field_names: if name.startswith('_'): raise ValueError( 'Field names cannot start with an underscore: %r' % name) if name in seen_names: raise ValueError('Encountered duplicate field name: %r' % name) seen_names.add(name) # Create and fill-in the class template numfields = len(field_names) argtxt = repr(field_names).replace( "'", "")[1:-1] # tuple repr without parens or quotes reprtxt = ', '.join('%s=%%r' % name for name in field_names) dicttxt = ', '.join('%r: t[%d]' % (name, pos) for pos, name in enumerate(field_names)) template = '''class %(typename)s(tuple): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n def __new__(cls, %(argtxt)s): return tuple.__new__(cls, (%(argtxt)s)) \n @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): 'Make a new %(typename)s object from a sequence or iterable' result = new(cls, iterable) if len(result) != %(numfields)d: raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) return result \n def __repr__(self): return '%(typename)s(%(reprtxt)s)' %% self \n def _asdict(t): 'Return a new dict which maps field names to their values' return {%(dicttxt)s} \n def _replace(self, **kwds): 'Return a new %(typename)s object replacing specified fields with new values' result = self._make(map(kwds.pop, %(field_names)r, self)) if kwds: raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) return result \n\n''' % locals() for i, name in enumerate(field_names): template += ' %s = property(itemgetter(%d))\n' % (name, i) if verbose: print template # Execute the template string in a temporary namespace namespace = dict(itemgetter=_itemgetter) try: exec template in namespace except SyntaxError, e: raise SyntaxError(e.message + ':\n' + template)
def namedtuple(typename, field_names, verbose = False, rename = False): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessable by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() field_names = map(str, field_names) if rename: seen = set() for index, name in enumerate(field_names): if not all((c.isalnum() or c == '_' for c in name)) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen: field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: if not all((c.isalnum() or c == '_' for c in name)): raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with a number: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: %r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) class_definition = _class_template.format(typename=typename, field_names=tuple(field_names), num_fields=len(field_names), arg_list=repr(tuple(field_names)).replace("'", '')[1:-1], repr_fmt=', '.join((_repr_template.format(name=name) for name in field_names)), field_defs='\n'.join((_field_template.format(index=index, name=name) for index, name in enumerate(field_names)))) if verbose: print class_definition namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, OrderedDict=OrderedDict, _property=property, _tuple=tuple) try: exec class_definition in namespace except SyntaxError as e: raise SyntaxError(e.message + ':\n' + class_definition) result = namespace[typename] try: result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass return result
def _namedtuple(typename, field_names, verbose=False, rename=False): if isinstance(field_names, basestring): field_names = field_names.replace(",", " ").split() field_names = tuple(map(str, field_names)) if rename: names = list(field_names) seen = set() for i, name in enumerate(names): if ( not min(c.isalnum() or c == "_" for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith("_") or name in seen ): names[i] = "_%d" % i seen.add(name) field_names = tuple(names) for name in (typename,) + field_names: if not min(c.isalnum() or c == "_" for c in name): raise ValueError( "Type names and field names can only contain" " alphanumeric characters and underscores: %r" % name ) if _iskeyword(name): raise ValueError("Type names and field names cannot be a" " keyword: %r" % name) if name[0].isdigit(): raise ValueError("Type names and field names cannot start" " with a number: %r" % name) seen_names = set() for name in field_names: if name.startswith("_") and not rename: raise ValueError("Field names cannot start with an" " underscore: %r" % name) if name in seen_names: raise ValueError("Encountered duplicate field name: %r" % name) seen_names.add(name) # Create and fill-in the class template numfields = len(field_names) argtxt = repr(field_names).replace("'", "")[1:-1] reprtxt = ", ".join("%s=%%r" % name for name in field_names) template = ( """class %(typename)s(tuple): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n def __new__(_cls, %(argtxt)s): return _tuple.__new__(_cls, (%(argtxt)s)) \n @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): 'Make a new %(typename)s object from a sequence or iterable' result = new(cls, iterable) if len(result) != %(numfields)d: raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) return result \n def __repr__(self): return '%(typename)s(%(reprtxt)s)' %% self \n def _asdict(self): 'Return a new dict which maps field names to their values' return dict(zip(self._fields, self)) \n def _replace(_self, **kwds): 'Return a new %(typename)s object replacing specified fields with new values' result = _self._make(map(kwds.pop, %(field_names)r, _self)) if kwds: raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) return result \n def __getnewargs__(self): return tuple(self) \n\n""" % locals() ) for i, name in enumerate(field_names): template += " %s = _property(_itemgetter(%d))\n" % (name, i) if verbose: print template # Execute the template string in a temporary namespace namespace = dict(_itemgetter=_itemgetter, __name__="namedtuple_%s" % typename, _property=property, _tuple=tuple) try: exec template in namespace except SyntaxError, e: raise SyntaxError(e.message + ":\n" + template)
def namedtuple(typename, field_names, verbose=False, rename=False): """A collections.namedtuple implementation written in Python to support Python versions < 2.6. Taken from: http://code.activestate.com/recipes/500261/ """ # Parse and validate the field names. Validation serves two # purposes, generating informative error messages and preventing # template injection attacks. if isinstance(field_names, basestring): # names separated by whitespace and/or commas field_names = field_names.replace(',', ' ').split() field_names = tuple(map(str, field_names)) if rename: names = list(field_names) seen = set() for i, name in enumerate(names): if (not min(c.isalnum() or c=='_' for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen): names[i] = '_%d' % i seen.add(name) field_names = tuple(names) for name in (typename,) + field_names: if not min(c.isalnum() or c=='_' for c in name): raise ValueError('Type names and field names can only contain ' \ 'alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a keyword: %r' \ % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with a ' \ 'number: %r' % name) seen_names = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: %r' % name) if name in seen_names: raise ValueError('Encountered duplicate field name: %r' % name) seen_names.add(name) # Create and fill-in the class template numfields = len(field_names) # tuple repr without parens or quotes argtxt = repr(field_names).replace("'", "")[1:-1] reprtxt = ', '.join('%s=%%r' % name for name in field_names) template = '''class %(typename)s(tuple): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n def __new__(_cls, %(argtxt)s): return _tuple.__new__(_cls, (%(argtxt)s)) \n @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): 'Make a new %(typename)s object from a sequence or iterable' result = new(cls, iterable) if len(result) != %(numfields)d: raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) return result \n def __repr__(self): return '%(typename)s(%(reprtxt)s)' %% self \n def _asdict(self): 'Return a new dict which maps field names to their values' return dict(zip(self._fields, self)) \n def _replace(_self, **kwds): 'Return a new %(typename)s object replacing specified fields with new values' result = _self._make(map(kwds.pop, %(field_names)r, _self)) if kwds: raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) return result \n def __getnewargs__(self): return tuple(self) \n\n''' % locals() for i, name in enumerate(field_names): template += ' %s = _property(_itemgetter(%d))\n' % (name, i) if verbose: print template # Execute the template string in a temporary namespace namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, _property=property, _tuple=tuple) try: exec template in namespace except SyntaxError, e: raise SyntaxError(e.message + ':\n' + template)
def namedtuple(typename, field_names, verbose=False, rename=False): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', 'x y') >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessable by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Parse and validate the field names. Validation serves two purposes, # generating informative error messages and preventing template injection attacks. if isinstance(field_names, str): field_names = field_names.replace( ',', ' ').split() # names separated by whitespace and/or commas field_names = tuple(map(str, field_names)) if rename: names = list(field_names) seen = set() for i, name in enumerate(names): if (not all(c.isalnum() or c == '_' for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen): names[i] = '_%d' % i seen.add(name) field_names = tuple(names) for name in (typename, ) + field_names: if not all(c.isalnum() or c == '_' for c in name): raise ValueError( 'Type names and field names can only contain alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError( 'Type names and field names cannot be a keyword: %r' % name) if name[0].isdigit(): raise ValueError( 'Type names and field names cannot start with a number: %r' % name) seen_names = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError( 'Field names cannot start with an underscore: %r' % name) if name in seen_names: raise ValueError('Encountered duplicate field name: %r' % name) seen_names.add(name) # Create and fill-in the class template numfields = len(field_names) argtxt = repr(field_names).replace( "'", "")[1:-1] # tuple repr without parens or quotes reprtxt = ', '.join('%s=%%r' % name for name in field_names) template = '''class %(typename)s(tuple): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n def __new__(_cls, %(argtxt)s): return _tuple.__new__(_cls, (%(argtxt)s)) \n @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): 'Make a new %(typename)s object from a sequence or iterable' result = new(cls, iterable) if len(result) != %(numfields)d: raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) return result \n def __repr__(self): return '%(typename)s(%(reprtxt)s)' %% self \n def _asdict(self): 'Return a new OrderedDict which maps field names to their values' return OrderedDict(zip(self._fields, self)) \n def _replace(_self, **kwds): 'Return a new %(typename)s object replacing specified fields with new values' result = _self._make(map(kwds.pop, %(field_names)r, _self)) if kwds: raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) return result \n def __getnewargs__(self): return tuple(self) \n\n''' % locals() for i, name in enumerate(field_names): template += ' %s = _property(_itemgetter(%d))\n' % (name, i) if verbose: print(template) # Execute the template string in a temporary namespace and # support tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, OrderedDict=OrderedDict, _property=property, _tuple=tuple) try: exec(template, namespace) except SyntaxError as e: raise SyntaxError(e.msg + ':\n' + template) from e result = namespace[typename] # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in enviroments where # sys._getframe is not defined (Jython for example) or sys._getframe is not # defined for arguments greater than 0 (IronPython). try: result.__module__ = _sys._getframe(1).f_globals.get( '__name__', '__main__') except (AttributeError, ValueError): pass return result
def struct(**kwargs): """Creates an immutable container using the keyword arguments as attributes. It can be used to group multiple values and/or functions together. Example: def _my_function(): return 3 s = struct(x = 2, foo = _my_function) return s.x + s.foo() # returns 5 The implementation is almost identical to namedtuple, but: - it does not forbid fields starting with underscores - does not implement methods for pickling - does not support copy/deepcopy """ field_names = tuple(kwargs.keys()) for name in field_names: if not all(c.isalnum() or c == "_" for c in name): raise ValueError( "Field names can only contain alphanumeric characters and underscores: %r" % name) if _iskeyword(name): raise ValueError("Field names cannot be a keyword: %r" % name) if name[0].isdigit(): raise ValueError("Field names cannot start with a number: %r" % name) # Variables used in the methods and docstrings arg_list = repr(field_names).replace("'", "")[1:-1] repr_fmt = "(" + ", ".join(name + "=%r" for name in field_names) + ")" tuple_new = tuple.__new__ # Create all the named tuple methods to be added to the class namespace s = ("def __new__(_cls, " + arg_list + "): return _tuple_new(_cls, (" + arg_list + "))") namespace = {"_tuple_new": tuple_new, "__name__": _TYPENAME} # Note: exec() has the side-effect of interning the field names exec s in namespace __new__ = namespace["__new__"] def __repr__(self): """Return a nicely formatted representation string""" return self.__class__.__name__ + repr_fmt % self def _asdict(self): """Return a new OrderedDict which maps field names to their values.""" return OrderedDict(zip(self._fields, self)) def to_json(self): """Creates a JSON string representation of this struct instance.""" return json.dumps(self, cls=StructEncoder, separators=(",", ":"), sort_keys=True) # Build-up the class namespace dictionary # and use type() to build the result class class_namespace = { "__slots__": (), "_fields": field_names, "__new__": __new__, "__repr__": __repr__, "_asdict": _asdict, "to_json": to_json, } cache = _nt_itemgetters for index, name in enumerate(field_names): try: itemgetter_object = cache[index] except KeyError: itemgetter_object = _itemgetter(index) cache[index] = itemgetter_object class_namespace[name] = property(itemgetter_object) result = type(_TYPENAME, (tuple, ), class_namespace) return result(**kwargs)
def namedtuple(typename, field_names, verbose=False, rename=False): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessable by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Validate the field names. At the user's option, either generate an error # message or automatically replace the field name with a valid name. if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() field_names = map(str, field_names) if rename: seen = set() for index, name in enumerate(field_names): if (not all(c.isalnum() or c=='_' for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen): field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: if not all(c.isalnum() or c=='_' for c in name): raise ValueError('Type names and field names can only contain ' 'alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' 'keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with ' 'a number: %r' % name) seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: ' '%r' % name) if name in seen: raise ValueError('Encountered duplicate field name: %r' % name) seen.add(name) # Fill-in the class template class_definition = _class_template.format( typename = typename, field_names = tuple(field_names), num_fields = len(field_names), arg_list = repr(tuple(field_names)).replace("'", "")[1:-1], repr_fmt = ', '.join(_repr_template.format(name=name) for name in field_names), field_defs = '\n'.join(_field_template.format(index=index, name=name) for index, name in enumerate(field_names)) ) if verbose: print class_definition # Execute the template string in a temporary namespace and support # tracing utilities by setting a value for frame.f_globals['__name__'] namespace = newdict('module') namespace['__name__'] = 'namedtuple_%s' % typename namespace['OrderedDict'] = OrderedDict namespace['_property'] = property namespace['_tuple'] = tuple try: exec class_definition in namespace except SyntaxError as e: raise SyntaxError(e.message + ':\n' + class_definition) result = namespace[typename] # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in environments where # sys._getframe is not defined (Jython for example) or sys._getframe is not # defined for arguments greater than 0 (IronPython). try: result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass return result
def _namedtuple(typename, field_names, verbose=False, rename=False): if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() field_names = tuple(map(str, field_names)) if rename: names = list(field_names) seen = set() for i, name in enumerate(names): if (not min(c.isalnum() or c=='_' for c in name) or _iskeyword(name) or not name or name[0].isdigit() or name.startswith('_') or name in seen): names[i] = '_%d' % i seen.add(name) field_names = tuple(names) for name in (typename,) + field_names: if not min(c.isalnum() or c=='_' for c in name): raise ValueError('Type names and field names can only contain' ' alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a' ' keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start' ' with a number: %r' % name) seen_names = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an' ' underscore: %r' % name) if name in seen_names: raise ValueError('Encountered duplicate field name: %r' % name) seen_names.add(name) # Create and fill-in the class template numfields = len(field_names) argtxt = repr(field_names).replace("'", "")[1:-1] reprtxt = ', '.join('%s=%%r' % name for name in field_names) template = '''class %(typename)s(tuple): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n def __new__(_cls, %(argtxt)s): return _tuple.__new__(_cls, (%(argtxt)s)) \n @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): 'Make a new %(typename)s object from a sequence or iterable' result = new(cls, iterable) if len(result) != %(numfields)d: raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) return result \n def __repr__(self): return '%(typename)s(%(reprtxt)s)' %% self \n def _asdict(self): 'Return a new dict which maps field names to their values' return dict(zip(self._fields, self)) \n def _replace(_self, **kwds): 'Return a new %(typename)s object replacing specified fields with new values' result = _self._make(map(kwds.pop, %(field_names)r, _self)) if kwds: raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) return result \n def __getnewargs__(self): return tuple(self) \n\n''' % locals() for i, name in enumerate(field_names): template += ' %s = _property(_itemgetter(%d))\n' % (name, i) if verbose: print template # Execute the template string in a temporary namespace namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, _property=property, _tuple=tuple) try: exec template in namespace except SyntaxError, e: raise SyntaxError(e.message + ':\n' + template)