diff --git a/README.rst b/README.rst index 5b0e2a3..4cc7fea 100644 --- a/README.rst +++ b/README.rst @@ -62,14 +62,13 @@ Main features - Extensible : easy to add more protocols or more base types. - Framework independence : adapters are provided to easily integrate your API in any web framework, for example a wsgi container, - Pecan_, TurboGears_, Flask_, cornice_... + Pecan_, Flask_, cornice_... - Very few runtime dependencies: webob, simplegeneric. Optionnaly lxml and simplejson if you need better performances. - Integration in `Sphinx`_ for making clean documentation with ``wsmeext.sphinxext``. .. _Pecan: http://pecanpy.org/ -.. _TurboGears: http://www.turbogears.org/ .. _Flask: http://flask.pocoo.org/ .. _cornice: http://pypi.python.org/pypi/cornice diff --git a/doc/changes.rst b/doc/changes.rst index 4ee6432..bccc5dd 100644 --- a/doc/changes.rst +++ b/doc/changes.rst @@ -1,11 +1,26 @@ Changes ======= -0.9.4 (future) +1.0.0 (future) -------------- -* SQLAlchemy support is deprecated and will be removed in one of the next - releases. It has never actually worked to begin with. +* Remove support for turbogears +* Remove SQLAlchemy support. It has never actually worked to begin with. + +0.9.2 (2017-02-14) +------------------ + +TODO. + +0.9.1 (2017-01-04) +------------------ + +Fix packaging issues. + +0.9.0 (2017-01-04) +------------------ + +TODO. 0.8.0 (2015-08-25) ------------------ diff --git a/doc/integrate.rst b/doc/integrate.rst index 5adaaca..3857f05 100644 --- a/doc/integrate.rst +++ b/doc/integrate.rst @@ -241,74 +241,6 @@ The `example 50" - - def test_custom_clientside_error(self): - response = self.app.post( - "/divide", - simplejson.dumps({'a': 5, 'b': 0}), - {'Content-Type': 'application/json', 'Accept': 'application/json'}, - expect_errors=True - ) - assert response.status_int == 400 - assert simplejson.loads(response.body) == { - "debuginfo": None, - "faultcode": "Server", - "faultstring": "(400, 'Cannot divide by zero!')" - } - - response = self.app.post( - "/divide", - simplejson.dumps({'a': 5, 'b': 0}), - {'Content-Type': 'application/json', 'Accept': 'text/xml'}, - expect_errors=True - ) - assert response.status_int == 400 - assert response.body == ("Server" - "(400, 'Cannot divide by zero!')" - "") - - def test_soap_wsdl(self): - ts = test_soap.TestSOAP('test_wsdl') - ts.app = self.app - ts.ws_path = '/ws/' - ts.run() - #wsdl = self.app.get('/ws/api.wsdl').body - #print wsdl - #assert 'multiply' in wsdl - - def test_soap_call(self): - ts = test_soap.TestSOAP('test_wsdl') - ts.app = self.app - ts.ws_path = '/ws/' - - print ts.ws_path - assert ts.call('multiply', a=5, b=10, _rt=int) == 50 - - def test_scan_api_loops(self): - class MyRoot(object): - pass - - MyRoot.loop = MyRoot() - - root = MyRoot() - - api = list(wsmeext.tg1._scan_api(root)) - print(api) - - self.assertEqual(len(api), 0) - - def test_scan_api_maxlen(self): - class ARoot(object): - pass - - def make_subcontrollers(n): - c = type('Controller%s' % n, (object,), {}) - return c - - c = ARoot - for n in range(55): - subc = make_subcontrollers(n) - c.sub = subc() - c = subc - root = ARoot() - self.assertRaises(ValueError, list, wsmeext.tg1._scan_api(root)) - - def test_templates_content_type(self): - self.assertEqual( - "application/json", - wsmeext.tg1.AutoJSONTemplate().get_content_type('dummy') - ) - self.assertEqual( - "text/xml", - wsmeext.tg1.AutoXMLTemplate().get_content_type('dummy') - ) diff --git a/tests/test_tg15.py b/tests/test_tg15.py deleted file mode 100644 index 91609d2..0000000 --- a/tests/test_tg15.py +++ /dev/null @@ -1,177 +0,0 @@ -import wsmeext.tg15 -from wsme import WSRoot - -from turbogears.controllers import RootController -import cherrypy - -from wsmeext.tests import test_soap - -import simplejson - - -class Subcontroller(object): - @wsmeext.tg15.wsexpose(int, int, int) - def add(self, a, b): - return a + b - - -class Root(RootController): - class UselessSubClass: - # This class is here only to make sure wsmeext.tg1.scan_api - # does its job properly - pass - - sub = Subcontroller() - - ws = WSRoot(webpath='/ws') - ws.addprotocol('soap', - tns=test_soap.tns, - typenamespace=test_soap.typenamespace, - baseURL='/ws/' - ) - ws = wsmeext.tg15.adapt(ws) - - @wsmeext.tg15.wsexpose(int) - @wsmeext.tg15.wsvalidate(int, int) - def multiply(self, a, b): - return a * b - - @wsmeext.tg15.wsexpose(int) - @wsmeext.tg15.wsvalidate(int, int) - def divide(self, a, b): - if b == 0: - raise cherrypy.HTTPError(400, 'Cannot divide by zero!') - return a / b - - -from turbogears import testutil - - -class TestController(testutil.TGTest): - root = Root - -# def setUp(self): -# "Tests the output of the index method" -# self.app = testutil.make_app(self.root) -# #print cherrypy.root -# testutil.start_server() - -# def tearDown(self): -# # implementation copied from turbogears.testutil.stop_server. -# # The only change is that cherrypy.root is set to None -# # AFTER stopTurbogears has been called so that wsmeext.tg15 -# # can correctly uninstall its filter. -# if config.get("cp_started"): -# cherrypy.server.stop() -# config.update({"cp_started": False}) -# -# if config.get("server_started"): -# startup.stopTurboGears() -# config.update({"server_started": False}) - - def test_restcall(self): - response = self.app.post("/multiply", - simplejson.dumps({'a': 5, 'b': 10}), - {'Content-Type': 'application/json'} - ) - print response - assert simplejson.loads(response.body) == 50 - - response = self.app.post("/multiply", - simplejson.dumps({'a': 5, 'b': 10}), - {'Content-Type': 'application/json', 'Accept': 'application/json'} - ) - print response - assert simplejson.loads(response.body) == 50 - - response = self.app.post("/multiply", - simplejson.dumps({'a': 5, 'b': 10}), - {'Content-Type': 'application/json', 'Accept': 'text/javascript'} - ) - print response - assert simplejson.loads(response.body) == 50 - - response = self.app.post("/multiply", - simplejson.dumps({'a': 5, 'b': 10}), - {'Content-Type': 'application/json', - 'Accept': 'text/xml'} - ) - print response - assert response.body == "50" - - def test_custom_clientside_error(self): - response = self.app.post( - "/divide", - simplejson.dumps({'a': 5, 'b': 0}), - {'Content-Type': 'application/json', 'Accept': 'application/json'}, - expect_errors=True - ) - assert response.status_int == 400 - assert simplejson.loads(response.body) == { - "debuginfo": None, - "faultcode": "Client", - "faultstring": "(400, 'Cannot divide by zero!')" - } - - response = self.app.post( - "/divide", - simplejson.dumps({'a': 5, 'b': 0}), - {'Content-Type': 'application/json', 'Accept': 'text/xml'}, - expect_errors=True - ) - assert response.status_int == 400 - assert response.body == ("Client" - "(400, 'Cannot divide by zero!')" - "") - - def test_soap_wsdl(self): - wsdl = self.app.get('/ws/api.wsdl').body - print wsdl - assert 'multiply' in wsdl - - def test_soap_call(self): - ts = test_soap.TestSOAP('test_wsdl') - ts.app = self.app - ts.ws_path = '/ws/' - - print ts.ws_path - assert ts.call('multiply', a=5, b=10, _rt=int) == 50 - - def test_scan_api_loops(self): - class MyRoot(object): - pass - - MyRoot.loop = MyRoot() - - root = MyRoot() - - api = list(wsmeext.tg1._scan_api(root)) - print(api) - - self.assertEqual(len(api), 0) - - def test_scan_api_maxlen(self): - class ARoot(object): - pass - - def make_subcontrollers(n): - c = type('Controller%s' % n, (object,), {}) - return c - - c = ARoot - for n in range(55): - subc = make_subcontrollers(n) - c.sub = subc() - c = subc - root = ARoot() - self.assertRaises(ValueError, list, wsmeext.tg1._scan_api(root)) - - def test_templates_content_type(self): - self.assertEqual( - "application/json", - wsmeext.tg1.AutoJSONTemplate().get_content_type('dummy') - ) - self.assertEqual( - "text/xml", - wsmeext.tg1.AutoXMLTemplate().get_content_type('dummy') - ) diff --git a/wsmeext/tg1.py b/wsmeext/tg1.py deleted file mode 100644 index b978166..0000000 --- a/wsmeext/tg1.py +++ /dev/null @@ -1,173 +0,0 @@ -try: - import json -except ImportError: - import simplejson as json # noqa - -import functools -import sys - -import cherrypy -import webob -from turbogears import expose, util -import turbogears.view - -from wsme.rest import validate as wsvalidate -import wsme.api -import wsme.rest -import wsme.rest.args -import wsme.rest.json -from wsme.utils import is_valid_code - -import inspect - -APIPATH_MAXLEN = 50 - -__all__ = ['wsexpose', 'wsvalidate'] - - -def wsexpose(*args, **kwargs): - tg_json_expose = expose( - 'wsmejson:', - accept_format='application/json', - content_type='application/json', - tg_format='json' - ) - tg_altjson_expose = expose( - 'wsmejson:', - accept_format='text/javascript', - content_type='application/json' - ) - tg_xml_expose = expose( - 'wsmexml:', - accept_format='text/xml', - content_type='text/xml', - tg_format='xml' - ) - sig = wsme.signature(*args, **kwargs) - - def decorate(f): - sig(f) - funcdef = wsme.api.FunctionDefinition.get(f) - - @functools.wraps(f) - def callfunction(self, *args, **kwargs): - args, kwargs = wsme.rest.args.get_args( - funcdef, args, kwargs, - cherrypy.request.params, None, - cherrypy.request.body, - cherrypy.request.headers['Content-Type'] - ) - if funcdef.pass_request: - kwargs[funcdef.pass_request] = cherrypy.request - try: - result = f(self, *args, **kwargs) - except Exception: - try: - exception_info = sys.exc_info() - orig_exception = exception_info[1] - if isinstance(orig_exception, cherrypy.HTTPError): - orig_code = getattr(orig_exception, 'status', None) - else: - orig_code = getattr(orig_exception, 'code', None) - data = wsme.api.format_exception(exception_info) - finally: - del exception_info - - cherrypy.response.status = 500 - if data['faultcode'] == 'client': - cherrypy.response.status = 400 - elif orig_code and is_valid_code(orig_code): - cherrypy.response.status = orig_code - - accept = cherrypy.request.headers.get('Accept', "").lower() - accept = util.simplify_http_accept_header(accept) - - decorators = {'text/xml': wsme.rest.xml.encode_error} - return decorators.get( - accept, - wsme.rest.json.encode_error - )(None, data) - - return dict( - datatype=funcdef.return_type, - result=result - ) - - callfunction = tg_xml_expose(callfunction) - callfunction = tg_altjson_expose(callfunction) - callfunction = tg_json_expose(callfunction) - callfunction._wsme_original_function = f - return callfunction - - return decorate - - -class AutoJSONTemplate(object): - def __init__(self, extra_vars_func=None, options=None): - pass - - def render(self, info, format="json", fragment=False, template=None): - "Renders the template to a string using the provided info." - return wsme.rest.json.encode_result( - info['result'], info['datatype'] - ) - - def get_content_type(self, user_agent): - return "application/json" - - -class AutoXMLTemplate(object): - def __init__(self, extra_vars_func=None, options=None): - pass - - def render(self, info, format="json", fragment=False, template=None): - "Renders the template to a string using the provided info." - return wsme.rest.xml.encode_result( - info['result'], info['datatype'] - ) - - def get_content_type(self, user_agent): - return "text/xml" - - -turbogears.view.engines['wsmejson'] = AutoJSONTemplate(turbogears.view.stdvars) -turbogears.view.engines['wsmexml'] = AutoXMLTemplate(turbogears.view.stdvars) - - -class Controller(object): - def __init__(self, wsroot): - self._wsroot = wsroot - - @expose() - def default(self, *args, **kw): - req = webob.Request(cherrypy.request.wsgi_environ) - res = self._wsroot._handle_request(req) - cherrypy.response.header_list = res.headerlist - cherrypy.response.status = res.status - return res.body - - -def _scan_api(controller, path=[], objects=[]): - """ - Recursively iterate a controller api entries. - """ - for name in dir(controller): - if name.startswith('_'): - continue - a = getattr(controller, name) - if a in objects: - continue - if inspect.ismethod(a): - if wsme.api.iswsmefunction(a): - yield path + [name], a._wsme_original_function, [controller] - elif inspect.isclass(a): - continue - else: - if len(path) > APIPATH_MAXLEN: - raise ValueError("Path is too long: " + str(path)) - for i in _scan_api(a, path + [name], objects + [a]): - yield i - - -def scan_api(root=None): - return _scan_api(cherrypy.root) diff --git a/wsmeext/tg11.py b/wsmeext/tg11.py deleted file mode 100644 index 80ec50c..0000000 --- a/wsmeext/tg11.py +++ /dev/null @@ -1,40 +0,0 @@ -from turbogears import config -import cherrypy -from cherrypy.filters.basefilter import BaseFilter -from turbogears.startup import call_on_startup, call_on_shutdown -from wsmeext.tg1 import wsexpose, wsvalidate -import wsmeext.tg1 - -__all__ = ['adapt', 'wsexpose', 'wsvalidate'] - - -class WSMECherrypyFilter(BaseFilter): - def __init__(self, controller): - self.controller = controller - self.webpath = None - - def on_start_resource(self): - path = cherrypy.request.path - if path.startswith(self.controller._wsroot._webpath): - cherrypy.request.processRequestBody = False - - -def adapt(wsroot): - wsroot._scan_api = wsmeext.tg1.scan_api - controller = wsmeext.tg1.Controller(wsroot) - filter_ = WSMECherrypyFilter(controller) - - def install_filter(): - filter_.webpath = config.get('server.webpath') or '' - controller._wsroot._webpath = \ - filter_.webpath + controller._wsroot._webpath - cherrypy.root._cp_filters.append(filter_) - - def uninstall_filter(): - cherrypy.root._cp_filters.remove(filter_) - controller._wsroot._webpath = \ - controller._wsroot._webpath[len(filter_.webpath):] - - call_on_startup.append(install_filter) - call_on_shutdown.insert(0, uninstall_filter) - return controller diff --git a/wsmeext/tg15.py b/wsmeext/tg15.py deleted file mode 100644 index 4b8a9b1..0000000 --- a/wsmeext/tg15.py +++ /dev/null @@ -1,20 +0,0 @@ -import cherrypy - -from wsmeext.tg1 import wsexpose, wsvalidate -import wsmeext.tg1 - - -__all__ = ['adapt', 'wsexpose', 'wsvalidate'] - - -def scan_api(root=None): - for baseurl, instance in cherrypy.tree.apps.items(): - path = [token for token in baseurl.split('/') if token] - for i in wsmeext.tg1._scan_api(instance.root, path): - yield i - - -def adapt(wsroot): - wsroot._scan_api = scan_api - controller = wsmeext.tg1.Controller(wsroot) - return controller