[MERGE] forward port of branch saas-3 up to revid 5115 chs@openerp.com-20140410102422-fcwxhjk40z0oy8x5
bzr revid: chs@openerp.com-20140410105454-6utwgruj80h8wdss
This commit is contained in:
commit
15680c94e1
|
@ -31,6 +31,7 @@ import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
import urllib
|
import urllib
|
||||||
import urllib2
|
import urllib2
|
||||||
|
import urlparse
|
||||||
import zipfile
|
import zipfile
|
||||||
import zipimport
|
import zipimport
|
||||||
import lxml.html
|
import lxml.html
|
||||||
|
@ -41,6 +42,7 @@ except ImportError:
|
||||||
from StringIO import StringIO # NOQA
|
from StringIO import StringIO # NOQA
|
||||||
|
|
||||||
import openerp
|
import openerp
|
||||||
|
import openerp.exceptions
|
||||||
from openerp import modules, tools
|
from openerp import modules, tools
|
||||||
from openerp.modules.db import create_categories
|
from openerp.modules.db import create_categories
|
||||||
from openerp.modules import get_module_resource
|
from openerp.modules import get_module_resource
|
||||||
|
@ -621,40 +623,14 @@ class module(osv.osv):
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def download(self, cr, uid, ids, download=True, context=None):
|
def download(self, cr, uid, ids, download=True, context=None):
|
||||||
res = []
|
return []
|
||||||
default_version = modules.adapt_version('1.0')
|
|
||||||
for mod in self.browse(cr, uid, ids, context=context):
|
|
||||||
if not mod.url:
|
|
||||||
continue
|
|
||||||
match = re.search('-([a-zA-Z0-9\._-]+)(\.zip)', mod.url, re.I)
|
|
||||||
version = default_version
|
|
||||||
if match:
|
|
||||||
version = match.group(1)
|
|
||||||
if parse_version(mod.installed_version) >= parse_version(version):
|
|
||||||
continue
|
|
||||||
res.append(mod.url)
|
|
||||||
if not download:
|
|
||||||
continue
|
|
||||||
zip_content = urllib.urlopen(mod.url).read()
|
|
||||||
fname = modules.get_module_path(str(mod.name) + '.zip', downloaded=True)
|
|
||||||
try:
|
|
||||||
with open(fname, 'wb') as fp:
|
|
||||||
fp.write(zip_content)
|
|
||||||
except Exception:
|
|
||||||
_logger.exception('Error when trying to create module '
|
|
||||||
'file %s', fname)
|
|
||||||
raise orm.except_orm(_('Error'), _('Can not create the module file:\n %s') % (fname,))
|
|
||||||
terp = self.get_module_info(mod.name)
|
|
||||||
self.write(cr, uid, mod.id, self.get_values_from_terp(terp))
|
|
||||||
cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s', (mod.id,))
|
|
||||||
self._update_dependencies(cr, uid, mod, terp.get('depends', []))
|
|
||||||
self._update_category(cr, uid, mod, terp.get('category', 'Uncategorized'))
|
|
||||||
# Import module
|
|
||||||
zimp = zipimport.zipimporter(fname)
|
|
||||||
zimp.load_module(mod.name)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def install_from_urls(self, cr, uid, urls, context=None):
|
def install_from_urls(self, cr, uid, urls, context=None):
|
||||||
|
if not self.pool['res.users'].has_group(cr, uid, 'base.group_system'):
|
||||||
|
raise openerp.exceptions.AccessDenied()
|
||||||
|
|
||||||
|
apps_server = urlparse.urlparse(self.get_apps_server(cr, uid, context=context))
|
||||||
|
|
||||||
OPENERP = 'openerp'
|
OPENERP = 'openerp'
|
||||||
tmp = tempfile.mkdtemp()
|
tmp = tempfile.mkdtemp()
|
||||||
_logger.debug('Install from url: %r', urls)
|
_logger.debug('Install from url: %r', urls)
|
||||||
|
@ -663,6 +639,11 @@ class module(osv.osv):
|
||||||
for module_name, url in urls.items():
|
for module_name, url in urls.items():
|
||||||
if not url:
|
if not url:
|
||||||
continue # nothing to download, local version is already the last one
|
continue # nothing to download, local version is already the last one
|
||||||
|
|
||||||
|
up = urlparse.urlparse(url)
|
||||||
|
if up.scheme != apps_server.scheme or up.netloc != apps_server.netloc:
|
||||||
|
raise openerp.exceptions.AccessDenied()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_logger.info('Downloading module `%s` from OpenERP Apps', module_name)
|
_logger.info('Downloading module `%s` from OpenERP Apps', module_name)
|
||||||
content = urllib2.urlopen(url).read()
|
content = urllib2.urlopen(url).read()
|
||||||
|
@ -727,8 +708,8 @@ class module(osv.osv):
|
||||||
finally:
|
finally:
|
||||||
shutil.rmtree(tmp)
|
shutil.rmtree(tmp)
|
||||||
|
|
||||||
def install_by_names(self, cr, uid, names, context=None):
|
def get_apps_server(self, cr, uid, context=None):
|
||||||
raise NotImplementedError('# TODO')
|
return tools.config.get('apps_server', 'https://apps.openerp.com/apps')
|
||||||
|
|
||||||
def _update_dependencies(self, cr, uid, mod_browse, depends=None):
|
def _update_dependencies(self, cr, uid, mod_browse, depends=None):
|
||||||
if depends is None:
|
if depends is None:
|
||||||
|
|
|
@ -63,8 +63,8 @@ openerp.base = function(instance) {
|
||||||
if (instance.base.apps_client) {
|
if (instance.base.apps_client) {
|
||||||
return check_client_available(instance.base.apps_client);
|
return check_client_available(instance.base.apps_client);
|
||||||
} else {
|
} else {
|
||||||
var ICP = new instance.web.Model('ir.config_parameter');
|
var Mod = new instance.web.Model('ir.module.module');
|
||||||
return ICP.call('get_param', ['apps.server', 'https://apps.openerp.com/apps']).then(function(u) {
|
return Mod.call('get_apps_server').then(function(u) {
|
||||||
var link = $(_.str.sprintf('<a href="%s"></a>', u))[0];
|
var link = $(_.str.sprintf('<a href="%s"></a>', u))[0];
|
||||||
var host = _.str.sprintf('%s//%s', link.protocol, link.host);
|
var host = _.str.sprintf('%s//%s', link.protocol, link.host);
|
||||||
var dbname = link.pathname;
|
var dbname = link.pathname;
|
||||||
|
|
|
@ -183,7 +183,7 @@ class test_expression(common.TransactionCase):
|
||||||
self.assertIn('res_partner_bank', sql_query[0],
|
self.assertIn('res_partner_bank', sql_query[0],
|
||||||
"_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect main table")
|
"_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect main table")
|
||||||
|
|
||||||
expected = "%s like %s" % (unaccent('"res_partner_bank"."name"'), unaccent('%s'))
|
expected = "%s::text like %s" % (unaccent('"res_partner_bank"."name"'), unaccent('%s'))
|
||||||
self.assertIn(expected, sql_query[1],
|
self.assertIn(expected, sql_query[1],
|
||||||
"_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect where condition")
|
"_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect where condition")
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@ class test_expression(common.TransactionCase):
|
||||||
self.assertIn('"res_partner_bank" as "res_partner__bank_ids"', sql_query[0],
|
self.assertIn('"res_partner_bank" as "res_partner__bank_ids"', sql_query[0],
|
||||||
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join")
|
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join")
|
||||||
|
|
||||||
expected = "%s like %s" % (unaccent('"res_partner__bank_ids"."name"'), unaccent('%s'))
|
expected = "%s::text like %s" % (unaccent('"res_partner__bank_ids"."name"'), unaccent('%s'))
|
||||||
self.assertIn(expected, sql_query[1],
|
self.assertIn(expected, sql_query[1],
|
||||||
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect where condition")
|
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect where condition")
|
||||||
|
|
||||||
|
@ -305,7 +305,7 @@ class test_expression(common.TransactionCase):
|
||||||
self.assertIn('"res_country"', sql_query[0],
|
self.assertIn('"res_country"', sql_query[0],
|
||||||
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table")
|
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table")
|
||||||
|
|
||||||
expected = "%s like %s" % (unaccent('"res_country"."code"'), unaccent('%s'))
|
expected = "%s::text like %s" % (unaccent('"res_country"."code"'), unaccent('%s'))
|
||||||
self.assertIn(expected, sql_query[1],
|
self.assertIn(expected, sql_query[1],
|
||||||
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
|
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
|
||||||
|
|
||||||
|
@ -339,7 +339,7 @@ class test_expression(common.TransactionCase):
|
||||||
self.assertIn('"res_country" as "res_country_state__country_id"', sql_query[0],
|
self.assertIn('"res_country" as "res_country_state__country_id"', sql_query[0],
|
||||||
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join")
|
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join")
|
||||||
|
|
||||||
expected = "%s like %s" % (unaccent('"res_country_state__country_id"."code"'), unaccent('%s'))
|
expected = "%s::text like %s" % (unaccent('"res_country_state__country_id"."code"'), unaccent('%s'))
|
||||||
self.assertIn(expected, sql_query[1],
|
self.assertIn(expected, sql_query[1],
|
||||||
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
|
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
|
||||||
|
|
||||||
|
@ -373,7 +373,7 @@ class test_expression(common.TransactionCase):
|
||||||
self.assertIn('"res_country" as "res_partner__state_id__country_id"', sql_query[0],
|
self.assertIn('"res_country" as "res_partner__state_id__country_id"', sql_query[0],
|
||||||
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join")
|
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join")
|
||||||
|
|
||||||
expected = "%s like %s" % (unaccent('"res_partner__state_id__country_id"."code"'), unaccent('%s'))
|
expected = "%s::text like %s" % (unaccent('"res_partner__state_id__country_id"."code"'), unaccent('%s'))
|
||||||
self.assertIn(expected, sql_query[1],
|
self.assertIn(expected, sql_query[1],
|
||||||
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect where condition")
|
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect where condition")
|
||||||
|
|
||||||
|
@ -403,7 +403,7 @@ class test_expression(common.TransactionCase):
|
||||||
# Test produced queries that domains effectively present
|
# Test produced queries that domains effectively present
|
||||||
sql_query = self.query_list[0].get_sql()
|
sql_query = self.query_list[0].get_sql()
|
||||||
|
|
||||||
expected = "%s like %s" % (unaccent('"res_partner__child_ids__bank_ids"."acc_number"'), unaccent('%s'))
|
expected = "%s::text like %s" % (unaccent('"res_partner__child_ids__bank_ids"."acc_number"'), unaccent('%s'))
|
||||||
self.assertIn(expected, sql_query[1],
|
self.assertIn(expected, sql_query[1],
|
||||||
"_auto_join on one2many with domains incorrect result")
|
"_auto_join on one2many with domains incorrect result")
|
||||||
# TDE TODO: check first domain has a correct table name
|
# TDE TODO: check first domain has a correct table name
|
||||||
|
|
|
@ -1182,14 +1182,15 @@ class expression(object):
|
||||||
else:
|
else:
|
||||||
need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike')
|
need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike')
|
||||||
sql_operator = {'=like': 'like', '=ilike': 'ilike'}.get(operator, operator)
|
sql_operator = {'=like': 'like', '=ilike': 'ilike'}.get(operator, operator)
|
||||||
|
cast = '::text' if sql_operator.endswith('like') else ''
|
||||||
|
|
||||||
if left in model._columns:
|
if left in model._columns:
|
||||||
format = need_wildcard and '%s' or model._columns[left]._symbol_set[0]
|
format = need_wildcard and '%s' or model._columns[left]._symbol_set[0]
|
||||||
unaccent = self._unaccent if sql_operator.endswith('like') else lambda x: x
|
unaccent = self._unaccent if sql_operator.endswith('like') else lambda x: x
|
||||||
column = '%s.%s' % (table_alias, _quote(left))
|
column = '%s.%s' % (table_alias, _quote(left))
|
||||||
query = '(%s %s %s)' % (unaccent(column), sql_operator, unaccent(format))
|
query = '(%s%s %s %s)' % (unaccent(column), cast, sql_operator, unaccent(format))
|
||||||
elif left in MAGIC_COLUMNS:
|
elif left in MAGIC_COLUMNS:
|
||||||
query = "(%s.\"%s\" %s %%s)" % (table_alias, left, sql_operator)
|
query = "(%s.\"%s\"%s %s %%s)" % (table_alias, left, cast, sql_operator)
|
||||||
params = right
|
params = right
|
||||||
else: # Must not happen
|
else: # Must not happen
|
||||||
raise ValueError("Invalid field %r in domain term %r" % (left, leaf))
|
raise ValueError("Invalid field %r in domain term %r" % (left, leaf))
|
||||||
|
|
|
@ -167,7 +167,7 @@ class Cursor(object):
|
||||||
self.sql_log_count = 0
|
self.sql_log_count = 0
|
||||||
self._closed = True # avoid the call of close() (by __del__) if an exception
|
self._closed = True # avoid the call of close() (by __del__) if an exception
|
||||||
# is raised by any of the following initialisations
|
# is raised by any of the following initialisations
|
||||||
self._pool = pool
|
self.__pool = pool
|
||||||
self.dbname = dbname
|
self.dbname = dbname
|
||||||
|
|
||||||
# Whether to enable snapshot isolation level for this cursor.
|
# Whether to enable snapshot isolation level for this cursor.
|
||||||
|
@ -313,7 +313,7 @@ class Cursor(object):
|
||||||
chosen_template = tools.config['db_template']
|
chosen_template = tools.config['db_template']
|
||||||
templates_list = tuple(set(['template0', 'template1', 'postgres', chosen_template]))
|
templates_list = tuple(set(['template0', 'template1', 'postgres', chosen_template]))
|
||||||
keep_in_pool = self.dbname not in templates_list
|
keep_in_pool = self.dbname not in templates_list
|
||||||
self._pool.give_back(self._cnx, keep_in_pool=keep_in_pool)
|
self.__pool.give_back(self._cnx, keep_in_pool=keep_in_pool)
|
||||||
|
|
||||||
@check
|
@check
|
||||||
def autocommit(self, on):
|
def autocommit(self, on):
|
||||||
|
@ -537,12 +537,12 @@ class Connection(object):
|
||||||
|
|
||||||
def __init__(self, pool, dbname):
|
def __init__(self, pool, dbname):
|
||||||
self.dbname = dbname
|
self.dbname = dbname
|
||||||
self._pool = pool
|
self.__pool = pool
|
||||||
|
|
||||||
def cursor(self, serialized=True):
|
def cursor(self, serialized=True):
|
||||||
cursor_type = serialized and 'serialized ' or ''
|
cursor_type = serialized and 'serialized ' or ''
|
||||||
_logger.debug('create %scursor to %r', cursor_type, self.dbname)
|
_logger.debug('create %scursor to %r', cursor_type, self.dbname)
|
||||||
return Cursor(self._pool, self.dbname, serialized=serialized)
|
return Cursor(self.__pool, self.dbname, serialized=serialized)
|
||||||
|
|
||||||
def test_cursor(self, serialized=True):
|
def test_cursor(self, serialized=True):
|
||||||
cursor_type = serialized and 'serialized ' or ''
|
cursor_type = serialized and 'serialized ' or ''
|
||||||
|
|
|
@ -71,7 +71,8 @@ _SAFE_OPCODES = _EXPR_OPCODES.union(set(opmap[x] for x in [
|
||||||
'CONTINUE_LOOP', 'RAISE_VARARGS',
|
'CONTINUE_LOOP', 'RAISE_VARARGS',
|
||||||
# New in Python 2.7 - http://bugs.python.org/issue4715 :
|
# New in Python 2.7 - http://bugs.python.org/issue4715 :
|
||||||
'JUMP_IF_FALSE_OR_POP', 'JUMP_IF_TRUE_OR_POP', 'POP_JUMP_IF_FALSE',
|
'JUMP_IF_FALSE_OR_POP', 'JUMP_IF_TRUE_OR_POP', 'POP_JUMP_IF_FALSE',
|
||||||
'POP_JUMP_IF_TRUE', 'SETUP_EXCEPT', 'END_FINALLY'
|
'POP_JUMP_IF_TRUE', 'SETUP_EXCEPT', 'END_FINALLY', 'LOAD_FAST',
|
||||||
|
'LOAD_GLOBAL', # Only allows access to restricted globals
|
||||||
] if x in opmap))
|
] if x in opmap))
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
@ -86,16 +87,65 @@ def _get_opcodes(codeobj):
|
||||||
[100, 100, 23, 100, 100, 102, 103, 83]
|
[100, 100, 23, 100, 100, 102, 103, 83]
|
||||||
"""
|
"""
|
||||||
i = 0
|
i = 0
|
||||||
opcodes = []
|
|
||||||
byte_codes = codeobj.co_code
|
byte_codes = codeobj.co_code
|
||||||
while i < len(byte_codes):
|
while i < len(byte_codes):
|
||||||
code = ord(byte_codes[i])
|
code = ord(byte_codes[i])
|
||||||
opcodes.append(code)
|
yield code
|
||||||
|
|
||||||
if code >= HAVE_ARGUMENT:
|
if code >= HAVE_ARGUMENT:
|
||||||
i += 3
|
i += 3
|
||||||
else:
|
else:
|
||||||
i += 1
|
i += 1
|
||||||
return opcodes
|
|
||||||
|
def assert_no_dunder_name(code_obj, expr):
|
||||||
|
""" assert_no_dunder_name(code_obj, expr) -> None
|
||||||
|
|
||||||
|
Asserts that the code object does not refer to any "dunder name"
|
||||||
|
(__$name__), so that safe_eval prevents access to any internal-ish Python
|
||||||
|
attribute or method (both are loaded via LOAD_ATTR which uses a name, not a
|
||||||
|
const or a var).
|
||||||
|
|
||||||
|
Checks that no such name exists in the provided code object (co_names).
|
||||||
|
|
||||||
|
:param code_obj: code object to name-validate
|
||||||
|
:type code_obj: CodeType
|
||||||
|
:param str expr: expression corresponding to the code object, for debugging
|
||||||
|
purposes
|
||||||
|
:raises NameError: in case a forbidden name (containing two underscores)
|
||||||
|
is found in ``code_obj``
|
||||||
|
|
||||||
|
.. note:: actually forbids every name containing 2 underscores
|
||||||
|
"""
|
||||||
|
for name in code_obj.co_names:
|
||||||
|
if "__" in name:
|
||||||
|
raise NameError('Access to forbidden name %r (%r)' % (name, expr))
|
||||||
|
|
||||||
|
def assert_valid_codeobj(allowed_codes, code_obj, expr):
|
||||||
|
""" Asserts that the provided code object validates against the bytecode
|
||||||
|
and name constraints.
|
||||||
|
|
||||||
|
Recursively validates the code objects stored in its co_consts in case
|
||||||
|
lambdas are being created/used (lambdas generate their own separated code
|
||||||
|
objects and don't live in the root one)
|
||||||
|
|
||||||
|
:param allowed_codes: list of permissible bytecode instructions
|
||||||
|
:type allowed_codes: set(int)
|
||||||
|
:param code_obj: code object to name-validate
|
||||||
|
:type code_obj: CodeType
|
||||||
|
:param str expr: expression corresponding to the code object, for debugging
|
||||||
|
purposes
|
||||||
|
:raises ValueError: in case of forbidden bytecode in ``code_obj``
|
||||||
|
:raises NameError: in case a forbidden name (containing two underscores)
|
||||||
|
is found in ``code_obj``
|
||||||
|
"""
|
||||||
|
assert_no_dunder_name(code_obj, expr)
|
||||||
|
for opcode in _get_opcodes(code_obj):
|
||||||
|
if opcode not in allowed_codes:
|
||||||
|
raise ValueError(
|
||||||
|
"opcode %s not allowed (%r)" % (opname[opcode], expr))
|
||||||
|
for const in code_obj.co_consts:
|
||||||
|
if isinstance(const, CodeType):
|
||||||
|
assert_valid_codeobj(allowed_codes, const, 'lambda')
|
||||||
|
|
||||||
def test_expr(expr, allowed_codes, mode="eval"):
|
def test_expr(expr, allowed_codes, mode="eval"):
|
||||||
"""test_expr(expression, allowed_codes[, mode]) -> code_object
|
"""test_expr(expression, allowed_codes[, mode]) -> code_object
|
||||||
|
@ -110,15 +160,13 @@ def test_expr(expr, allowed_codes, mode="eval"):
|
||||||
# eval() does not like leading/trailing whitespace
|
# eval() does not like leading/trailing whitespace
|
||||||
expr = expr.strip()
|
expr = expr.strip()
|
||||||
code_obj = compile(expr, "", mode)
|
code_obj = compile(expr, "", mode)
|
||||||
except (SyntaxError, TypeError):
|
except (SyntaxError, TypeError, ValueError):
|
||||||
raise
|
raise
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
import sys
|
import sys
|
||||||
exc_info = sys.exc_info()
|
exc_info = sys.exc_info()
|
||||||
raise ValueError, '"%s" while compiling\n%r' % (ustr(e), expr), exc_info[2]
|
raise ValueError, '"%s" while compiling\n%r' % (ustr(e), expr), exc_info[2]
|
||||||
for code in _get_opcodes(code_obj):
|
assert_valid_codeobj(allowed_codes, code_obj, expr)
|
||||||
if code not in allowed_codes:
|
|
||||||
raise ValueError("opcode %s not allowed (%r)" % (opname[code], expr))
|
|
||||||
return code_obj
|
return code_obj
|
||||||
|
|
||||||
|
|
||||||
|
@ -187,19 +235,13 @@ def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=Fal
|
||||||
This can be used to e.g. evaluate
|
This can be used to e.g. evaluate
|
||||||
an OpenERP domain expression from an untrusted source.
|
an OpenERP domain expression from an untrusted source.
|
||||||
|
|
||||||
Throws TypeError, SyntaxError or ValueError (not allowed) accordingly.
|
:throws TypeError: If the expression provided is a code object
|
||||||
|
:throws SyntaxError: If the expression provided is not valid Python
|
||||||
>>> safe_eval("__import__('sys').modules")
|
:throws NameError: If the expression provided accesses forbidden names
|
||||||
Traceback (most recent call last):
|
:throws ValueError: If the expression provided uses forbidden bytecode
|
||||||
...
|
|
||||||
ValueError: opcode LOAD_NAME not allowed
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if isinstance(expr, CodeType):
|
if isinstance(expr, CodeType):
|
||||||
raise ValueError("safe_eval does not allow direct evaluation of code objects.")
|
raise TypeError("safe_eval does not allow direct evaluation of code objects.")
|
||||||
|
|
||||||
if '__subclasses__' in expr:
|
|
||||||
raise ValueError('expression not allowed (__subclasses__)')
|
|
||||||
|
|
||||||
if globals_dict is None:
|
if globals_dict is None:
|
||||||
globals_dict = {}
|
globals_dict = {}
|
||||||
|
|
4
setup.py
4
setup.py
|
@ -150,7 +150,7 @@ setuptools.setup(
|
||||||
#include_package_data = True,
|
#include_package_data = True,
|
||||||
install_requires = [
|
install_requires = [
|
||||||
'pychart', # not on pypi, use: pip install http://download.gna.org/pychart/PyChart-1.39.tar.gz
|
'pychart', # not on pypi, use: pip install http://download.gna.org/pychart/PyChart-1.39.tar.gz
|
||||||
'babel',
|
'babel >= 1.0',
|
||||||
'docutils',
|
'docutils',
|
||||||
'feedparser',
|
'feedparser',
|
||||||
'gdata',
|
'gdata',
|
||||||
|
@ -171,7 +171,7 @@ setuptools.setup(
|
||||||
'python-openid',
|
'python-openid',
|
||||||
'pytz',
|
'pytz',
|
||||||
'pyusb >= 1.0.0b1',
|
'pyusb >= 1.0.0b1',
|
||||||
'pywebdav',
|
'pywebdav <= 0.9.4',
|
||||||
'pyyaml',
|
'pyyaml',
|
||||||
'qrcode',
|
'qrcode',
|
||||||
'reportlab', # windows binary pypi.python.org/pypi/reportlab
|
'reportlab', # windows binary pypi.python.org/pypi/reportlab
|
||||||
|
|
Loading…
Reference in New Issue