[REVERT] revert commit fme@openerp.com-20130418171750-7oldgiewo1eewxk7: do not break stable API !!!

bzr revid: chs@openerp.com-20130423124151-h025b891xp77flg3
This commit is contained in:
Christophe Simonis 2013-04-23 14:41:51 +02:00
parent d50b081191
commit e5736828f9
5 changed files with 22 additions and 445 deletions

View File

@ -1,9 +1,11 @@
# -*- coding: utf-8 -*-
import ast
import base64
import csv
import glob
import itertools
import logging
import operator
import datetime
import hashlib
@ -28,6 +30,7 @@ except ImportError:
xlwt = None
import openerp
import openerp.modules.registry
from openerp.tools.translate import _
from .. import http
@ -1436,8 +1439,7 @@ class Action(openerpweb.Controller):
else:
return False
class Export(openerpweb.Controller):
class Export(View):
_cp_path = "/web/export"
@openerpweb.jsonrequest
@ -1578,13 +1580,7 @@ class Export(openerpweb.Controller):
(prefix + '/' + k, prefix_string + '/' + v)
for k, v in self.fields_info(req, model, export_fields).iteritems())
class ExportFormat(object):
"""
Superclass for export formats, should probably be an abc and have a way to
generate _cp_path from fmt but it's a pain to deal with conflicts with
ControllerType
"""
#noinspection PyPropertyDefinition
@property
def content_type(self):
""" Provides the format's content type """
@ -1625,14 +1621,14 @@ class ExportFormat(object):
else:
columns_headers = [val['label'].strip() for val in fields]
return req.make_response(self.from_data(columns_headers, import_data),
headers=[('Content-Disposition',
content_disposition(self.filename(model), req)),
('Content-Type', self.content_type)],
cookies={'fileToken': int(token)})
class CSVExport(ExportFormat, http.Controller):
class CSVExport(Export):
_cp_path = '/web/export/csv'
fmt = {'tag': 'csv', 'label': 'CSV'}
@ -1667,8 +1663,7 @@ class CSVExport(ExportFormat, http.Controller):
fp.close()
return data
class ExcelExport(ExportFormat, http.Controller):
class ExcelExport(Export):
_cp_path = '/web/export/xls'
fmt = {
'tag': 'xls',
@ -1707,8 +1702,7 @@ class ExcelExport(ExportFormat, http.Controller):
fp.close()
return data
class Reports(openerpweb.Controller):
class Reports(View):
_cp_path = "/web/report"
POLLING_DELAY = 0.25
TYPES_MAPPING = {

View File

@ -355,64 +355,18 @@ def httprequest(f):
#----------------------------------------------------------
addons_module = {}
addons_manifest = {}
controllers_class = {}
controllers_class = []
controllers_object = {}
controllers_path = {}
class ControllerType(type):
def __init__(cls, name, bases, attrs):
super(ControllerType, cls).__init__(name, bases, attrs)
# Only for root "Controller"
if bases == (object,):
assert name == 'Controller'
return
path = attrs.get('_cp_path')
if Controller in bases:
assert path, "Controller subclass %s missing a _cp_path" % name
else:
parent_paths = set(base._cp_path for base in bases
if issubclass(base, Controller))
assert len(parent_paths) == 1,\
"%s inheriting from multiple controllers is not supported" % (
name)
[parent_path] = parent_paths
[parent] = [
controller for controller in controllers_class.itervalues()
if controller._cp_path == parent_path]
# inherit from a Controller subclass
if path:
# if extending in place with same URL, ignore URL
if parent_path == path:
_logger.warn(
"Controller %s extending %s in-place should not "
"explicitly specify URL", name, parent)
return
_logger.warn("Re-exposing %s at %s.\n"
"\tThis usage is unsupported.",
parent.__name__,
attrs['_cp_path'])
if path:
assert path not in controllers_class,\
"Trying to expose %s at the same URL as %s" % (
name, controllers_class[path])
controllers_class[path] = cls
controllers_class.append(("%s.%s" % (cls.__module__, cls.__name__), cls))
class Controller(object):
__metaclass__ = ControllerType
def __new__(cls, *args, **kwargs):
subclasses = [c for c in cls.__subclasses__()
if c._cp_path == cls._cp_path]
if subclasses:
name = "%s (+%s)" % (
cls.__name__,
'+'.join(sub.__name__ for sub in subclasses))
cls = type(name, tuple(reversed(subclasses)), {})
return object.__new__(cls)
#----------------------------------------------------------
# Session context manager
#----------------------------------------------------------
@ -612,8 +566,12 @@ class Root(object):
addons_manifest[module] = manifest
self.statics['/%s/static' % module] = path_static
for c in controllers_class.itervalues():
controllers_path[c._cp_path] = c()
for k, v in controllers_class:
if k not in controllers_object:
o = v()
controllers_object[k] = o
if hasattr(o, '_cp_path'):
controllers_path[o._cp_path] = o
app = werkzeug.wsgi.SharedDataMiddleware(self.dispatch, self.statics)
self.dispatch = DisableCacheMiddleware(app)

View File

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
from . import test_dataset, test_menu, test_serving_base, test_js, test_dispatch
from . import test_dataset, test_menu, test_serving_base, test_js
fast_suite = []
checks = [
test_dataset,
test_menu,
test_serving_base,
test_dispatch,
]

View File

@ -1,373 +0,0 @@
# -*- coding: utf-8 -*-
import contextlib
import json
import logging
import logging.handlers
import types
import unittest2
from .. import http
import werkzeug.test
def setUpModule():
"""
Force load_addons once to import all the crap we don't care for as this
thing is full of side-effects
"""
http.Root().load_addons()
class DispatchCleanup(unittest2.TestCase):
"""
Cleans up controllers registries in the web client so it's possible to
test controllers registration and dispatching in isolation.
"""
def setUp(self):
self.classes = http.controllers_class
self.paths = http.controllers_path
http.controllers_class = {}
http.controllers_path = {}
def tearDown(self):
http.controllers_path = self.paths
http.controllers_class = self.classes
def jsonrpc_body(params=None):
"""
Builds and dumps the body of a JSONRPC request with params ``params``
"""
return json.dumps({
'jsonrpc': '2.0',
'method': 'call',
'id': None,
'params': params or {},
})
def jsonrpc_response(result=None):
"""
Builds a JSONRPC response (as a Python dict) with result ``result``
"""
return {
u'jsonrpc': u'2.0',
u'id': None,
u'result': result,
}
class TestHandler(logging.handlers.BufferingHandler):
def __init__(self):
logging.handlers.BufferingHandler.__init__(self, 0)
def shouldFlush(self, record):
return False
@contextlib.contextmanager
def capture_logging(logger, level=logging.DEBUG):
logger = logging.getLogger(logger)
old_level = logger.level
old_handlers = logger.handlers
old_propagate = logger.propagate
test_handler = TestHandler()
logger.handlers = [test_handler]
logger.setLevel(level)
logger.propagate = False
try:
yield test_handler
finally:
logger.propagate = old_propagate
logger.setLevel(old_level)
logger.handlers = old_handlers
class TestDispatching(DispatchCleanup):
def setUp(self):
super(TestDispatching, self).setUp()
self.app = http.Root()
self.client = werkzeug.test.Client(self.app)
def test_not_exposed(self):
class CatController(http.Controller):
_cp_path = '/cat'
def index(self):
return 'Blessid iz da feline'
self.app.load_addons()
body, status, headers = self.client.get('/cat')
self.assertEqual('404 NOT FOUND', status)
def test_basic_http(self):
class CatController(http.Controller):
_cp_path = '/cat'
@http.httprequest
def index(self, req):
return 'no walk in counsil of wickid,'
self.app.load_addons()
body, status, headers = self.client.get('/cat')
self.assertEqual('200 OK', status)
self.assertEqual('no walk in counsil of wickid,', ''.join(body))
def test_basic_jsonrpc(self):
class CatController(http.Controller):
_cp_path = '/cat'
@http.jsonrequest
def index(self, req):
return 'no place paws in path of da sinnerz,'
self.app.load_addons()
body, status, headers = self.client.post('/cat', data=jsonrpc_body())
self.assertEqual('200 OK', status)
self.assertEqual(
jsonrpc_response('no place paws in path of da sinnerz,'),
json.loads(''.join(body)))
class TestSubclassing(DispatchCleanup):
def setUp(self):
super(TestSubclassing, self).setUp()
self.app = http.Root()
self.client = werkzeug.test.Client(self.app)
def test_add_method(self):
class CatController(http.Controller):
_cp_path = '/cat'
@http.httprequest
def index(self, req):
return 'no sit and purr with da mockerz.'
class CeilingController(CatController):
@http.httprequest
def lol(self, req):
return 'But der delightz in lawz of Ceiling Cat,'
self.app.load_addons()
body, status, headers = self.client.get('/cat')
self.assertEqual('200 OK', status)
self.assertEqual('no sit and purr with da mockerz.', ''.join(body))
body, status, headers = self.client.get('/cat/lol')
self.assertEqual('200 OK', status)
self.assertEqual('But der delightz in lawz of Ceiling Cat,',
''.join(body))
def test_override_method(self):
class CatController(http.Controller):
_cp_path = '/cat'
@http.httprequest
def index(self, req):
return 'an ponderz'
class CeilingController(CatController):
@http.httprequest
def index(self, req):
return '%s much.' % super(CeilingController, self).index(req)
self.app.load_addons()
body, status, headers = self.client.get('/cat')
self.assertEqual('200 OK', status)
self.assertEqual('an ponderz much.', ''.join(body))
def test_make_invisible(self):
class CatController(http.Controller):
_cp_path = '/cat'
@http.httprequest
def index(self, req):
return 'Tehy liek treez bai teh waterz,'
class CeilingController(CatController):
def index(self, req):
return super(CeilingController, self).index(req)
self.app.load_addons()
body, status, headers = self.client.get('/cat')
self.assertEqual('404 NOT FOUND', status)
def test_make_json_invisible(self):
class CatController(http.Controller):
_cp_path = '/cat'
@http.jsonrequest
def index(self, req):
return 'Tehy liek treez bai teh waterz,'
class CeilingController(CatController):
def index(self, req):
return super(CeilingController, self).index(req)
self.app.load_addons()
body, status, headers = self.client.post('/cat')
self.assertEqual('404 NOT FOUND', status)
def test_extends(self):
"""
When subclassing an existing Controller new classes are "merged" into
the base one
"""
class A(http.Controller):
_cp_path = '/foo'
@http.httprequest
def index(self, req):
return '1'
class B(A):
@http.httprequest
def index(self, req):
return "%s 2" % super(B, self).index(req)
class C(A):
@http.httprequest
def index(self, req):
return "%s 3" % super(C, self).index(req)
self.app.load_addons()
body, status, headers = self.client.get('/foo')
self.assertEqual('200 OK', status)
self.assertEqual('1 2 3', ''.join(body))
def test_extends_same_path(self):
"""
When subclassing an existing Controller and specifying the same
_cp_path as the parent, ???
"""
class A(http.Controller):
_cp_path = '/foo'
@http.httprequest
def index(self, req):
return '1'
class B(A):
_cp_path = '/foo'
@http.httprequest
def index(self, req):
return '2'
self.app.load_addons()
body, status, headers = self.client.get('/foo')
self.assertEqual('200 OK', status)
self.assertEqual('2', ''.join(body))
def test_re_expose(self):
"""
An existing Controller should not be extended with a new cp_path
(re-exposing somewhere else)
"""
class CatController(http.Controller):
_cp_path = '/cat'
@http.httprequest
def index(self, req):
return '[%s]' % self.speak()
def speak(self):
return 'Yu ordered cheezburgerz,'
with capture_logging('openerp.addons.web.http') as handler:
class DogController(CatController):
_cp_path = '/dog'
def speak(self):
return 'Woof woof woof woof'
[record] = handler.buffer
self.assertEqual(logging.WARN, record.levelno)
self.assertEqual("Re-exposing CatController at /dog.\n"
"\tThis usage is unsupported.",
record.getMessage())
def test_fail_redefine(self):
"""
An existing Controller can't be overwritten by a new one on the same
path (? or should this generate a warning and still work as if it was
an extend?)
"""
class FooController(http.Controller):
_cp_path = '/foo'
with self.assertRaises(AssertionError):
class BarController(http.Controller):
_cp_path = '/foo'
def test_fail_no_path(self):
"""
A Controller must have a path (and thus be exposed)
"""
with self.assertRaises(AssertionError):
class FooController(http.Controller):
pass
def test_mixin(self):
"""
Can mix "normal" python classes into a controller directly
"""
class Mixin(object):
@http.httprequest
def index(self, req):
return 'ok'
class FooController(http.Controller, Mixin):
_cp_path = '/foo'
class BarContoller(Mixin, http.Controller):
_cp_path = '/bar'
self.app.load_addons()
body, status, headers = self.client.get('/foo')
self.assertEqual('200 OK', status)
self.assertEqual('ok', ''.join(body))
body, status, headers = self.client.get('/bar')
self.assertEqual('200 OK', status)
self.assertEqual('ok', ''.join(body))
def test_mixin_extend(self):
"""
Can mix "normal" python class into a controller by extension
"""
class FooController(http.Controller):
_cp_path = '/foo'
class M1(object):
@http.httprequest
def m1(self, req):
return 'ok 1'
class M2(object):
@http.httprequest
def m2(self, req):
return 'ok 2'
class AddM1(FooController, M1):
pass
class AddM2(M2, FooController):
pass
self.app.load_addons()
body, status, headers = self.client.get('/foo/m1')
self.assertEqual('200 OK', status)
self.assertEqual('ok 1', ''.join(body))
body, status, headers = self.client.get('/foo/m2')
self.assertEqual('200 OK', status)
self.assertEqual('ok 2', ''.join(body))

View File

@ -1,10 +1,9 @@
from openerp.addons.web.http import Controller, jsonrequest
import openerp
class Diagram(Controller):
class DiagramView(openerp.addons.web.controllers.main.View):
_cp_path = "/web_diagram/diagram"
@jsonrequest
@openerp.addons.web.http.jsonrequest
def get_diagram_info(self, req, id, model, node, connector,
src_node, des_node, label, **kw):