Skip to content

Instantly share code, notes, and snippets.

@harvimt
Last active December 19, 2015 14:49
Show Gist options
  • Save harvimt/5972099 to your computer and use it in GitHub Desktop.
Save harvimt/5972099 to your computer and use it in GitHub Desktop.
Example metaclass implementation for Python 3 for the construct3 library
class CustomCollection():
def __init__(self):
super().__init__()
self.items = []
def __setitem__(self, key, value):
if key == '_':
self.items.append(value)
else:
self.items.append((key, value))
def __getitem__(self, key):
if key == '_':
raise KeyError('_')
return dict(self.items)[key]
class _MetaStruct(type):
""" metaclass for use by magic struct"""
@classmethod
def __prepare__(metacls, name, bases):
return CustomCollection()
def __new__(cls, name, bases, classdict):
if bases:
return Struct(*classdict.items())
else:
return super().__new__(name, bases, classdict.items())
class MagicStruct(metaclass=_MetaStruct):
""" syntactic sugar for making Structs"""
pass
class ExampleStruct(MagicStruct)
named = uint64l
_ = Embedded(Flags(uint64l,
flag1=0x01,
flag2=0x20,
))
@harvimt
Copy link
Author

harvimt commented Jul 14, 2013

how do you suppose to embed two structs? _ is already taken and OrderedDict won't help here. Also, there's Padding which doesn't get a name in the containing struct.

look again, I'm not using an OrderedDict, I'm using a CustomCollection, you can reuse names as much as you want, if the name is anything but _ it's passed to Struct.__init__ as a k,v tuple, and if it's _ it's just passed as v.

__prepare__ is pretty magical, it's lets you override the instance of dict used by class as the locals() while evaluating class body.

"Nested" becomes a field of the struct, since it's inserted into the class' dict... but it completely breaks the semantics of what a class is. that's bad.

This doesn't bother me since other schema-definition with metaclasses work this way. (see Flatland, SQLAlchemy, etc.), You're already breaking semantics by just using metaclasses to have class make an instance instead of a sub-class (though the Zen of metaclasses is that a sub-class is a new instance of type)

now what about the other constructs? e.g., Sequence is like an "unnamed struct", i.e., a tuple. that would clearly not work

yeah probably not you could make a metaclass interface, but it would be silly.

and how would "more complex" constructs, such as Switch, work?

class EthernetHeader(Struct):
     src = MacAddr()
     dst = MacAddr()
     type = ubint16
     class next(Switch(this.type)):
          @Switch.condition(0x1234)
          class IPHeader(Struct):
              #inline defintion of IPHeader, name would be ignored
              pass
         # - or - #
          _ = Switch.condition(0x1234)(IPHeader) #external types, would also be needed for simple types like 

ok that's pretty ugly. I suppose the solution to metaprogramming is MORE METAPROGRAMMING:

since __prepare__ lets us change how locals() every variable access or retrieval inside the class block can be overridden, I think you could make this work (with enough patience)

class EthernetHeader(Struct):
     src = MacAddr()
     dst = MacAddr()
     type = ubint16
     if this.type == 0x1234:
         next = IPHeader
     elif this.type == 0x2345:
         next = SomethingElse

(the access of this and the assignments to next can both be overriden)

But I fear that way lies madness.

(nm, there's no way to execute all if blocks, you'd have to use with or macros, but I think my point about madness still stands.)

I'm gonna play with the code a little bit and update it, see If I can get something reasonable for Switch (w/o macros)

@harvimt
Copy link
Author

harvimt commented Jul 14, 2013

How about:

next = Switch(this.type)
next[0x1234] = IPHeader

@next.condition(0x2345)
class next(MagicStruct):
pass

next[Switch.default] = uint64l

that could be made to work (w/o macros), but now there's machinery to do Switch inside the Struct metaclass, which I suppose isn't composable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment