A first working implemetation for the core controller code

This commit is contained in:
Christophe de Vienne 2011-09-18 21:37:40 +02:00
commit 127a245d7e
10 changed files with 268 additions and 0 deletions

14
.hgignore Normal file
View File

@ -0,0 +1,14 @@
syntax: glob
*.pyc
*.pyo
*.egg-info
.coverage
syntax: regexp
^lib
^bin
^include
^dist

2
ews/__init__.py Normal file
View File

@ -0,0 +1,2 @@
from controller import *
from wsgiapp import *

99
ews/controller.py Normal file
View File

@ -0,0 +1,99 @@
import inspect
__all__ = ['expose', 'validate', 'WSRoot']
registered_protocols = {}
def scan_api(controller, path=[]):
for name in dir(controller):
if name.startswith('_'):
continue
a = getattr(controller, name)
if hasattr(a, '_ews_definition'):
yield path, a._ews_definition
else:
for i in scan_api(a, path + [name]):
yield i
class FunctionArgument(object):
def __init__(self, name, datatype, mandatory, default):
self.name = name
self.datatype = datatype
self.mandatory = mandatory
self.default = default
class FunctionDefinition(object):
def __init__(self, name):
self.name = name
self.return_type = None
self.arguments = []
@classmethod
def get(cls, func):
fd = getattr(func, '_ews_definition', None)
if fd is None:
fd = FunctionDefinition(func.__name__)
func._ews_definition = fd
return fd
def register_protocol(protocol):
global registered_protocols
registered_protocols[protocol.name] = protocol
class expose(object):
def __init__(self, return_type=None):
self.return_type = return_type
def __call__(self, func):
fd = FunctionDefinition.get(func)
fd.return_type = self.return_type
return func
class validate(object):
def __init__(self, *args, **kw):
self.param_types = args
def __call__(self, func):
fd = FunctionDefinition.get(func)
args, varargs, keywords, defaults = inspect.getargspec(func)
print args, defaults
if args[0] == 'self':
args = args[1:]
for i, argname in enumerate(args):
datatype = self.param_types[i]
mandatory = defaults is None or i <= len(defaults)
default = None
if not mandatory:
default = defaults[i-(len(args)-len(defaults))]
print argname, datatype, mandatory, default
fd.arguments.append(FunctionArgument(argname, datatype,
mandatory, default))
return func
class WSRoot(object):
def __init__(self, protocols=None):
if protocols is None:
protocols = registered_protocols.values()
self.protocols = {}
for protocol in protocols:
if isinstance(protocol, str):
protocol = registered_protocols[protocol]
self.protocols[protocol.name] = protocol
def _handle_request(self, request):
protocol = None
if 'ewsproto' in request.params:
protocol = self.protocols[request.params['ewsproto']]
else:
for p in self.protocols.values():
if p.accept(self, request):
protocol = p
break
return protocol.handle(self, request)

13
ews/rest.py Normal file
View File

@ -0,0 +1,13 @@
class RestProtocol(object):
name = None
dataformat = None
content_types = []
def accept(self, root, request):
if request.path.endswith('.' + self.dataformat):
return True
return request.headers.get('Content-Type') in self.content_types
def handle(self, root, request):
path = request.path.split('/')

7
ews/restjson.py Normal file
View File

@ -0,0 +1,7 @@
class RestJsonProtocol(RestProtocol):
name = 'REST+Json'
dataformat = 'json'
content_types = [None, 'application/json', 'text/json']
controller.register_protocol(RestJsonProtocol)

7
ews/restxml.py Normal file
View File

@ -0,0 +1,7 @@
class RestXmlProtocol(RestProtocol):
name = 'REST+XML'
import controller
controller.register_protocol(RestXmlProtocol)

9
ews/soap.py Normal file
View File

@ -0,0 +1,9 @@
class SoapProtocol(object):
name = 'soap'
accept = ''
def __init__(self):
pass
controller.register_protocol(SoapProtocol)

0
ews/tests/__init__.py Normal file
View File

View File

@ -0,0 +1,110 @@
import unittest
import webob
from webob.dec import wsgify
import webtest
from ews import *
from ews.controller import scan_api
class DummyProtocol(object):
name = 'dummy'
def __init__(self):
self.hits = 0
def accept(self, root, req):
return True
def handle(self, root, req):
self.lastreq = req
self.lastroot = root
res = webob.Response()
self.hits += 1
return res
def serve_ws(req, root):
return root._handle_request(req)
class TestController(unittest.TestCase):
def test_expose(self):
class MyWS(WSRoot):
@expose(int)
def getint(self):
return 1
assert MyWS.getint._ews_definition.return_type == int
def test_validate(self):
class MyWS(object):
@expose(int)
@validate(int, int, int)
def add(self, a, b, c=0):
return a + b + c
args = MyWS.add._ews_definition.arguments
assert args[0].name == 'a'
assert args[0].datatype == int
assert args[0].mandatory
assert args[0].default is None
assert args[1].name == 'b'
assert args[1].datatype == int
assert args[1].mandatory
assert args[1].default is None
assert args[2].name == 'c'
assert args[2].datatype == int
assert not args[2].mandatory
assert args[2].default == 0
def test_register_protocol(self):
p = DummyProtocol()
import ews.controller
ews.controller.register_protocol(p)
assert ews.controller.registered_protocols['dummy'] == p
r = WSRoot()
assert r.protocols['dummy']
def test_scan_api(self):
class NS(object):
@expose(int)
@validate(int, int)
def multiply(self, a, b):
return a * b
class MyRoot(WSRoot):
ns = NS()
r = MyRoot()
api = [i for i in scan_api(r)]
assert len(api) == 1
assert api[0][0] == ['ns']
fd = api[0][1]
assert fd.name == 'multiply'
def test_handle_request(self):
class MyRoot(WSRoot):
pass
p = DummyProtocol()
r = MyRoot(protocols=[p])
app = webtest.TestApp(
wsgify(r._handle_request))
res = app.get('/')
assert p.lastreq.path == '/'
assert p.lastroot == r
assert p.hits == 1
res = app.get('/?ewsproto=dummy')
assert p.lastreq.path == '/'
assert p.lastroot == r
assert p.hits == 2

7
setup.py Normal file
View File

@ -0,0 +1,7 @@
from setuptools import setup
setup(
name='EWS',
packages=['ews'],
install_requires=['webob'],
)