Is it prohibited to call uv Python executable file from Addon to execute code?

Anki’s new uv launcher uses a python executable file, so it seems to me that it is possible to get the python path from sys.executable and execute a code with it. (I already mentioned this a bit in another thread. [Wiki] Bundling Python modules with add-ons - #22 by Shigeyuki)

There are various things to keep in mind during development (e.g. it’s probably better not to import aqt, and there is a risk of the laptop crashing if an infinite loop occurs). But I thought that this might not be the intended use of Python, so it might be prohibited or strongly discouraged by the official Anki in the first place.(e.g. increased security risk)

Is it possible to call Python executable files from add-ons and execute code(for now)?

Minimal code example is like this:

# __init__.py
from .sample_addon import run_uv_python
run_uv_python()
# sample_addon.py

import os
import sys
import subprocess

def run_uv_python():
    py_script_path = os.path.abspath(__file__)
    uv_python_path = sys.executable

    creationflags = 0
    if sys.platform == "win32":
        creationflags = (
            subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS
        )

    subprocess.Popen(
        [uv_python_path, py_script_path],
        start_new_session=True,
        stdin=subprocess.DEVNULL,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        creationflags=creationflags,
    )

if __name__ == "__main__":
    from PyQt6.QtWidgets import QApplication, QWidget
    app = QApplication(sys.argv)
    window = QWidget()
    window.setWindowTitle("sample")
    window.resize(300, 100)
    window.show()
    app.exec()

I think these are advantages:

  • It is possible to develop add-ons that run after Anki is closed. (e.g. AnkiRestart, like AnkiConnect that works even after Anki is closed, auto launch Anki at a specified time)
  • Separate the UI event loop: e.g. Custom console. When displaying a window within Anki, the add-on UI also freezes when Anki’s UI is loading. This problem does not occur when creating a window with a Python executable file and communicating via a socket. (I am currently developing a prototype of this.)
  • It is also possible to prevent Anki from freezing due to add-ons. Calculations can be run in the background, but UI processing cannot be run in the background, so if an add-on processes a lot of UI, Anki will freeze. (e.g. Leaderboard)
  • File size: e.g. if I build AnkiRestart for Win, MacOS, and Linux using PyInstaller and incorporate it into an add-on it will be 18 MB in size using only Python. The actual code required is only 3 KB, so this method is compact and can also use PyQt.
  • Code signing: Apps built with PyInstaller often get blocked from running if they don’t have code signing. With this way code signing probably isn’t needed, so development costs are lower.

and disadvantages:

  • not sure if it works cross platform.
  • not sure if sys.executable will reliably return the Python path.
  • Perhaps this is not the intended use of Anki or UV, so unexpected problems may occur. (it looks the same to me as using normal Python)
  • If this works without code signing it seems likely that there will be high security risks. (e.g. the program may continue to run in the background after the user closes Anki. but I think existing add-ns also have almost the same risk when Anki is running.)
  • If an unexpected error occurs users may not be able to close the program.

You can use uv_binary() instead: anki/qt/aqt/package.py at d13c117e80199a39a31eb611b3b8e2ca0c688a8a · ankitects/anki · GitHub

package.py has some methods intended for use by add-ons (see Add some helpers to allow add-ons to install packages into the venv · ankitects/anki@bb1b289 · GitHub) so I assume uv_binary() is safe to use. You can add any requirements you need to run your script using add_python_requirements().

1 Like

That’s convenient, in Windows if the console is to be hidden it seems that “uvw.exe” needs to be used instead of “uv.exe”. (if python is to be used directly “pythonw.exe” is used instead of “python.exe”. I haven’t tried MacOS and Linux yet)

e.g. use uvw.exe, for win
import os
import platform

from aqt.package import uv_binary
uv_binary_path = uv_binary()

test_script_path = r"C:\Users\USERNAME\AppData\Roaming\Anki2\addons21\myAddon\test_script.py"

uvw_path = os.path.join(os.path.dirname(uv_binary_path), "uvw.exe")
if platform.system() == "Windows" and os.path.exists(uvw_path):
    uv_binary_path = uvw_path

popen_arguments = [
    uv_binary_path,
    "run",
    "python",
    test_script_path
]

creationflags = 0
if platform.system() == "Windows":
    creationflags = (
        subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS
    )
subprocess.Popen(
    popen_arguments,
    start_new_session=True,
    stdin=subprocess.DEVNULL,
    stdout=subprocess.DEVNULL,
    stderr=subprocess.DEVNULL,
    creationflags=creationflags,
)

Another way is to use uv to create a new virtual environment. This way it is possible to use a different version of python.

e.g. meke new venv in addon folder and use python3.9
import platform
from aqt.package import uv_binary
uv_binary_path = uv_binary()

user_files_dir = os.path.join(os.path.dirname(__file__), "user_files")
os.makedirs(user_files_dir, exist_ok=True)
venv_path = os.path.join(user_files_dir, "addonVenvPy3.9")
if not os.path.exists(venv_path):
    create_venv_args = [
        uv_binary_path,
        "venv",
        "--python",
        "3.9",
        venv_path
    ]
    result = subprocess.run(create_venv_args, capture_output=True, text=True)

if platform.system() == "Windows":
    venv_python = os.path.join(venv_path, "Scripts", "python.exe")
    venv_pythonw = os.path.join(venv_path, "Scripts", "pythonw.exe")
    if os.path.exists(venv_pythonw):
        venv_python = venv_pythonw
else:
    venv_python = os.path.join(venv_path, "bin", "python")

test_script_path = r"C:\Users\USERNAME\AppData\Roaming\Anki2\addons21\myAddon\test_script.py"
result = subprocess.run([venv_python, test_script_path], capture_output=True, text=True)


I think additional advantages:

  • Python and modules can be locked, so add-ons are less likely to break.
  • No interference with other add-ons.
  • Maybe it will be easier to incorporate external programs. (e.g. projects written in Python 3.9 will be incorporated as is.)

and disadvantages:

  • It is need to develop a mechanism to communicate with Anki. (like AnkiConnect, HTTP, socket)
  • This increases the burden on the PC during execution. (e.g. +80MB)
  • Like C extensions, additional processing is needed for deletion and updating.
  • Adding modules increases the file size of the add-on. (e.g. PyQt6 added +202 MB)
  • If the uv operation is incorrect, it may affect the Anki launcher.

I developed one add-on that calls Python from uv and executes it. :wrench:Mini Console - for quick add-on debug It seems to me like there are no major problems for now.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.