A first working implemetation for the core controller code
This commit is contained in:
commit
127a245d7e
14
.hgignore
Normal file
14
.hgignore
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
syntax: glob
|
||||||
|
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.egg-info
|
||||||
|
.coverage
|
||||||
|
|
||||||
|
syntax: regexp
|
||||||
|
|
||||||
|
^lib
|
||||||
|
^bin
|
||||||
|
^include
|
||||||
|
^dist
|
||||||
|
|
2
ews/__init__.py
Normal file
2
ews/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from controller import *
|
||||||
|
from wsgiapp import *
|
99
ews/controller.py
Normal file
99
ews/controller.py
Normal 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
13
ews/rest.py
Normal 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
7
ews/restjson.py
Normal 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
7
ews/restxml.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
class RestXmlProtocol(RestProtocol):
|
||||||
|
name = 'REST+XML'
|
||||||
|
|
||||||
|
import controller
|
||||||
|
controller.register_protocol(RestXmlProtocol)
|
||||||
|
|
||||||
|
|
9
ews/soap.py
Normal file
9
ews/soap.py
Normal 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
0
ews/tests/__init__.py
Normal file
110
ews/tests/test_controller.py
Normal file
110
ews/tests/test_controller.py
Normal 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
|
Loading…
x
Reference in New Issue
Block a user