Replace safe_eval by simpleeval
and use decistmt from tools issue4274 review10571003
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
* Replace safe_eval by simpleeval
|
||||
|
||||
Version 3.4.0 - 2014-10-20
|
||||
* Bug fixes (see mercurial logs for details)
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user