async def resolve_plugins( request: PluginsRequest, global_options: GlobalOptions) -> ResolvedPluginDistributions: """This rule resolves plugins using a VenvPex, and exposes the absolute paths of their dists. NB: This relies on the fact that PEX constructs venvs in a stable location (within the `named_caches` directory), but consequently needs to disable the process cache: see the ProcessCacheScope reference in the body. """ requirements = PexRequirements( req_strings=sorted(global_options.plugins), constraints_strings=(str(constraint) for constraint in request.constraints), ) if not requirements: return ResolvedPluginDistributions() python: PythonExecutable | None = None if not request.interpreter_constraints: python = cast( PythonExecutable, PythonExecutable.fingerprinted( sys.executable, ".".join(map(str, sys.version_info[:3])).encode("utf8")), ) plugins_pex = await Get( VenvPex, PexRequest( output_filename="pants_plugins.pex", internal_only=True, python=python, requirements=requirements, interpreter_constraints=request.interpreter_constraints, description= f"Resolving plugins: {', '.join(requirements.req_strings)}", ), ) # NB: We run this Process per-restart because it (intentionally) leaks named cache # paths in a way that invalidates the Process-cache. See the method doc. cache_scope = (ProcessCacheScope.PER_SESSION if global_options.plugins_force_resolve else ProcessCacheScope.PER_RESTART_SUCCESSFUL) plugins_process_result = await Get( ProcessResult, VenvPexProcess( plugins_pex, argv= ("-c", "import os, site; print(os.linesep.join(site.getsitepackages()))" ), description="Extracting plugin locations", level=LogLevel.DEBUG, cache_scope=cache_scope, ), ) return ResolvedPluginDistributions( plugins_process_result.stdout.decode().strip().split("\n"))
async def find_interpreter( interpreter_constraints: InterpreterConstraints, pex_runtime_env: PexRuntimeEnvironment) -> PythonExecutable: formatted_constraints = " OR ".join( str(constraint) for constraint in interpreter_constraints) result = await Get( ProcessResult, PexCliProcess( description= f"Find interpreter for constraints: {formatted_constraints}", subcommand=(), # Here, we run the Pex CLI with no requirements, which just selects an interpreter. # Normally, this would start an isolated repl. By passing `--`, we force the repl to # instead act as an interpreter (the selected one) and tell us about itself. The upshot # is we run the Pex interpreter selection logic unperturbed but without resolving any # distributions. extra_args=( *interpreter_constraints.generate_pex_arg_list(), "--", "-c", # N.B.: The following code snippet must be compatible with Python 2.7 and # Python 3.5+. # # When hashing, we pick 8192 for efficiency of reads and fingerprint updates # (writes) since it's a common OS buffer size and an even multiple of the # hash block size. dedent("""\ import hashlib, os, sys python = os.path.realpath(sys.executable) print(python) hasher = hashlib.sha256() with open(python, "rb") as fp: for chunk in iter(lambda: fp.read(8192), b""): hasher.update(chunk) print(hasher.hexdigest()) """), ), level=LogLevel.DEBUG, # NB: We want interpreter discovery to re-run fairly frequently # (PER_RESTART_SUCCESSFUL), but not on every run of Pants (NEVER, which is effectively # per-Session). See #10769 for a solution that is less of a tradeoff. cache_scope=ProcessCacheScope.PER_RESTART_SUCCESSFUL, ), ) path, fingerprint = result.stdout.decode().strip().splitlines() if pex_runtime_env.verbosity > 0: log_output = result.stderr.decode() if log_output: logger.info("%s", log_output) return PythonExecutable(path=path, fingerprint=fingerprint)
async def find_interpreter( interpreter_constraints: PexInterpreterConstraints ) -> PythonExecutable: formatted_constraints = " OR ".join( str(constraint) for constraint in interpreter_constraints) process = await Get( Process, PexCliProcess( description= f"Find interpreter for constraints: {formatted_constraints}", # Here, we run the Pex CLI with no requirements, which just selects an interpreter. # Normally, this would start an isolated repl. By passing `--`, we force the repl to # instead act as an interpreter (the selected one) and tell us about itself. The upshot # is we run the Pex interpreter selection logic unperturbed but without resolving any # distributions. argv=( *interpreter_constraints.generate_pex_arg_list(), "--", "-c", # N.B.: The following code snippet must be compatible with Python 2.7 and # Python 3.5+. dedent("""\ import hashlib import os import sys python = os.path.realpath(sys.executable) print(python) hasher = hashlib.sha256() with open(python, "rb") as fp: # We pick 8192 for efficiency of reads and fingerprint updates # (writes) since it's a common OS buffer size and an even # multiple of the hash block size. for chunk in iter(lambda: fp.read(8192), b""): hasher.update(chunk) print(hasher.hexdigest()) """), ), level=LogLevel.DEBUG, ), ) result = await Get( ProcessResult, UncacheableProcess(process=process, scope=ProcessScope.PER_SESSION)) path, fingerprint = result.stdout.decode().strip().splitlines() return PythonExecutable(path=path, fingerprint=fingerprint)