From graham.dumpleton at gmail.com Mon Jun 21 06:17:26 2010 From: graham.dumpleton at gmail.com (Graham Dumpleton) Date: Mon, 21 Jun 2010 14:17:26 +1000 Subject: [Web-SIG] Fwd: bytes / unicode In-Reply-To: <4C1EE2E1.5030105@udel.edu> References: <87sk4jcejy.fsf@uwakimon.sk.tsukuba.ac.jp> <201006201204.30795.steve@pearwood.info> <20100620184120.10EFB3A4099@sparrow.telecommunity.com> <20100620234723.600ad4a8@pitrou.net> <20100621013405.19DC33A4099@sparrow.telecommunity.com> <4C1EE2E1.5030105@udel.edu> Message-ID: Can you please join the Python WEB-SIG and continue the existing conversation there. http://groups.google.com/group/python-web-sig?lnk= At the time I was merely facilitating a discussion and am not an expert on the issues. I have cc'd the web-sig for those who still may be interested in this. Graham ---------- Forwarded message ---------- From: Terry Reedy Date: 21 June 2010 13:56 Subject: Re: bytes / unicode To: Cc: graham.dumpleton at gmail.com On 6/20/2010 9:33 PM, P.J. Eby wrote: > > At 07:33 PM 6/20/2010 -0400, Terry Reedy wrote: >> >> Do you have in mind any tools that could and should operate on both, >> but do not? > > ?From http://mail.python.org/pipermail/web-sig/2009-September/004105.html : Thank for the concrete examples in this and your other post. I am cc-ing the author of the above. > """The problem which arises is that unquoting of URLs in Python 3.X > stdlib can only be done on unicode strings. Actually, I believe this is an encoding rather than bytes versus unicode issue. > If though a string > > contains non UTF-8 encoded characters it can fail.""" Which is to say, I believe, if the ascii text in the (unicode) string has a % encoding of a byte that that is not a legal utf-8 encoding of anything. The specific example is >>> urllib.parse.parse_qsl('a=b%e0') [('a', 'b?')] where the character after 'b' is white ? in dark diamond, indicating an error. parse_qsl() splits that input on '=' and sends each piece to urllib.parse.unquote unquote() attempts to "Replace %xx escapes by their single-character equivalent.". unquote has an encoding parameter that defaults to 'utf-8' in *its* call to .decode. parse_qsl does not have an encoding parameter. If it did, and it passed that to unquote, then the above example would become (simulated interaction) >>> urllib.parse.parse_qsl('a=b%e0', encoding='latin-1') [('a', 'b?')] I got that output by copying the file and adding "encoding-'latin-1'" to the unquote call. Does this solve this problem? Has anything like this been added for 3.2? Should it be? > I don't have any direct experience with the specific issue demonstrated > in that post, but in the context of the discussion as a whole, I > understood the overall issue as "if you pass bytes to certain stdlib > functions, you might get back unicode, an explicit error, or (at least > in the case shown above) something that's just plain wrong." As indicated above, I so far think that the problem is with the application of the new model, not the model itself. Just for 'fun', I tried feeding bytes to the function. >>> p.parse_qsl(b'a=b%e0') Traceback (most recent call last): ?File "", line 1, in ? ?p.parse_qsl(b'a=b%e0') ?File "C:\Programs\Python31\lib\urllib\parse.py", line 377, in parse_qsl ? ?pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')] TypeError: Type str doesn't support the buffer API I do not know if that message is correct, but certainly trying to split bytes with unicode is (now, at least) a mistake. This could be 'fixed' by replacing the typed literals with expressions that match the type of the input. But I am not sure if that is sensible since the next step is to unquote and decode to unicode anyway. I just do not know the use case. Terry Jan Reedy From solipsis at pitrou.net Wed Jun 23 21:36:45 2010 From: solipsis at pitrou.net (Antoine Pitrou) Date: Wed, 23 Jun 2010 21:36:45 +0200 Subject: [Web-SIG] bytes / unicode References: <20100620184120.10EFB3A4099@sparrow.telecommunity.com> <20100620234723.600ad4a8@pitrou.net> <87wrtsd44p.fsf@uwakimon.sk.tsukuba.ac.jp> <87631c4bca.fsf@uwakimon.sk.tsukuba.ac.jp> <20100621165611.GW5787@unaka.lan> <87r5jz3h8u.fsf@uwakimon.sk.tsukuba.ac.jp> <20100622055040.GE5787@unaka.lan> <87d3vj2tj2.fsf@uwakimon.sk.tsukuba.ac.jp> <0D1D2134-2CF9-4F93-BE82-912C5297D36F@fuhm.net> <87zkymns55.fsf@uwakimon.sk.tsukuba.ac.jp> <13837.1277311608@parc.com> Message-ID: <20100623213645.658517d7@pitrou.net> On Wed, 23 Jun 2010 14:23:33 -0400 Tres Seaver wrote: > > Perhaps such decisions need revisiting in light of subsequent experience > / pain / learning. E.g: > > - - the repeated inability of the web-sig to converge on appropriate > semantics for a Python3-compatible version of the WSGI spec; > > - - the subsequent quirkiness of the Python3 wsgiref implementation; The way wsgiref was adapted is admittedly suboptimal. It was totally broken at first, and PJE didn't want to look very deeply into it. We therefore had to settle on a series of small modifications that seemed rather reasonable, but without any in-depth discussion of what WSGI had to look like under Python 3 (since it was not our job and responsibility). Therefore, I don't think wsgiref should be taken as a guide to what a cleaned up, Python 3-specific WSGI must look like. > - - the slow adoption / porting rate of major web frameworks and libraries > to Python 3. Some of the major web frameworks and libraries have a ton of dependencies, which would explain why they really haven't bothered yet. I don't think you can't claim, though, that Python 3 makes things significantly harder for these frameworks. The proof is that many of them already give the user unicode strings in Python 2.x. They must have somehow got the decoding right. Regards Antoine. From henry at precheur.org Wed Jun 23 23:35:38 2010 From: henry at precheur.org (Henry Precheur) Date: Wed, 23 Jun 2010 14:35:38 -0700 Subject: [Web-SIG] bytes / unicode In-Reply-To: <20100623213645.658517d7@pitrou.net> References: <20100622055040.GE5787@unaka.lan> <87d3vj2tj2.fsf@uwakimon.sk.tsukuba.ac.jp> <0D1D2134-2CF9-4F93-BE82-912C5297D36F@fuhm.net> <87zkymns55.fsf@uwakimon.sk.tsukuba.ac.jp> <13837.1277311608@parc.com> <20100623213645.658517d7@pitrou.net> Message-ID: <20100623213538.GB9501@banane.novuscom.net> On Wed, Jun 23, 2010 at 09:36:45PM +0200, Antoine Pitrou wrote: > I don't think you can't claim, though, that Python 3 makes things > significantly harder for these frameworks. The proof is that many of > them already give the user unicode strings in Python 2.x. They must > have somehow got the decoding right. Well... Frameworks usually 'simplify' the problem by partly ignoring it. By default they assume the data in the request in UTF-8. You can specify an alternative encoding in most of them. Django [1], Werkzeug [2], and WebOb [3] do that. The problem with this approach is that you still have to deal with weird requests where one thing is unicode, and another is latin-1. Sometime you can even have 2 different encodings in a single header like Cookies. There's no solution to this problem, it has to be solved on a case by case basis. There was a big discussion a while ago on web-sig. I think the consensus was that WSGI for Python 3 should assume that the data is encoded in latin-1 since it's the default encoding according to the RFC. [1] http://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.encoding [2] http://werkzeug.pocoo.org/documentation/dev/unicode.html#request-and-response-objects [3] http://pythonpaste.org/webob/reference.html#unicode-variables -- Henry Pr?cheur From aaron.fransen at gmail.com Mon Jun 28 21:01:47 2010 From: aaron.fransen at gmail.com (Aaron Fransen) Date: Mon, 28 Jun 2010 13:01:47 -0600 Subject: [Web-SIG] Emulating req.write() in WSGI Message-ID: One of the nice things about mod_python is the req.write() function. Although I realize it's somewhat of an abuse to the http protocol, it's handy being able to periodically update the client browser with a status message for a long-running job. So handy in fact that I have a number of applications that rely fairly heavily on it as a means of keeping the client (person) happy instead of just showing them the default "browser busy" notification. There are a couple of workarounds, neither of which are ideal: 1. Take them immediately to a secondary page, then submit the actual job automatically on that second page. 2. Instead of using HTTP POST, use an HTTP Request Object (ie. Ajax). Both of them involve significantly more development effort than an equivalent req.write(). Is there a way to emulate the periodic-write functionality in WSGI? -------------- next part -------------- An HTML attachment was scrubbed... URL: From me at gustavonarea.net Mon Jun 28 21:50:35 2010 From: me at gustavonarea.net (Gustavo Narea) Date: Mon, 28 Jun 2010 20:50:35 +0100 Subject: [Web-SIG] Emulating req.write() in WSGI Message-ID: <201006282050.35932.me@gustavonarea.net> http://pythonpaste.org/waitforit/ HTH. - Gustavo. Aaron said: > One of the nice things about mod_python is the req.write() function. > > Although I realize it's somewhat of an abuse to the http protocol, it's > handy being able to periodically update the client browser with a status > message for a long-running job. > > So handy in fact that I have a number of applications that rely fairly > heavily on it as a means of keeping the client (person) happy instead of > just showing them the default "browser busy" notification. > > There are a couple of workarounds, neither of which are ideal: > 1. Take them immediately to a secondary page, then submit the actual job > automatically on that second page. > 2. Instead of using HTTP POST, use an HTTP Request Object (ie. Ajax). > > Both of them involve significantly more development effort than an > equivalent req.write(). > > Is there a way to emulate the periodic-write functionality in WSGI? -- Gustavo Narea . | Tech blog: =Gustavo/(+blog)/tech ~ About me: =Gustavo/about | From pje at telecommunity.com Mon Jun 28 23:11:49 2010 From: pje at telecommunity.com (P.J. Eby) Date: Mon, 28 Jun 2010 17:11:49 -0400 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: References: Message-ID: <20100628211203.C01BE3A404D@sparrow.telecommunity.com> At 01:01 PM 6/28/2010 -0600, Aaron Fransen wrote: >One of the nice things about mod_python is the req.write() function. > >Although I realize it's somewhat of an abuse to the http protocol, >it's handy being able to periodically update the client browser with >a status message for a long-running job. > >So handy in fact that I have a number of applications that rely >fairly heavily on it as a means of keeping the client (person) happy >instead of just showing them the default "browser busy" notification. > >There are a couple of workarounds, neither of which are ideal: >1. Take them immediately to a secondary page, then submit the actual >job automatically on that second page. >2. Instead of using HTTP POST, use an HTTP Request Object (ie. Ajax). > >Both of them involve significantly more development effort than an >equivalent req.write(). > >Is there a way to emulate the periodic-write functionality in WSGI? Each string yielded (or passed to the write() callable returned by start_response) is supposed to be sent straight through to the client. As long as your WSGI stack is actually conformant to the protocol, that's all you need to do. From aaron.fransen at gmail.com Mon Jun 28 23:43:00 2010 From: aaron.fransen at gmail.com (Aaron Fransen) Date: Mon, 28 Jun 2010 15:43:00 -0600 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: <20100628211203.C01BE3A404D@sparrow.telecommunity.com> References: <20100628211203.C01BE3A404D@sparrow.telecommunity.com> Message-ID: On Mon, Jun 28, 2010 at 3:11 PM, P.J. Eby wrote: > At 01:01 PM 6/28/2010 -0600, Aaron Fransen wrote: > >> One of the nice things about mod_python is the req.write() function. >> >> Although I realize it's somewhat of an abuse to the http protocol, it's >> handy being able to periodically update the client browser with a status >> message for a long-running job. >> >> So handy in fact that I have a number of applications that rely fairly >> heavily on it as a means of keeping the client (person) happy instead of >> just showing them the default "browser busy" notification. >> >> There are a couple of workarounds, neither of which are ideal: >> 1. Take them immediately to a secondary page, then submit the actual job >> automatically on that second page. >> 2. Instead of using HTTP POST, use an HTTP Request Object (ie. Ajax). >> >> Both of them involve significantly more development effort than an >> equivalent req.write(). >> >> Is there a way to emulate the periodic-write functionality in WSGI? >> > > Each string yielded (or passed to the write() callable returned by > start_response) is supposed to be sent straight through to the client. > > As long as your WSGI stack is actually conformant to the protocol, that's > all you need to do. > > Using mod_wsgi on Apache doesn't seem to exhibit that behavior. Experimentation with the write() functionality variously produces *only* the helper text, or only the final result page, it doesn't incrementally update the user. This behaviour appears to be dependent on the inclusion of the Content-Length header field. Yield command has not produced better results either, as it seems to produce the yield output then, as far as what's presented to the browser, exit the program completely (yet no errors in the log to speak of). I'll experiment with yield some more to see if I can more sharply define what's going on. -------------- next part -------------- An HTML attachment was scrubbed... URL: From me at gustavonarea.net Mon Jun 28 23:43:43 2010 From: me at gustavonarea.net (Gustavo Narea) Date: Mon, 28 Jun 2010 22:43:43 +0100 Subject: [Web-SIG] WSGI get-together during EuroPython? Message-ID: <201006282243.43554.me@gustavonarea.net> Hello, Sorry for this slightly off-topic message, but I was thinking it'd be nice to meet other people interested in WSGI at EuroPython. I don't have anything specific in mind. Probably share some beers. I'm open to suggestions. I'll be in Birmingham on the 17th and 20th of July; Saturday and Tuesday respectively. But I live just one hour away from Birmingham so I could go again any day after work. Please raise your hand if you're interested! :) -- Gustavo Narea . | Tech blog: =Gustavo/(+blog)/tech ~ About me: =Gustavo/about | From pje at telecommunity.com Tue Jun 29 00:41:54 2010 From: pje at telecommunity.com (P.J. Eby) Date: Mon, 28 Jun 2010 18:41:54 -0400 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: References: <20100628211203.C01BE3A404D@sparrow.telecommunity.com> Message-ID: <20100628224209.92B523A404D@sparrow.telecommunity.com> At 03:43 PM 6/28/2010 -0600, Aaron Fransen wrote: >Using mod_wsgi on Apache doesn't seem to exhibit that behavior. You may need "WSGIOutputBuffering Off" in your config; see: http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIOutputBuffering Another possibility is that you've got some middleware or something else buffering between your app and mod_wsgi, I suppose. From graham.dumpleton at gmail.com Tue Jun 29 00:57:31 2010 From: graham.dumpleton at gmail.com (Graham Dumpleton) Date: Tue, 29 Jun 2010 08:57:31 +1000 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: <20100628224209.92B523A404D@sparrow.telecommunity.com> References: <20100628211203.C01BE3A404D@sparrow.telecommunity.com> <20100628224209.92B523A404D@sparrow.telecommunity.com> Message-ID: On 29 June 2010 08:41, P.J. Eby wrote: > At 03:43 PM 6/28/2010 -0600, Aaron Fransen wrote: >> >> Using mod_wsgi on Apache doesn't seem to exhibit that behavior. > > You may need "WSGIOutputBuffering Off" in your config; see: > > http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIOutputBuffering > > Another possibility is that you've got some middleware or something else > buffering between your app and mod_wsgi, I suppose. Actually, that directive doesn't exist any more, plus the default even when it was was unbuffered. I have removed it from the documentation. If they are experiencing delays when using write() then possibly that have an Apache output filter installed which is buffering up response content and delaying it. For example, CONTENT_LENGTH of DEFLATE output filters. In other words, mod_wsgi doesn't delay it. Graham From graham.dumpleton at gmail.com Tue Jun 29 01:42:49 2010 From: graham.dumpleton at gmail.com (Graham Dumpleton) Date: Tue, 29 Jun 2010 09:42:49 +1000 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: References: Message-ID: On 29 June 2010 05:01, Aaron Fransen wrote: > One of the nice things about mod_python is the req.write() function. One thing I should warn you about req.write() in Apache is that for streaming data as you seem to be using it, it will accumulate memory against a request for each write call and that will not be reused, albeit it will be released again at the end of the request. The problem here isn't actually in mod_python but in the underlying Apache ap_rwrite() call. What this function does is that for each call to it, it creates what is called a bucket to hold the data to be written. The memory for this bucket is allocated from the per request memory pool each time. This bucket is then passed down the Apache output filter chain and eventually the data gets written out. Now, because the code doesn't attempt to reuse the bucket, that memory then remains unused, but still allocated against the memory pool, with the memory pool only being destroyed at the end of the request. The outcome of this is that if you had a long running request which continually wrote out response data in small bits using req.write(), for each call there is a small increase in amount of memory taken from the per request memory pool with it not being reused. Thus if the request were running for a very long time, you will see a gradual increase in overall memory usage of the process. When the request finishes, the memory is reclaimed and reused, but you have by then already set the high ceiling on ongoing process memory in use. Anyway, thought I should just warn you about this. In part this issue may even be why mod_python got a reputation for memory bloat in some situations. That is, the fundamental way of returning response data could cause unnecessary increase in process size if called many times for a request. Graham From aaron.fransen at gmail.com Tue Jun 29 15:37:47 2010 From: aaron.fransen at gmail.com (Aaron Fransen) Date: Tue, 29 Jun 2010 07:37:47 -0600 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: References: Message-ID: On Mon, Jun 28, 2010 at 5:42 PM, Graham Dumpleton < graham.dumpleton at gmail.com> wrote: > On 29 June 2010 05:01, Aaron Fransen wrote: > > One of the nice things about mod_python is the req.write() function. > > One thing I should warn you about req.write() in Apache is that for > streaming data as you seem to be using it, it will accumulate memory > against a request for each write call and that will not be reused, > albeit it will be released again at the end of the request. > > The problem here isn't actually in mod_python but in the underlying > Apache ap_rwrite() call. > > What this function does is that for each call to it, it creates what > is called a bucket to hold the data to be written. The memory for this > bucket is allocated from the per request memory pool each time. This > bucket is then passed down the Apache output filter chain and > eventually the data gets written out. > > Now, because the code doesn't attempt to reuse the bucket, that memory > then remains unused, but still allocated against the memory pool, with > the memory pool only being destroyed at the end of the request. > > The outcome of this is that if you had a long running request which > continually wrote out response data in small bits using req.write(), > for each call there is a small increase in amount of memory taken from > the per request memory pool with it not being reused. Thus if the > request were running for a very long time, you will see a gradual > increase in overall memory usage of the process. When the request > finishes, the memory is reclaimed and reused, but you have by then > already set the high ceiling on ongoing process memory in use. > > Anyway, thought I should just warn you about this. In part this issue > may even be why mod_python got a reputation for memory bloat in some > situations. That is, the fundamental way of returning response data > could cause unnecessary increase in process size if called many times > for a request. > > Graham > Fortunately we're not talking about a huge amount of data here, basically just a couple of notices to keep the user happy (less than 1K usually). When using yield, it's as if the module where the yield command is run is completely ignored. The page returned is a "default" page generated by the application. Errors are being trapped, but none are being generated, it's just exiting without any kind of notice. When using write() without a Content-Length header, nothing shows on the browser. When using write() with a Content-Length header, the first update shows (and only after the entire page has been generated), but none of the subsequent ones nor the final page. When using write() with a Content-Length header set large enough to encompass the entire final result, the final result page shows, but none of the informational messages leading up to the generation of the page appear. I haven't really done anything to the base wsgi installation; just set it up in daemon mode. -------------- next part -------------- An HTML attachment was scrubbed... URL: From aaron.fransen at gmail.com Tue Jun 29 18:14:05 2010 From: aaron.fransen at gmail.com (Aaron Fransen) Date: Tue, 29 Jun 2010 10:14:05 -0600 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: References: Message-ID: On Tue, Jun 29, 2010 at 7:37 AM, Aaron Fransen wrote: > > > On Mon, Jun 28, 2010 at 5:42 PM, Graham Dumpleton < > graham.dumpleton at gmail.com> wrote: > >> On 29 June 2010 05:01, Aaron Fransen wrote: >> > One of the nice things about mod_python is the req.write() function. >> >> One thing I should warn you about req.write() in Apache is that for >> streaming data as you seem to be using it, it will accumulate memory >> against a request for each write call and that will not be reused, >> albeit it will be released again at the end of the request. >> >> The problem here isn't actually in mod_python but in the underlying >> Apache ap_rwrite() call. >> >> What this function does is that for each call to it, it creates what >> is called a bucket to hold the data to be written. The memory for this >> bucket is allocated from the per request memory pool each time. This >> bucket is then passed down the Apache output filter chain and >> eventually the data gets written out. >> >> Now, because the code doesn't attempt to reuse the bucket, that memory >> then remains unused, but still allocated against the memory pool, with >> the memory pool only being destroyed at the end of the request. >> >> The outcome of this is that if you had a long running request which >> continually wrote out response data in small bits using req.write(), >> for each call there is a small increase in amount of memory taken from >> the per request memory pool with it not being reused. Thus if the >> request were running for a very long time, you will see a gradual >> increase in overall memory usage of the process. When the request >> finishes, the memory is reclaimed and reused, but you have by then >> already set the high ceiling on ongoing process memory in use. >> >> Anyway, thought I should just warn you about this. In part this issue >> may even be why mod_python got a reputation for memory bloat in some >> situations. That is, the fundamental way of returning response data >> could cause unnecessary increase in process size if called many times >> for a request. >> >> Graham >> > > > Fortunately we're not talking about a huge amount of data here, basically > just a couple of notices to keep the user happy (less than 1K usually). > > When using yield, it's as if the module where the yield command is run is > completely ignored. The page returned is a "default" page generated by the > application. Errors are being trapped, but none are being generated, it's > just exiting without any kind of notice. > > When using write() without a Content-Length header, nothing shows on the > browser. > > When using write() with a Content-Length header, the first update shows > (and only after the entire page has been generated), but none of the > subsequent ones nor the final page. > > When using write() with a Content-Length header set large enough to > encompass the entire final result, the final result page shows, but none of > the informational messages leading up to the generation of the page appear. > > I haven't really done anything to the base wsgi installation; just set it > up in daemon mode. > > Couple more things I've been able to discern. The first happened after I "fixed" the html code. Originally under mod_python, I guess I was cheating more than a little bit by sending code blocks twice, once for the incremental notices, once for the final content. Once I changed the code to send a single properly parsed block, the entire document showed up as expected, however it still did not send any part of the html incrementally. Watching the line with Wireshark, all of the data was transmitted at the same time, so nothing was sent to the browser incrementally. (This is using the write() functionality, I haven't tried watching the line with yield yet.) -------------- next part -------------- An HTML attachment was scrubbed... URL: From pje at telecommunity.com Tue Jun 29 20:30:28 2010 From: pje at telecommunity.com (P.J. Eby) Date: Tue, 29 Jun 2010 14:30:28 -0400 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: References: Message-ID: <20100629183044.D36133A404D@sparrow.telecommunity.com> At 10:14 AM 6/29/2010 -0600, Aaron Fransen wrote: >Couple more things I've been able to discern. > >The first happened after I "fixed" the html code. Originally under >mod_python, I guess I was cheating more than a little bit by sending > code blocks twice, once for the incremental notices, >once for the final content. Once I changed the code to send a single >properly parsed block, the entire document showed up as expected, >however it still did not send any part of the html incrementally. > >Watching the line with Wireshark, all of the data was transmitted at >the same time, so nothing was sent to the browser incrementally. So, you're not sending a multipart/x-mixed-replace ("server push") transmission? From pje at telecommunity.com Tue Jun 29 21:21:05 2010 From: pje at telecommunity.com (P.J. Eby) Date: Tue, 29 Jun 2010 15:21:05 -0400 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: References: <20100629183044.D36133A404D@sparrow.telecommunity.com> Message-ID: <20100629192121.4D8973A404D@sparrow.telecommunity.com> At 12:33 PM 6/29/2010 -0600, Aaron Fransen wrote: >I was sending text/html (I probably should have used multipart >before) ... should I try multipart now, even with having everything >in a single stream? Heck if I know. I just assumed that what you're doing would be unlikely to work, whereas multipart has at least been previously documented as working with Apache (at least for nph scripts). Dunno if mod_wsgi'll do that or not. Actually, what I'd do in your place is try a "nph-" CGI in Python (using a wsgiref CGIHandler with its 'origin_server' attribute set to True), have it send multipart, and see if that works. If it doesn't work, then it's probably a problem with your app. If it *does* work, but the same app doesn't work under mod_wsgi, then it's a mod_wsgi issue; possibly related to configuration. From what Graham's said, mod_wsgi shouldn't be buffering anything, which means it has to either be Apache or your app that's buffering. If it's Apache, doing a proper nph+multipart ought to fix it, unless there's something else going on in the Apache configuration. From ianb at colorstudy.com Tue Jun 29 22:25:45 2010 From: ianb at colorstudy.com (Ian Bicking) Date: Tue, 29 Jun 2010 15:25:45 -0500 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: <201006282050.35932.me@gustavonarea.net> References: <201006282050.35932.me@gustavonarea.net> Message-ID: On Mon, Jun 28, 2010 at 2:50 PM, Gustavo Narea wrote: > http://pythonpaste.org/waitforit/ > It will address this problem, but not with mod_wsgi as it relies on threads to handle the queuing (the request is deferred to a thread, and in-memory data structures are used to get the result). One of the solutions proposed ("Take them immediately to a secondary page, then submit the actual job automatically on that second page.") actually seems most reasonable to me, as it allows you to give any UI you want (where writing out a response incrementally is fairly constrained), and can be generalized fairly easily. And if you use a little persistence (something WaitForIt doesn't have the benefit of using) you could fire off XMLHttpRequests to get an update. Hmm... WaitForIt would almost be better simply doing this, except it tries to be clever by avoiding interstitial pages if the response isn't slow. And then there's really cheap tricks, like: -- Ian Bicking | http://blog.ianbicking.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From graham.dumpleton at gmail.com Wed Jun 30 02:13:00 2010 From: graham.dumpleton at gmail.com (Graham Dumpleton) Date: Wed, 30 Jun 2010 10:13:00 +1000 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: References: Message-ID: On 29 June 2010 23:37, Aaron Fransen wrote: > Fortunately we're not talking about a huge amount of data here, basically > just a couple of notices to keep the user happy (less than 1K usually). > > When using yield, it's as if the module where the yield command is run is > completely ignored. The page returned is a "default" page generated by the > application. Errors are being trapped, but none are being generated, it's > just exiting without any kind of notice. > > When using write() without a Content-Length header, nothing shows on the > browser. > > When using write() with a Content-Length header, the first update shows (and > only after the entire page has been generated), but none of the subsequent > ones nor the final page. > > When using write() with a Content-Length header set large enough to > encompass the entire final result, the final result page shows, but none of > the informational messages leading up to the generation of the page appear. These statements concerns me. The Content-Length header if you are sending a response of unknown length should not be set. Further, you definitely cannot return/write more response data than is specified by Content-Length. Doing so breaks HTTP and mod_wsgi will actually deliberately discard anything returned over what Content-Length specifies. Can you clarify this? Are you setting Content-Length to a value less than the amount of data you could actually return? Graham From graham.dumpleton at gmail.com Wed Jun 30 02:17:38 2010 From: graham.dumpleton at gmail.com (Graham Dumpleton) Date: Wed, 30 Jun 2010 10:17:38 +1000 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: References: Message-ID: On 30 June 2010 02:14, Aaron Fransen wrote: > Couple more things I've been able to discern. > > The first happened after I "fixed" the html code. Originally under > mod_python, I guess I was cheating more than a little bit by sending > code blocks twice, once for the incremental notices, once for > the final content. Once I changed the code to send a single properly parsed > block, the entire document showed up as expected, however it still did not > send any part of the html incrementally. > > Watching the line with Wireshark, all of the data was transmitted at the > same time, so nothing was sent to the browser incrementally. > > (This is using the write() functionality, I haven't tried watching the line > with yield yet.) Use a variation of WSGI middleware wrapper in: http://code.google.com/p/modwsgi/wiki/DebuggingTechniques#Tracking_Request_and_Response using it to 'print' returned data to Apache log and then tail Apache error log to see when that data is output. Alternatively, change the code there to output a time stamp against each chunk of data written to the file recording the response content. This will show what data is returned by WSGI application, before mod_wsgi truncates anything greater than content length specified, plus also show whether it is your WSGI application which is delaying output somehow, or whether Apache output filters are doing it. Graham From aaron.fransen at gmail.com Wed Jun 30 13:35:49 2010 From: aaron.fransen at gmail.com (Aaron Fransen) Date: Wed, 30 Jun 2010 05:35:49 -0600 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: References: Message-ID: On Tue, Jun 29, 2010 at 6:17 PM, Graham Dumpleton < graham.dumpleton at gmail.com> wrote: > On 30 June 2010 02:14, Aaron Fransen wrote: > > Couple more things I've been able to discern. > > > > The first happened after I "fixed" the html code. Originally under > > mod_python, I guess I was cheating more than a little bit by sending > > code blocks twice, once for the incremental notices, once > for > > the final content. Once I changed the code to send a single properly > parsed > > block, the entire document showed up as expected, however it still did > not > > send any part of the html incrementally. > > > > Watching the line with Wireshark, all of the data was transmitted at the > > same time, so nothing was sent to the browser incrementally. > > > > (This is using the write() functionality, I haven't tried watching the > line > > with yield yet.) > > Use a variation of WSGI middleware wrapper in: > > > http://code.google.com/p/modwsgi/wiki/DebuggingTechniques#Tracking_Request_and_Response > > using it to 'print' returned data to Apache log and then tail Apache > error log to see when that data is output. Alternatively, change the > code there to output a time stamp against each chunk of data written > to the file recording the response content. > > This will show what data is returned by WSGI application, before > mod_wsgi truncates anything greater than content length specified, > plus also show whether it is your WSGI application which is delaying > output somehow, or whether Apache output filters are doing it. > > Graham > I've actually tried a variation on this already using a built-in logging facility in the application that writes date/time values to an external log file with comments, and in the case of testing wsgi I actually included some time.sleep() statements to force a delay in the application. To give you an idea of the flow, here's essentially what's going on: def application(environ,start_response): mydict = {} mydict['environ']=environ mydict['startresponse'] = start_response # run program in another .py file that has been imported RunTest(mydict) Then in the other module you would have something like: def RunTest(mydict): status = '200 OK' response_headers = [('Content-type','text/html')] writeobj = detail['startresponse'](status,response_headers) writeobj('Fetching sales for 2009...') time.sleep(2) writeobj('
Fetching sales for 2010...') ...then finally... writeobj('5000 results returned.') return This is obviously a truncated (and fake) example, but it gives you an idea of the flow. -------------- next part -------------- An HTML attachment was scrubbed... URL: From graham.dumpleton at gmail.com Wed Jun 30 14:26:17 2010 From: graham.dumpleton at gmail.com (Graham Dumpleton) Date: Wed, 30 Jun 2010 22:26:17 +1000 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: References: Message-ID: On 30 June 2010 21:35, Aaron Fransen wrote: > > > On Tue, Jun 29, 2010 at 6:17 PM, Graham Dumpleton > wrote: >> >> On 30 June 2010 02:14, Aaron Fransen wrote: >> > Couple more things I've been able to discern. >> > >> > The first happened after I "fixed" the html code. Originally under >> > mod_python, I guess I was cheating more than a little bit by sending >> > code blocks twice, once for the incremental notices, once >> > for >> > the final content. Once I changed the code to send a single properly >> > parsed >> > block, the entire document showed up as expected, however it still did >> > not >> > send any part of the html incrementally. >> > >> > Watching the line with Wireshark, all of the data was transmitted at the >> > same time, so nothing was sent to the browser incrementally. >> > >> > (This is using the write() functionality, I haven't tried watching the >> > line >> > with yield yet.) >> >> Use a variation of WSGI middleware wrapper in: >> >> >> ?http://code.google.com/p/modwsgi/wiki/DebuggingTechniques#Tracking_Request_and_Response >> >> using it to 'print' returned data to Apache log and then tail Apache >> error log to see when that data is output. Alternatively, change the >> code there to output a time stamp against each chunk of data written >> to the file recording the response content. >> >> This will show what data is returned by WSGI application, before >> mod_wsgi truncates anything greater than content length specified, >> plus also show whether it is your WSGI application which is delaying >> output somehow, or whether Apache output filters are doing it. >> >> Graham > > I've actually tried a variation on this already using a built-in logging > facility in the application that writes date/time values to an external log > file with comments, and in the case of testing wsgi I actually included some > time.sleep() statements to force a delay in the application. > > To give you an idea of the flow, here's essentially what's going on: > > def application(environ,start_response): > ??? mydict = {} > ??? mydict['environ']=environ > ??? mydict['startresponse'] = start_response > ??? # run program in another .py file that has been imported > ??? RunTest(mydict) > > Then in the other module you would have something like: > > def RunTest(mydict): > ??? status = '200 OK' > ??? response_headers = [('Content-type','text/html')] > ??? writeobj = detail['startresponse'](status,response_headers) > ??? writeobj('Fetching sales for 2009...') > ??? time.sleep(2) > ??? writeobj('
Fetching sales for 2010...') > > ??? ...then finally... > > ??? writeobj('5000 results returned.') > ??? return > > This is obviously a truncated (and fake) example, but it gives you an idea > of the flow. Now go try the following two examples as illustrated instead. In both cases, do not use a web browser, instead telnet to the port of the web server and enter HTTP GET directly. If you are not using VirtualHost, use something like: telnet localhost 80 GET /stream-yield.wsgi HTTP/1.0 If using a VirtualHost, use something like: telnet localhost 80 GET /stream-yield.wsgi HTTP/1.1 Host: tests.example.com Ensure additional blank line entered to indicate end of headers. First example uses yield. # stream-yield.wsgi import time def application(environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) for i in range(10): yield '%d\n' % i time.sleep(1) Second example uses write: # stream-write.wsgi import time def application(environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain')] write = start_response(status, response_headers) for i in range(10): write('%d\n' % i) time.sleep(1) return [] For me, using stock standard operating system supplied Apache on Mac OS X, I see a line returned every second. If I use Safari as a web browser, in both cases the browser only shows the response after all data has been written and the socket connection closed. If I use Firefox however, they display as data comes in. This delay in display is thus possibly just the behaviour of a specific browser delaying the display until the socket is closed. The example for multipart/x-mixed-replace which others mention is: import time def application(environ, start_response): status = '200 OK' response_headers = [('Content-Type', 'multipart/x-mixed-replace; boundary=xstringx')] start_response(status, response_headers) yield '--xstrinx\n' for i in range(10): yield 'Content-type: text/plain\n' yield '\n' yield '%d\n' % i yield '--xstringx\n' time.sleep(1) With telnet you will see the various sections, but with Safari again only shows at end, although you will find that it only shows the data line, ie., the number and not all the other stuff. So, understands multipart format but doesn't support x-mixed-replace. It was always the case that only certain browsers supported that mime type. In the case of Firefox, it doesn't seem to like it at all and seems to give up and not display anything, not even replacing the previously displayed page contents. What this means is that you cant rely on browsers to handle multipart mixed replace alone. If you were really going to use that format, you really want to use JavaScript and AJAX stuff to process it. The same applies for progressive display of plain text content when streamed over time. In summary, you really want to be using some JavaScript/AJAX stuff on browser side to get uniform behaviour on all the browsers. Graham From graham.dumpleton at gmail.com Wed Jun 30 14:34:11 2010 From: graham.dumpleton at gmail.com (Graham Dumpleton) Date: Wed, 30 Jun 2010 22:34:11 +1000 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: References: Message-ID: On 30 June 2010 22:26, Graham Dumpleton wrote: > On 30 June 2010 21:35, Aaron Fransen wrote: >> >> >> On Tue, Jun 29, 2010 at 6:17 PM, Graham Dumpleton >> wrote: >>> >>> On 30 June 2010 02:14, Aaron Fransen wrote: >>> > Couple more things I've been able to discern. >>> > >>> > The first happened after I "fixed" the html code. Originally under >>> > mod_python, I guess I was cheating more than a little bit by sending >>> > code blocks twice, once for the incremental notices, once >>> > for >>> > the final content. Once I changed the code to send a single properly >>> > parsed >>> > block, the entire document showed up as expected, however it still did >>> > not >>> > send any part of the html incrementally. >>> > >>> > Watching the line with Wireshark, all of the data was transmitted at the >>> > same time, so nothing was sent to the browser incrementally. >>> > >>> > (This is using the write() functionality, I haven't tried watching the >>> > line >>> > with yield yet.) >>> >>> Use a variation of WSGI middleware wrapper in: >>> >>> >>> ?http://code.google.com/p/modwsgi/wiki/DebuggingTechniques#Tracking_Request_and_Response >>> >>> using it to 'print' returned data to Apache log and then tail Apache >>> error log to see when that data is output. Alternatively, change the >>> code there to output a time stamp against each chunk of data written >>> to the file recording the response content. >>> >>> This will show what data is returned by WSGI application, before >>> mod_wsgi truncates anything greater than content length specified, >>> plus also show whether it is your WSGI application which is delaying >>> output somehow, or whether Apache output filters are doing it. >>> >>> Graham >> >> I've actually tried a variation on this already using a built-in logging >> facility in the application that writes date/time values to an external log >> file with comments, and in the case of testing wsgi I actually included some >> time.sleep() statements to force a delay in the application. >> >> To give you an idea of the flow, here's essentially what's going on: >> >> def application(environ,start_response): >> ??? mydict = {} >> ??? mydict['environ']=environ >> ??? mydict['startresponse'] = start_response >> ??? # run program in another .py file that has been imported >> ??? RunTest(mydict) >> >> Then in the other module you would have something like: >> >> def RunTest(mydict): >> ??? status = '200 OK' >> ??? response_headers = [('Content-type','text/html')] >> ??? writeobj = detail['startresponse'](status,response_headers) >> ??? writeobj('Fetching sales for 2009...') >> ??? time.sleep(2) >> ??? writeobj('
Fetching sales for 2010...') >> >> ??? ...then finally... >> >> ??? writeobj('5000 results returned.') >> ??? return >> >> This is obviously a truncated (and fake) example, but it gives you an idea >> of the flow. > > Now go try the following two examples as illustrated instead. > > In both cases, do not use a web browser, instead telnet to the port of > the web server and enter HTTP GET directly. If you are not using > VirtualHost, use something like: > > ?telnet localhost 80 > ?GET /stream-yield.wsgi HTTP/1.0 > > If using a VirtualHost, use something like: > > ?telnet localhost 80 > ?GET /stream-yield.wsgi HTTP/1.1 > ?Host: tests.example.com > > Ensure additional blank line entered to indicate end of headers. > > First example uses yield. > > # stream-yield.wsgi > > import time > > def application(environ, start_response): > ? ?status = '200 OK' > > ? ?response_headers = [('Content-type', 'text/plain')] > ? ?start_response(status, response_headers) > > ? ?for i in range(10): > ? ? ?yield '%d\n' % i > ? ? ?time.sleep(1) > > Second example uses write: > > # stream-write.wsgi > > import time > > def application(environ, start_response): > ? ?status = '200 OK' > > ? ?response_headers = [('Content-type', 'text/plain')] > ? ?write = start_response(status, response_headers) > > ? ?for i in range(10): > ? ? ?write('%d\n' % i) > ? ? ?time.sleep(1) > > ? ?return [] > > For me, using stock standard operating system supplied Apache on Mac > OS X, I see a line returned every second. > > If I use Safari as a web browser, in both cases the browser only shows > the response after all data has been written and the socket connection > closed. If I use Firefox however, they display as data comes in. > > This delay in display is thus possibly just the behaviour of a > specific browser delaying the display until the socket is closed. > > The example for multipart/x-mixed-replace which others mention is: > > import time > > def application(environ, start_response): > ? ?status = '200 OK' > > ? ?response_headers = [('Content-Type', 'multipart/x-mixed-replace; > boundary=xstringx')] > ? ?start_response(status, response_headers) > > ? ?yield '--xstrinx\n' > > ? ?for i in range(10): > > ? ? ?yield 'Content-type: text/plain\n' > ? ? ?yield '\n' > ? ? ?yield '%d\n' % i > ? ? ?yield '--xstringx\n' > > ? ? ?time.sleep(1) > > With telnet you will see the various sections, but with Safari again > only shows at end, although you will find that it only shows the data > line, ie., the number and not all the other stuff. So, understands > multipart format but doesn't support x-mixed-replace. It was always > the case that only certain browsers supported that mime type. In the > case of Firefox, it doesn't seem to like it at all and seems to give > up and not display anything, not even replacing the previously > displayed page contents. > > What this means is that you cant rely on browsers to handle multipart > mixed replace alone. If you were really going to use that format, you > really want to use JavaScript and AJAX stuff to process it. The same > applies for progressive display of plain text content when streamed > over time. > > In summary, you really want to be using some JavaScript/AJAX stuff on > browser side to get uniform behaviour on all the browsers. Based on some Googling, sees that Firefox may have got rid of support for multipart mixed replace in version 3.0. Graham From aaron.fransen at gmail.com Wed Jun 30 14:55:18 2010 From: aaron.fransen at gmail.com (Aaron Fransen) Date: Wed, 30 Jun 2010 06:55:18 -0600 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: References: Message-ID: On Wed, Jun 30, 2010 at 6:26 AM, Graham Dumpleton < graham.dumpleton at gmail.com> wrote: > On 30 June 2010 21:35, Aaron Fransen wrote: > > > > > > On Tue, Jun 29, 2010 at 6:17 PM, Graham Dumpleton > > wrote: > >> > >> On 30 June 2010 02:14, Aaron Fransen wrote: > >> > Couple more things I've been able to discern. > >> > > >> > The first happened after I "fixed" the html code. Originally under > >> > mod_python, I guess I was cheating more than a little bit by sending > >> > code blocks twice, once for the incremental notices, > once > >> > for > >> > the final content. Once I changed the code to send a single properly > >> > parsed > >> > block, the entire document showed up as expected, however it still did > >> > not > >> > send any part of the html incrementally. > >> > > >> > Watching the line with Wireshark, all of the data was transmitted at > the > >> > same time, so nothing was sent to the browser incrementally. > >> > > >> > (This is using the write() functionality, I haven't tried watching the > >> > line > >> > with yield yet.) > >> > >> Use a variation of WSGI middleware wrapper in: > >> > >> > >> > http://code.google.com/p/modwsgi/wiki/DebuggingTechniques#Tracking_Request_and_Response > >> > >> using it to 'print' returned data to Apache log and then tail Apache > >> error log to see when that data is output. Alternatively, change the > >> code there to output a time stamp against each chunk of data written > >> to the file recording the response content. > >> > >> This will show what data is returned by WSGI application, before > >> mod_wsgi truncates anything greater than content length specified, > >> plus also show whether it is your WSGI application which is delaying > >> output somehow, or whether Apache output filters are doing it. > >> > >> Graham > > > > I've actually tried a variation on this already using a built-in logging > > facility in the application that writes date/time values to an external > log > > file with comments, and in the case of testing wsgi I actually included > some > > time.sleep() statements to force a delay in the application. > > > > To give you an idea of the flow, here's essentially what's going on: > > > > def application(environ,start_response): > > mydict = {} > > mydict['environ']=environ > > mydict['startresponse'] = start_response > > # run program in another .py file that has been imported > > RunTest(mydict) > > > > Then in the other module you would have something like: > > > > def RunTest(mydict): > > status = '200 OK' > > response_headers = [('Content-type','text/html')] > > writeobj = detail['startresponse'](status,response_headers) > > writeobj('Fetching sales for 2009...') > > time.sleep(2) > > writeobj('
Fetching sales for 2010...') > > > > ...then finally... > > > > writeobj('5000 results returned.') > > return > > > > This is obviously a truncated (and fake) example, but it gives you an > idea > > of the flow. > > Now go try the following two examples as illustrated instead. > > In both cases, do not use a web browser, instead telnet to the port of > the web server and enter HTTP GET directly. If you are not using > VirtualHost, use something like: > > telnet localhost 80 > GET /stream-yield.wsgi HTTP/1.0 > > If using a VirtualHost, use something like: > > telnet localhost 80 > GET /stream-yield.wsgi HTTP/1.1 > Host: tests.example.com > > Ensure additional blank line entered to indicate end of headers. > > First example uses yield. > > # stream-yield.wsgi > > import time > > def application(environ, start_response): > status = '200 OK' > > response_headers = [('Content-type', 'text/plain')] > start_response(status, response_headers) > > for i in range(10): > yield '%d\n' % i > time.sleep(1) > > Second example uses write: > > # stream-write.wsgi > > import time > > def application(environ, start_response): > status = '200 OK' > > response_headers = [('Content-type', 'text/plain')] > write = start_response(status, response_headers) > > for i in range(10): > write('%d\n' % i) > time.sleep(1) > > return [] > > For me, using stock standard operating system supplied Apache on Mac > OS X, I see a line returned every second. > > If I use Safari as a web browser, in both cases the browser only shows > the response after all data has been written and the socket connection > closed. If I use Firefox however, they display as data comes in. > > This delay in display is thus possibly just the behaviour of a > specific browser delaying the display until the socket is closed. > > The example for multipart/x-mixed-replace which others mention is: > > import time > > def application(environ, start_response): > status = '200 OK' > > response_headers = [('Content-Type', 'multipart/x-mixed-replace; > boundary=xstringx')] > start_response(status, response_headers) > > yield '--xstrinx\n' > > for i in range(10): > > yield 'Content-type: text/plain\n' > yield '\n' > yield '%d\n' % i > yield '--xstringx\n' > > time.sleep(1) > > With telnet you will see the various sections, but with Safari again > only shows at end, although you will find that it only shows the data > line, ie., the number and not all the other stuff. So, understands > multipart format but doesn't support x-mixed-replace. It was always > the case that only certain browsers supported that mime type. In the > case of Firefox, it doesn't seem to like it at all and seems to give > up and not display anything, not even replacing the previously > displayed page contents. > > What this means is that you cant rely on browsers to handle multipart > mixed replace alone. If you were really going to use that format, you > really want to use JavaScript and AJAX stuff to process it. The same > applies for progressive display of plain text content when streamed > over time. > > In summary, you really want to be using some JavaScript/AJAX stuff on > browser side to get uniform behaviour on all the browsers. > > Graham > I can see that this could potentially get very ugly very quickly. Using stock Apache on the current Ubuntu server, using yield produced a response error and using write() (over the telnet interface) returned the 0 only and disconnected. Similar behavior in Firefox. How odd that nobody's come up with a simple streaming/update schema (at least to my mind). It would have been nice to be able to provide some kind of in-stream feedback for long running jobs, but it looks like I'm going to have to abandon that approach. The only issue with either of the other solutions is that each subsequent request depends on data provided by the prior, so the amount of traffic going back & forth could potentially become a problem. Alternatively I could simply create a session database that saves the required objects then each subsequent request simply fetches the required one from the table and... Well, you can see why streaming seemed like such a simple solution! Back to the drawing board, as it were. Thanks all. -------------- next part -------------- An HTML attachment was scrubbed... URL: From merwok at netwok.org Wed Jun 30 15:29:07 2010 From: merwok at netwok.org (=?UTF-8?B?w4lyaWMgQXJhdWpv?=) Date: Wed, 30 Jun 2010 15:29:07 +0200 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: References: Message-ID: <4C2B46A3.6040503@netwok.org> > How odd that nobody's come up with a simple streaming/update schema (at > least to my mind). Well, if I understand HTTP correctly, it wasn?t designed to do that. > Well, you can see why streaming seemed like such a simple solution! Back to > the drawing board, as it were. Perhaps new things like Comet?, HTML 5?s Server Events, HTML 5?s WebSockets, or even XMPP over HTTP. Regards From merwok at netwok.org Wed Jun 30 15:31:54 2010 From: merwok at netwok.org (=?UTF-8?B?w4lyaWMgQXJhdWpv?=) Date: Wed, 30 Jun 2010 15:31:54 +0200 Subject: [Web-SIG] Emulating req.write() in WSGI In-Reply-To: <4C2B46A3.6040503@netwok.org> References: <4C2B46A3.6040503@netwok.org> Message-ID: <4C2B474A.10704@netwok.org> Forgot the footnote: ? http://en.wikipedia.org/wiki/Comet_%28programming%29