3

I have recently been trying to learning about WSGI and moreover, how the web works in regards to Python. So I've been reading through Werkzeug and PEP333 to learn.

However I've run up against a small question, that I think I understand but probably don't, so I would appreciate your steering in the right direction.

PEP333 states:

The application object is simply a callable object that accepts two arguments. The term "object" should not be misconstrued as requiring an actual object instance: a function, method, class, or instance with a call method are all acceptable for use as an application object. Application objects must be able to be invoked more than once, as virtually all servers/gateways (other than CGI) will make such repeated requests.

The implementation:

class AppClass:
    """Produce the same output, but using a class

    (Note: 'AppClass' is the "application" here, so calling it
    returns an instance of 'AppClass', which is then the iterable
    return value of the "application callable" as required by
    the spec.

    If we wanted to use *instances* of 'AppClass' as application
    objects instead, we would have to implement a '__call__'
    method, which would be invoked to execute the application,
    and we would need to create an instance for use by the
    server or gateway.
    """

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

    def __iter__(self):
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        self.start(status, response_headers)
        yield "Hello world!\n"

My question here is just to clarify if I have understood it correctly.

It states that AppClass is the application, and when we call it, it returns an instance of AppClass. But then further down states that 'if we wanted to use instances of AppClass ass application objects instead', is this saying that when the server side of WSGI calls the AppClass object, there is only one instance running?

For example. The server can issue multiple requests (200 OK's) to the Application for more responses, hence why iter is placed into the class. But each request runs through the same singular AppClass instance, each request to the server basically doesn't instantiate more than one instance of the AppClass?

Sorry if this is long winded, and apologies again if I haven't made much sense. I'm trying to improve atm.

Appreciate your inputs as always.

Thanks.

John Von Neumann
  • 617
  • 4
  • 15
  • 1
    The server technology will construct `AppClass` for *each* request (as each request will have a potentially unique `environ`) – anthony sottile Jul 16 '17 at 22:15
  • 1
    btw, I'd also suggest reading [PEP3333](https://www.python.org/dev/peps/pep-3333/) (the updated version of PEP333) – anthony sottile Jul 16 '17 at 22:16
  • Cheers man! I think that makes a bit more sense, I intend to move onto PEP 3333 after I'm done with understanding a bit more about PEP333, or do you recommend moving on now due to it being more relevant? – John Von Neumann Jul 16 '17 at 22:19
  • In fact actually that makes a lot of sense now that I think about it, with the environ dict being passed in and that obviously has to be unique due to it coming in from requestors. – John Von Neumann Jul 16 '17 at 22:21
  • 1
    I'll elaborate in an answer – anthony sottile Jul 16 '17 at 22:47

2 Answers2

1

The server technology will call your app (in this case the class AppClass, causing an object construction) for each request. This is because each request will have a potentially unique environ.

The neat thing about this is it doesn't mean your app has to be a class, I often find it useful to define my wsgi app (or middleware) as a function returning a function:

# I'd strongly suggest using a web framework instead to define your application
def my_application(environ, start_response):
    start_response(str('200 OK'), [(str('Content-Type'), str('text/plain'))])
    return [b'hello world!\n']

def my_middleware(app):
    def middleware_func(environ, start_response):
        # do something or call the inner app
        return app(environ, start_response)
    return middleware_func

# expose `app` for whatever server tech you're using (such as uwsgi)
app = my_application
app = my_middleware(app)

Another common pattern involves defining an object to store some application state which is constructed once:

class MyApplication(object):
    def __init__(self):
        # potentially some expensive initialization
        self.routes = ...

    def __call__(self, environ, start_response):
        # Called once per request, must call `start_response` and then
        # return something iterable -- could even be `return self` if
        # this class were to define `__iter__`
        ...
        return [...]

app = MyApplication(...)

As for PEP333, I'd suggest reading PEP3333 instead -- it contains largely the same information, but clarifies the datatypes used throughout.

anthony sottile
  • 61,815
  • 15
  • 148
  • 207
1

For background on various ways that WSGI application objects can be implemented read this blog post on the topic.

I would also suggest reading the following, which talks about how Python web servers in general work.

Unless you really have a need you probably just want to use a framework. Avoid trying to write anything from scratch with WSGI.

Graham Dumpleton
  • 57,726
  • 6
  • 119
  • 134
  • I actually came across your blogs in my travels for information haha. I found them useful. I intend to use a framework for all my work, I just don't enjoy using things I don't understand, it always feels lazy. – John Von Neumann Jul 17 '17 at 03:30