def recordclass(typename,
                field_names,
                rename=False,
                defaults=None,
                module=None,
                verbose=False,
                source=True):
    """Returns a new subclass of array with named fields.

    >>> Point = recordarray('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, str):
        field_names = field_names.replace(',', ' ').split()
    else:
        field_names = list(map(str, field_names))
    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))
    num_fields = len(field_names)
    arg_list = repr(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))

    # Fill-in the class template
    class_definition = _class_template.format(typename=typename,
                                              field_names=field_names,
                                              field_defaults=field_defaults,
                                              num_fields=num_fields,
                                              arg_list=arg_list,
                                              repr_fmt=repr_fmt,
                                              field_defs=field_defs)

    # Execute the template string in a temporary namespace and support
    # tracing utilities by setting a value for frame.f_globals['__name__']
    namespace = dict(_memoryslots_new=memoryslots.__new__,
                     __name__='recordclass_' + typename)

    code = compile(class_definition, "", "exec")
    eval(code, namespace)
    result = namespace[typename]

    if defaults is not None:
        result.__new__.__defaults__ = defaults
    if source:
        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).
    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
示例#2
0
def new_datatype(typename, fields=0, bases=None, namespace=None, 
                 varsize=False,  use_dict=False, use_weakref=False, hashable=True,
                 sequence=False, readonly=False, gc=False,
                 defaults=None, module=None, argsonly=False):

    annotations = {}
    fields_is_int = False
    if isinstance(fields, str):
        fields = fields.replace(',', ' ').split()
    elif isinstance(fields, int_type):
        fields_is_int = True
    else:
        msg = "new_datatype('Name', [(f0, t0), (f1, t1), ...]); each t must be a type"
        field_names = []
        if isinstance(fields, dict):
            for fn in fields:
                t = fields[fn]
                t = _type_check(t, msg)
                annotations[fn] = t
                field_names.append(fn)
        else:
            for fn in fields:
                if type(fn) is tuple:
                    n, t = fn
                    t = _type_check(t, msg)
                    annotations[n] = t
                    field_names.append(n)
                else:
                    field_names.append(fn)
        fields = field_names

    typename = _intern(str(typename))
    
    if not fields_is_int and defaults is not None:
        n_fields = len(fields)
        defaults = tuple(defaults)
        n_defaults = len(defaults)
        if n_defaults > n_fields:
            raise TypeError('Got more default values than fields')
    else:
        defaults = None

    options = {
        'dict':use_dict, 'weakref':use_weakref,
        'hash':hashable, 'varsize':varsize,
        'sequence':sequence, 'readonly':readonly,
        'defaults':defaults, 'argsonly':argsonly,
    }

    if namespace is None:
        ns = {}
    else:
        ns = namespace

    if defaults:
        for i in range(-n_defaults, 0):
            fname = fields[i]
            val = defaults[i]
            ns[fname] = val

    ns['__options__'] = options
    ns['__fields__'] = fields
    if annotations:
        ns['__annotations__'] = annotations
    cls = datatype(typename, bases, ns)
    
    if gc:
        cls = enable_gc(cls)

    if module is None:
        try:
            module = _sys._getframe(1).f_globals.get('__name__', '__main__')
        except (AttributeError, ValueError):
            module = None
    if module is not None:
        cls.__module__ = module

    return cls
示例#3
0
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 = immutabletuple
    else:
        baseclass = mutabletuple

    # 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 = immutabletuple.__new__
    else:
        new_func_template = """\
def __new__(_cls, {1}):
    'Create new instance: {0}({1})'
    return _method_new(_cls, {1})
"""
        _method_new = mutabletuple.__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.__fields__, 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 = mutabletuple_itemget(index)
            else:
                item_object = mutabletuple_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

    if module is None:
        try:
            module = _sys._getframe(1).f_globals.get('__name__', '__main__')
        except (AttributeError, ValueError):
            pass

    class_namespace.update({
        '__slots__': (),
        '__doc__': typename + '(' + arg_list + ')',
        '__fields__': field_names,
        '__new__': __new__,
        '_make': _make,
        '_replace': _replace,
        '__repr__': __repr__,
        '_asdict': _asdict,
        '__getnewargs__': __getnewargs__,
        '__getstate__': __getstate__,
        '__reduce__': __reduce__,
        '__dict__': property(_asdict),
        '__options__': __options__,
        '__module__': module,
    })

    _result = recordclasstype(typename, (baseclass, ), class_namespace)
    if annotations:
        _result.__annotations__ = annotations

    return _result
示例#4
0
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
示例#5
0
def structclass(typename, fields, rename=False, defaults=None, 
                readonly=False, usedict=False, gc=False, weakref=False,
                hashable=False, assequence=True, module=None):
    """Returns a new subclass of array with named fields.

    >>> Point = structclass('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=33, y=22)
    >>> p._replace(x=100)               # _replace() is like str.replace() but targets named fields
    Point(x=100, y=22)
    """

    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_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 usedict:
        new_func_template = """\
def __new__(_cls, {1}, **kw):
    'Create new instance of {0}({1})'
    return _baseclass.__new__(_cls, {1}, **kw)
""" 
    else:
        new_func_template = """\
def __new__(_cls, {1}):
    'Create new instance of {0}({1})'
    return _baseclass.__new__(_cls, {1})
""" 
    new_func_def = new_func_template.format(typename, arg_list)
    
    #print(new_func_def)
        
    namespace = dict(_baseclass=recordobject)    
    code = compile(new_func_def, "", "exec")
    eval(code, namespace)
    
    __new__ = namespace['__new__']
    if defaults is not None:
        __new__.__defaults__ = defaults
    
    #@classmethod
    def _make(_cls, iterable):
        ob = _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

    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'
        args_text = repr_fmt % tuple(self)
        try:
            kw = self.__dict__
        except AttributeError:
            kw = None
        if kw:
            kw_text = repr(kw)
            return self.__class__.__name__ + "(" + args_text + ", **" + kw_text + ")" 
        else:
            return self.__class__.__name__ + "(" + args_text + ")" 
    
    def _asdict(self):
        'Return a new OrderedDict which maps field names to their values.'
        return OrderedDict(zip(self.__attrs__, self))
        
    for method in (__new__, _make, _replace, __repr__, _asdict,):
        method.__qualname__ = typename + "." + method.__name__        
        
    _make = classmethod(_make)

    __options__ = {'readonly':readonly, 'usedict':usedict, 'gc':gc, 'weakref':weakref, 
                   'hashable':hashable, 'assequence':assequence}
    
    class_namespace = {
        '__doc__': typename+'('+arg_list+')',
        '__attrs__': field_names,
        '__new__': __new__,
        '_make': _make,
        '_replace': _replace,
        '__repr__': __repr__,
        '_asdict': _asdict,
        '__options__': __options__,
    }
        
    _result = structclasstype(typename, (recordobject,), 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
示例#6
0
def structclass(typename,
                fields=None,
                use_dict=False,
                use_weakref=False,
                hashable=True,
                sequence=True,
                mapping=False,
                readonly=False,
                defaults=None,
                module=None,
                gc=False):

    from ._dataobject import _clsconfig, _enable_gc
    from ._dataobject import dataobject
    from .datatype import datatype

    annotations = {}
    if isinstance(fields, str):
        field_names = fields.replace(',', ' ').split()
        field_names = [fn.strip() for fn in field_names]
    else:
        msg = "make_dataclass('Name', [(f0, t0), (f1, t1), ...]); each t must be a type"
        field_names = []
        if isinstance(fields, dict):
            for fn, tp in fields.items():
                tp = _type_check(tp, msg)
                check_name(fn)
                fn = _intern(fn)
                annotations[fn] = tp
                field_names.append(fn)
        else:
            for fn in fields:
                if type(fn) is tuple:
                    fn, tp = fn
                    tp = _type_check(tp, msg)
                    annotations[fn] = tp
                check_name(fn)
                fn = _intern(fn)
                field_names.append(fn)

    n_fields = len(field_names)
    typename = check_name(typename)

    if defaults is not None:
        n_fields = len(field_names)
        defaults = tuple(defaults)
        n_defaults = len(defaults)
        if n_defaults > n_fields:
            raise TypeError('Got more default values than fields')
    else:
        defaults = None

    def _make(_cls, iterable):
        ob = _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

    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 _asdict(self):
        'Return a new OrderedDict which maps field names to their values.'
        return OrderedDict(zip(self.__fields__, self))

    for method in (
            _make,
            _replace,
            _asdict,
    ):
        method.__qualname__ = typename + "." + method.__name__

    _make = classmethod(_make)

    options = {
        'readonly': readonly,
        'defaults': defaults,
        'argsonly': False,
        'sequence': sequence,
        'mapping': mapping,
        'iterable': sequence,
        'use_dict': use_dict,
        'use_weakref': use_weakref,
        'readonly': readonly,
        'hashable': hashable,
        'gc': gc,
    }

    ns = {
        '_make': _make,
        '_replace': _replace,
        '_asdict': _asdict,
        '__doc__': typename + '(' + ', '.join(field_names) + ')',
        '__module__': module
    }

    if defaults:
        for i in range(-n_defaults, 0):
            fname = field_names[i]
            val = defaults[i]
            ns[fname] = val

    if use_dict and '__dict__' not in field_names:
        field_names.append('__dict__')
    if use_weakref and '__weakref__' not in field_names:
        field_names.append('__weakref__')

    ns['__options__'] = options
    ns['__fields__'] = field_names
    if annotations:
        ns['__annotations__'] = annotations

    bases = (dataobject, )

    if module is None:
        try:
            module = _sys._getframe(1).f_globals.get('__name__', '__main__')
        except (AttributeError, ValueError):
            pass

    ns['__module__'] = module

    cls = datatype(typename, bases, ns)

    if gc:
        _enable_gc(cls)

    return cls
示例#7
0
def structclass(typename,
                fields,
                rename=False,
                defaults=None,
                readonly=False,
                usedict=False,
                gc=False,
                weakref=False,
                hashable=False,
                assequence=True,
                module=None):
    """Returns a new subclass of array with named fields.

    >>> Point = structclass('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=33, y=22)
    >>> p._replace(x=100)               # _replace() is like str.replace() but targets named fields
    Point(x=100, y=22)
    """

    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_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 usedict:
        new_func_template = """\
def __new__(_cls, {1}, **kw):
    'Create new instance of {0}({1})'
    return _baseclass.__new__(_cls, {1}, **kw)
"""
    else:
        new_func_template = """\
def __new__(_cls, {1}):
    'Create new instance of {0}({1})'
    return _baseclass.__new__(_cls, {1})
"""
    new_func_def = new_func_template.format(typename, arg_list)

    #print(new_func_def)

    namespace = dict(_baseclass=recordobject)
    code = compile(new_func_def, "", "exec")
    eval(code, namespace)

    __new__ = namespace['__new__']
    if defaults is not None:
        __new__.__defaults__ = defaults

    #@classmethod
    def _make(_cls, iterable):
        ob = _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

    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'
        args_text = repr_fmt % tuple(self)
        try:
            kw = self.__dict__
        except AttributeError:
            kw = None
        if kw:
            kw_text = repr(kw)
            return self.__class__.__name__ + "(" + args_text + ", **" + kw_text + ")"
        else:
            return self.__class__.__name__ + "(" + args_text + ")"

    def _asdict(self):
        'Return a new OrderedDict which maps field names to their values.'
        return OrderedDict(zip(self.__attrs__, self))

    for method in (
            __new__,
            _make,
            _replace,
            __repr__,
            _asdict,
    ):
        method.__qualname__ = typename + "." + method.__name__

    _make = classmethod(_make)

    __options__ = {
        'readonly': readonly,
        'usedict': usedict,
        'gc': gc,
        'weakref': weakref,
        'hashable': hashable,
        'assequence': assequence
    }

    class_namespace = {
        '__doc__': typename + '(' + arg_list + ')',
        '__attrs__': field_names,
        '__new__': __new__,
        '_make': _make,
        '_replace': _replace,
        '__repr__': __repr__,
        '_asdict': _asdict,
        '__options__': __options__,
    }

    _result = structclasstype(typename, (recordobject, ), 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
示例#8
0
def make_dataclass(typename,
                   fields=None,
                   bases=None,
                   namespace=None,
                   varsize=False,
                   use_dict=False,
                   use_weakref=False,
                   hashable=True,
                   sequence=False,
                   mapping=False,
                   iterable=False,
                   readonly=False,
                   defaults=None,
                   module=None,
                   argsonly=False,
                   gc=False):

    from ._dataobject import _clsconfig, _enable_gc
    from ._dataobject import dataobject, datatuple
    from .datatype import datatype

    annotations = {}
    if isinstance(fields, str):
        fields = fields.replace(',', ' ').split()
        fields = [fn.strip() for fn in fields]
    else:
        msg = "make_dataclass('Name', [(f0, t0), (f1, t1), ...]); each t must be a type"
        field_names = []
        if isinstance(fields, dict):
            for fn, tp in fields.items():
                tp = _type_check(tp, msg)
                check_name(fn)
                fn = _intern(fn)
                annotations[fn] = tp
                field_names.append(fn)
        else:
            for fn in fields:
                if type(fn) is tuple:
                    fn, tp = fn
                    tp = _type_check(tp, msg)
                    annotations[fn] = tp
                check_name(fn)
                fn = _intern(fn)
                field_names.append(fn)
        fields = field_names
    typename = check_name(typename)

    if defaults is not None:
        n_fields = len(fields)
        defaults = tuple(defaults)
        n_defaults = len(defaults)
        if n_defaults > n_fields:
            raise TypeError('Got more default values than fields')
    else:
        defaults = None

    options = {
        'readonly': readonly,
        'defaults': defaults,
        'argsonly': argsonly,
        'sequence': sequence,
        'mapping': mapping,
        'iterable': iterable,
        'use_dict': use_dict,
        'use_weakref': use_weakref,
        'readonly': readonly,
        'hashable': hashable,
        'gc': gc,
    }

    if namespace is None:
        ns = {}
    else:
        ns = namespace.copy()

    if defaults:
        for i in range(-n_defaults, 0):
            fname = fields[i]
            val = defaults[i]
            ns[fname] = val

    if use_dict and '__dict__' not in fields:
        fields.append('__dict__')
    if use_weakref and '__weakref__' not in fields:
        fields.append('__weakref__')

    ns['__options__'] = options
    ns['__fields__'] = fields
    if annotations:
        ns['__annotations__'] = annotations

    if bases:
        base0 = bases[0]
        if varsize:
            if not isinstance(base0, datatuple):
                raise TypeError(
                    "First base class should be subclass of datatuple")
        else:
            if not isinstance(base0, dataobject):
                raise TypeError(
                    "First base class should be subclass of dataobject")
    else:
        if varsize:
            bases = (datatuple, )
        else:
            bases = (dataobject, )

    if varsize:
        _sequence = True
    else:
        _sequence = sequence

    if module is None:
        try:
            module = _sys._getframe(1).f_globals.get('__name__', '__main__')
        except (AttributeError, ValueError):
            pass

    ns['__module__'] = module

    cls = datatype(typename, bases, ns)

    if gc:
        _enable_gc(cls)

    return cls
示例#9
0
    def __new__(metatype, typename, bases, ns):        
        from ._dataobject import _clsconfig, _dataobject_type_init, dataslotgetset

        options = ns.pop('__options__', {})
        readonly = options.get('readonly', False)
        hashable = options.get('hashable', False)
        sequence = options.get('sequence', False)
        mapping = options.get('mapping', False)
        iterable = options.get('iterable', False)
        argsonly = options.get('argsonly', False)
        use_dict = options.get('use_dict', False)
        use_weakref = options.get('use_weakref', False)

        use_dict = False
        use_weakref = False

        if not bases:
            raise TypeError("The base class in not specified")

        if bases[0].__itemsize__:
            varsize = True
        else:
            varsize = False

        annotations = ns.get('__annotations__', {})

        if '__fields__' in ns:
            fields = ns['__fields__']
        else:
            fields = [name for name in annotations]

        has_fields = True
        if isinstance(fields, int_type):
            has_fields = False
            n_fields = fields
            sequence = True
            iterable = True
            fields = ()
        else:
            fields = [_intern(check_name(fn)) for fn in fields]

        if varsize:
            sequence = True
            iterable = True
            
        if sequence or mapping:
            iterable = True

        if has_fields:
            if annotations:
                annotations = {fn:annotations[fn] for fn in fields if fn in annotations}

            if '__dict__' in fields:
                fields.remove('__dict__')
                if '__dict__' in annotations:
                    del annotations['__dict__']
                use_dict = True

            if '__weakref__' in fields:
                fields.remove('__weakref__')
                if '__weakref__' in annotations:
                    del annotations['__weakref__']
                use_weakref = True

            _fields, _defaults, _annotations = collect_info_from_bases(bases)

            defaults = {f:ns[f] for f in fields if f in ns}
            _matching_annotations_and_defaults(annotations, defaults)

            if fields:
                fields = [f for f in fields if f not in _fields]
            fields = _fields + fields
            fields = tuple(fields)
            n_fields = len(fields)

            _defaults.update(defaults)
            defaults = _defaults

            _annotations.update(annotations)
            annotations = _annotations

            if fields and (not argsonly or defaults) and '__new__' not in ns:
                __new__ = _make_new_function(typename, fields, defaults, annotations, varsize, use_dict)
                __new__.__qualname__ = typename + '.' + '__new__'

                ns['__new__'] = __new__

        if has_fields:
            if readonly:
                if type(readonly) is type(True):
                    readonly_fields = set(fields)
                else:
                    readonly_fields = set(readonly)
            else:
                readonly_fields = set()

            for i, name in enumerate(fields):
                offset = dataslot_offset(i, n_fields, varsize)
                if name in readonly_fields:
                    ns[name] = dataslotgetset(offset, True)
                else:
                    ns[name] = dataslotgetset(offset)

        module = ns.get('__module__', None)
        if module is None:
            try:
                module = _sys._getframe(2).f_globals.get('__name__', '__main__')
                ns['__module'] = module
            except (AttributeError, ValueError):
                pass
        else:
            pass
                    
        cls = type.__new__(metatype, typename, bases, ns)
        
        if has_fields:
            cls.__fields__ = fields
            if defaults:
                cls.__defaults__ = defaults
            if annotations:
                cls.__annotations__ = annotations

        _dataobject_type_init(cls)
        _clsconfig(cls, sequence=sequence, mapping=mapping, readonly=readonly, use_dict=use_dict,
                   use_weakref=use_weakref, iterable=iterable, hashable=hashable)

        return cls