Last active
December 19, 2015 14:49
-
-
Save harvimt/5972099 to your computer and use it in GitHub Desktop.
Example metaclass implementation for Python 3 for the construct3 library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | |
)) |
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
look again, I'm not using an
OrderedDict
, I'm using aCustomCollection
, you can reuse names as much as you want, if the name is anything but _ it's passed toStruct.__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 ofdict
used byclass
as thelocals()
while evaluating class body.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 oftype
)yeah probably not you could make a metaclass interface, but it would be silly.
ok that's pretty ugly. I suppose the solution to metaprogramming is MORE METAPROGRAMMING:
since
__prepare__
lets us change howlocals()
every variable access or retrieval inside theclass
block can be overridden, I think you could make this work (with enough patience)(the access of
this
and the assignments tonext
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)