Skip to content

deduce_python

Tools for deducing things about python.

In this module, we mainly use 'python' and 'python3'. Use of 'py' alias can be a bit fickle.

TODO(@sas): Check python version before enabling note adding as this is a python 3.11 feature.

deduce_default_python_executable()

Deduce default python executable location.

This is done by attempt at subprocessing python/python3 depending on platform.

Returns:

Type Description
str | None

Path to python executable or None if not found.

Source code in client/ayon_comfyui/api/deduce_python.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def deduce_default_python_executable() -> str | None:
    """Deduce default python executable location.

    This is done by attempt at subprocessing
    python/python3 depending on platform.

    Returns:
        Path to python executable or None if not found.
    """
    script = "import sys;print(sys.executable)"

    python_name = "python"
    if sys.platform != "win32":
        python_name += "3"  # python3

    try:
        proc = subprocess.Popen(
            [python_name, "-c", script],
            stdout=subprocess.PIPE,
            text=True,
        )
    except FileNotFoundError:
        return None

    out, _ = proc.communicate()

    return out.strip()

deduce_default_python_version()

Deduce default python executable version.

This is done by attempt at subprocessing python/python3 depending on platform.

Returns:

Type Description
str | None

Python version as '3.X.X' or None if not found.

Source code in client/ayon_comfyui/api/deduce_python.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def deduce_default_python_version() -> str | None:
    """Deduce default python executable version.

    This is done by attempt at subprocessing
    python/python3 depending on platform.

    Returns:
        Python version as '3.X.X' or None if not found.
    """
    script = (
        "import sys;"
        "print(sys.version_info.major,sys.version_info.minor,sys.version_info.micro,sep='.')"
    )

    python_name = "python"
    if sys.platform != "win32":
        python_name += "3"  # python3

    try:
        proc = subprocess.Popen(
            [python_name, "-c", script],
            stdout=subprocess.PIPE,
            text=True,
        )
    except FileNotFoundError:
        return None

    out, _ = proc.communicate()

    return out.strip()

pip_install_requirements(python_exec_path, requirements_path)

Uses pip to install/update modules from a requirements file.

This process should go pretty quick if requirements are already met.

Raises ChildProcessError if somehow, packages failed to install.

Source code in client/ayon_comfyui/api/deduce_python.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def pip_install_requirements(
    python_exec_path: Path, requirements_path: Path
) -> None:
    """Uses pip to install/update modules from a requirements file.

    This process should go pretty quick if requirements are already met.

    Raises ChildProcessError if somehow, packages failed to install.
    """
    args = [python_exec_path, "-m", "pip", "install", "-r", requirements_path]

    delim = ":"
    if sys.platform == "win32":
        delim = ";"

    envdict: dict[str, str] = os.environ._data  # noqa: SLF001
    envdict["PATH"] = delim.join(
        [
            pth
            for pth in envdict["PATH"].split(delim)
            if "AYON" not in pth or "Ayon" not in pth or "ayon" not in pth
        ]
    )
    envdict.pop("PYTHONPATH", None)

    proc = subprocess.Popen(
        args,
        stderr=subprocess.PIPE,
        stdout=subprocess.PIPE,
        text=True,
    )

    out, err = proc.communicate()
    out, err = out.strip(), err.strip()

    if "Error" in out or "ERROR" in out or "Error" in err or "ERROR" in err:
        error = ChildProcessError(
            "Failed to install packages from requirements."
        )
        # error.add_note(
        #     "Failed to install packages from requirements. "
        #     "Try manually installing them with "
        #     f"{python_exec_path} -m pip instal -r {requirements_path}"
        # )
        raise error

python_setup_venv_with_depends(python_exec_path, environment_path, requirements_path)

Spawn a virtual environment using venv.

Because a venv points to a path that could be local, we should carefully consider where to place the environment path. Then, set it up with requirements.txt

Returns:

Type Description
Path

path to executable within python environment,

Path

environment_path/Scripts/python(.exe)

Source code in client/ayon_comfyui/api/deduce_python.py
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
def python_setup_venv_with_depends(
    python_exec_path: Path, environment_path: Path, requirements_path: Path
) -> Path:
    """Spawn a virtual environment using venv.

    Because a venv points to a path that could be local, we should carefully
    consider where to place the environment path.
    Then, set it up with requirements.txt

    Returns:
        path to executable within python environment,
        environment_path/Scripts/python(.exe)

    Raises:
        ChildProcessError when failed to set up a virtual environment.
        ChildProcessError from setting up environment and failing.
    """
    # Only do this if there's no venv already.
    if not venv_check_existence(environment_path):
        venv_args = [python_exec_path, "-m", "venv", environment_path]

        proc = subprocess.Popen(
            venv_args,
            stderr=subprocess.PIPE,
            stdout=subprocess.PIPE,
            text=True,
        )

        out, err = proc.communicate()
        out, err = out.strip(), err.strip()
        if out or err:
            error = ChildProcessError(
                "Failed to set up a virtual environment."
            )
            # error.add_note("Failed to set up a virtual environment.")
            raise error

    venv_python = venv_get_python(environment_path)

    # Can fail too.
    pip_install_requirements(venv_python, requirements_path)

    return venv_python

venv_check_existence(environment_path)

Returns whether a valid python environment exists at location.

Utility function to check if an environment has previously been made. It is kind of naive, it only checks if the environment has been set up, not if it actually works.

Source code in client/ayon_comfyui/api/deduce_python.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def venv_check_existence(environment_path: Path) -> bool:
    """Returns whether a valid python environment exists at location.

    Utility function to check if an environment has previously been made.
    It is kind of naive, it only checks if the environment has been set up,
    not if it actually works.
    """
    py_path = venv_get_python(environment_path)
    bin_path = py_path.parent

    asserts = [
        environment_path.exists(),
        bin_path.exists(),
        py_path.exists(),
    ]

    return all(asserts)

venv_get_python(environment_path)

Returns path to python executable in environment.

Warning: Does not check if the environment exists / is valid. For that, use venv_check_existence.

Source code in client/ayon_comfyui/api/deduce_python.py
79
80
81
82
83
84
85
86
87
88
89
90
91
92
def venv_get_python(environment_path: Path) -> Path:
    """Returns path to python executable in environment.

    Warning: Does not check if the environment exists / is valid.
    For that, use `venv_check_existence`.
    """
    bin_directory = "bin"

    python_name = "python"
    if sys.platform == "win32":
        python_name += ".exe"
        bin_directory = "Scripts"

    return environment_path / bin_directory / python_name