使用virtualen时使用python service.exe托管python服务

2024-05-13 06:41:51 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个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中,导入my service.py并调用它。

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


Tags: 模块installpy环境virtualenvvenv源代码windows
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.dllpywintypes35.dll。所以,在同一台机器上的同一个主要Python点发行版上的虚拟环境将共享这些。。。这是一个小小的折衷:)
  • myvenv\Lib\site-packages\win32\pythonservice.exe复制到myvenv\Scripts\pythonservice.exe
    • 在服务类(无论是哪个子类win32serviceutil.ServiceFramework)上,将类属性_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文件中导入脚本中的模块。例如,它需要allowimport servicemanager;因为servicemanager.pyd不作为.py文件存在于包中,并且具有一些很酷的Windows事件日志功能。

我在接受答案时遇到的一个问题是,我不知道如何让它准确地获取package.egg-link路径,这些路径是在使用setup.py develop时创建的。当包不在myvenv\Lib\site-packages下的virtualenv中时,这些.egg链接文件包含包的路径。

如果一切顺利,应该可以安装、启动和测试示例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 myservice.py debug完全不同的环境中执行python代码。例如,运行服务时os.environ['VIRTUAL_ENV']将为空。这可以通过以下任一方式处理:

对于2018年的读者来说,我在上面的任何一个解决方案(Win10,Python 3.6)上都没有任何运气——所以我就是这么做的。工作目录在启动时位于site packages/win32中,因此在尝试导入任何项目代码之前,需要更改工作目录并修复sys.path。假设venv位于您的项目目录中,否则您可能只需要硬编码一些路径:

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):
    [...]

在将虚拟环境添加到Python 3.3之前,这似乎用于正确处理virtualenv模块。有传闻证据(见这个答案:https://stackoverflow.com/a/12424980/1055722)表明,Python的site.py用于从可执行文件向上查找,直到找到满足导入的目录。然后,它会将其用于sys.prefix,这就足以让PythonService.exe找到它所在的virtualenv并使用它。

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

为了解决这个问题,我修改了原始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

我遇到麻烦的另一个因素是,pywin32有一个新的pypi控制盘,它是由Twisted人员提供的,使pip更易于安装。该包中的PythonService.exe与使用easy_install将正式的win32 exe包安装到虚拟环境中时获得的pywin32 dll相比,运行异常(调用时找不到pywin32 dll)。

相关问题 更多 >