5

我已经在 Windows 7 的 IIS 上安装 Python 作为 CGI 应用程序。这非常简单,但我想使用 WSGI 的东西,以获得更好的灵活性。

我下载了isapi_wsgi的存档,解压缩,然后按照说明运行安装,如下所示:

\python27\python.exe setup.py install

这成功了:

在此处输入图像描述

然后我编写了一个包含 wsgi 胶水的 .py 模块,并尝试安装它。这失败了:

在此处输入图像描述

这是一个 COM Moniker 错误,我知道 IIS6 兼容的管理东西是基于 COM Monikers 的,这提醒我,IIS6 兼容的管理东西的 isapi_wsgi 有一个 pre-req。我运行\windows\system32\OptionalFeatures.exe并安装了它,然后重新运行 .py 模块并正确安装。

C:\dev\wsgi>\Python27\python.exe app1_wsgi.py
Configured Virtual Directory: /wsgi
Installation complete.

好的,太好了。现在,当我查看当前目录时,我看到了一个名为 _app1_wsgi.dll 的新 DLL,当我查看 IIS 管理器时,我可以看到一个新的 IIS vdir,以及该 vdir 中用于 '*' 的脚本映射,它映射到_app1_wsgi.DLL。都好。但!提出请求http://localhost/wsgi给我一个 500 错误。

通过一些反复试验,我看到定义我的处理程序的 .py 模块必须在 site-packages目录中。我对此感到非常惊讶。

我可以避免这种情况吗?我可以简单地将 .py 模块与生成的 .dll 文件放在同一目录中吗?或者我是否需要将我的所有 python 逻辑部署到站点包以便从 WSGI 机制运行它?

4

2 回答 2

1

答案是:

  • 问题中描述的 isapi_wsgi 的安装是正确的。

  • 使用 app.py 的基本样板,如 isapi_wsgi 随附的示例代码所示,Web 应用程序的 python 类需要位于 site-packages 目录中。

  • 可以允许 python 源模块驻留在与生成的 *.dll 文件相同的目录中,但它需要在 *wsgi.py 文件中进行一些特殊处理。

  • a better way to run python on Windows for development purposes is to simply download the Google App Engine and use the builtin dedicated http server. The framework that comes with the GAE SDK handles reloading and allows the .py modules to be placed in particular directories.


If you don't want to download and install the GAE SDK, then you might try the following. Using this code, when a request arrives on isapi_wsgi, the handler looks in the home directory for a py module, and loads it. If the module is already loaded, it checks the file "last modified time" and reloads the module if the last mod time is later than the time from the prior load. It works for simplistic cases but I suppose it will be brittle when there are nested module dependencies.

import sys
import os
import win32file
from win32con import *

# dictionary of [mtime, module] tuple;  uses file path as key
loadedPages = {}

def request_handler(env, start_response):
    '''Demo app from wsgiref'''
    cr = lambda s='': s + '\n'
    if hasattr(sys, "isapidllhandle"):
        h = None
        # get the path of the ISAPI Extension DLL
        hDll = getattr(sys, "isapidllhandle", None)
        import win32api
        dllName = win32api.GetModuleFileName(hDll)
        p1 = repr(dllName).split('?\\\\')
        p2 = p1[1].split('\\\\')
        sep = '\\'
        homedir = sep.join(p2[:-1])

        # the name of the Python module is in the PATH_INFO
        moduleToImport = env['PATH_INFO'].split('/')[1]

        pyFile = homedir + sep + moduleToImport + '.py'

        fd = None
        try:
            fd = win32file.CreateFile(pyFile, GENERIC_READ, FILE_SHARE_DELETE, None, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
        except Exception as exc1:
            fd = None

        if fd is not None:
            # file exists, get mtime
            fd.close()
            mt = os.path.getmtime(pyFile)
        else:
            mt = None


        if mt is not None:
            h = None
            if not pyFile in loadedPages:
                # need a new import
                if homedir not in sys.path:
                    sys.path.insert(0, homedir)

                h = __import__(moduleToImport, globals(), locals(), [])
                # remember
                loadedPages[pyFile] = [mt, h]
            else:
                # retrieve handle to module
                h = loadedPages[pyFile][1]
                if mt != loadedPages[pyFile][0]:
                    # need to reload the page
                    reload(h)
                    loadedPages[pyFile][0] = mt

            if h is not None:
                if 'handler' in h.__dict__:
                    for x in h.handler(env, start_response):
                        yield x
                else:
                    start_response("400 Bad Request", [('Content-Type', 'text/html')])
            else:
                start_response("404 Not Found", [('Content-Type', 'text/html')])
                yield cr()
                yield cr("<html><head><title>Module not found</title>" \
                             "</head><body>")
                yield cr("<h3>404 Not Found</h3>")
                yield cr("<h3>No handle</h3></body></html>")

        else:
            start_response("404 Not Found", [('Content-Type', 'text/html')])
            yield cr()
            yield cr("<html><head><title>Module not found</title>" \
                 "</head><body>")
            yield cr("<h3>404 Not Found</h3>")
            yield cr("<h3>That module (" + moduleToImport + ") was not found.</h3></body></html>")


    else:
        start_response("500 Internal Server Error", [('Content-Type', 'text/html')])
        yield cr()
        yield cr("<html><head><title>Server Error</title>" \
                 "</head><body><h1>Server Error - No ISAPI Found</h1></body></html>")


# def test(environ, start_response):
#     '''Simple app as per PEP 333'''
#     status = '200 OK'
#     start_response(status, [('Content-type', 'text/plain')])
#     return ['Hello world from isapi!']


import isapi_wsgi
# The entry point(s) for the ISAPI extension.
def __ExtensionFactory__():
    return isapi_wsgi.ISAPISimpleHandler(request_handler)


def PostInstall(params, options):
    print "The Extension has been installed"


# Handler for our custom 'status' argument.
def status_handler(options, log, arg):
    "Query the status of the ISAPI?"
    print "Everything seems to be fine..."


if __name__=='__main__':
    # This logic gets invoked when the script is run from the command-line.
    # In that case, it installs this module as an ISAPI.

    #
    # The API provided by isapi_wsgi for this is a bit confusing.  There
    # is an ISAPIParameters object. Within that object there is a
    # VirtualDirs property, which itself is a list of
    # VirtualDirParameters objects, one per vdir.  Each vdir has a set
    # of scriptmaps, usually this set of script maps will be a wildcard
    # (*) so that all URLs in the vdir will be served through the ISAPI.
    #
    # To configure a single vdir to serve Python scripts through an
    # ISAPI, create a scriptmap, and stuff it into the
    # VirtualDirParameters object. Specify the vdir path and other
    # things in the VirtualDirParameters object.  Stuff that vdp object
    # into a sequence and set it into the ISAPIParameters thing, then
    # call the vaguely named "HandleCommandLine" function, passing that
    # ISAPIParameters thing.
    #
    # Clear as mud?
    #
    # Seriously, this thing could be so much simpler, if it had
    # reasonable defaults and a reasonable model, but I guess it will
    # work as is.

    from isapi.install import *

    # Setup the virtual directories -
    # To serve from root, set Name="/"
    sm = [ ScriptMapParams(Extension="*", Flags=0) ]
    vdp = VirtualDirParameters(Name="wsgi", # name of vdir/IIS app
                              Description = "ISAPI-WSGI Demo",
                              ScriptMaps = sm,
                              ScriptMapUpdate = "replace"
                              )

    params = ISAPIParameters(PostInstall = PostInstall)
    params.VirtualDirs = [vdp]
    cah = {"status": status_handler}

    # from isapi.install, part of pywin32
    HandleCommandLine(params, custom_arg_handlers = cah)

使用此模型,请求http://foo/wsgi/bar 将尝试使用 WSGI .dll 文件从主目录加载 bar.py。如果找不到 bar.py,则会收到 404。如果 bar.py 自上次运行后已更新,则会重新加载。如果无法加载 bar,您将获得 500。

bar.py 必须公开导出一个名为 的方法handler。该方法必须是生成器。像这样:

import time

def handler(env, start_response):
    start_response("200 OK", [('Content-Type', 'text/html')])
    cr = lambda s='': s + '\n'
    yield cr("<html><head><title>Hello world!</title></head><body>")
    yield cr("<h1>Bargle Bargle Bargle</h1>")
    yield cr("<p>From the handler...</p>")
    yield cr("<p>(bargle)</p>")
    yield cr("<p>The time is now: " + time.asctime() + " </p>")
    yield cr("</body></html>")

__all__ = ['handler']

但正如我所说,我认为 GAE 可能是使用 Windows 开发 Python webapps 的更好方法。

于 2012-03-20T04:12:06.727 回答
-2

把它放在你的脚本上:

导入站点 site.addsitedir('path/to/your/site-packages')

你有同样的问题,用这两行解决了

于 2015-02-21T03:31:42.650 回答