Last active
August 7, 2017 17:47
-
-
Save karanlyons/99d6432dea951a7d92f9036b65971d24 to your computer and use it in GitHub Desktop.
method_missing for Python: All the headaches of Ruby, now with added whitespace!
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
import dis | |
import inspect | |
class MethodMissingMixin: | |
def __getattr__(self, attr): | |
if hasattr(getattr(self, '__methodmissing__', None), '__call__'): | |
parent_frame = inspect.currentframe().f_back | |
instructions = dis.get_instructions(parent_frame.f_code) | |
for instruction in instructions: | |
if ( | |
instruction.offset >= parent_frame.f_lasti and | |
instruction.opname == 'LOAD_ATTR' and | |
instruction.argval == attr | |
): | |
stack_offset = 0 | |
break | |
for instruction in instructions: | |
stack_offset += dis.stack_effect(instruction.opcode, instruction.arg) | |
if stack_offset == 0 and instruction.opname.startswith('CALL_FUNCTION'): | |
return lambda *args, **kwargs: self.__methodmissing__(attr, *args, **kwargs) | |
elif stack_offset < 0: | |
break | |
return self.__methodmissing__(attr) | |
return super().__getattr__(attr) |
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
from method_missing import MethodMissingMixin | |
class XML(MethodMissingMixin, object): | |
def __init__(self, stack=None): | |
self._stack = stack if stack is not None else [] | |
def __methodmissing__(self, name, *args, **kwargs): | |
return XML(self._stack + [(name, args, kwargs)]) | |
def _build_tree(self, i_start=0): | |
tree = [] | |
for i, (name, children, attrs) in enumerate(self._stack, i_start): | |
tree.append('%s<%s%s%s>' % ( | |
'\t' * i, name, | |
' ' if attrs else '', | |
' '.join('%s=%r' % (key, value) for key, value in attrs.items())) | |
) | |
for child in children: | |
if isinstance(child, XML): | |
tree.extend(child._build_tree(i + 1)) | |
else: | |
tree.append('%s%s' % ('\t' * (i + 1), child)) | |
for j, (name, children, attrs) in enumerate(reversed(self._stack)): | |
tree.append('%s</%s>' % ('\t' * (i - j), name)) | |
return tree | |
def __str__(self, i_start=0): | |
return '\n'.join(self._build_tree()) | |
x = XML() | |
print(x.html( | |
x.head, | |
x.body( | |
x.p( | |
'What hath God wrought.', | |
id='abomination', | |
) | |
), | |
lang='en' | |
)) | |
# Output: | |
# <html lang='en'> | |
# <head> | |
# </head> | |
# <body> | |
# <p id='abomination'> | |
# What hath God wrought. | |
# </p> | |
# </body> | |
# </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment