使用virtualenv时使用PythonService.exe来托管python服务

Dav*_*ess 10 python windows pywin32 virtualenv python-3.4

我有一个Windows 7环境,我需要使用Python 3.4开发Python Windows服务.我正在使用pywin32的win32service模块来设置服务,大多数钩子似乎都正常工作.

问题是当我尝试从源代码运行服务时(使用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并调用它.

运行service.py时如何让PythonService.exe使用我的virtualenv?

小智 10

非常感谢您发布此问题和解决方案.我采取了一种稍微不同的方法,这也可能有用.很难找到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.dllpywintypes35.dll.因此,在相同主要Python点发布的同一台机器上的虚拟环境将共享这些...这是一个小的权衡:)
  • 复制myvenv\Lib\site-packages\win32\pythonservice.exemyvenv\Scripts\pythonservice.exe
    • 在服务类(无论子类win32serviceutil.ServiceFramework)上,将class属性设置_exe_path_为指向此重定位的exe.这将成为服务binPath.例如:_exe_path_ = os.path.join(*[os.environ['VIRTUAL_ENV'], 'Scripts', 'pythonservice.exe']).

讨论

我认为为什么这样做是因为Python向上查找Libs文件夹的位置并基于它设置包导入路径,类似于接受的答案.当pythonservice.exe位于原始位置时,这似乎并不顺利.

它还解决了DLL链接问题(可从http://www.dependencywalker.com/上的depends.exe发现).如果没有整理DLL业务,将无法从venv\Lib\site-packages\win32脚本中的模块中导出*.pyd文件.例如,它需要允许import servicemanager; 因为servicemanager.pyd不在包中作为.py文件,并且具有一些很酷的Windows事件日志功能.

我接受的答案之一就是我无法弄清楚如何准确地接受使用时创建的package.egg-link路径setup.py develop.这些.egg-link文件包含该软件包的路径,当它不在virtualenv下时myvenv\Lib\site-packages.

如果一切顺利,应该可以安装,启动和测试示例win32服务(从激活的virtualenv中的Admin提示符):

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
Run Code Online (Sandbox Code Playgroud)

服务环境

所有这一切的另一个重要注意事项是服务将在一个完全独立的环境中执行python代码到你可能运行的代码python myservice.py debug.因此,例如os.environ['VIRTUAL_ENV']在运行服务时将为空.这可以通过以下任一方式处理:

  • 从脚本内部设置环境变量,例如
    • 查找从sys.executable开始的当前路径,如接受的答案中所述.
    • 使用该路径查找配置文件.
    • 阅读配置文件并将其放入环境中os.environ.
  • 使用环境变量向服务添加注册表项.

  • 此外,不应设置`_exe_path_`,而是设置`_exe_name_` 参数。并且很可能它应该包含在 `if not hasattr(sys, 'frozen'):` 中。感谢您的详细指导! (3认同)

ora*_*ant 8

我阅读了所有答案,但没有解决方案可以解决我的问题。

在仔细研究了David K. Hess的代码后,我做了一些修改,终于可以工作了。

但我的名气还不够,所以我只是在这里发布代码。

# 1. Custom your Project's name and Virtual Environment folder's name
# 2. Import this before all third part models
# 3. If you still failed, check the link below:
# /sf/ask/2428777081/
# 2019-05-29 by oraant, modified from David K. Hess's answer.

import os, sys, site

project_name = "PythonService"  # Change this for your own project !!!!!!!!!!!!!!
venv_folder_name = "venv"  # Change this for your own venv path !!!!!!!!!!!!!!

if sys.executable.lower().endswith("pythonservice.exe"):

    # Get root path for the project
    service_directory = os.path.abspath(os.path.dirname(__file__))
    project_directory = service_directory[:service_directory.find(project_name)+len(project_name)]

    # Get venv path for the project
    def file_path(x): return os.path.join(project_directory, x)
    venv_base = file_path(venv_folder_name)
    venv_scripts = os.path.join(venv_base, "Scripts")
    venv_packages = os.path.join(venv_base, 'Lib', 'site-packages')

    # Change current working directory from PythonService.exe location to something better.
    os.chdir(project_directory)
    sys.path.append(".")
    prev_sys_path = list(sys.path)

    # Manually activate a virtual environment inside an already initialized interpreter.
    os.environ['PATH'] = venv_scripts + os.pathsep + os.environ['PATH']

    site.addsitedir(venv_packages)
    sys.real_prefix = sys.prefix
    sys.prefix = venv_base

    # Move some sys path in front of others
    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
Run Code Online (Sandbox Code Playgroud)

如何使用它?这很简单,只需将其粘贴到一个新的 python 文件中,然后在任何第三方模型之前导入它,如下所示:

import service_in_venv  # import at top
import win32serviceutil
import win32service
import win32event
import servicemanager
import time
import sys, os
........
Run Code Online (Sandbox Code Playgroud)

现在你应该解决你的问题。


Dav*_*ess 5

virtualenv在将虚拟环境添加到 Python 3.3 之前,这似乎可以与模块一起正常工作。有轶事证据(请参阅此答案:/sf/answers/869748631/),Pythonsite.py用于从可执行文件向上查找,直到找到满足导入的目录。然后它会使用它sys.prefix,这足以让 PythonService.exe 找到它所在的 virtualenv 并使用它。

如果这是这种行为,那么site.py随着模块的引入,似乎不再这样做了venv。相反,它会向上查找一个文件级别pyvenv.cfg,并仅在这种情况下为虚拟环境进行配置。这当然不适用于隐藏在 site-packages 下的 pywin32 模块中的 PythonService.exe。

为了解决这个问题,我调整了activate_this.py原始模块附带的代码virtualenv(请参阅此答案: 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
Run Code Online (Sandbox Code Playgroud)

我遇到麻烦的另一个因素是 Twisted 人员为 pywin32 提供了一个新的 pypi 轮,可以更轻松地使用 pip 安装。与使用 easy_install 将官方 win32 exe 包安装到虚拟环境中时得到的情况相比,该包中的 PythonService.exe 表现得很奇怪(调用时找不到 pywin32 dll)。


归档时间:

查看次数:

9761 次

最近记录:

6 年,3 月 前