Skip to content

Instantly share code, notes, and snippets.

@me2beats
Last active September 16, 2019 22:46
Show Gist options
  • Save me2beats/6ade679d95a23f4c4c0cb83a5d065b68 to your computer and use it in GitHub Desktop.
Save me2beats/6ade679d95a23f4c4c0cb83a5d065b68 to your computer and use it in GitHub Desktop.
Deco
from inspect import currentframe, getouterframes
class CalledFromHereBhv:
def __init__(self):
curframe = currentframe()
self.called_from_module_fn,\
self.called_from_start_line =\
self._get_module_and_start_line(curframe)
def _get_module_and_start_line(self, curframe):
module, start_line = None, None
calframe = getouterframes(curframe, 2)
for frame_info in calframe:
context = frame_info.code_context
for i in context:
if 'with Deco' in i:
module, start_line = frame_info[1:3]
return (module, start_line)
from sys import modules as sys_modules
import inspect
from os import path
from called_from_here_bhv import CalledFromHereBhv
from SkipWith import SkipWith
from dict_utils import get_by_val_attr
class Deco(CalledFromHereBhv, SkipWith):
def __init__(self, decorator_name):
super().__init__()
self.decorator_name = decorator_name
module_fn = self.called_from_module_fn
module = get_by_val_attr(sys_modules, '__file__', module_fn)
start_line = self.called_from_start_line
src_lines = self.get_codeblock_source(module_fn, start_line)
new_foo_lines = self.make_decorated_foo(src_lines)
new_foo_str = '\n'.join(new_foo_lines)
module_globals = module.__dict__
exec (new_foo_str, module_globals)
def get_codeblock_source(self,module_fn, start_line):
l = []
first_line_spaces = 0
with open(module_fn) as src:
for i, line in enumerate(src):
if i<start_line-1: continue
l.append(line)
if i==start_line-1:
first_line_spaces = self.count_first_spaces(line)
elif i > start_line-1:
if self.count_first_spaces(line)<=first_line_spaces:
break
l = [self.undent(line, first_line_spaces) for (line) in l]
return l
def count_first_spaces(self, s):
return len(s) - len(s.lstrip(' '))
def undent(self, s, min_):
return s[min_:]
def make_decorated_foo(self, src_lines):
foo_lines = self.replace_head(src_lines)
decorated_foo_lines = self.decorate(foo_lines, self.decorator_name)
self.add_call(decorated_foo_lines)
return decorated_foo_lines
def replace_head(self, src_lines):
src_lines[0] = 'def my_temp_foo():'
return src_lines
def decorate(self, src_lines, decorator_name):
src_lines.insert(0, '@'+decorator_name)
return src_lines
def add_call(self, src_lines):
src_lines.append('my_temp_foo()')
def get_by_val_attr(dct, attr_name, attr_val, ret = 'v'):
for k, v in dct.items():
try:
if getattr(v,attr_name) != attr_val: continue
if ret == 'v':
return v
elif ret == 'k':
return k
elif ret == 'kv':
return (k, v)
except: pass
import test
from sys import settrace, _getframe
class SkipWithExc(Exception):
pass
class SkipWith:
def __enter__(self):
settrace(lambda *args, **keys: None)
frame = _getframe(1)
frame.f_trace = self.trace
def trace(self, frame, event, arg):
raise SkipWithExc()
def __exit__(self, type, value, traceback):
if type is None:
return # No exception
if issubclass(type, SkipWithExc):
return True # Suppress special SkipWithBlock exception
from Deco import *
def decorated(foo):
def inner_decorator():
foo()
foo()
return inner_decorator
def decorated1(foo):
def inner_decorator():
print('ok')
return inner_decorator
h = 'hewwo'
w = 'worwd'
with Deco('decorated'):
print(h, w)
if True:
with Deco('decorated1'):
print(h)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment