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 create_message(self, preamble=None): # type: (Optional[str]) -> str """Create a message describing failure to find matching interpreters with an optional preamble. :param preamble: An optional preamble to the message that will be displayed above it separated by an empty blank line. :return: A descriptive message useable for display to an end user. """ preamble = "{}\n\n".format(preamble) if preamble else "" failures_message = "" if self.failures: seen = set() broken_interpreters = [] for python, error in self.failures: canonical_python = PythonInterpreter.canonicalize_path(python) if canonical_python not in seen: broken_interpreters.append((canonical_python, error)) seen.add(canonical_python) failures_message = ( "{}\n" "\n" "(See https://github.com/pantsbuild/pex/issues/1027 for a list of known breaks and " "workarounds.)").format("\n".join( "{index}.) {binary}:\n{error}".format( index=i, binary=python, error=error) for i, (python, error) in enumerate(broken_interpreters, start=1))) if not self.candidates: if failures_message: return ( "{preamble}" "Interpreters were found but they all appear to be broken:\n" "{failures}").format(preamble=preamble, failures=failures_message) return "{}No interpreters could be found on the system.".format( preamble) binary_column_width = max( len(candidate.binary) for candidate in self.candidates) interpreters_format = "{{index}}.) {{binary: >{}}} {{requirement}}".format( binary_column_width) qualifier = "" if failures_message: failures_message = "Skipped the following broken interpreters:\n{}".format( failures_message) qualifier = "working " constraints_message = "" if self.constraints: constraints_message = ( "No {qualifier}interpreter compatible with the requested constraints was found:\n" " {constraints}").format(qualifier=qualifier, constraints="\n ".join( self.constraints)) problems = "\n\n".join(msg for msg in (failures_message, constraints_message) if msg) if problems: problems = "\n\n{}".format(problems) return ( "{preamble}" "Examined the following {qualifier}interpreters:\n" "{interpreters}" "{problems}").format( preamble=preamble, qualifier=qualifier, interpreters="\n".join( interpreters_format.format( index=i, binary=candidate.binary, requirement=candidate.identity.requirement) for i, candidate in enumerate(self.candidates, start=1)), problems=problems, )