Используя PythonService.exe для размещения службы python при использовании virtualenv
у меня есть среда Windows 7, где мне нужно разработать службу Python Windows с использованием Python 3.4. Я использую модуль win32service pywin32 для настройки службы, и большинство крючков, похоже, работают нормально.
проблема в том, что я пытаюсь запустить службу из исходного кода (используя python service.py install
следовал по python service.py start
). Это использует PythonService.exe для размещения service.py -но я использую виртуальную среду venv, и скрипт не может найти ее модули (обнаружено сообщение об ошибке с python service.py debug
).
Pywin32 устанавливается в virtualenv и при просмотре исходного кода PythonService.exe, он динамически связывается в Python34.dll, импортирует мой service.py и вызывает его.
как я могу получить PythonService.exe для использования моего virtualenv при запуске service.py?
3 ответов
большое спасибо за публикацию этого вопроса и решения. Я выбрал несколько иной подход, который тоже может оказаться полезным. Довольно сложно найти рабочие советы для сервисов Python, не говоря уже о том, чтобы делать это с помощью virtualenv. В любом случае...
шаги
это использование Windows 7 x64, Python 3.5.1 x64, pywin32-220 (или pypiwin32-219).
- Откройте командную строку администратора.
- создайте virtualenv.
C:\Python35\python -m venv myvenv
- активировать virtualenv.
call myvenv\scripts\activate.bat
- установить pywin32, либо:
- Из Pypi:
pip install pypiwin32
, - от http://www.lfd.uci.edu / ~gohlke / pythonlibs/:
pip install path\to\pywin32.whl
- Из Pypi:
- запустите сценарий после установки
python myvenv\Scripts\pywin32_postinstall.py -install
.- этот скрипт регистрирует DLL в системе и копирует их в
C:\Windows\System32
. DLL называютсяpythoncom35.dll
иpywintypes35.dll
. Так что виртуальные среды на том же машина на том же главном выпуске Python point будет делиться ими... это незначительный компромисс :)
- этот скрипт регистрирует DLL в системе и копирует их в
- скопировать
myvenv\Lib\site-packages\win32\pythonservice.exe
tomyvenv\Scripts\pythonservice.exe
- на класс обслуживания (любой подклассы win32serviceutil.ServiceFramework), задайте свойство класса
_exe_path_
чтобы указать на этот перемещенный exe. Это станет сервисом binPath. Например:_exe_path_ = os.path.join(*[os.environ['VIRTUAL_ENV'], 'Scripts', 'pythonservice.exe'])
.
- на класс обслуживания (любой подклассы win32serviceutil.ServiceFramework), задайте свойство класса
Обсуждение
я думаю, почему это работает, это Python смотрит вверх, чтобы выяснить, где находятся папки Libs, и на основе этого устанавливает пути импорта пакетов, аналогичные принятому ответу. Когда питонсервис.exe находится в исходном месте, что, похоже, не работает гладко.
он также решает проблемы связывания DLL (обнаруживается с зависит.exe от http://www.dependencywalker.com/). Без DLL-бизнеса, отсортированного, невозможно будет импортировать из *.pyd файлы из venv\Lib\site-packages\win32
как модули в ваших скриптах. Например, необходимо разрешить import servicemanager
; as servicemanager.pyd
нет в пакете как a .py-файл, и имеет некоторые интересные возможности журнала событий Windows.
одна из проблем, которые у меня были с принятым ответом, заключается в том, что я не мог понять, как заставить его точно забрать пакет.egg-link пути, которые создаются при использовании setup.py develop
. Эти.файлы egg-link включают путь к пакету, когда он не находится в virtualenv в разделе myvenv\Lib\site-packages
.
если все прошло гладко, должна быть возможность установить, запустить и протестировать пример службы win32 (из приглашения администратора в активированном virtualenv):
python venv\Lib\site-packages\win32\Demos\service\pipeTestService.py install
python venv\Lib\site-packages\win32\Demos\service\pipeTestService.py start
python venv\Lib\site-packages\win32\Demos\service\pipeTestServiceClient.py
Среда Обслуживания
еще одно важное замечание во всем этом заключается в том, что служба будет выполнять код python в совершенно отдельной среде для того, который вы можете запустить python myservice.py debug
. Так, например,os.environ['VIRTUAL_ENV']
будет пустым при запуске службы. Это может быть обработано либо:
- настройка переменные среды внутри скрипта, например
- найти текущий путь, начиная с sys.исполняемый файл, как описано в принятой ответ.
- использовать этот путь, чтобы найти файл config.
- прочитайте файл конфигурации и поместите их в среду с
os.environ
.
- добавление разделов реестра в службу с переменными среды.
- посмотреть доступ к переменным среды из Windows Услуги для выполнения этого вручную с помощью regedit.exe
- посмотреть REG добавьте значение многострочного реестра REG_MULTI_SZ для этого из командной строки.
похоже, это используется для правильной работы с virtualenv
модуль до добавления виртуальных сред в Python 3.3. Есть анекдотические доказательства (см. Этот ответ:https://stackoverflow.com/a/12424980/1055722) этот питон site.py
используется для поиска вверх от исполняемого файла, пока он не нашел каталог, который удовлетворял бы импорту. Затем он будет использовать это для sys.prefix
и этого было достаточно для PythonService.exe, чтобы найти virtualenv он был внутри и использовать он.
если это было поведение, похоже, что site.py
больше не делает этого с введением venv
модуль. Вместо этого он выглядит на один уровень выше для pyvenv.cfg
file и настраивает для виртуальной среды только в этом случае. Это, конечно, не работает для PythonService.exe, который похоронен в модуле pywin32 под сайтами-пакетами.
чтобы обойти это, я приспособил activate_this.py
код, который поставляется с оригинальной virtualenv
module (см. Этот ответ: https://stackoverflow.com/a/33637378/1055722). Он используется для загрузки интерпретатора, встроенного в исполняемый файл (как в случае с PythonService.exe) в использование virtualenv. К сожалению, venv
не включает это.
вот что сработало для меня. Обратите внимание, при этом предполагается, что виртуальная среда именем my-venv и находится на один уровень выше исходного кода.
import os
import sys
if sys.executable.endswith("PythonService.exe"):
# Change current working directory from PythonService.exe location to something better.
service_directory = os.path.dirname(__file__)
source_directory = os.path.abspath(os.path.join(service_directory, ".."))
os.chdir(source_directory)
sys.path.append(".")
# Adapted from virtualenv's activate_this.py
# Manually activate a virtual environment inside an already initialized interpreter.
old_os_path = os.environ['PATH']
venv_base = os.path.abspath(os.path.join(source_directory, "..", "my-venv"))
os.environ['PATH'] = os.path.join(venv_base, "Scripts") + os.pathsep + old_os_path
site_packages = os.path.join(venv_base, 'Lib', 'site-packages')
prev_sys_path = list(sys.path)
import site
site.addsitedir(site_packages)
sys.real_prefix = sys.prefix
sys.prefix = venv_base
new_sys_path = []
for item in list(sys.path):
if item not in prev_sys_path:
new_sys_path.append(item)
sys.path.remove(item)
sys.path[:0] = new_sys_path
еще один фактор в моих проблемах - есть новое колесо pypi для pywin32, который предоставляется Twisted folks, что упрощает установку с pip. В PythonService.exe в этом пакете действовал странно (не мог найти dll pywin32 при вызове) по сравнению с тем, который вы получаете при установке официального пакета win32 exe в виртуальный env с помощью easy_install.
для тех, кто читал в 2018 году, мне не повезло с любым решением выше (Win10, Python 3.6) - так что это то, что я сделал, чтобы заставить его работать. Рабочий каталог находится в site-packages / win32 при запуске, поэтому вам нужно изменить рабочий каталог и исправить sys.путь перед попыткой импортировать любой код проекта. Этот предполагаемый venv сидит в вашем проекте dir, иначе вам может просто понадобиться жесткий код некоторых путей:
import sys
import os
if sys.executable.lower().endswith("pythonservice.exe"):
for i in range(4): # goes up 4 directories to project folder
os.chdir("..")
# insert site-packages 2nd in path (behind project folder)
sys.path.insert(1, os.path.join("venv",'Lib','site-packages'))
[REST OF IMPORTS]
class TestService(win32serviceutil.ServiceFramework):
[...]