WSGI: what's the purpose of start_response function
Asked Answered
M

3

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..." :()

Malca answered 27/5, 2013 at 14:5 Comment(0)
A
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.

Afternoon answered 27/5, 2013 at 14:53 Comment(12)
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.Woodrum
@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.Afternoon
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.Woodrum
@GrahamDumpleton, @Afternoon 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)?Malca
@GrahamDumpleton, @Afternoon 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?Malca
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.Woodrum
@GrahamDumpleton I'm speaking of PEP section about write() callable: 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".Malca
@Afternoon 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.Malca
@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.Afternoon
@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.Afternoon
@Afternoon Ah, good though about the HTTP version, depending on the server. Thank you very much, Aya!Malca
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?Missive
E
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.

Entree answered 19/6, 2015 at 2:32 Comment(0)
L
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.

Lorylose answered 8/5, 2021 at 20:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.