예제 #1
0
def process_python_dependencies(localpath, proto_dir, action, pkg_vars):
        """Given the path to a python file, the proto area containing that file,
        the action that produced the dependency, and the variants against which
        the action's package was published, produce a list of PythonDependency
        objects."""

        mf = modulefinder.DepthLimitedModuleFinder(proto_dir)
        mf.run_script(localpath, depth=1)
        deps = []
        errs = []
        for m in mf.modules.values():
                if m.__name__ == "__main__":
                        # The file at localpath is returned as a loaded module
                        # under the name __main__.
                        continue
                
                if m.__file__ is not None:
                        deps.append(PythonDependency(action, m.__file__,
                            pkg_vars, proto_dir))
                else:
                        errs.append(PythonModuleMissingPath(m.__name__,
                            localpath))
        return deps, errs
예제 #2
0
파일: python.py 프로젝트: omniosorg/pkg5
def process_python_dependencies(action, pkg_vars, script_path, run_paths):
    """Analyze the file delivered by the action for any python dependencies.

        The 'action' parameter contain the action which delivers the file.

        The 'pkg_vars' parameter contains the variants against which
        the action's package was published.

        The 'script_path' parameter is None of the file is not executable, or
        is the path for the binary which is used to execute the file.

        The 'run_paths' parameter is a list of paths that should be searched
        for modules.
        """

    # There are three conditions which determine whether python dependency
    # analysis is performed on a file with python in its #! line.
    # 1) Is the file executable. (Represented in the table below by X)
    # 2) Is the file installed into a directory which provides information
    #     about what version of python should be used for it.
    #     (Represented by D)
    # 3) Does the first line of the file include a specific version of
    #     python. (Represented by F)
    #
    # Conditions || Perform Analysis
    #  X  D  F   || Y, if F and D disagree, display a warning in the output
    #            ||     and use D to analyze the file.
    #  X  D !F   || Y
    #  X !D  F   || Y
    #  X !D !F   || N, and display a warning in the output.
    # !X  D  F   || Y
    # !X  D !F   || Y
    # !X !D  F   || N
    # !X !D !F   || N

    local_file = action.attrs[PD_LOCAL_PATH]
    deps = []
    errs = []
    path_version = None

    dir_major = None
    dir_minor = None
    file_major = None
    file_minor = None
    cur_major = None
    cur_minor = None
    executable = bool(script_path)

    # Version of python to use to do the analysis.
    analysis_major = None
    analysis_minor = None

    cur_major, cur_minor = sys.version_info[0:2]
    install_match = py_lib_re.match(action.attrs["path"])
    if install_match:
        dir_major = install_match.group("major")
        dir_minor = install_match.group("minor")

    script_match = None
    if script_path:
        script_match = py_bin_re.match(script_path)
        if script_match:
            file_major = script_match.group("major")
            file_minor = script_match.group("minor")

    if executable:
        # Check whether the version of python declared in the #! line
        # of the file and the version of python implied by the directory
        # the file is delivered into match.
        if install_match and script_match and \
            (file_major != dir_major or file_minor != dir_minor):
            errs.append(
                PythonMismatchedVersion(
                    "{0}.{1}".format(dir_major, dir_minor),
                    "{0}.{1}".format(file_major, file_minor), local_file,
                    action.attrs["path"]))
        if install_match:
            analysis_major = dir_major
            analysis_minor = dir_minor
        elif script_match:
            analysis_major = file_major
            analysis_minor = file_minor
        else:
            # An example of this case is an executable file in
            # /usr/bin with #!/usr/bin/python as its first line.
            errs.append(
                PythonUnspecifiedVersion(local_file, action.attrs["path"]))
    elif install_match:
        analysis_major = dir_major
        analysis_minor = dir_minor

    if analysis_major is None or analysis_minor is None:
        return deps, errs, {}

    analysis_major = int(analysis_major)
    analysis_minor = int(analysis_minor)

    # If the version implied by the directory hierarchy matches the version
    # of python running, use the default analyzer and don't fork and exec.
    if cur_major == analysis_major and cur_minor == analysis_minor:
        mf = modulefinder.DepthLimitedModuleFinder(os.path.dirname(
            action.attrs["path"]),
                                                   run_paths=run_paths)
        try:
            loaded_modules = mf.run_script(local_file)

            for names, dirs in set([(tuple(m.get_file_names()), tuple(m.dirs))
                                    for m in loaded_modules]):
                # Add the directory the python file will be
                # installed in to the paths used to find modules
                # for import.  This allows relative imports to
                # work correctly.
                deps.append(
                    PythonDependency(action, names, dirs, pkg_vars,
                                     action.attrs[PD_PROTO_DIR]))
            missing, maybe = mf.any_missing_maybe()
            for name in missing:
                errs.append(
                    PythonModuleMissingPath(name, action.attrs[PD_LOCAL_PATH]))
        except SyntaxError as e:
            errs.append(
                PythonSyntaxError(action.attrs["path"], local_file, s_err=e))
        except Exception as e:
            errs.append(e)
        return deps, errs, {}

    # If the version implied by the directory hierarchy does not match the
    # version of python running, it's necessary to fork and run the
    # appropriate version of python.
    root_dir = os.path.dirname(__file__)
    exec_file = os.path.join(root_dir, "depthlimitedmf.py")
    cmd = [
        "python{0}.{1}".format(analysis_major, analysis_minor), exec_file,
        os.path.dirname(action.attrs["path"]), local_file
    ]
    newenv = os.environ.copy()
    # Tell Python to not create .pyc, .pyo, etc. cache files for any Python
    # modules our script imports.
    newenv["PYTHONDONTWRITEBYTECODE"] = "1"

    if run_paths:
        cmd.extend(run_paths)
    try:
        sp = subprocess.Popen(cmd,
                              env=newenv,
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE,
                              encoding="utf-8")
    except Exception as e:
        return [], [PythonSubprocessError(None, " ".join(cmd),\
            str(e))], {}
    out, err = sp.communicate()
    out = force_str(out)
    if sp.returncode:
        errs.append(PythonSubprocessError(sp.returncode, " ".join(cmd), err))
    bad_lines = []
    for l in out.splitlines():
        l = l.strip()
        if l.startswith("DEP "):
            try:
                names, dirs = eval(l[4:])
            except Exception:
                bad_lines.append(l)
            else:
                deps.append(
                    PythonDependency(action, names, dirs, pkg_vars,
                                     action.attrs[PD_PROTO_DIR]))
        elif l.startswith("ERR MISSING "):
            errs.append(
                PythonModuleMissingPath(l[len("ERR MISSING "):],
                                        action.attrs[PD_LOCAL_PATH]))
        elif l.startswith("ERR SYNTAX "):
            errs.append(
                PythonSyntaxError(action.attrs["path"], local_file, msg=l))
        elif l.startswith("ERR "):
            # Generic error which is assigned as a missing path
            errs.append(
                PythonModuleMissingPath(l[4:], action.attrs[PD_LOCAL_PATH]))
        else:
            bad_lines.append(l)
    if bad_lines:
        errs.append(PythonSubprocessBadLine(" ".join(cmd), bad_lines))
    return deps, errs, {}
예제 #3
0
def process_python_dependencies(action, pkg_vars, script_path):
    """Analyze the file delivered by the action for any python dependencies.

        The 'action' parameter contain the action which delivers the file.

        The 'pkg_vars' parameter contains the variants against which
        the action's package was published.

        The 'script_path' parameter is None of the file is not executable, or
        is the path for the binary which is used to execute the file.
        """

    # There are three conditions which determine whether python dependency
    # analysis is performed on a file with python in its #! line.
    # 1) Is the file executable. (Represented in the table below by X)
    # 2) Is the file installed into a directory which provides information
    #     about what version of python should be used for it.
    #     (Represented by D)
    # 3) Does the first line of the file include a specific version of
    #     python. (Represented by F)
    #
    # Conditions || Perform Analysis
    #  X  D  F   || Y, if F and D disagree, display a warning in the output
    #            ||     and use D to analyze the file.
    #  X  D !F   || Y
    #  X !D  F   || Y
    #  X !D !F   || N, and display a warning in the output.
    # !X  D  F   || Y
    # !X  D !F   || Y
    # !X !D  F   || N
    # !X !D !F   || N

    local_file = action.attrs[PD_LOCAL_PATH]
    deps = []
    errs = []
    path_version = None

    dir_major = None
    dir_minor = None
    file_major = None
    file_minor = None
    cur_major = None
    cur_minor = None
    executable = bool(script_path)

    # Version of python to use to do the analysis.
    analysis_major = None
    analysis_minor = None

    cur_major, cur_minor = sys.version_info[0:2]
    install_match = py_lib_re.match(action.attrs["path"])
    if install_match:
        dir_major = install_match.group("major")
        dir_minor = install_match.group("minor")

    script_match = None
    if script_path:
        script_match = py_bin_re.match(script_path)
        if script_match:
            file_major = script_match.group("major")
            file_minor = script_match.group("minor")

    if executable:
        # Check whether the version of python declared in the #! line
        # of the file and the version of python implied by the directory
        # the file is delivered into match.
        if install_match and script_match and \
            (file_major != dir_major or file_minor != dir_minor):
            errs.append(
                PythonMismatchedVersion("%s.%s" % (dir_major, dir_minor),
                                        "%s.%s" % (file_major, file_minor),
                                        local_file, action.attrs["path"]))
        if install_match:
            analysis_major = dir_major
            analysis_minor = dir_minor
        elif script_match:
            analysis_major = file_major
            analysis_minor = file_minor
        else:
            # An example of this case is an executable file in
            # /usr/bin with #!/usr/bin/python as its first line.
            errs.append(
                PythonUnspecifiedVersion(local_file, action.attrs["path"]))
    elif install_match:
        analysis_major = dir_major
        analysis_minor = dir_minor

    if analysis_major is None or analysis_minor is None:
        return deps, errs, {}

    analysis_major = int(analysis_major)
    analysis_minor = int(analysis_minor)

    # If the version implied by the directory hierarchy matches the version
    # of python running, use the default analyzer and don't fork and exec.
    if cur_major == analysis_major and cur_minor == analysis_minor:
        mf = modulefinder.DepthLimitedModuleFinder(action.attrs[PD_PROTO_DIR])
        loaded_modules = mf.run_script(local_file)

        for names, dirs in set([(tuple(m.get_file_names()), tuple(m.dirs))
                                for m in loaded_modules]):
            deps.append(
                PythonDependency(action, names, dirs, pkg_vars,
                                 action.attrs[PD_PROTO_DIR]))
        missing, maybe = mf.any_missing_maybe()
        for name in missing:
            errs.append(
                PythonModuleMissingPath(name, action.attrs[PD_LOCAL_PATH]))
        return deps, errs, {}

    # If the version implied by the directory hierarchy does not match the
    # version of python running, it's necessary to fork and run the
    # appropriate version of python.
    root_dir = os.path.dirname(__file__)
    exec_file = os.path.join(
        root_dir, "depthlimitedmf%s%s.py" % (analysis_major, analysis_minor))
    cmd = [
        "python%s.%s" % (analysis_major, analysis_minor), exec_file,
        action.attrs[PD_PROTO_DIR], local_file
    ]
    try:
        sp = subprocess.Popen(cmd,
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE)
    except Exception, e:
        return [], [PythonSubprocessError(None, " ".join(cmd),\
            str(e))], {}