21

Could you supply a real-life example of WSGI start_response function? (Web-server provides that function to wsgi application)

I can't understand the purpose of introducing the start_response.

(I've read like 10 identical texts about the WSGI standard. They all say "WSGI standard is..." None of them says "WSGI is designed this way in order to..." :()

Boris Burkov
  • 13,420
  • 17
  • 74
  • 109

3 Answers3

16

Could you supply a real-life example of WSGI start_response() function?

Well, the start_response() function for mod_wsgi is defined on line 2678 of mod_wgsi.c

None of them says "WSGI is designed this way in order to..."

There doesn't seem to be much rationale for this aspect of WSGI's design in PEP3333. Looking through the web-sig mailing list archives, I came across this message...

Some time ago I objected the decision to remove start_response function from next version WSGI, using as rationale the fact that without start_callable, asynchronous extension are impossible to support.

Now I have found that removing start_response will also make impossible to support coroutines (or, at least, some coroutines usage).

[...]

...which started a long thread about the rationale for this part of the implementation which might be worth a read.

If you really want to know the origins of this aspect of the WSGI interface, you'll have to read a lot of the messages between this initial draft in December 2003, and this later draft in August 2004.


Update

How would that be compatible with that other protocol?

I'm not quite sure what you mean. Ignoring all the early drafts, the WSGI 1.x interface can be used in two different ways.

The 'deprecated' method is...

def application(environ, start_response):
    write = start_response(status, headers)
    write('content block 1')
    write('content block 2')
    write('content block 3')
    return None

...and the 'recommended' method is...

def application(environ, start_response):
    start_response(status, headers)
    return ['content block 1',
            'content block 2',
            'content block 3']

Presumably, you could use both, with...

def application(environ, start_response):
    write = start_response(status, headers)
    write('content block 1')
    return ['content block 2',
            'content block 3']

...but the resulting behavior may be undefined.

By the looks of this blog post, the new WSGI 2.x method being considered is...

def application(environ):
    return (status,
            headers,
            ['content block 1',
             'content block 2',
             'content block 3'])

...which eliminates the start_response() callable, and, obviously, the write() callable, but there's no indication as to when (or even if) this is likely to supercede WSGI 1.x.

Aya
  • 39,884
  • 6
  • 55
  • 55
  • 6
    Please don't let the async proponents muddy the waters over what start_response() was about. One of the primary reasons was to allow a server to return a write() callable to support existing Python web applications that were used to being able to call a write() function to produce content. This saved them from being rewritten to return an iterable. – Graham Dumpleton May 27 '13 at 22:16
  • @GrahamDumpleton Well, given that I wasn't involved in the design process, nor could I be bothered to read every single message in the web-sig archive, I could really only speculate. However, it seems as if the initial draft I linked to, which proposed using a function `def runCGI(input,output,errors,environ)`, would also have provided such a callable via `output.write()`. It looked more like there was some objection to the web application having to manually write the `HTTP/1.1 200 OK` line, and the response headers itself. – Aya May 28 '13 at 09:58
  • 1
    I wasn't there either and you have dug up stuff I haven't even read before. The runCGI version would have been before it was changed to return an iterable, so output was exclusively by way of write(). They could have ditched the write() at that point, which is what WSGI 2+ solutions have finally been proposing. In WSGI 2+ where write() isn't required, start_response() also vanishes. There is no need to hang onto write() any more because the very old applications which depended on being able to do a write() don't exist any more or have been rewritten to now return an iterable for the response. – Graham Dumpleton May 28 '13 at 10:53
  • @GrahamDumpleton, @Aya First of all, thank you guys for useful links and human-readable explaination. Strangely, there was no example in the link pages of how the returned `write` function can be used by the application. Is it true, that an application, meant to be compatible both with WSGI and other gateway protocol, should use it as follows: `write = start_response(status,headers); write(data)`? – Boris Burkov May 28 '13 at 23:59
  • @GrahamDumpleton, @Aya How would that be compatible with that other protocol? I suppose, that the implied other protocol would just call the application as `application(environ, start_response=gateway_write)`, supplying its custom `gateway_write` function to application's input? Then the application should directly call `start_response(data)` instead of `write = start_response(status,headers); write(data)` and WSGI is incompatible with that other protocol? – Boris Burkov May 29 '13 at 00:00
  • By other protocol do you mean the early drafts? Those early drafts were superseded and the resultant WSGI was quite different. You shouldn't be taking much from the drafts or trying to apply them in any way. – Graham Dumpleton May 29 '13 at 03:55
  • @GrahamDumpleton I'm speaking of PEP section about write() callable: http://www.python.org/dev/peps/pep-0333/#the-write-callable. "New WSGI applications and frameworks should not use the write() callable if it is possible to avoid doing so. The write() callable is strictly a hack to support imperative streaming APIs." So they probably call that other protocol "imperative framework API". – Boris Burkov May 29 '13 at 12:11
  • @Aya Thank you again, Aya. What I mean is that the whole purpose of introducing `start_response` was to make older applications, utilizing some other older gateway API compatible with WSGI. I just wonder, how that older gateway API worked and what was it? Good thing that there's an intent to remove that confusing `start_response` in WSGI2. – Boris Burkov May 29 '13 at 17:12
  • @Bob TBH, I don't think that was the main reason for `start_response()`. I think it's more likely that the problem with the `runCGI(input,output,errors,environ)` approach is that the application would have to write the `HTTP/1.1 200 OK` line, and it's not the application's responsibility to know whether the underlying server actually supports HTTP/1.1. If the underlying server only supports HTTP/1.0, then the `start_response()` could strip out any HTTP/1.1 headers, and it it's only HTTP/0.9, then it can ignore the parameters completely - HTTP/0.9 doesn't have either a status line or headers. – Aya May 29 '13 at 17:23
  • @Bob ...but that's still somewhat speculative, based on the web-sig posts. If you really want to know, you'll probably have to ask the author, Phillip J. Eby. – Aya May 29 '13 at 17:27
  • @Aya Ah, good though about the HTTP version, depending on the server. Thank you very much, Aya! – Boris Burkov May 29 '13 at 19:22
  • Am I correct then that we do not need to worry too much about the start_response callable with a new application? start_response is "just there" for old stuff? – johnny Nov 02 '15 at 19:28
4

I found a old thread may explain why.

Why is there a start_response and then a separate return?

One reason is that it allows you to write an application as a generator. But more importantly, it's necessary in order to support 'write()' for backward compatibility with existing frameworks, and that's pretty much the "killer reason" it's structured how it is. This particular innovation was Tony Lownds' brainchild, though, not mine. In my original WSGI concept, the application received an output stream and just wrote headers and everything to it.

Community
  • 1
  • 1
Jeffrey4l
  • 123
  • 3
  • 6
1

TLDR :

It's just for backwards compatibility for some existing frameworks. New Frameworks are suggested by WSGI to avoid using it.

Slightly long Version :

This section of Official Python WSGI Standards Page mentions :

The write() callable is returned by the start_response() callable.

...

Some existing application framework APIs support unbuffered output in a different manner than WSGI. Specifically, they provide a "write" function or method of some kind to write an unbuffered block of data, or else they provide a buffered "write" function and a "flush" mechanism to flush the buffer.

Unfortunately, such APIs cannot be implemented in terms of WSGI's "iterable" application return value, unless threads or other special mechanisms are used.

Therefore, to allow these frameworks to continue using an imperative API, WSGI includes a special write() callable, returned by the start_response callable.

New WSGI applications and frameworks should not use the write() callable if it is possible to avoid doing so. The write() callable is strictly a hack to support imperative streaming APIs

The overall article beautifully answers the WSGI standard to write Servers and Frameworks and would be helpful for any beginners starting with backend web development in python.

bad programmer
  • 818
  • 7
  • 12