|
|
from types import StringTypes |
from types import StringTypes |
|
|
|
__all__ = [ |
|
'structType', 'struct', 'makeStructType' |
|
] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class structType(type): |
class structType(type): |
|
|
"""Sets up __fieldmap__ and field properties""" |
"""Sets up __fieldmap__ and field properties""" |
|
|
|
classmethods = ( |
|
'fromArgs', 'fromOther', 'fromString', |
|
'fromMapping', 'extractFromMapping', |
|
) |
|
|
def __new__(klass, name, bases, cdict): |
def __new__(klass, name, bases, cdict): |
|
|
cdict = cdict.copy() |
cdict = cdict.copy() |
cdict['__slots__']=[] |
cdict['__slots__']=[] |
|
|
|
for cm in klass.classmethods: |
|
if cm in cdict: |
|
cdict[cm] = classmethod(cdict[cm]) |
|
|
return super(structType,klass).__new__(klass, name, bases, cdict) |
return super(structType,klass).__new__(klass, name, bases, cdict) |
|
|
|
|
setattr(klass, fieldName, makeFieldProperty(fieldName, fieldNum)) |
setattr(klass, fieldName, makeFieldProperty(fieldName, fieldNum)) |
|
|
|
|
|
def addField(klass, fieldName): |
|
|
|
"""Add field 'fieldName' to an existing struct class""" |
|
|
|
fm = klass.__fieldmap__ |
|
|
|
if fieldName not in fm: |
|
|
|
fm[fieldName] = fieldNum = len(fm) |
|
setattr(klass, fieldName, makeFieldProperty(fieldName,fieldNum)) |
|
klass.__fields__.append(fieldName) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Note: if you define custom properties, they only determine the *attributes* |
Note: if you define custom properties, they only determine the *attributes* |
of the instance. All other behaviors including string representation, |
of the instance. All other behaviors including string representation, |
iteration, item retrieval, etc., will be unaffected. It's probably best |
iteration, item retrieval, etc., will be unaffected. It's probably best |
to define a 'defaultCreate' classmethod to manage the initial construction |
to redefine the 'fromArgs' classmethod to manage the initial construction |
of the fields instead.""" |
of the fields instead.""" |
|
|
__metaclass__ = structType |
__metaclass__ = structType |
|
|
|
__fields__ = __converters__ = __defaults__ = () |
|
|
|
|
def __new__(klass, *__args, **__kw): |
def __new__(klass, *__args, **__kw): |
|
|
if __args: |
if len(__args)==1 and not __kw: |
|
|
arg = __args[0] |
arg = __args[0] |
|
|
if isinstance(arg,StringTypes): |
if isinstance(arg,StringTypes): |
return klass.fromString(*__args, **__kw) |
return klass.fromString(arg) |
|
|
elif isinstance(arg,dict): |
elif isinstance(arg,dict): |
return klass.fromMapping(*__args, **__kw) |
return klass.fromMapping(arg) |
|
|
|
return klass.fromOther(arg) |
|
|
|
|
elif __kw: |
elif __kw and not __args: |
return klass.fromMapping(__kw) |
return klass.fromMapping(__kw) |
|
|
return klass.defaultCreate(*__args, **__kw) |
return klass.fromArgs(*__args, **__kw) |
|
|
|
|
def defaultCreate(klass, *args, **kw): |
def fromString(klass, arg): |
"""Create from sequence/other objects |
|
|
|
You can define a classmethod here, to be used in place of |
"""Override this classmethod to enable parsing from a string""" |
'tuple.__new__' when the struct is being created from something |
|
other than a dict, keywords, or a string. This is also a good |
raise NotImplementedError |
place to do any calculations or manipulations on the field values |
|
before they're cast in stone. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def fromArgs(klass, *__args, **__kw): |
|
|
|
"""Create from arguments |
|
|
|
By default, this classmethod is where all the other creation |
|
methods "call down" to, so that you can do any validation or |
|
conversions. The default implementation just calls |
|
'tuple.__new__' on the '*__args' tuple. You should override |
|
this with a classmethod that takes the arguments you want, in |
|
the same order as your '__fields__' definition, supplying |
|
defaults if desired. |
|
|
The default version of this method will accept input sequences |
The default version of this method will accept input sequences |
with more items than there are fields to fill. The extra data |
with more items than there are fields to fill. The extra data |
If you want different behavior, such as truncating the sequence |
If you want different behavior, such as truncating the sequence |
or raising an exception, you'll need to override this method. |
or raising an exception, you'll need to override this method. |
""" |
""" |
# this is just a dummy so HappyDoc will document the method, |
|
# we'll replace it with tuple.__new__ below for performance |
|
|
|
defaultCreate = classmethod(tuple.__new__, __doc__= defaultCreate.__doc__) |
if __kw: |
|
raise TypeError("Invalid keyword arguments for " + klass.__name__) |
|
|
|
return tuple.__new__(klass, __args) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def fromString(klass, arg): |
|
"""Override this classmethod to enable parsing from a string""" |
|
raise NotImplementedError |
|
|
|
fromString = classmethod(fromString) |
|
|
def fromOther(klass, arg): |
|
|
|
"""Create from a single argument |
|
|
|
You can define a classmethod here, to be used in place of |
|
'tuple.__new__' when the struct is being created from a single |
|
argument that is not a dictionary, keywords, or a string. |
|
|
|
The default simply hands the argument through to the |
|
'fromArgs()' method, where it will be treated as being the |
|
first field of the struct. |
|
""" |
|
return klass.fromArgs(arg) |
|
|
|
|
def fromMapping(klass, arg): |
def fromMapping(klass, arg): |
% klass.__name__), arg |
% klass.__name__), arg |
) |
) |
|
|
return tuple.__new__(klass, map(arg.get, klass.__fields__)) |
return klass.fromArgs(*tuple(map(arg.get, klass.__fields__))) |
|
|
fromMapping = classmethod(fromMapping) |
|
|
|
|
|
def extractFromMapping(klass, arg): |
def extractFromMapping(klass, arg): |
"""Fast extraction from a mapping; ignores undefined fields""" |
"""Fast extraction from a mapping; ignores undefined fields""" |
return tuple.__new__(klass, map(arg.get, klass.__fields__)) |
return klass.fromArgs(*tuple(map(arg.get, klass.__fields__))) |
|
|
extractFromMapping = classmethod(extractFromMapping) |
|
|
|
def __getitem__(self, key): |
def __getitem__(self, key): |
|
|
except (KeyError,IndexError): |
except (KeyError,IndexError): |
return default |
return default |
|
|
|
|
def copy(self): return dict(self.items()) |
def copy(self): return dict(self.items()) |
def keys(self): return list(self.__fields__[:len(self)]) |
def keys(self): return list(self.__fields__[:len(self)]) |
def iterkeys(self): return iter(self.__fields__[:len(self)]) |
def iterkeys(self): return iter(self.__fields__[:len(self)]) |
def values(self): return list(self) |
def values(self): return list(self) |
def itervalues(self): return iter(self) |
def itervalues(self): return iter(self) |
|
|
|
|
|
|
|
|
|
|
__safe_for_unpickling__ = True |
__safe_for_unpickling__ = True |
|
|
def __reduce__(self): return self.__class__, (tuple(self),) |
def __reduce__(self): return self.__class__, (tuple(self),) |
|
|
has_key = __contains__ |
has_key = __contains__ |
|
|
def makeFieldProperty(klass, fieldName, fieldNum): |
|
|
|
|
def makeFieldProperty(fieldName, fieldNum): |
|
|
def get(self): |
def get(self): |
try: |
try: |
return tuple.__getitem__(self,fieldNum) |
return tuple.__getitem__(self,fieldNum) |
except IndexError: |
except IndexError: |
pass |
return None |
|
|
return property(get) |
return property(get) |
|
|
|
|
def makeStructType(name, fields, baseType=struct, **kw): |
def makeStructType(name, fields, baseType=struct, **kw): |
bases = bases or (struct,) |
|
kw['__fields__'] = fields |
kw['__fields__'] = fields |
return structType(name or 'anonymous_struct', (baseType,), kw) |
return structType(name or 'anonymous_struct', (baseType,), kw) |
|
|