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)
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
def abi(self): # type: () -> str return cast(str, super(Platform, self).abi)
def __str__(self): # type: () -> str return cast(str, self.SEP.join(self))
def version(self): # type: () -> str return cast(str, super(Platform, self).version)
def impl(self): # type: () -> str return cast(str, super(Platform, self).impl)
def platform(self): # type: () -> str return cast(str, super(Platform, self).platform)
def _parse_message(message): # type: (Text) -> Message return cast(Message, Parser().parsestr(message))
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())