def test_conform_closure(): mul_three = muller(3) assert alaska.mul_two(6) == 12 assert mul_three(6) == 18 codefind.conform(alaska.mul_two.__code__, alaska.div_three) assert alaska.mul_two(6) == 3 assert mul_three(6) == 2
def test_custom_conform(): from . import jackfruit assert jackfruit.jack1(3, 4) == 12 assert jackfruit.jack2(3, 4) == 12 assert jackfruit.jack1.__code__.co_name == "jack1" assert jackfruit.jack2.__code__.co_name == "jack2" codefind.conform(jackfruit.jack1.__code__, jackfruit.newjack) assert jackfruit.jack1(3, 4) == 7 assert jackfruit.jack2(3, 4) == 7 assert jackfruit.jack1.__code__.co_name == "newjack" assert jackfruit.jack2.__code__.co_name == "jack2" # Trigger a special path in collect_all codefind.collect_all() assert len(codefind.code_registry.functions[jackfruit.jack1.__code__]) == 3
def test_conform(): assert ice() == "berg" codefind.conform(ice, ice2) assert ice() == "cream" codefind.conform(ice, ice3) assert ice() == "box" codefind.conform(ice, None) assert ice() == "box"
def _process_child_correspondence(self, ccorr, order, controller): orig = ccorr.original new = ccorr.new try: if orig is None: if controller("pre-add", ccorr): # Addition self.append(new, ensure_separation=True) self.evaluate_child(new) controller("post-add", ccorr) elif new is None: if controller("pre-delete", ccorr): # Deletion conform(orig.get_object(), None) controller("post-delete", ccorr) else: self.append(orig, ensure_separation=True) elif ccorr.changed: # Change self.append(orig, ensure_separation=True) try: orig.apply_correspondence( ccorr, order=order, controller=controller, ) except ConformException: self.children.pop() self._process_child_correspondence( Correspondence.valid(None, new), order=order, controller=controller, ) else: self.append(orig, ensure_separation=True) except Exception as exc: controller("error", ccorr, exc=exc)
def recode(self, new_code, recode_current=True, use_cache=False): # Gather the code objects of all closures into subcodes subcodes = {} def _fill_subcodes(code, path): subcodes[path] = code for co in code.co_consts: if isinstance(co, CodeType): _fill_subcodes(co, (*path, co.co_name)) here = self.codepath() _fill_subcodes(new_code, here) if not recode_current: del subcodes[here] # Synchronize changes in closure codes for closure in self.walk(): if isinstance(closure, FunctionDefinition) and (subcode := subcodes.get( closure.codepath(), None)): co = closure.get_object() if co is not subcode: conform(co, subcode, use_cache=use_cache) closure._codeobj = subcode
def test_bad_conform_method(): with pytest.raises(codefind.ConformException): # evolve uses super() which is a closure over __class__ codefind.conform(alaska.Bear.evolve, ice)
def test_bad_conform_different_varnames(): with pytest.raises(codefind.ConformException): # Different set of free variables codefind.conform(alaska.pluz_one, alaska.plus_one)
def reevaluate(self, new_node, glb): ext = new_node.extent closure = False lcl = {} new_node = type(new_node)( name=new_node.name, args=new_node.args, body=new_node.body, decorator_list=[], returns=new_node.returns, type_comment=new_node.type_comment, lineno=new_node.lineno, col_offset=new_node.col_offset, end_lineno=new_node.end_lineno, end_col_offset=new_node.end_col_offset, ) previous = lcl.get(self.name, None) if self.variables.closure: # Because reevaluate is typically not run on closures, this code # path is essentially only entered for functions that use super(), # since they are implicit closures on __class__ closure = True names = tuple(sorted(self.variables.closure)) wrap = ast.copy_location( ast.FunctionDef( name="##create_closure", args=ast.arguments( posonlyargs=[], args=[ ast.arg(arg=name, lineno=new_node.lineno, col_offset=0) for name in names ], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[], ), body=[ new_node, ast.Return(ast.Name(id=new_node.name, ctx=ast.Load())), ], decorator_list=[], returns=None, ), new_node, ) ast.fix_missing_locations(wrap) node = ast.Module(body=[wrap], type_ignores=[]) else: node = ast.Module(body=[new_node], type_ignores=[]) code = compile(node, mode="exec", filename=ext.filename) code = code.replace(co_name="<adjust>") exec(code, glb, lcl) if closure: creator = lcl["##create_closure"] # It does not matter what arguments we provide here, because we will move the # function's __code__ elsewhere, so it will use a different closure new_obj = creator(*names) else: new_obj = lcl[self.name] lcl[self.name] = previous node.extent = ext self.node = node conform(self.get_object(), new_obj) self._codeobj = new_obj.__code__ return new_obj