[FIX] evaluation context structures not being round-tripped through eval
JS objects are converted to py.object when passed in through the evaluation context. py.object are not serializable by default (because that doesn't really make sense), which breaks when the input is intended as a dict and returned (e.g. o2m values, which are triples of (int, int?, dict?)). Intuitively, JS objects passed as part of the context should be mostly JSON-ish and thus dicts, but that turns out not work work as some addons use attribute accesses within contexts (e.g. parent.access in account/account_invoice_view.xml) => Temporarily solve by converting raw js objects to an "attributed dict" which acts as both a dict and an object and can be converted to JSON. Ideally, py.js should provide for a pluggable conversion, or should use an attributed mapping internally. See issues 21 and 23. lp bug: https://launchpad.net/bugs/1182101 fixed bzr revid: xmo@openerp.com-20130624055929-3rtkgqrp4o87pvau
This commit is contained in:
parent
e2c795e297
commit
11a0ece543
|
@ -602,6 +602,85 @@ openerp.web.pyeval = function (instance) {
|
|||
}
|
||||
});
|
||||
|
||||
// recursively wraps JS objects passed into the context to attributedicts
|
||||
// which jsonify back to JS objects
|
||||
var wrap = function (value) {
|
||||
if (value === null) { return py.None; }
|
||||
|
||||
switch (typeof value) {
|
||||
case 'undefined': throw new Error("No conversion for undefined");
|
||||
case 'boolean': return py.bool.fromJSON(value);
|
||||
case 'number': return py.float.fromJSON(value);
|
||||
case 'string': return py.str.fromJSON(value);
|
||||
}
|
||||
|
||||
switch(value.constructor) {
|
||||
case Object: return wrapping_dict.fromJSON(value);
|
||||
case Array: return wrapping_list.fromJSON(value);
|
||||
}
|
||||
|
||||
throw new Error("ValueError: unable to wrap " + value);
|
||||
};
|
||||
var wrapping_dict = py.type('wrapping_dict', null, {
|
||||
__init__: function () {
|
||||
this._store = {};
|
||||
},
|
||||
__getitem__: function (key) {
|
||||
var k = key.toJSON();
|
||||
if (!(k in this._store)) {
|
||||
throw new Error("KeyError: '" + k + "'");
|
||||
}
|
||||
return wrap(this._store[k]);
|
||||
},
|
||||
__getattr__: function (key) {
|
||||
return this.__getitem__(py.str.fromJSON(key));
|
||||
},
|
||||
get: function () {
|
||||
var args = py.PY_parseArgs(arguments, ['k', ['d', py.None]]);
|
||||
|
||||
if (!(args.k.toJSON() in this._store)) { return args.d; }
|
||||
return this.__getitem__(args.k);
|
||||
},
|
||||
fromJSON: function (d) {
|
||||
var instance = py.PY_call(wrapping_dict);
|
||||
instance._store = d;
|
||||
return instance;
|
||||
},
|
||||
toJSON: function () {
|
||||
return this._store;
|
||||
},
|
||||
});
|
||||
var wrapping_list = py.type('wrapping_list', null, {
|
||||
__init__: function () {
|
||||
this._store = [];
|
||||
},
|
||||
__getitem__: function (index) {
|
||||
return wrap(this._store[index.toJSON()]);
|
||||
},
|
||||
fromJSON: function (ar) {
|
||||
var instance = py.PY_call(wrapping_list);
|
||||
instance._store = ar;
|
||||
return instance;
|
||||
},
|
||||
toJSON: function () {
|
||||
return this._store;
|
||||
},
|
||||
});
|
||||
var wrap_context = function (context) {
|
||||
for (var k in context) {
|
||||
if (!context.hasOwnProperty(k)) { continue; }
|
||||
var val = context[k];
|
||||
|
||||
if (val.constructor === Array) {
|
||||
context[k] = wrapping_list.fromJSON(val);
|
||||
} else if (val.constructor === Object
|
||||
&& !py.PY_isInstance(val, py.object)) {
|
||||
context[k] = wrapping_dict.fromJSON(val);
|
||||
}
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
var eval_contexts = function (contexts, evaluation_context) {
|
||||
evaluation_context = _.extend(instance.web.pyeval.context(), evaluation_context || {});
|
||||
return _(contexts).reduce(function (result_context, ctx) {
|
||||
|
@ -615,8 +694,8 @@ openerp.web.pyeval = function (instance) {
|
|||
var evaluated = ctx;
|
||||
switch(ctx.__ref) {
|
||||
case 'context':
|
||||
evaluation_context.context = py.dict.fromJSON(evaluation_context);
|
||||
evaluated = py.eval(ctx.__debug, evaluation_context);
|
||||
evaluation_context.context = evaluation_context;
|
||||
evaluated = py.eval(ctx.__debug, wrap_context(evaluation_context));
|
||||
break;
|
||||
case 'compound_context':
|
||||
var eval_context = eval_contexts([ctx.__eval_context]);
|
||||
|
@ -640,9 +719,9 @@ openerp.web.pyeval = function (instance) {
|
|||
}
|
||||
switch(domain.__ref) {
|
||||
case 'domain':
|
||||
evaluation_context.context = py.dict.fromJSON(evaluation_context);
|
||||
evaluation_context.context = evaluation_context;
|
||||
result_domain.push.apply(
|
||||
result_domain, py.eval(domain.__debug, evaluation_context));
|
||||
result_domain, py.eval(domain.__debug, wrap_context(evaluation_context)));
|
||||
break;
|
||||
case 'compound_domain':
|
||||
var eval_context = eval_contexts([domain.__eval_context]);
|
||||
|
@ -669,8 +748,8 @@ openerp.web.pyeval = function (instance) {
|
|||
var evaluated = ctx;
|
||||
switch(ctx.__ref) {
|
||||
case 'context':
|
||||
evaluation_context.context = py.dict.fromJSON(evaluation_context);
|
||||
evaluated = py.eval(ctx.__debug, evaluation_context);
|
||||
evaluation_context.context = evaluation_context;
|
||||
evaluated = py.eval(ctx.__debug, wrap_context(evaluation_context));
|
||||
break;
|
||||
case 'compound_context':
|
||||
var eval_context = eval_contexts([ctx.__eval_context]);
|
||||
|
@ -712,7 +791,6 @@ openerp.web.pyeval = function (instance) {
|
|||
instance.web.pyeval.eval = function (type, object, context, options) {
|
||||
options = options || {};
|
||||
context = _.extend(instance.web.pyeval.context(), context || {});
|
||||
context['context'] = py.dict.fromJSON(context);
|
||||
|
||||
//noinspection FallthroughInSwitchStatementJS
|
||||
switch(type) {
|
||||
|
|
|
@ -704,6 +704,73 @@ openerp.testing.section('eval.contexts', {
|
|||
}]);
|
||||
deepEqual(result, {type: 'out_invoice'});
|
||||
});
|
||||
test('return-input-value', function (instance) {
|
||||
var result = instance.web.pyeval.eval('contexts', [{
|
||||
__ref: 'compound_context',
|
||||
__contexts: ["{'line_id': line_id , 'journal_id': journal_id }"],
|
||||
__eval_context: {
|
||||
__ref: 'compound_context',
|
||||
__contexts: [{
|
||||
__ref: 'compound_context',
|
||||
__contexts: [
|
||||
{lang: 'en_US', tz: 'Europe/Paris', uid: 1},
|
||||
{lang: 'en_US', tz: 'Europe/Paris', uid: 1},
|
||||
{}
|
||||
],
|
||||
__eval_context: null,
|
||||
}, {
|
||||
active_id: false,
|
||||
active_ids: [],
|
||||
active_model: 'account.move',
|
||||
amount: 0,
|
||||
company_id: 1,
|
||||
date: '2013-06-21',
|
||||
id: false,
|
||||
journal_id: 14,
|
||||
line_id: [
|
||||
[0, false, {
|
||||
account_id: 55,
|
||||
amount_currency: 0,
|
||||
analytic_account_id: false,
|
||||
credit: 0,
|
||||
currency_id: false,
|
||||
date_maturity: false,
|
||||
debit: 0,
|
||||
name: "dscsd",
|
||||
partner_id: false,
|
||||
tax_amount: 0,
|
||||
tax_code_id: false,
|
||||
}]
|
||||
],
|
||||
name: '/',
|
||||
narration: false,
|
||||
parent: {},
|
||||
partner_id: false,
|
||||
period_id: 6,
|
||||
ref: false,
|
||||
state: 'draft',
|
||||
to_check: false,
|
||||
}],
|
||||
__eval_context: null,
|
||||
},
|
||||
}]);
|
||||
deepEqual(result, {
|
||||
journal_id: 14,
|
||||
line_id: [[0, false, {
|
||||
account_id: 55,
|
||||
amount_currency: 0,
|
||||
analytic_account_id: false,
|
||||
credit: 0,
|
||||
currency_id: false,
|
||||
date_maturity: false,
|
||||
debit: 0,
|
||||
name: "dscsd",
|
||||
partner_id: false,
|
||||
tax_amount: 0,
|
||||
tax_code_id: false,
|
||||
}]],
|
||||
});
|
||||
});
|
||||
});
|
||||
openerp.testing.section('eval.domains', {
|
||||
dependencies: ['web.coresetup', 'web.dates']
|
||||
|
|
Loading…
Reference in New Issue