... if urllib.parse.urlparse(app).scheme: ifnot app.endswith(".py"): raise PipxError( """ pipx will only execute apps from the internet directly if they end with '.py'. To run from an SVN, try pipx --spec URL BINARY """ ) logger.info("Detected url. Downloading and executing as a Python file.")
defexec_app( cmd: Sequence[Union[str, Path]], env: Optional[Dict[str, str]] = None, extra_python_paths: Optional[List[str]] = None, ) -> NoReturn: """Run command, do not return POSIX: replace current processs with command using os.exec*() Windows: Use subprocess and sys.exit() to run command """
if env isNone: env = dict(os.environ) env = _fix_subprocess_env(env)
if extra_python_paths isnotNone: env["PYTHONPATH"] = os.path.pathsep.join( extra_python_paths + ( os.getenv("PYTHONPATH", "").split(os.path.pathsep) if os.getenv("PYTHONPATH") else [] ) )
# make sure we show cursor again before handing over control show_cursor()
logger.info("exec_app: " + " ".join([str(c) for c in cmd]))
if WINDOWS: sys.exit( subprocess.run( cmd, env=env, stdout=None, stderr=None, encoding="utf-8", universal_newlines=True, ).returncode ) else: os.execvpe(str(cmd[0]), [str(x) for x in cmd], env)
上面说的是从网络 url 下载 .py 文件并执行,如果不是 .py 而是 binary 该如何处理呢?也就是运行 pipx run BINARY的情况。这里涉及到一个概念 __pypackages__ 的概念,也称为 python local packages directory。具体定义可以看这里:
pythonloc is a drop in replacement for python and pip that automatically recognizes a __pypackages__ directory and prefers importing packages installed in this location over user or global site-packages. If you are familiar with node, __pypackages__ works similarly to node_modules.
pipx 也支持这个特性, 通过下面的函数获得了 local packages 的 binary 文件:
shared_libs.create(self.verbose) pipx_pth = get_site_packages(self.python_path) / PIPX_SHARED_PTH # write path pointing to the shared libs site-packages directory # example pipx_pth location: # ~/.local/pipx/venvs/black/lib/python3.8/site-packages/pipx_shared.pth # example shared_libs.site_packages location: # ~/.local/pipx/shared/lib/python3.6/site-packages # # https://docs.python.org/3/library/site.html # A path configuration file is a file whose name has the form 'name.pth'. # its contents are additional items (one per line) to be added to sys.path pipx_pth.write_text(f"{shared_libs.site_packages}\n", encoding="utf-8")
cmd = ( [str(self.python_path), "-m", "pip", "install"] + pip_args + [package_or_url] ) # no logging because any errors will be specially logged by # subprocess_post_check_handle_pip_error() pip_process = run_subprocess(cmd, log_stdout=False, log_stderr=False)
安装完成后最终还是调用 venv.run_app 方法来执行。到这里 run 命令的主体就学习完了。下面补充几个过程中值得注意的知识点。
# venv.py shared_libs.create(self.verbose) pipx_pth = get_site_packages(self.python_path) / PIPX_SHARED_PTH # write path pointing to the shared libs site-packages directory # example pipx_pth location: # ~/.local/pipx/venvs/black/lib/python3.8/site-packages/pipx_shared.pth # example shared_libs.site_packages location: # ~/.local/pipx/shared/lib/python3.6/site-packages # # https://docs.python.org/3/library/site.html # A path configuration file is a file whose name has the form 'name.pth'. # its contents are additional items (one per line) to be added to sys.path pipx_pth.write_text(f"{shared_libs.site_packages}\n", encoding="utf-8")
# ignore installed packages to ensure no unexpected patches from the OS vendor # are used self.upgrade(pip_args=["--force-reinstall"], verbose=verbose)