Replace safe_eval by simpleeval

and use decistmt from tools

issue4274
review10571003
This commit is contained in:
Cédric Krier
2014-10-27 23:17:02 +01:00
parent 1853a4ef63
commit d148c30b0b
4 changed files with 33 additions and 89 deletions
+2
View File
@@ -1,3 +1,5 @@
* Replace safe_eval by simpleeval
Version 3.4.0 - 2014-10-20
* Bug fixes (see mercurial logs for details)
+1
View File
@@ -5,6 +5,7 @@ Prerequisites
-------------
* Python 2.7 or later (http://www.python.org/)
* simpleeval (https://github.com/danthedeckie/simpleeval)
* trytond (http://www.tryton.org/)
* trytond_product (http://www.tryton.org/)
* trytond_party (http://www.tryton.org/)
+29 -88
View File
@@ -1,10 +1,11 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from decimal import Decimal
import tokenize
from StringIO import StringIO
from simpleeval import simple_eval
from trytond.model import ModelView, ModelSQL, MatchMixin, fields
from trytond.tools import safe_eval
from trytond.tools import decistmt
from trytond.pyson import If, Eval
from trytond.transaction import Transaction
from trytond.pool import Pool
@@ -13,37 +14,6 @@ from trytond import backend
__all__ = ['PriceList', 'PriceListLine']
# code snippet taken from http://docs.python.org/library/tokenize.html
def decistmt(s):
"""Substitute Decimals for floats in a string of statements.
>>> from decimal import Decimal
>>> s = 'print +21.3e-5*-.1234/81.7'
>>> decistmt(s)
"print +Decimal ('21.3e-5')*-Decimal ('.1234')/Decimal ('81.7')"
>>> exec(s)
-3.21716034272e-007
>>> exec(decistmt(s))
-3.217160342717258261933904529E-7
"""
result = []
# tokenize the string
g = tokenize.generate_tokens(StringIO(s).readline)
for toknum, tokval, _, _, _ in g:
# replace NUMBER tokens
if toknum == tokenize.NUMBER and '.' in tokval:
result.extend([
(tokenize.NAME, 'Decimal'),
(tokenize.OP, '('),
(tokenize.STRING, repr(tokval)),
(tokenize.OP, ')')
])
else:
result.append((toknum, tokval))
return tokenize.untokenize(result)
class PriceList(ModelSQL, ModelView):
'Price List'
__name__ = 'product.price_list'
@@ -60,36 +30,16 @@ class PriceList(ModelSQL, ModelView):
def default_company():
return Transaction().context.get('company')
def _get_context_price_list_line(self, party, product, unit_price,
quantity, uom):
'''
Get price list context for unit price
:param party: the BrowseRecord of the party.party
:param product: the BrowseRecord of the product.product
:param unit_price: a Decimal for the default unit price in the
company's currency and default uom of the product
:param quantity: the quantity of product
:param uom: the BrowseRecord of the product.uom
:return: a dictionary
'''
def get_context_formula(self, party, product, unit_price, quantity, uom):
return {
'unit_price': unit_price,
}
'names': {
'unit_price': unit_price,
},
}
def compute(self, party, product, unit_price, quantity, uom,
pattern=None):
'''
Compute price based on price list of party
:param unit_price: a Decimal for the default unit price in the
company's currency and default uom of the product
:param quantity: the quantity of product
:param uom: a instance of the product.uom
:param pattern: a dictionary with price list field as key
and match value as value
:return: the computed unit price
'''
'Compute price based on price list of party'
Uom = Pool().get('product.uom')
@@ -101,12 +51,11 @@ class PriceList(ModelSQL, ModelView):
pattern['quantity'] = Uom.compute_qty(uom, quantity,
product.default_uom, round=False) if product else quantity
context = self.get_context_formula(
party, product, unit_price, quantity, uom)
for line in self.lines:
if line.match(pattern):
with Transaction().set_context(
self._get_context_price_list_line(party, product,
unit_price, quantity, uom)):
return line.get_unit_price()
return line.get_unit_price(**context)
return unit_price
@@ -133,7 +82,7 @@ class PriceListLine(ModelSQL, ModelView, MatchMixin):
cls._order.insert(0, ('sequence', 'ASC'))
cls._error_messages.update({
'invalid_formula': ('Invalid formula "%(formula)s" in price '
'list line "%(line)s".'),
'list line "%(line)s" with exception "%(exception)s".'),
})
@classmethod
@@ -172,23 +121,18 @@ class PriceListLine(ModelSQL, ModelView, MatchMixin):
'''
Check formula
'''
pool = Pool()
PriceList = pool.get('product.price_list')
context = PriceList()._get_context_price_list_line(None, None,
Decimal('0.0'), 0, None)
context = self.price_list.get_context_formula(
None, None, Decimal('0'), 0, None)
with Transaction().set_context(**context):
try:
if not isinstance(self.get_unit_price(), Decimal):
self.raise_user_error('invalid_formula', {
'formula': self.formula,
'line': self.rec_name,
})
except Exception:
self.raise_user_error('invalid_formula', {
'formula': self.formula,
'line': self.rec_name,
})
try:
if not isinstance(self.get_unit_price(**context), Decimal):
raise ValueError
except Exception, exception:
self.raise_user_error('invalid_formula', {
'formula': self.formula,
'line': self.rec_name,
'exception': exception,
})
def match(self, pattern):
if 'quantity' in pattern:
@@ -197,10 +141,7 @@ class PriceListLine(ModelSQL, ModelView, MatchMixin):
return False
return super(PriceListLine, self).match(pattern)
def get_unit_price(self):
'''
Return unit price (as Decimal)
'''
context = Transaction().context.copy()
context['Decimal'] = Decimal
return safe_eval(decistmt(self.formula), context)
def get_unit_price(self, **context):
'Return unit price (as Decimal)'
context.setdefault('functions', {})['Decimal'] = Decimal
return simple_eval(decistmt(self.formula), **context)
+1 -1
View File
@@ -41,7 +41,7 @@ if minor_version % 2:
'hg+http://hg.tryton.org/modules/%s#egg=%s-%s' % (
name[8:], name, version))
requires = []
requires = ['simpleeval']
for dep in info.get('depends', []):
if not re.match(r'(ir|res|webdav)(\W|$)', dep):
requires.append(get_require_version('trytond_%s' % dep))