Issues using the Python Plugin with a Virtual Environment (venv)

Hello,

here is a little background:

I am using Orthanc on a VM that runs Windows Server 2016 with addition of the Python plugin (3.10) to add my own callbacks. Since the VM is not only dedicated to Orthanc, I want to encapsulate Orthanc and all of my Python code as much as possibe, which brings up the idea of a venv.

I am by no means an experienced developer so I tried the naive approach of compiling the plugin from source to be able to link it against the python.exe of my venv instead of the system wide Python installation. However, the build process failed with the error message saying that it is not able to find the python.h header files.

Did anyone figure out how to use the Python plugin in combination with a venv or is that even possible?

Thanks a lot in advance!

Hi @nukqc

There is currently nothing foreseen for selecting a virtual env in the plugin.

However, I just made a few tests on my Linux machine where I modify the sys.path at the very beginning of the python script, before loading other non-system modules. (note: I have created a virtual env in /tmp/.env/ :

import orthanc

import sys

print("sys.path before modification: " + ", ".join(sys.path))

sys.path = ["/usr/lib/python3.8", "/usr/lib/python3.8/lib-dynload", "/tmp/.env/lib/python3.8/site-packages", "/tmp/.env/lib64/python3.8/site-packages"]

print("sys.path after modification: " + ", ".join(sys.path))

import requests

Orthanc succeeds to start only once I have installed requests in my virtual env so it seems to work.

FYI, here’s what I have in the logs:

sys.path before modification: /home/alain/o/build/orthanc, /usr/lib/python38.zip, /usr/lib/python3.8, /usr/lib/python3.8/lib-dynload, /home/alain/.local/lib/python3.8/site-packages, /usr/local/lib/python3.8/dist-packages, /usr/lib/python3/dist-packages
sys.path after modification: /usr/lib/python3.8, /usr/lib/python3.8/lib-dynload, /tmp/.env/lib/python3.8/site-packages, /tmp/.env/lib64/python3.8/site-packages

Can you give it a try on Windows and share your findings ? I will then add it to the orthanc book.

Alain

3 Likes

Thank you, @alainmazy, for your quick response!

I have conducted some tests on my system (Windows Server 2016 and Windows 10 | Python 3.10.11) and here are my findings:

The workaround you proposed worked for me as well.

When I completely redefine sys.path during runtime, I discovered that the minimum PATH required (in my case) to utilize the packages in /.venv/ is:

sys.path = [
    "C:/Program Files/Python310/Lib",
    "C:/Program Files/Python310/DLLs",
    "C:/Users/me/Documents/my_project/.venv/Lib/site-packages"
]

I tested this using your example of whether requests is present in the venv when starting up Orthanc with my Python code.

Alternative: Instead of defining sys.path from scratch, it’s possible to insert the venv-packages like this (note: The venv is in the same directory as my executed Python file):

current_dir = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, os.path.join(current_dir, "./.venv/Lib/site-packages"))

By adding the venv to an early index (0), any package required by my code will be looked up in the venv first. However, as a consequence, if the package is not present, the system-wide installation of that package might be chosen. This is something to be mindful of.

Using a Virtual Environment (venv) in that way is not exactly encouraged but probably sufficient for my use case.

2 Likes

Hi @nukqc

Thanks for your feedback.

FYI, I have added the info from this thread in a section about virtual env in the orthanc bok.

Best regards,

Alain.

Stumbled onto the same thing right two days ago.

There’s no issue on altering sys.path, and no need to trim it down.

An additional way to achieve the same is setting PYTHONPATH in the env of the Orthanc process, this will be looked for and used by libpython interpreter init.

The output with a print(f"DEBUG: {sys.path=}") is:

$ sudo -u orthanc env PYTHONPATH=/tmp/venv /usr/local/sbin/Orthanc /etc/orthanc/ 2>&1 | grep DEBUG
DEBUG: sys.path=['/usr/local/share/orthanc/scripts',
                 '/tmp/venv', 
                 '/usr/lib/python39.zip', 
                 '/usr/lib/python3.9',
                 '/usr/lib/python3.9/lib-dynload', 
                 '/usr/local/lib/python3.9/dist-packages', 
                 '/usr/lib/python3/dist-packages']

I formatted the output for ease of reading, I am on linux but I am reasonably sure it will work the same on Windows.

1 Like

Hi @adamore.s4c

Thanks for that. I have included it as well in the orthanc-book and provided a sample for Docker containers:

FROM orthancteam/orthanc-pre-release:bookworm

# This example is using a virtual env that is not mandatory when using Docker containers
# but recommended since python 3.11 and Debian bookworm based images where you get a warning
# when installing system-wide packages.
RUN apt-get update && apt install -y python3-venv
RUN python3 -m venv /.venv

RUN /.venv/bin/pip install pydicom
ENV PYTHONPATH=/.venv/lib64/python3.11/site-packages/

...
1 Like

Following you example i tried it on my Windows 10 machine. I used this batch script for startup:

set "PYTHONPATH=C:\Users\Documents\Scripts\.venv\"
Orthanc.exe .\Configuration\

In addition to this Python script to be executed by Orthanc:

import sys
import os

import orthanc

orthanc.LogWarning("sys.path: " + str(sys.path))
orthanc.LogWarning("PYTHONPATH: " + str(os.getenv("PYTHONPATH")))

try:
    import dotenv
except ImportError:
    orthanc.LogError("dotenv not installed.")

And get this result (formatted):

sys.path: [
    'C:\\Users\\Documents\\Scripts\\experimental', 
    'C:\\Users\\Documents\\Scripts\\.venv',
    'C:\\Program Files\\Python310\\python310.zip',
    'C:\\Program Files\\Python310\\Lib',
    'C:\\Program Files\\Python310\\DLLs',
    'C:\\Users\\Documents\\Orthanc Server', 
    'C:\\Users\\AppData\\Roaming\\Python\\Python310\\site-packages', 
    'C:\\Users\\AppData\\Roaming\\Python\\Python310\\site-packages\\win32', 
    'C:\\Users\\AppData\\Roaming\\Python\\Python310\\site-packages\\win32\\lib', 
    'C:\\Users\\AppData\\Roaming\\Python\\Python310\\site-packages\\Pythonwin', 
    'C:\\Program Files\\Python310',
    'C:\\Program Files\\Python310\\lib\\site-packages'
PYTHONPATH: C:\Users\Documents\Scripts\.venv\
dotenv not installed.

Honestly I would have expected it to work as PATH looks kind of similar to your example (I am not very proficient at Unix systems).

Also tried activating the venv beforehand in order to set important env variables:

.\.venv\Scripts\activate
set "PYTHONPATH=C:\Users\Documents\Scripts\.venv\"
Orthanc.exe .\Configuration\

However, it failed the same way. What am I missing?

I now changed the environment variable from…

PYTHONPATH=C:\Users\Documents\Scripts\.venv\

…to…

PYTHONPATH=C:\Users\Documents\Scripts\.venv\Lib\site-packages

…and it seems to be working now!

My bad, my example was meant to just show how to inject a path into sys.path.

I see how the choice of /tmp/venv was misleading.

I think activating the environment before starting Orthanc in a batch file is not going to work as the interpreter is being inited by Orthanc and the activation script is not setting PYTHONPATH in env, but I am not sure about it.