在使用 virtualenv 时使用 PythonService.exe 来托管 python 服务 [英] Using PythonService.exe to host python service while using virtualenv

查看:44
本文介绍了在使用 virtualenv 时使用 PythonService.exe 来托管 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,导入我的service.py并调用.

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

解决方案

在将虚拟环境添加到 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,它隐藏在 site-packages 下的 pywin32 模块中.

为了解决这个问题,我修改了原始 virtualenv 模块附带的 activate_this.py 代码(请参阅此答案:https://stackoverflow.com/a/33637378/1055722).它用于引导嵌入在可执行文件(PythonService.exe 就是这种情况)中的解释器使用 virtualenv.不幸的是,venv 不包括这个.

这对我有用.请注意,这里假设虚拟环境名为 my-venv 并且位于源代码位置的上一级.

导入操作系统导入系统如果 sys.executable.endswith("PythonService.exe"):# 将当前工作目录从 PythonService.exe 位置更改为更好的位置.service_directory = os.path.dirname(__file__)source_directory = os.path.abspath(os.path.join(service_directory, ".."))os.chdir(source_directory)sys.path.append(".")# 改编自 virtualenv 的 activate_this.py# 在已经初始化的解释器中手动激活虚拟环境.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_pathsite_packages = os.path.join(venv_base, 'Lib', 'site-packages')prev_sys_path = list(sys.path)进口网站site.addsitedir(site_packages)sys.real_prefix = sys.prefixsys.prefix = venv_basenew_sys_path = []对于列表中的项目(sys.path):如果项目不在 prev_sys_path 中:new_sys_path.append(item)sys.path.remove(item)sys.path[:0] = new_sys_path

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

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's site.py used to look upward from the executable file until it found a directory that would satisfy imports. It would then use that for sys.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 the venv module. Instead, it looks one level up for a pyvenv.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 original virtualenv 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.

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

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.

这篇关于在使用 virtualenv 时使用 PythonService.exe 来托管 python 服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆