Skip to content

Instantly share code, notes, and snippets.

@karanlyons
Last active August 7, 2017 17:47
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save karanlyons/99d6432dea951a7d92f9036b65971d24 to your computer and use it in GitHub Desktop.
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!
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)
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