mbligh | e8819cd | 2008-02-15 16:48:40 +0000 | [diff] [blame] | 1 | """\ |
| 2 | RPC request handler Django. Exposed RPC interface functions should be |
| 3 | defined in rpc_interface.py. |
| 4 | """ |
| 5 | |
| 6 | __author__ = 'showard@google.com (Steve Howard)' |
| 7 | |
| 8 | import django.http |
| 9 | import traceback, pydoc |
| 10 | |
| 11 | from frontend.afe.json_rpc import serviceHandler |
showard | 7c78528 | 2008-05-29 19:45:12 +0000 | [diff] [blame] | 12 | from frontend.afe import rpc_utils |
mbligh | e8819cd | 2008-02-15 16:48:40 +0000 | [diff] [blame] | 13 | |
mbligh | e8819cd | 2008-02-15 16:48:40 +0000 | [diff] [blame] | 14 | |
| 15 | class RpcMethodHolder(object): |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame^] | 16 | 'Dummy class to hold RPC interface methods as attributes.' |
mbligh | e8819cd | 2008-02-15 16:48:40 +0000 | [diff] [blame] | 17 | |
| 18 | |
showard | 7c78528 | 2008-05-29 19:45:12 +0000 | [diff] [blame] | 19 | class RpcHandler(object): |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame^] | 20 | def __init__(self, rpc_interface_modules, document_module=None): |
| 21 | self._rpc_methods = RpcMethodHolder() |
| 22 | self._dispatcher = serviceHandler.ServiceHandler( |
| 23 | self._rpc_methods) |
mbligh | e8819cd | 2008-02-15 16:48:40 +0000 | [diff] [blame] | 24 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame^] | 25 | # store all methods from interface modules |
| 26 | for module in rpc_interface_modules: |
| 27 | self._grab_methods_from(module) |
showard | 7c78528 | 2008-05-29 19:45:12 +0000 | [diff] [blame] | 28 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame^] | 29 | # get documentation for rpc_interface we can send back to the |
| 30 | # user |
| 31 | if document_module is None: |
| 32 | document_module = rpc_interface_modules[0] |
| 33 | self.html_doc = pydoc.html.document(document_module) |
showard | 7c78528 | 2008-05-29 19:45:12 +0000 | [diff] [blame] | 34 | |
| 35 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame^] | 36 | def handle_rpc_request(self, request): |
| 37 | response = django.http.HttpResponse() |
| 38 | if len(request.POST): |
| 39 | response.write(self._dispatcher.handleRequest( |
| 40 | request.raw_post_data)) |
| 41 | else: |
| 42 | response.write(self.html_doc) |
showard | 7c78528 | 2008-05-29 19:45:12 +0000 | [diff] [blame] | 43 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame^] | 44 | response['Content-length'] = str(len(response.content)) |
| 45 | return response |
showard | 7c78528 | 2008-05-29 19:45:12 +0000 | [diff] [blame] | 46 | |
| 47 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame^] | 48 | @staticmethod |
| 49 | def _allow_keyword_args(f): |
| 50 | """\ |
| 51 | Decorator to allow a function to take keyword args even though |
| 52 | the RPC layer doesn't support that. The decorated function |
| 53 | assumes its last argument is a dictionary of keyword args and |
| 54 | passes them to the original function as keyword args. |
| 55 | """ |
| 56 | def new_fn(*args): |
| 57 | assert args |
| 58 | keyword_args = args[-1] |
| 59 | args = args[:-1] |
| 60 | return f(*args, **keyword_args) |
| 61 | new_fn.func_name = f.func_name |
| 62 | return new_fn |
showard | 7c78528 | 2008-05-29 19:45:12 +0000 | [diff] [blame] | 63 | |
| 64 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame^] | 65 | def _grab_methods_from(self, module): |
| 66 | for name in dir(module): |
| 67 | attribute = getattr(module, name) |
| 68 | if not callable(attribute): |
| 69 | continue |
| 70 | decorated_function = ( |
| 71 | RpcHandler._allow_keyword_args(attribute)) |
| 72 | setattr(self._rpc_methods, name, decorated_function) |