python-split()不在__init__里面工作

时间:2017-08-20 13:22:29

标签: python split wsgi

我正在编写使用wsgi来提供html文件的代码。当我编写一个直接的函数时,我没有得到任何错误:

from wsgiref.simple_server import make_server
import os
   ...
   ...
def app(environ, start_response):
    path_info = environ["PATH_INFO"]
    resource = path_info.split("/")[1] #I get no error here the split works totally fine.

现在,当我尝试将代码放入类中时,我得到错误NoneType没有属性拆分。 也许environ里面__init__没有被初始化,这就是为什么它分裂没有返回任何东西。以下是我的班级Candy所在的文件:

import os
class Candy:
    def __init__(self):
            #self.environ = environ
            #self.start = start_response
            self.status = "200 OK"
            self.headers = []

    def __call__(self , environ , start_response):
        self.environ = environ
        self.start = start_response

    #headers = []
    def content_type(path):
        if path.endswith(".css"):
            return "text/css"
        else:
            return "text/html"


    def app(self):
        path_info = self.environ["PATH_INFO"]
        resource = path_info.split("/")[1]

        #headers = []
        self.headers.append(("Content-Type", content_type(resource)))

        if not resource:
            resource = "login.html"
        resp_file = os.path.join("static", resource)

        try:
            with open(resp_file, "r") as f:
                resp_file = f.read()
        except Exception:
            self.start("404 Not Found", self.headers)
            return ["404 Not Found"]

        self.start("200 0K", self.headers)
        return [resp_file]

以下是我调用make_server的server.py文件:

from wsgiref.simple_server import make_server
from candy import Candy
#from app import candy_request

candy_class = Candy()

httpd = make_server('localhost', 8000, candy_class.app)
print "Serving HTTP on port 8000..."

# Respond to requests until process is killed
httpd.serve_forever()

# Alternative: serve one request, then exit
#httpd.handle_request()

有任何帮助吗?如何对这个错误进行排序,我的假设是正确的吗?

1 个答案:

答案 0 :(得分:3)

为了解释你在这里做错了什么,让我们从简单的概念开始 - 一个WSGI应用程序是什么。

WSGI应用程序只是一个可调用的接收请求环境,以及一个启动响应的回调函数(将状态行和标题发送回用户)。然后,此callable必须返回一个或多个构成响应主体的字符串。

在最简单的形式中,你已经完成了它只是

def app(environ, start_response):
    start_response("200 OK", [("Content-Type", "text/plain")])
    return "hello, world"

make_server('localhost', 8000, app).serve_forever()

每当请求到来时,app函数被调用,它会启动响应并返回一个字符串(或者它可以返回多个字符串的迭代,例如["hello, ", "world"]

现在,如果你想让它成为一个类,它就像这样:

 class MyApp(object):
     def __init__(self):
         pass

     def __call__(self, environ, start_response):
         start_response("200 OK", [("Content-Type", "text/plain")])
         return "something"

 app = MyApp()
 make_server("localhost", 8000, app).serve_forever()

在这种情况下,callable是app,它实际上是__call__ class instance Caddy方法。

当请求到来时,app.__call__被调用(__call__是将您的类实例转换为可调用的魔术方法),否则它与app函数完全相同第一个例子。除了你有一个类实例(self),所以你可以在__init__方法中进行一些预配置。没有在__init__中做任何事情就没用了。例如,一个更现实的例子是:

 class MyApp(object):
     def __init__(self):
         self.favorite_color = "blue"

     def __call__(self, environ, start_response):
         start_response("200 OK", [("Content-Type", "text/plain")])
         return "my favorite color is {}".format(self.favorite_color)
 ...

然后,还有另外一件事。有时您需要随时间生成的流式响应。也许它很大,或者可能需要一段时间。这就是为什么WSGI应用程序可以返回一个可迭代的,而不仅仅是一个字符串。

def app(environ, start_response):
    start_response("200 OK", [("Content-Type", "text/plain")]))
    yield "This was a triumph\n"
    time.sleep(1)
    yield "I'm making a note here\n"
    time.sleep(1)
    yield "HUGE SUCCESS\n"

make_server("localhost", 8000, app).serve_forever()

此函数返回一个生成器,它一个接一个地返回文本。虽然您的浏览器可能并不总是这样显示,但请尝试运行curl http://localhost:8000/

现在,与类相同的是:

class MyApp(object):
    def __init__(self, environ, start_response):
        self.environ = environ
        self.start = start_response

    def __iter__(self):
        self.start("200 OK", [("Content-Type", "text/plain")]))
        yield "This was a triumph\n"
        time.sleep(1)
        yield "I'm making a note here\n"
        time.sleep(1)
        yield "HUGE SUCCESS\n"

make_server("localhost", 8000, MyApp).serve_forever()

在这里,您将MyApp(该类)作为可调用的应用程序传递 - 它就是这样。当请求到来时,它会被调用,就像有人在某处写了MyApp(environ, start_response),因此__init__启动并为此特定请求创建一个实例。然后,当迭代实例时,__iter__开始产生响应。完成后,实例将被丢弃。

基本上就是这样。这里的类只是保存数据的便利闭包。如果你不需要它们,不要使用类,使用普通函数 - flat比嵌套更好。

现在,关于你的代码。

您的代码用于callable的是Candy().app。这不起作用,因为它甚至没有接收environstart_response它将被传递。它可能会因500错误而失败,说app() takes 1 positional arguments but 3 were given

我认为,在您遇到NoneType has no attribute split问题后,您的问题中的代码会被修改,并且当__init__仍在candy_instance = Candy()时创建__init__时,您已将某些内容传递给self有2个参数(3个make_server)。甚至不确定究竟是什么 - 它应该早先失败。

基本上,你将错误的对象传递给Candy,你的课程是两种不同想法的混合。

我建议查看上面的示例(并阅读PEP-333),确定您实际需要的内容,并构建您的__call__类。

  • 如果您只是需要在每个请求上返回一些内容,并且您没有持久状态 - 您根本不需要课程。

  • 如果您需要持久状态(配置,或者,可能是数据库连接) - 使用带有__call__方法的类实例,并使__iter__返回响应。< / p>

  • 如果需要以块的形式进行响应,请使用生成器函数或具有__call__方法的类。或者yields function mkcd() { mkdir $1; cd $1 ;} 的课程(就像函数一样)。

希望这有帮助。