Ejemplo n.º 1
0
    def visit_other_field(self, node: Node) -> VisitResult:
        if isinstance(node, dict):
            return self.visit(node)
        elif isinstance(node, list) or isinstance(node, tuple):
            return [self.visit(x) for x in node]
        else:
            # string attribute
            return node


if __name__ == '__main__':
    import sys
    import importlib.util
    from pprint import pprint

    if len(sys.argv) > 1:
        from pydetector.ast2dict import ast2dict
        codestr = open(sys.argv[1]).read()
        testdict = ast2dict(codestr)
    else:
        codestr = open("../test/fixtures/detector.py").read()
        spec = importlib.util.spec_from_file_location("module.testmod",
                                                      "../test/fixtures/exported_dict.py")
        testmod = importlib.util.module_from_spec(spec)

        if spec.loader:
            spec.loader.exec_module(testmod)
            testdict = testmod.testdict  # type: ignore

    pprint(AstImprover(codestr, testdict).parse())
def check_ast(code,
              try_other_on_sucess=False,
              verbosity=0,
              py2_exec='/usr/bin/python2',
              py3_exec='/usr/bin/python3'):
    """
    Try with the ast.parse of both Python 2 and 3 and then
    iterate over the retrieved AST to find specific syntax elements.

    Args:
        code (str) the source code to extract the AST for.

        try_other_on_sucess (bool): if the AST parser of the Python version that is
            running this script work try anyway with the other interpreter (True) or
            not (False). Please note that enabling this will usually increase a lot
            the "detection" rate of files as the version of the current interpreter.

        verbosity (int): level from 0 to 2. > 1 will show exceptions when parsing
            the AST.

        py2_exec (str): path or name (if in PATH) of the Python 2 interpreter when running
            this under Python 3.

        py3_exec (str): path or name (if in PATH) of the Python 3 interpreter to use when
            running this under Python 2.
    """

    current_ok = other_ok = False
    current_ast = other_ast = None
    py2_error = py3_error = current_error = other_error = ""

    pyexec_other = py2_exec if PYMAJOR_OTHER == 2 else py3_exec

    try:
        current_ast = ast2dict(code)
        current_ok = True
    except:
        # current_ok remains false
        current_error = format_exc()
        if verbosity > 1:
            print(
                '>>>> ASTCHECK: exception while parsing AST with Python%d:\n%s\n<<<< exception output end'
                % (PYMAJOR_CURRENT, current_error))

    if verbosity:
        print('AST extractable with version %d?: %s' %
              (PYMAJOR_CURRENT, str(current_ok)))

    if not current_ok or try_other_on_sucess:
        # Open an external interpreter and try to export its AST
        cmd = [
            pyexec_other, "-c", "import ast,pydetector.ast2dict,sys;"
            "r=sys.stdin.read();"
            "print(pydetector.ast2dict.ast2dict(r))"
        ]

        if verbosity > 1:
            print('Running in other Python:\n%s' % ' '.join(cmd))

        try:
            p = subprocess.Popen(cmd,
                                 stdin=subprocess.PIPE,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
            out, err = p.communicate(code.encode('utf-8'))
            if p.returncode == 0:
                if PYMAJOR_CURRENT == 3:
                    # decode to (unicode) str and remove the "l or L" from long literals
                    out = re.sub(r"('n': [0-9]+)[lL],", "\\1,",
                                 out.decode('utf-8'))

                other_ast = ast.literal_eval(out)
                other_ok = True
            else:
                other_error = err
                if verbosity > 1:
                    print(
                        '>>>> ASTCHECK: error while parsing AST with Python%d:\n%s\n<<<< error output end'
                        % (PYMAJOR_OTHER, err))
        except:
            other_ok = False
            other_error = format_exc()
            if verbosity > 1:
                print(
                    '>>>> ASTCHECK: exception while parsing AST with Python%d:\n%s\n<<<< exception output end'
                    % (PYMAJOR_OTHER, other_error))

    if verbosity:
        print('AST extractable with version %d?: %s' %
              (PYMAJOR_OTHER, str(other_ok)))

    if PYMAJOR_CURRENT == 2:
        py2_ast = current_ast
        py2_error = current_error
        py3_ast = other_ast
        py3_error = other_error
    else:
        py3_ast = current_ast
        py3_error = current_error
        py2_ast = other_ast
        py2_error = other_error

    version = 0
    if current_ok and not other_ok:
        version = PYMAJOR_CURRENT
    elif other_ok and not current_ok:
        version = PYMAJOR_OTHER
    elif current_ok and other_ok:
        version = 6

    return version, py2_ast, py3_ast, py2_error, py3_error