python自动使用虚拟环境和安装依赖

waketzheng / 2023-06-16 / 原文

代码如下,Windows环境测试通过

import os
import platform
import re
import sys
from pathlib import Path


def is_venv() -> bool:
    """判断是否处于虚拟环境(也适用于poetry的)"""
    if hasattr(sys, "real_prefix"):
        return True
    return hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix


def run_and_echo(cmd) -> int:
    print("-->\n", cmd, flush=True)
    return os.system(cmd)


def install_and_rerun(*packages):
    py = Path(sys.executable)
    if not is_venv():
        if not (p := Path("venv")).exists():
            if run_and_echo(f"{py} -m venv venv"):
                return 1
        if platform.platform().lower().startswith("win"):
            py = p / "Scripts" / "python.exe"
        else:
            py = p / "bin/python"
    if run_and_echo(f"{py} -m pip install {' '.join(packages)}"):
        return 2
    cmd = f"{py} {sys.argv[0]} --no-install {' '.join(sys.argv[1:])}"
    return run_and_echo(cmd)


try:
    import kivy
except ImportError as e:
    if "--no-install" in sys.argv:
        raise e
    modules = re.findall(r"'([a-zA-Z_-]+)'", str(e))
    sys.exit(install_and_rerun(*modules))


def main():
    pass

if __name__ == '__main__':
    main() 

运行时,如果import失败,会判断是否处于虚拟环境,是的话,直接pip install报错的缺失包,然后自动重新执行脚本;

否则,判断当前路径是否有venv文件,有的话使用venv/*/python,否则使用python -m venv venv创建它

优化成class以便PyCharm可以折叠

import os
import platform
import random
import re
import sys
from pathlib import Path


class EnsureImport:
    def __init__(self, e):
        self.exception = e

    def run(self):
        e = self.exception
        if "--no-install" in sys.argv:
            raise e
        modules = re.findall(r"'([a-zA-Z_-]+)'", str(e))
        sys.exit(self.install_and_rerun(*modules))

    @staticmethod
    def is_venv() -> bool:
        """判断是否处于虚拟环境(也适用于poetry的)"""
        if hasattr(sys, "real_prefix"):
            return True
        return hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix

    @staticmethod
    def run_and_echo(cmd) -> int:
        print("-->\n", cmd, flush=True)
        return os.system(cmd)

    @classmethod
    def install_and_rerun(cls, *packages):
        py = Path(sys.executable)
        command = " ".join(sys.argv)
        if not cls.is_venv():
            if not (p := Path("venv")).exists():
                if cls.run_and_echo(f"{py} -m venv venv"):
                    return 1
            if platform.platform().lower().startswith("win"):
                py = p / "Scripts" / "python.exe"
            else:
                py = p / "bin/python"
            return cls.run_and_echo(f"{py} {command}")
        depends = " ".join(packages)
        if depends.lower() == "dearpygui" and sys.version_info >= (3, 11):
            depends += "-i https://pypi.waketzheng.top/simple"
        if cls.run_and_echo(f"{py} -m pip install {depends}"):
            return 2
        cmd = f"{py} {command} --no-install"
        return cls.run_and_echo(cmd)


try:
    import dearpygui.dearpygui as dpg
except ImportError as e:
    EnsureImport(e).run()


def main():
    print(dpg)


if __name__ == "__main__":
    main()

  使用了isort+black+ruff进行代码格式化