Используя 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, либо:
  • запустите сценарий после установки python myvenv\Scripts\pywin32_postinstall.py -install.
    • этот скрипт регистрирует DLL в системе и копирует их в C:\Windows\System32. DLL называются pythoncom35.dll и pywintypes35.dll. Так что виртуальные среды на том же машина на том же главном выпуске Python point будет делиться ими... это незначительный компромисс :)
  • скопировать myvenv\Lib\site-packages\win32\pythonservice.exe to myvenv\Scripts\pythonservice.exe
    • на класс обслуживания (любой подклассы win32serviceutil.ServiceFramework), задайте свойство класса _exe_path_ чтобы указать на этот перемещенный exe. Это станет сервисом binPath. Например: _exe_path_ = os.path.join(*[os.environ['VIRTUAL_ENV'], 'Scripts', 'pythonservice.exe']).

Обсуждение

я думаю, почему это работает, это 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.
  • добавление разделов реестра в службу с переменными среды.

похоже, это используется для правильной работы с 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):
    [...]