简体   繁体   English

如何在IIS上为Python2.7正确安装isapi_wsgi?

[英]How to properly install isapi_wsgi on IIS for Python2.7?

I've worked through installing Python as a CGI application on IIS on Windows 7. This is pretty straightforward, but I'd like to use the WSGI stuff, for better flexibility. 我已经在Windows 7上的IIS上安装了Python作为CGI应用程序。这非常简单,但我想使用WSGI的东西,以获得更好的灵活性。

I downloaded the archive for isapi_wsgi , unzipped it, and then ran the install as per the instructions , like this: 我下载了isapi_wsgi的存档, 压缩它,然后根据说明运行安装,如下所示:

\python27\python.exe setup.py install

This succeeded: 这成功了:

在此输入图像描述

Then I coded a .py module that had the wsgi glue in it, and tried installing it. 然后我编写了一个带有wsgi胶水的.py模块,并尝试安装它。 This failed like so: 这样失败了:

在此输入图像描述

It's a COM Moniker error, and I know that the IIS6-compatible management stuff is based on COM Monikers, which reminded me that there is a pre-req for isapi_wsgi of the IIS6-compatible management stuff. 这是一个COM Moniker错误,我知道IIS6兼容的管理内容基于COM Monikers,这提醒我有一个isapi_wsgi与IIS6兼容管理内容的预先请求。 I ran \\windows\\system32\\OptionalFeatures.exe and installed that, then re-ran the .py module and it installed correctly. 我运行\\windows\\system32\\OptionalFeatures.exe并安装它,然后重新运行.py模块并正确安装。

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

Ok, wonderful. 好的,很好。 Now when I look in the current directory, I see a new DLL named _app1_wsgi.dll, and when I look in IIS Manager I can see a new IIS vdir, and a scriptmap within that vdir for '*', which is mapped to the _app1_wsgi.DLL. 现在,当我查看当前目录时,我看到一个名为_app1_wsgi.dll的新DLL,当我查看IIS管理器时,我可以看到一个新的IIS vdir,以及该vdir中用于'*'的脚本映射,它映射到_app1_wsgi.DLL。 All good. 都好。 But! 但! making a request to http://localhost/wsgi gives me a 500 error. http://localhost/wsgi发出请求给出了500错误。

Through some trial-and-error I see that the .py module that defines my handlers must be in the site-packages directory. 通过一些反复试验,我发现定义我的处理程序的.py模块必须位于site-packages目录中。 I am very surprised by this. 我对此感到非常惊讶。

Can I avoid this? 我可以避免这个吗? Can I simply put the .py module in the same directory as the generated .dll file? 我可以简单地将.py模块放在与生成的.dll文件相同的目录中吗? Or do I need to deploy all of my python logic to site-packages in order to run it from the WSGI mechanism? 或者我是否需要将所有python逻辑部署到site-packages以便从WSGI机制运行它?

The answer is: 答案是:

  • the installation of isapi_wsgi as described in the question, is correct. 如问题中所述安装isapi_wsgi是正确的。

  • with the basic boilerplate of app.py as shown in the example code accompanying isapi_wsgi, the python classes for the web app need to be in the site-packages directory. 使用app.py的基本样板,如isapi_wsgi附带的示例代码所示,Web应用程序的python类需要位于site-packages目录中。

  • it is possible to allow the python source modules to reside in the same directory as with the generated *.dll file, but it requires some special handling in the *wsgi.py file. 可以允许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. 在Windows上运行python以进行开发的更好方法是简单地下载Google App Engine并使用内置的专用http服务器。 The framework that comes with the GAE SDK handles reloading and allows the .py modules to be placed in particular directories. GAE SDK附带的框架处理重新加载,并允许将.py模块放在特定目录中。


If you don't want to download and install the GAE SDK, then you might try the following. 如果您不想下载并安装GAE SDK,则可以尝试以下操作。 Using this code, when a request arrives on isapi_wsgi, the handler looks in the home directory for a py module, and loads it. 使用此代码,当请求到达isapi_wsgi时,处理程序在主目录中查找py模块,并加载它。 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)

Using this model, requesting http://foo/wsgi/bar will try loading bar.py from the home directory with the WSGI .dll file. 使用此模型,请求http:// foo / wsgi / bar将尝试使用WSGI .dll文件从主目录加载bar.py. If bar.py cannot be found, you get a 404. If bar.py has been updated since the last run, it reloads. 如果找不到bar.py,则会得到404.如果自上次运行以来bar.py已更新,则会重新加载。 If bar cannot be loaded, you get a 500. 如果无法加载条形,则会获得500。

bar.py must export a method called handler , publicly. bar.py必须公开导出一个名为handler的方法。 That method must be a generator. 该方法必须是生成器。 like so: 像这样:

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']

But as I said, I think GAE is probably a better way to develop Python webapps using Windows. 但正如我所说,我认为GAE可能是使用Windows开发Python webapps的更好方法。

put this on top of your scrip: 把它放在你的脚本之上:

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

the same problem you had, was solved with this two lines 你有同样的问题,用这两行解决了

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM