I've got a Windows 7 environment where I need to develop a Python Windows Service using Python 3.4. I'm using pywin32's win32service module to setup the service and most of the hooks seem to be working ok.
The problem is when I attempt to run the service from source code (using python service.py install
followed by python service.py start
). This uses PythonService.exe to host service.py - but I'm using a venv virtual environment and the script can't find it's modules (error message discovered with python service.py debug
).
Pywin32 is installed in the virtualenv and in looking at the source code of PythonService.exe, it dynamically links in Python34.dll, imports my service.py and invokes it.
How can I get PythonService.exe to use my virtualenv when running my service.py?
It appears this used to work correctly with the
virtualenv
module before virtual environments were added to Python 3.3. There's anecdotal evidence (see this answer: https://stackoverflow.com/a/12424980/1055722) that Python'ssite.py
used to look upward from the executable file until it found a directory that would satisfy imports. It would then use that forsys.prefix
and this was sufficient for PythonService.exe to find the virtualenv it was inside of and use it.If that was the behavior, it appears that
site.py
no longer does that with the introduction of thevenv
module. Instead, it looks one level up for apyvenv.cfg
file and configures for a virtual environment in that case only. This of course doesn't work for PythonService.exe which is buried down in the pywin32 module under site-packages.To work around it, I adapted the
activate_this.py
code that comes with the originalvirtualenv
module (see this answer: https://stackoverflow.com/a/33637378/1055722). It is used to bootstrap an interpreter embedded in an executable (which is the case with PythonService.exe) into using a virtualenv. Unfortunately,venv
does not include this.Here's what worked for me. Note, this assumes the virtual environment is named my-venv and is located one level above the source code location.
One other factor in my troubles - there is a new pypi wheel for pywin32 that is provided by the Twisted folks that makes it easier to install with pip. The PythonService.exe in that package was acting oddly (couldn't find a pywin32 dll when invoked) compared to the one you get when installing the official win32 exe package into the virtual env using easy_install.
Thanks very much for posting this question and a solution. I took a slightly different approach which might also be useful. It is pretty difficult to find working tips for Python services, let alone doing it with a virtualenv. Anyway...
Steps
This is using Windows 7 x64, Python 3.5.1 x64, pywin32-220 (or pypiwin32-219).
C:\Python35\python -m venv myvenv
call myvenv\scripts\activate.bat
pip install pypiwin32
,pip install path\to\pywin32.whl
python myvenv\Scripts\pywin32_postinstall.py -install
.C:\Windows\System32
. The DLL's are namedpythoncom35.dll
andpywintypes35.dll
. So virtual environments on the same machine on the same major Python point release will share these... it's a minor tradeoff :)myvenv\Lib\site-packages\win32\pythonservice.exe
tomyvenv\Scripts\pythonservice.exe
_exe_path_
to point to this relocated exe. This will become the service binPath. For example:_exe_path_ = os.path.join(*[os.environ['VIRTUAL_ENV'], 'Scripts', 'pythonservice.exe'])
.Discussion
I think why this works is that Python looks upwards to figure out where the Libs folders are and based on that sets package import paths, similar to the accepted answer. When pythonservice.exe is in the original location, that doesn't seem to work smoothly.
It also resolves DLL linking problems (discoverable with depends.exe from http://www.dependencywalker.com/). Without the DLL business sorted out, it won't be possible to import from the *.pyd files from
venv\Lib\site-packages\win32
as modules in your scripts. For example it's needed allowimport servicemanager
; asservicemanager.pyd
is not in the package as a .py file, and has some cool Windows Event Log capabilities.One of the problems I had with the accepted answer is that I couldn't figure out how to get it to accurately pick up on package.egg-link paths that are created when using
setup.py develop
. These .egg-link files include the path to the package when it's not located in the virtualenv undermyvenv\Lib\site-packages
.If it all went smoothly, it should be possible to install, start and test the example win32 service (from an Admin prompt in the activated virtualenv):
The Service Environment
Another important note in all this is that the service will execute the python code in a completely separate environment to the one you might run
python myservice.py debug
. So for exampleos.environ['VIRTUAL_ENV']
will be empty when running the service. This can be handled by either:os.environ
.For anyone reading in 2018, I didn't have any luck with either solution above (Win10, Python 3.6) - so this is what I did to get it working. The working directory is in site-packages/win32 on launch, so you need to change the working directory and fix the sys.path before you try and import any project code. This assumed venv sits in your project dir, otherwise you may just need to hard code some paths: