Esempio n. 1
0
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
Esempio n. 2
0
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
Esempio n. 3
0
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"
Esempio n. 4
0
    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)
Esempio n. 5
0
    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
Esempio n. 6
0
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)
Esempio n. 7
0
def test_bad_conform_different_varnames():
    with pytest.raises(codefind.ConformException):
        # Different set of free variables
        codefind.conform(alaska.pluz_one, alaska.plus_one)
Esempio n. 8
0
 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