[IMP] use non-literal domains to and from client, add a bunch of tests
Converted OpenERPSession's evaluations to be based on literal (dict) or nonliteral (Domain) objects. OpenERPSession will *not accept* to eval strings bzr revid: xmo@openerp.com-20110328141920-hlp6sb173o2j6ldw
This commit is contained in:
parent
fd9da7558a
commit
e7860c7e63
|
@ -21,7 +21,8 @@ class Xml2Json:
|
||||||
# URL: http://code.google.com/p/xml2json-direct/
|
# URL: http://code.google.com/p/xml2json-direct/
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def convert_to_json(s):
|
def convert_to_json(s):
|
||||||
return simplejson.dumps(Xml2Json.convert_to_structure(s), sort_keys=True, indent=4)
|
return simplejson.dumps(
|
||||||
|
Xml2Json.convert_to_structure(s), sort_keys=True, indent=4)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def convert_to_structure(s):
|
def convert_to_structure(s):
|
||||||
|
@ -174,10 +175,15 @@ class Menu(openerpweb.Controller):
|
||||||
actions = Values.get('action', 'tree_but_open', [('ir.ui.menu', menu_id)], False, {})
|
actions = Values.get('action', 'tree_but_open', [('ir.ui.menu', menu_id)], False, {})
|
||||||
|
|
||||||
for _, _, action in actions:
|
for _, _, action in actions:
|
||||||
|
# values come from the server, we can just eval them
|
||||||
action['context'] = req.session.eval_context(
|
action['context'] = req.session.eval_context(
|
||||||
action['context']) or {}
|
action['context']) or {}
|
||||||
action['domain'] = req.session.eval_domain(
|
|
||||||
action['domain'], action['context']) or []
|
if isinstance(action['domain'], basestring):
|
||||||
|
action['domain'] = eval(
|
||||||
|
action['domain'],
|
||||||
|
req.session.evaluation_context(
|
||||||
|
action['context'])) or []
|
||||||
|
|
||||||
return {"action": actions}
|
return {"action": actions}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ can't be sent there themselves).
|
||||||
"""
|
"""
|
||||||
import binascii
|
import binascii
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import simplejson.decoder
|
||||||
|
import simplejson.encoder
|
||||||
|
|
||||||
__all__ = ['Domain']
|
__all__ = ['Domain']
|
||||||
|
|
||||||
|
@ -13,6 +15,21 @@ __all__ = ['Domain']
|
||||||
#: with a million hashes, according to hg@67081329d49a
|
#: with a million hashes, according to hg@67081329d49a
|
||||||
SHORT_HASH_BYTES_SIZE = 6
|
SHORT_HASH_BYTES_SIZE = 6
|
||||||
|
|
||||||
|
class NonLiteralEncoder(simplejson.encoder.JSONEncoder):
|
||||||
|
def default(self, object):
|
||||||
|
if isinstance(object, Domain):
|
||||||
|
return {
|
||||||
|
'__ref': 'domain',
|
||||||
|
'__id': object.key
|
||||||
|
}
|
||||||
|
return super(NonLiteralEncoder, self).default(object)
|
||||||
|
|
||||||
|
def non_literal_decoder(dct):
|
||||||
|
if '__ref' in dct:
|
||||||
|
if dct['__ref'] == 'domain':
|
||||||
|
return Domain(None, key=dct['__id'])
|
||||||
|
return dct
|
||||||
|
|
||||||
class Domain(object):
|
class Domain(object):
|
||||||
def __init__(self, session, domain_string=None, key=None):
|
def __init__(self, session, domain_string=None, key=None):
|
||||||
""" Uses session information to store the domain string and map it to a
|
""" Uses session information to store the domain string and map it to a
|
||||||
|
|
|
@ -15,6 +15,7 @@ import cherrypy
|
||||||
import cherrypy.lib.static
|
import cherrypy.lib.static
|
||||||
import simplejson
|
import simplejson
|
||||||
|
|
||||||
|
import nonliterals
|
||||||
import xmlrpctimeout
|
import xmlrpctimeout
|
||||||
|
|
||||||
#----------------------------------------------------------
|
#----------------------------------------------------------
|
||||||
|
@ -197,31 +198,37 @@ class OpenERPSession(object):
|
||||||
current_context.update(final_context)
|
current_context.update(final_context)
|
||||||
return final_context
|
return final_context
|
||||||
|
|
||||||
def eval_domain(self, domain_string, context=None, use_base=True):
|
def eval_domain(self, domain, context=None):
|
||||||
""" Evaluates the provided domain_string using the provided context
|
""" Evaluates the provided domain_string using the provided context
|
||||||
(merged with the session's evaluation context)
|
(merged with the session's evaluation context)
|
||||||
|
|
||||||
:param str domain_string: an OpenERP domain as a string, to evaluate.
|
:param domain: an OpenERP domain as a dict or as a
|
||||||
|
:class:`openerpweb.nonliterals.Domain` instance
|
||||||
|
|
||||||
If not a string, is returned as-is
|
In the second case, it will be evaluated and returned.
|
||||||
|
:type domain: openerpweb.nonliterals.Domain
|
||||||
:param dict context: the context to use in the evaluation, if any.
|
:param dict context: the context to use in the evaluation, if any.
|
||||||
:param bool use_base: whether the base eval context (combination
|
|
||||||
of the default context and the session
|
|
||||||
context) should be used
|
|
||||||
:returns: the evaluated domain
|
:returns: the evaluated domain
|
||||||
:rtype: list
|
:rtype: list
|
||||||
|
|
||||||
|
:raises: ``TypeError`` if ``domain`` is neither a dict nor a Domain
|
||||||
"""
|
"""
|
||||||
if not isinstance(domain_string, basestring):
|
if not isinstance(domain, (list, nonliterals.Domain)):
|
||||||
return domain_string
|
raise TypeError("Domain %r is not a dict or a nonliteral Domain",
|
||||||
|
domain)
|
||||||
|
|
||||||
|
if isinstance(domain, list):
|
||||||
|
return domain
|
||||||
|
|
||||||
ctx = {}
|
ctx = {}
|
||||||
if use_base:
|
|
||||||
ctx.update(self.base_eval_context)
|
|
||||||
if context:
|
if context:
|
||||||
ctx.update(context)
|
ctx.update(context)
|
||||||
ctx['context'] = ctx
|
ctx['context'] = ctx
|
||||||
|
|
||||||
return eval(domain_string, ctx)
|
# if the domain was unpacked from JSON, it needs the current
|
||||||
|
# OpenERPSession for its data retrieval
|
||||||
|
domain.session = self
|
||||||
|
return domain.evaluate(ctx)
|
||||||
|
|
||||||
def eval_domains(self, domains, context=None):
|
def eval_domains(self, domains, context=None):
|
||||||
""" Evaluates and concatenates the provided domains using the
|
""" Evaluates and concatenates the provided domains using the
|
||||||
|
@ -236,14 +243,10 @@ class OpenERPSession(object):
|
||||||
:returns: the final combination of all domains in the sequence
|
:returns: the final combination of all domains in the sequence
|
||||||
:rtype: list
|
:rtype: list
|
||||||
"""
|
"""
|
||||||
ctx = dict(
|
|
||||||
self.base_eval_context,
|
|
||||||
**(context or {}))
|
|
||||||
|
|
||||||
final_domain = []
|
final_domain = []
|
||||||
for domain in domains:
|
for domain in domains:
|
||||||
final_domain.extend(
|
final_domain.extend(
|
||||||
self.eval_domain(domain, ctx))
|
self.eval_domain(domain, context))
|
||||||
return final_domain
|
return final_domain
|
||||||
|
|
||||||
#----------------------------------------------------------
|
#----------------------------------------------------------
|
||||||
|
@ -290,9 +293,11 @@ class JsonRequest(object):
|
||||||
:rtype: bytes
|
:rtype: bytes
|
||||||
'''
|
'''
|
||||||
if requestf:
|
if requestf:
|
||||||
request = simplejson.load(requestf)
|
request = simplejson.load(
|
||||||
|
requestf, object_hook=nonliterals.non_literal_decoder)
|
||||||
else:
|
else:
|
||||||
request = simplejson.loads(request)
|
request = simplejson.loads(
|
||||||
|
request, object_hook=nonliterals.non_literal_decoder)
|
||||||
try:
|
try:
|
||||||
print "--> %s.%s %s" % (controller.__class__.__name__, method.__name__, request)
|
print "--> %s.%s %s" % (controller.__class__.__name__, method.__name__, request)
|
||||||
error = None
|
error = None
|
||||||
|
@ -335,7 +340,8 @@ class JsonRequest(object):
|
||||||
print "<--", response
|
print "<--", response
|
||||||
print
|
print
|
||||||
|
|
||||||
content = simplejson.dumps(response)
|
content = simplejson.dumps(
|
||||||
|
response, cls=nonliterals.NonLiteralEncoder)
|
||||||
cherrypy.response.headers['Content-Type'] = 'application/json'
|
cherrypy.response.headers['Content-Type'] = 'application/json'
|
||||||
cherrypy.response.headers['Content-Length'] = len(content)
|
cherrypy.response.headers['Content-Length'] = len(content)
|
||||||
return content
|
return content
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import mock
|
import mock
|
||||||
|
import simplejson
|
||||||
import unittest2
|
import unittest2
|
||||||
|
|
||||||
from openerpweb.nonliterals import Domain
|
from openerpweb.nonliterals import Domain
|
||||||
|
import openerpweb.nonliterals
|
||||||
import openerpweb.openerpweb
|
import openerpweb.openerpweb
|
||||||
|
|
||||||
class NonLiteralDomainTest(unittest2.TestCase):
|
class NonLiteralDomainTest(unittest2.TestCase):
|
||||||
|
@ -41,3 +43,14 @@ class NonLiteralDomainTest(unittest2.TestCase):
|
||||||
result = Domain(self.session, "[('a', '=', foo)]").evaluate({'foo': 3})
|
result = Domain(self.session, "[('a', '=', foo)]").evaluate({'foo': 3})
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
result, [('a', '=', 3)])
|
result, [('a', '=', 3)])
|
||||||
|
|
||||||
|
class NonLiteralJSON(unittest2.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.session = mock.Mock(spec=openerpweb.openerpweb.OpenERPSession)
|
||||||
|
self.session.domains_store = {}
|
||||||
|
|
||||||
|
def test_encode(self):
|
||||||
|
d = Domain(self.session, "some arbitrary string")
|
||||||
|
self.assertEqual(
|
||||||
|
simplejson.dumps(d, cls=openerpweb.nonliterals.NonLiteralEncoder),
|
||||||
|
simplejson.dumps({'__ref': 'domain', '__id': d.key}))
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import unittest2
|
import unittest2
|
||||||
|
from openerpweb.nonliterals import Domain
|
||||||
import openerpweb.openerpweb
|
import openerpweb.openerpweb
|
||||||
|
|
||||||
class TestOpenERPSession(unittest2.TestCase):
|
class TestOpenERPSession(unittest2.TestCase):
|
||||||
|
@ -49,3 +50,61 @@ class TestOpenERPSession(unittest2.TestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
eval('foo + 3', self.session.evaluation_context({'foo': 4})),
|
eval('foo + 3', self.session.evaluation_context({'foo': 4})),
|
||||||
7)
|
7)
|
||||||
|
|
||||||
|
def test_eval_domain_typeerror(self):
|
||||||
|
self.assertRaises(
|
||||||
|
TypeError, self.session.eval_domain, "foo")
|
||||||
|
|
||||||
|
def test_eval_domain_list(self):
|
||||||
|
self.assertEqual(
|
||||||
|
self.session.eval_domain([]),
|
||||||
|
[])
|
||||||
|
|
||||||
|
def test_eval_nonliteral_domain(self):
|
||||||
|
d = Domain(self.session, "[('foo', 'is', 3)]")
|
||||||
|
self.assertEqual(
|
||||||
|
self.session.eval_domain(d),
|
||||||
|
[('foo', 'is', 3)])
|
||||||
|
|
||||||
|
def test_eval_nonliteral_domain_bykey(self):
|
||||||
|
key = Domain(
|
||||||
|
self.session, "[('foo', 'is', 3)]").key
|
||||||
|
|
||||||
|
d = Domain(None, key=key)
|
||||||
|
self.assertEqual(
|
||||||
|
self.session.eval_domain(d),
|
||||||
|
[('foo', 'is', 3)])
|
||||||
|
|
||||||
|
def test_eval_empty_domains(self):
|
||||||
|
self.assertEqual(
|
||||||
|
self.session.eval_domains([]),
|
||||||
|
[])
|
||||||
|
|
||||||
|
def test_eval_literal_domains(self):
|
||||||
|
domains = [
|
||||||
|
[('a', 'is', 3)],
|
||||||
|
[('b', 'ilike', 'foo')],
|
||||||
|
['|',
|
||||||
|
('c', '=', False),
|
||||||
|
('c', 'in', ['a', 'b', 'c'])]
|
||||||
|
]
|
||||||
|
self.assertEqual(
|
||||||
|
self.session.eval_domains(domains),
|
||||||
|
[
|
||||||
|
('a', 'is', 3),
|
||||||
|
('b', 'ilike', 'foo'),
|
||||||
|
'|',
|
||||||
|
('c', '=', False),
|
||||||
|
('c', 'in', ['a', 'b', 'c'])
|
||||||
|
])
|
||||||
|
def test_eval_nonliteral_domains(self):
|
||||||
|
domains = [
|
||||||
|
Domain(self.session, "[('uid', '=', uid)]"),
|
||||||
|
Domain(self.session,
|
||||||
|
"['|', ('date', '<', current_date),"
|
||||||
|
" ('date', '>', current_date)]")]
|
||||||
|
self.assertEqual(
|
||||||
|
self.session.eval_domains(domains),
|
||||||
|
[('uid', '=', -1),
|
||||||
|
'|', ('date', '<', '1945-08-05'), ('date', '>', '1945-08-05')]
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue