The PEAK Developers' Center   AsyncWSGISketch UserPreferences
 
HelpContents Search Diffs Info Edit Subscribe XML Print View
Version as of 2011-01-08 16:38:56

Clear message


    1 """Some sketches of implementing a WSGI 2 async API, in both sync and async servers"""
    2 
    3 def WSGI2(app):
    4     """Decorator that synchronously emulates WSGI2 futures under WSGI 1"""
    5     def wsgi1_app(environ, start_response):
    6         def process_response(shb):
    7             s, h, body = shb
    8             write = start_response(s, h)
    9 
   10             def body_trampoline(routine, yielded):
   11                 if type(yielded) is bytes:
   12                      # only accept from outermost middleware
   13                      if len(routine)==1:
   14                          return routine.synchronously(write, yielded)
   15                      else:
   16                          return routine.RETURN(yielded)
   17                 else:
   18                     return base_trampoline(routine, yielded)
   19             if not [body has send/throw methods]:
   20                 body = (item for item in body)
   21             Coroutine(body, body_trampoline)()
   22 
   23         def app_trampoline(routine, yielded):
   24             if type(yielded) is tuple:
   25                 return routine.RETURN(yielded)
   26             else:
   27                 return base_trampoline(routine, yielded)
   28 
   29         def base_trampoline(routine, yielded):
   30             if [yielded is a future of some sort]:
   31                 return routine.synchronously(future.result)
   32             elif [yielded has send/throw methods]:
   33                 return routine.CALL(yielded)
   34             else:
   35                 raise TypeError("Not a future, result, or generator:", yielded)
   36 
   37         # [add an executor to environ here]
   38         Coroutine(app(environ), app_trampoline, process_response)()
   39         return []
   40 
   41     return wsgi1_app
   42 
   43 
   44 def start_request(app, environ):
   45     """Template for asynchronous request handler"""
   46     def process_response(shb):
   47         s, h, body = shb
   48         [do an asynchronous start_response analog here]
   49         def body_trampoline(routine, yielded):
   50             if type(yielded) is bytes:
   51                 # only accept from outermost middleware
   52                 if len(routine)==1:
   53                     [arrange for the bytes to be sent out]
   54                     [arrange to invoke routine() when send is completed]
   55                     return routine.PAUSE
   56                 else:
   57                      return routine.RETURN(yielded)
   58             else:
   59                 return base_trampoline(routine, yielded)
   60 
   61         if not [body has send/throw methods]:
   62             body = (item for item in body)
   63         Coroutine(body, body_trampoline, [optional termination callback])()
   64 
   65     def app_trampoline(routine, yielded):
   66         if type(yielded) is tuple:
   67             return routine.RETURN(yielded)
   68         else:
   69             return base_trampoline(routine, yielded)
   70 
   71     def base_trampoline(routine, yielded):
   72         if [yielded is a future of some sort]:
   73             def done(f):
   74                 routine(*routine.synchronously(f.result))
   75             future.add_done_callback(done)
   76             return routine.PAUSE
   77         elif [yielded has send/throw methods]:
   78             return routine.CALL(yielded)
   79         else:
   80             raise TypeError("Not a future, result, or generator:", yielded)
   81 
   82     # [add an executor to environ here]
   83     Coroutine(app(environ), app_trampoline, process_response)()
   84 
   85 
   86 class Coroutine:
   87     """A thread-like stack of collaborating generators"""
   88 
   89     def __init__(iterator, trampoline=lambda r,v:"return", callback=lambda v:v):
   90         """Create and launch a general-purpose pseudo-thread"""
   91         self.stack = [iterator]
   92         self.trampoline = trampoline
   93         self.callback = callback
   94 
   95     PAUSE = ()
   96 
   97     def CALL(self, geniter):
   98         self.stack.append(geniter)
   99         return None, ()
  100 
  101     def RETURN(self, value=None):
  102         self.stack.pop()
  103         return value, ()
  104 
  105     def RESUME(self, value=None):
  106         return value, ()
  107 
  108     def RAISE(self, exc_info):
  109         return None, exc_info
  110 
  111     def __len__(self):
  112         return len(self.stack)
  113 
  114     def synchronously(self, func, *args, **kw):
  115         try:
  116             return self.RESUME(func(*args, **kw))
  117         except BaseException:
  118             return self.RAISE(sys.exc_info())
  119 
  120     def close():
  121         while self.stack:
  122             self.stack.pop().close()
  123 
  124     def __call__(self, value=None, exc_info=()):
  125         stack = self.stack
  126         while stack:
  127             try:
  128                 it = stack[-1]
  129                 if exc_info:
  130                     try:
  131                         rv = it.throw(*exc_info)
  132                     finally:
  133                         exc_info = ()
  134                 else:
  135                     rv = it.send(value)
  136             except BaseException:
  137                 value = None
  138                 exc_info = sys.exc_info()
  139                 if exc_info[0] is StopIteration:
  140                     # pass return value up the stack
  141                     value, = exc_info[1].args or (None,)
  142                     exc_info = ()   # but not the error
  143                 stack.pop()
  144             else:
  145                 switch = self.trampoline(self, rv)
  146                 if switch:
  147                     value, exc_info = switch
  148                 else:
  149                     return rv
  150 
  151         # Coroutine is entirely finished when the stack is empty
  152         return self.callback(value)

PythonPowered
EditText of this page (last modified 2011-01-08 16:38:56)
FindPage by browsing, title search , text search or an index
Or try one of these actions: AttachFile, DeletePage, LikePages, LocalSiteMap, SpellCheck