Exemple #1
0
def iter_compatible_interpreters(
    path=None,  # type: Optional[str]
    valid_basenames=None,  # type: Optional[Iterable[str]]
    interpreter_constraints=None,  # type: Optional[Iterable[str]]
    preferred_interpreter=None,  # type: Optional[PythonInterpreter]
):
    # type: (...) -> Iterator[PythonInterpreter]
    """Find all compatible interpreters on the system within the supplied constraints.

    :param path: A PATH-style string with files or directories separated by os.pathsep.
    :param valid_basenames: Valid basenames for discovered interpreter binaries. If not specified,
                            Then all typical names are accepted (i.e.: python, python3, python2.7,
                            pypy, etc.).
    :param interpreter_constraints: Interpreter type and version constraint strings as described in
                                    `--interpreter-constraint`.
    :param preferred_interpreter: For testing - an interpreter to prefer amongst all others.
                                  Defaults to the current running interpreter.

    Interpreters are searched for in `path` if specified and $PATH if not.

    If no interpreters are found and there are no further constraints (neither `valid_basenames` nor
    `interpreter_constraints` is specified) then the returned iterator will be empty. However, if
    there are constraints specified, the returned iterator, although emtpy, will raise
    `UnsatisfiableInterpreterConstraintsError` to provide information about any found interpreters
    that did not match all the constraints.
    """

    _valid_path = None  # type: Optional[PathFilter]
    if valid_basenames:
        _valid_basenames = frozenset(cast("Iterable[str]", valid_basenames))
        _valid_path = (
            lambda interpreter_path: os.path.basename(interpreter_path) in _valid_basenames
        )

    def _iter_interpreters():
        # type: () -> Iterator[InterpreterOrError]
        seen = set()

        normalized_paths = (
            OrderedSet(PythonInterpreter.canonicalize_path(p) for p in path.split(os.pathsep))
            if path
            else None
        )

        # Prefer the current interpreter, if valid.
        current_interpreter = preferred_interpreter or PythonInterpreter.get()
        if not _valid_path or _valid_path(current_interpreter.binary):
            if normalized_paths:
                candidate_paths = frozenset(
                    (current_interpreter.binary, os.path.dirname(current_interpreter.binary))
                )
                candidate_paths_in_path = candidate_paths.intersection(normalized_paths)
                if candidate_paths_in_path:
                    # In case the full path of the current interpreter binary was in the
                    # `normalized_paths` we're searching, remove it to prevent identifying it again
                    # just to then skip it as `seen`.
                    normalized_paths.discard(current_interpreter.binary)
                    seen.add(current_interpreter)
                    yield current_interpreter
            else:
                seen.add(current_interpreter)
                yield current_interpreter

        for interp in PythonInterpreter.iter_candidates(
            paths=normalized_paths, path_filter=_valid_path
        ):
            if interp not in seen:
                seen.add(interp)
                yield interp

    def _valid_interpreter(interp_or_error):
        # type: (InterpreterOrError) -> bool
        if not isinstance(interp_or_error, PythonInterpreter):
            return False

        if not interpreter_constraints:
            return True

        interp = cast(PythonInterpreter, interp_or_error)

        if any(
            interp.identity.matches(interpreter_constraint)
            for interpreter_constraint in interpreter_constraints
        ):
            TRACER.log(
                "Constraints on interpreters: {}, Matching Interpreter: {}".format(
                    interpreter_constraints, interp.binary
                ),
                V=3,
            )
            return True

        return False

    candidates = []  # type: List[PythonInterpreter]
    failures = []  # type: List[InterpreterIdentificationError]
    found = False

    for interpreter_or_error in _iter_interpreters():
        if isinstance(interpreter_or_error, PythonInterpreter):
            interpreter = cast(PythonInterpreter, interpreter_or_error)
            candidates.append(interpreter)
            if _valid_interpreter(interpreter_or_error):
                found = True
                yield interpreter
        else:
            error = cast("InterpreterIdentificationError", interpreter_or_error)
            failures.append(error)

    if not found and (interpreter_constraints or valid_basenames):
        constraints = []  # type: List[str]
        if interpreter_constraints:
            constraints.append("Version matches {}".format(" or ".join(interpreter_constraints)))
        if valid_basenames:
            constraints.append("Basename is {}".format(" or ".join(valid_basenames)))
        raise UnsatisfiableInterpreterConstraintsError(constraints, candidates, failures)
Exemple #2
0
def main(
        pex=None,  # type: Optional[PEX]
        pex_prog_path=None,  # type: Optional[str]
):
    # type: (...) -> int
    logging.basicConfig(format="%(levelname)s: %(message)s",
                        level=logging.INFO)
    with TRACER.timed("Executing PEX_TOOLS"):
        pex_prog_path = simplify_pex_path(pex_prog_path
                                          or pex.path()) if pex else None

        # By default, let argparse derive prog from sys.argv[0].
        prog = None  # type: Optional[str]
        if pex:
            prog = "PEX_TOOLS=1 {pex_path}".format(pex_path=pex_prog_path)
        elif os.path.basename(sys.argv[0]) == "__main__.py":
            prog = "{python} {module}".format(python=sys.executable,
                                              module=".".join(
                                                  __name__.split(".")[:-1]))

        parser = ArgumentParser(
            prog=prog,
            formatter_class=ArgumentDefaultsHelpFormatter,
            description="Tools for working with {}.".format(
                pex_prog_path if pex else "PEX files"),
        )
        parser.add_argument("-V",
                            "--version",
                            action="version",
                            version=__version__)
        if pex is None:
            parser.add_argument("pex",
                                nargs=1,
                                metavar="PATH",
                                help="The path of the PEX file to operate on.")
        parser.set_defaults(func=functools.partial(Command.show_help, parser))
        subparsers = parser.add_subparsers(
            description=
            "{} can be operated on using any of the following subcommands.".
            format("The PEX file {}".format(pex_prog_path
                                            ) if pex else "A PEX file"), )
        for command in commands.all_commands():
            name = command.__class__.__name__.lower()
            # N.B.: We want to trigger the default argparse description if the doc string is empty.
            description = command.__doc__ or None
            help_text = description.splitlines()[0] if description else None
            command_parser = subparsers.add_parser(
                name,
                formatter_class=ArgumentDefaultsHelpFormatter,
                help=help_text,
                description=description,
            )
            command.add_arguments(command_parser)
            command_parser.set_defaults(func=command.run)

        options = parser.parse_args()
        if pex is None:
            pex_info = PexInfo.from_pex(options.pex[0])
            pex_info.update(PexInfo.from_env())
            interpreter = pex_bootstrapper.find_compatible_interpreter(
                interpreter_constraints=pex_info.interpreter_constraints)
            pex = PEX(options.pex[0], interpreter=interpreter)

        func = cast("CommandFunc", options.func)
        result = func(pex, options)
        result.maybe_display()
        return result.exit_code
Exemple #3
0
 def abi(self):
     # type: () -> str
     return cast(str, super(Platform, self).abi)
Exemple #4
0
 def __str__(self):
     # type: () -> str
     return cast(str, self.SEP.join(self))
Exemple #5
0
 def version(self):
     # type: () -> str
     return cast(str, super(Platform, self).version)
Exemple #6
0
 def impl(self):
     # type: () -> str
     return cast(str, super(Platform, self).impl)
Exemple #7
0
 def platform(self):
     # type: () -> str
     return cast(str, super(Platform, self).platform)
Exemple #8
0
def _parse_message(message):
    # type: (Text) -> Message
    return cast(Message, Parser().parsestr(message))
Exemple #9
0
def parse_fabric_version_output(output):
    # type: (Text) -> Dict[Text, Text]
    return dict(cast("Tuple[Text, Text]", line.split(" ", 1)) for line in output.splitlines())