Exemple #1
0
    def _registerWithBlock(self, objectId, pyObject):
        """
        `_registerWithBlock`: register a `PyforaWithBlock.PyforaWithBlock`
        with `self.objectRegistry`.

        Recursively call `walkPyObject` on the resolvable free variable
        member access chains in the block and on the file object.
        """
        lineNumber = pyObject.lineNumber
        sourceTree = PyAstUtil.pyAstFromText(pyObject.sourceText)
        withBlockAst = PyAstUtil.withBlockAtLineNumber(sourceTree, lineNumber)

        withBlockFun = ast.FunctionDef(name="",
                                       args=ast.arguments(args=[],
                                                          defaults=[],
                                                          kwarg=None,
                                                          vararg=None),
                                       body=withBlockAst.body,
                                       decorator_list=[],
                                       lineno=lineNumber,
                                       col_offset=0)

        if PyAstUtil.hasReturnInOuterScope(withBlockFun):
            raise Exceptions.InvalidPyforaOperation(
                "return statement not supported in pyfora with-block (line %s)"
                % PyAstUtil.getReturnLocationsInOuterScope(withBlockFun)[0])

        if PyAstUtil.hasYieldInOuterScope(withBlockFun):
            raise Exceptions.InvalidPyforaOperation(
                "yield expression not supported in pyfora with-block (line %s)"
                % PyAstUtil.getYieldLocationsInOuterScope(withBlockFun)[0])

        freeVariableMemberAccessChainsWithPositions = \
            self._freeMemberAccessChainsWithPositions(withBlockFun)

        boundValuesInScopeWithPositions = \
            PyAstFreeVariableAnalyses.collectBoundValuesInScope(
                withBlockFun, getPositions=True)

        for boundValueWithPosition in boundValuesInScopeWithPositions:
            val, pos = boundValueWithPosition
            if val not in pyObject.unboundLocals and val in pyObject.boundVariables:
                freeVariableMemberAccessChainsWithPositions.add(
                    PyAstFreeVariableAnalyses.VarWithPosition(var=(val, ),
                                                              pos=pos))

        try:
            freeVariableMemberAccessChainResolutions = \
                self._resolveFreeVariableMemberAccessChains(
                    freeVariableMemberAccessChainsWithPositions, pyObject.boundVariables
                    )
        except UnresolvedFreeVariableException as e:
            _convertUnresolvedFreeVariableExceptionAndRaise(
                e, pyObject.sourceFileName)

        try:
            processedFreeVariableMemberAccessChainResolutions = {}
            for chain, (resolution, position) in \
                freeVariableMemberAccessChainResolutions.iteritems():
                processedFreeVariableMemberAccessChainResolutions['.'.join(chain)] = \
                    self.walkPyObject(resolution)
        except UnresolvedFreeVariableExceptionWithTrace as e:
            e.addToTrace(
                Exceptions.makeTraceElement(path=pyObject.sourceFileName,
                                            lineNumber=position.lineno))
            raise

        sourceFileId = self.walkPyObject(
            _FileDescription.cachedFromArgs(fileName=pyObject.sourceFileName))

        self._objectRegistry.defineWithBlock(
            objectId=objectId,
            freeVariableMemberAccessChainsToId=\
                processedFreeVariableMemberAccessChainResolutions,
            sourceFileId=sourceFileId,
            lineNumber=lineNumber
            )
Exemple #2
0
    def _registerWithBlock(self, objectId, pyObject):
        """
        `_registerWithBlock`: register a `PyforaWithBlock.PyforaWithBlock`
        with `self.objectRegistry`.

        Recursively call `walkPyObject` on the resolvable free variable
        member access chains in the block and on the file object.
        """
        lineNumber = pyObject.lineNumber
        sourceTree = PyAstUtil.pyAstFromText(pyObject.sourceText)
        withBlockAst = PyAstUtil.withBlockAtLineNumber(sourceTree, lineNumber)

        withBlockFun = ast.FunctionDef(
            name="",
            args=ast.arguments(args=[], defaults=[], kwarg=None, vararg=None),
            body=withBlockAst.body,
            decorator_list=[],
            lineno=lineNumber,
            col_offset=0
            )

        if PyAstUtil.hasReturnInOuterScope(withBlockFun):
            raise Exceptions.BadWithBlockError(
                "return statement not supported in pyfora with-block (line %s)" %
                PyAstUtil.getReturnLocationsInOuterScope(withBlockFun)[0])

        if PyAstUtil.hasYieldInOuterScope(withBlockFun):
            raise Exceptions.BadWithBlockError(
                "yield expression not supported in pyfora with-block (line %s)" %
                PyAstUtil.getYieldLocationsInOuterScope(withBlockFun)[0])

        freeVariableMemberAccessChainsWithPositions = \
            self._freeMemberAccessChainsWithPositions(withBlockFun)

        boundValuesInScopeWithPositions = \
            PyAstFreeVariableAnalyses.collectBoundValuesInScope(
                withBlockFun, getPositions=True)

        for boundValueWithPosition in boundValuesInScopeWithPositions:
            val, pos = boundValueWithPosition
            if val not in pyObject.unboundLocals and val in pyObject.boundVariables:
                freeVariableMemberAccessChainsWithPositions.add(
                    PyAstFreeVariableAnalyses.VarWithPosition(var=(val,), pos=pos)
                    )

        try:
            freeVariableMemberAccessChainResolutions = \
                self._resolveFreeVariableMemberAccessChains(
                    freeVariableMemberAccessChainsWithPositions, pyObject.boundVariables
                    )
        except UnresolvedFreeVariableException as e:
            _convertUnresolvedFreeVariableExceptionAndRaise(e, pyObject.sourceFileName)

        try:
            processedFreeVariableMemberAccessChainResolutions = {}
            for chain, (resolution, position) in \
                freeVariableMemberAccessChainResolutions.iteritems():
                processedFreeVariableMemberAccessChainResolutions['.'.join(chain)] = \
                    self.walkPyObject(resolution)
        except UnresolvedFreeVariableExceptionWithTrace as e:
            e.addToTrace(
                Exceptions.makeTraceElement(
                    path=pyObject.sourceFileName,
                    lineNumber=position.lineno
                    )
                )
            raise

        sourceFileId = self.walkPyObject(
            _FileDescription.cachedFromArgs(
                fileName=pyObject.sourceFileName
                )
            )

        self._objectRegistry.defineWithBlock(
            objectId=objectId,
            freeVariableMemberAccessChainsToId=\
                processedFreeVariableMemberAccessChainResolutions,
            sourceFileId=sourceFileId,
            lineNumber=lineNumber
            )
    def instantiateFunction(self, filename, lineNumber, memberDictionary,
                            file_text):
        """Instantiate a function instance."""

        memberDictionary = {
            k: v
            for k, v in memberDictionary.iteritems()
            if not isinstance(v, Exceptions.PyforaNameError)
        }

        objectOrNone = self.moduleLevelObject(filename, lineNumber)
        if objectOrNone is not None:
            return objectOrNone

        sourceAst = PyAstUtil.pyAstFromText(file_text)
        functionAst = PyAstUtil.functionDefOrLambdaOrWithBlockAtLineNumber(
            sourceAst, lineNumber)

        outputLocals = {}
        globalScope = {}
        globalScope.update(memberDictionary)
        self.importModuleMagicVariables(globalScope, filename)

        if isinstance(functionAst, ast.Lambda):
            expr = ast.FunctionDef()
            expr.name = '__pyfora_lambda_builder__'
            expr.args = ast.arguments()
            expr.args.args = []
            expr.args.defaults = []
            expr.args.vararg = None
            expr.args.kwarg = None

            expr.decorator_list = []
            expr.lineno = functionAst.lineno - 1
            expr.col_offset = functionAst.col_offset

            return_statement = ast.Return(functionAst)
            expr.body = [return_statement]

            expr = updatePyAstMemberChains(ast.Module([expr],
                                                      lineno=1,
                                                      col_offset=0),
                                           globalScope,
                                           isClassContext=True)

            code = compile(expr, filename, 'exec')

            exec code in globalScope, outputLocals

            return list(outputLocals.values())[0]()

        elif isinstance(functionAst, ast.With):
            expr = ast.FunctionDef()
            expr.name = '__pyfora_with_block_as_function__'
            expr.args = ast.arguments()
            expr.args.args = []
            expr.args.defaults = []
            expr.args.vararg = None
            expr.args.kwarg = None

            expr.decorator_list = []

            #make sure we copy the list - if we use the existing one, we will mess up the
            #cached copy!
            expr.body = list(functionAst.body)
            expr.lineno = functionAst.lineno - 1
            expr.col_offset = functionAst.col_offset

            bound_variables = PyAstFreeVariableAnalyses.collectBoundValuesInScope(
                expr)

            return_dict_list_src = "[" + ",".join(
                "'%s'" % k for k in bound_variables) + "]"
            return_dict_expr_src = "{{k: __locals[k] for k in {return_dict_list_src} if k in __locals}}".format(
                return_dict_list_src=return_dict_list_src)
            return_dict_expr_src = "(lambda __locals: {return_dict_expr_src})(dict(locals()))".format(
                return_dict_expr_src=return_dict_expr_src)

            return_dict_expr = ast.parse(return_dict_expr_src).body[0].value

            return_statement = ast.Return(
                ast.Tuple(
                    [return_dict_expr,
                     ast.Num(0), ast.Num(0)], ast.Load()))

            return_statement_exception = ast.Return(
                ast.Tuple([
                    return_dict_expr,
                    ast.Call(
                        ast.Name("__pyfora_get_exception_traceback__",
                                 ast.Load()), [], [], None, None),
                    ast.Name("__pyfora_exception_var__", ast.Load())
                ], ast.Load()))

            expr.body.append(return_statement)

            handler = ast.ExceptHandler(
                None, ast.Name("__pyfora_exception_var__", ast.Store()),
                [return_statement_exception])

            #now wrap in a try-catch block
            curBody = list(expr.body)
            expr.body = [ast.TryExcept(curBody, [handler], [])]

            #for every incoming variable 'x' that's also assigned to, create a dummy '__pyfora_var_guard_x' that actually
            #takes the value in from the surrounding scope, and immediately assign it
            for var in memberDictionary:
                if var in bound_variables:
                    newVar = "__pyfora_var_guard_" + var

                    var_copy_expr = ast.Assign(
                        targets=[ast.Name(var, ast.Store())],
                        value=ast.Name(newVar, ast.Load()))

                    globalScope[newVar] = globalScope[var]
                    del globalScope[var]

                    expr.body = [var_copy_expr] + expr.body

            expr = updatePyAstMemberChains(expr,
                                           globalScope,
                                           isClassContext=True)

            ast.fix_missing_locations(expr)

            def extractTrace():
                return sys.exc_info()[2]

            globalScope['__pyfora_get_exception_traceback__'] = extractTrace

            code = compile(ast.Module([expr]), filename, 'exec')

            exec code in globalScope, outputLocals
            assert len(outputLocals) == 1
            return list(outputLocals.values())[0]
        else:
            functionAst = updatePyAstMemberChains(ast.Module([functionAst],
                                                             lineno=1,
                                                             col_offset=0),
                                                  globalScope,
                                                  isClassContext=False)

            code = compile(functionAst, filename, 'exec')

            exec code in globalScope, outputLocals
            assert len(outputLocals) == 1
            return list(outputLocals.values())[0]