Shallow review
self.call_re seems to be variable that does not change, is the same for all instance and is used only in __mul__.
You could define it as a "constant" or as a local variable in __mul__. The same thing applies to sep_re.
def __init__(self, *_):
self.t = T()
def __mul__(self, code):
CALL_RE = f'{self.__class__.__name__}\((.+)\).*'
return CALL_RE * code.count(self.__class__.__name__)
def setattrs(self, t, varnames, f_back):
SEP_RE = '\s*,\s*'
for name in split(SEP_RE, varnames.strip(', ')):
I think that normalise_pos would be easier to understand if it was written like this:
def normalize_pos(self, no):
self.__class__.no += 1
if self.__class__.no == no:
self.__class__.no = 0
in walk_f_back: if bool(unpacked) is True: can be re-written if unpacked:.
Also, Python does not handle deep recursions very well because it does not perform Tail Recursion Optimisation. If needed, you can rewrite the function:
def walk_f_back(f_back, packed):
while True:
unpacked = [unpacked_name for unpacked_name, v in f_back.f_locals.items() if v in f_back.f_locals[packed]]
if unpacked:
return unpacked
f_back = f_back.f_back
The formatting in the tests make things hard to read for no good reason. It's ok to go a few characters above the 80-char limits if is makes the code better. There's nothing wrong with:
with FrameHack(w, x) as t0:
with FrameHack(y, z) as t1, FrameHack(*xy, w) as t2, FrameHack(w, z) as t3:
In setvarname, you can get rid of continue.
if var is True:
for varname in cached.copy():
module.__dict__.pop(varname)
cached.remove(varname)
elif var[0] not in clean:
setattr(module, *var)
cached.add(var[0])
I'll try to have a real look at your code asap but I must confess it is quite hard to dive into that kind of things :)