Ticket #3711: deferred-render-3.patch

File deferred-render-3.patch, 13.6 KB (added by esteve, 5 years ago)
  • twisted/web/resource.py

    # Bazaar merge directive format 2 (Bazaar 0.90)
    # revision_id: esteve@fluidinfo.com-20090331192619-j7gwbw96irvmsyqq
    # target_branch: ../trunk/
    # testament_sha1: 27792133b71a691a8c3b15dfd81dff9ca3164b69
    # timestamp: 2009-03-31 22:07:46 +0200
    # base_revision_id: svn-v4:bbbe8e31-12d6-0310-92fd-\
    #   ac37d47ddeeb:trunk:26540
    # 
    # Begin patch
    === modified file 'twisted/web/resource.py'
     
    4343        Render a request. This is called on the leaf resource for 
    4444        a request. Render must return either a string, which will 
    4545        be sent to the browser as the HTML for the request, or 
    46         server.NOT_DONE_YET. If NOT_DONE_YET is returned, 
     46        a Deferred containing a string as its resoult value. 
     47        Render can also return server.NOT_DONE_YET, but 
    4748        at some point later (in a Deferred callback, usually) 
    4849        call request.write("<html>") to write data to the request, 
    4950        and request.finish() to send the data to the browser. 
     51        The latter behavior is deprecated in favor of returning 
     52        Deferreds. 
    5053        """ 
    5154 
    5255 
  • twisted/web/server.py

    === modified file 'twisted/web/server.py'
     
    127127 
    128128 
    129129    def render(self, resrc): 
    130         try: 
    131             body = resrc.render(self) 
    132         except UnsupportedMethod, e: 
    133             allowedMethods = e.allowedMethods 
    134             if (self.method == "HEAD") and ("GET" in allowedMethods): 
    135                 # We must support HEAD (RFC 2616, 5.1.1).  If the 
    136                 # resource doesn't, fake it by giving the resource 
    137                 # a 'GET' request and then return only the headers, 
    138                 # not the body. 
    139                 log.msg("Using GET to fake a HEAD request for %s" % 
    140                         (resrc,)) 
    141                 self.method = "GET" 
    142                 body = resrc.render(self) 
     130        def _cbRender(body): 
     131            if body == NOT_DONE_YET: 
     132                warnings.warn( 
     133                    "Returning NOT_DONE_YET is deprecated, return a Deferred instead.", 
     134                    DeprecationWarning, stacklevel=2) 
     135                return 
     136            if type(body) is not types.StringType: 
     137                body = resource.ErrorPage( 
     138                    http.INTERNAL_SERVER_ERROR, 
     139                    "Request did not return a string", 
     140                    "Request: " + html.PRE(reflect.safe_repr(self)) + "<br />" + 
     141                    "Resource: " + html.PRE(reflect.safe_repr(resrc)) + "<br />" + 
     142                    "Value: " + html.PRE(reflect.safe_repr(body))).render(self) 
    143143 
    144                 if body is NOT_DONE_YET: 
    145                     log.msg("Tried to fake a HEAD request for %s, but " 
    146                             "it got away from me." % resrc) 
    147                     # Oh well, I guess we won't include the content length. 
    148                 else: 
     144            if self.method == "HEAD": 
     145                if len(body) > 0: 
     146                    # This is a Bad Thing (RFC 2616, 9.4) 
     147                    log.msg("Warning: HEAD request %s for resource %s is" 
     148                            " returning a message body." 
     149                            "  I think I'll eat it." 
     150                            % (self, resrc)) 
    149151                    self.setHeader('content-length', str(len(body))) 
    150  
    151152                self.write('') 
    152                 self.finish() 
    153                 return 
    154  
    155             if self.method in (supportedMethods): 
    156                 # We MUST include an Allow header 
    157                 # (RFC 2616, 10.4.6 and 14.7) 
    158                 self.setHeader('Allow', allowedMethods) 
    159                 s = ('''Your browser approached me (at %(URI)s) with''' 
    160                      ''' the method "%(method)s".  I only allow''' 
    161                      ''' the method%(plural)s %(allowed)s here.''' % { 
    162                     'URI': self.uri, 
    163                     'method': self.method, 
    164                     'plural': ((len(allowedMethods) > 1) and 's') or '', 
    165                     'allowed': string.join(allowedMethods, ', ') 
    166                     }) 
    167                 epage = resource.ErrorPage(http.NOT_ALLOWED, 
    168                                            "Method Not Allowed", s) 
    169                 body = epage.render(self) 
    170153            else: 
    171                 epage = resource.ErrorPage(http.NOT_IMPLEMENTED, "Huh?", 
    172                                            "I don't know how to treat a" 
    173                                            " %s request." % (self.method,)) 
    174                 body = epage.render(self) 
    175         # end except UnsupportedMethod 
    176  
    177         if body == NOT_DONE_YET: 
    178             return 
    179         if type(body) is not types.StringType: 
    180             body = resource.ErrorPage( 
    181                 http.INTERNAL_SERVER_ERROR, 
    182                 "Request did not return a string", 
    183                 "Request: " + html.PRE(reflect.safe_repr(self)) + "<br />" + 
    184                 "Resource: " + html.PRE(reflect.safe_repr(resrc)) + "<br />" + 
    185                 "Value: " + html.PRE(reflect.safe_repr(body))).render(self) 
    186  
    187         if self.method == "HEAD": 
    188             if len(body) > 0: 
    189                 # This is a Bad Thing (RFC 2616, 9.4) 
    190                 log.msg("Warning: HEAD request %s for resource %s is" 
    191                         " returning a message body." 
    192                         "  I think I'll eat it." 
    193                         % (self, resrc)) 
    194154                self.setHeader('content-length', str(len(body))) 
    195             self.write('') 
    196         else: 
    197             self.setHeader('content-length', str(len(body))) 
    198             self.write(body) 
    199         self.finish() 
     155                self.write(body) 
     156            self.finish() 
     157 
     158        def _ebRender(fail): 
     159            r = fail.trap(UnsupportedMethod) 
     160            if r == UnsupportedMethod: 
     161                allowedMethods = fail.value.allowedMethods 
     162 
     163                if (self.method == "HEAD") and ("GET" in allowedMethods): 
     164                    # We must support HEAD (RFC 2616, 5.1.1).  If the 
     165                    # resource doesn't, fake it by giving the resource 
     166                    # a 'GET' request and then return only the headers, 
     167                    # not the body. 
     168                    log.msg("Using GET to fake a HEAD request for %s" % 
     169                            (resrc,)) 
     170                    self.method = "GET" 
     171                    body = resrc.render(self) 
     172 
     173                    if body is NOT_DONE_YET: 
     174                        log.msg("Tried to fake a HEAD request for %s, but " 
     175                                "it got away from me." % resrc) 
     176                        # Oh well, I guess we won't include the content length. 
     177                    else: 
     178                        self.setHeader('content-length', str(len(body))) 
     179 
     180                    self.write('') 
     181                    self.finish() 
     182                    return 
     183 
     184                if self.method in (supportedMethods): 
     185                    # We MUST include an Allow header 
     186                    # (RFC 2616, 10.4.6 and 14.7) 
     187                    self.setHeader('Allow', allowedMethods) 
     188                    s = ('''Your browser approached me (at %(URI)s) with''' 
     189                         ''' the method "%(method)s".  I only allow''' 
     190                         ''' the method%(plural)s %(allowed)s here.''' % { 
     191                        'URI': self.uri, 
     192                        'method': self.method, 
     193                        'plural': ((len(allowedMethods) > 1) and 's') or '', 
     194                        'allowed': string.join(allowedMethods, ', ') 
     195                        }) 
     196                    epage = resource.ErrorPage(http.NOT_ALLOWED, 
     197                                               "Method Not Allowed", s) 
     198                    body = epage.render(self) 
     199                else: 
     200                    epage = resource.ErrorPage(http.NOT_IMPLEMENTED, "Huh?", 
     201                                               "I don't know how to treat a" 
     202                                               " %s request." % (self.method,)) 
     203                    body = epage.render(self) 
     204                # end except UnsupportedMethod 
     205                return _cbRender(body) 
     206 
     207        d = defer.maybeDeferred(resrc.render, self) 
     208        d.addCallbacks(_cbRender, _ebRender) 
     209        return d 
    200210 
    201211    def processingFailed(self, reason): 
    202212        log.err(reason) 
  • twisted/web/test/test_web.py

    === modified file 'twisted/web/test/test_web.py'
     
    181181        else: 
    182182            return "correct" 
    183183 
     184class SimpleDeferredResource(resource.Resource): 
     185    def render(self, request): 
     186        d = defer.Deferred() 
     187        if http.CACHED in (request.setLastModified(10), 
     188                           request.setETag('MatchingTag')): 
     189            reactor.callLater(0, d.callback, '') 
     190        else: 
     191            reactor.callLater(0, d.callback, "correct") 
     192        return d 
    184193 
    185194class DummyChannel: 
    186195    class TCP: 
     
    231240            site.getResourceFor(DummyRequest([''])), 
    232241            sres2, "Got the wrong resource.") 
    233242 
     243    def test_simplestSiteDeferred(self): 
     244        """ 
     245        L{Site.getResourceFor} returns the C{""} child of the root resource it 
     246        is constructed with when processing a request for I{/}. 
     247        """ 
     248        sres1 = SimpleDeferredResource() 
     249        sres2 = SimpleDeferredResource() 
     250        sres1.putChild("",sres2) 
     251        site = server.Site(sres1) 
     252        self.assertIdentical( 
     253            site.getResourceFor(DummyRequest([''])), 
     254            sres2, "Got the wrong resource.") 
    234255 
    235256 
    236257class SessionTest(unittest.TestCase):