summaryrefslogtreecommitdiff
path: root/odoo/tools
diff options
context:
space:
mode:
authorxmo-odoo <xmo@odoo.com>2017-05-10 09:39:55 +0200
committerGitHub <noreply@github.com>2017-05-10 09:39:55 +0200
commitfffaf735f58f1ac57781c095eb552de6d1fcdc47 (patch)
tree88ab0ab65430d871862a836524e0ecc890d1674d /odoo/tools
parent10eee18a047d35305dc46282e610e88936d81ffc (diff)
[FIX] P3: list -> iterable builtins (#16811)
In Python 3: * various builtins and dict methods were changed to return view/iterable objects rather than lists * and the separate Python 2 view/iterable builtins and methods were removed altogether This is problematic when using these items as list (which the happens repeatedly in Odoo), but more viciously when iterating *multiple times* over them (which also happens, which I've messed up multiple times while writing this, and which is a pain to debug even when you've just created the issue). Convert all code using these to semantics-matching cross-version helper functions to get the LCD behaviour between P2 and P3, and forbid the builtins via lint. issue #8530
Diffstat (limited to 'odoo/tools')
-rw-r--r--odoo/tools/amount_to_text_en.py2
-rw-r--r--odoo/tools/appdirs.py2
-rw-r--r--odoo/tools/cache.py10
-rw-r--r--odoo/tools/config.py17
-rw-r--r--odoo/tools/convert.py10
-rw-r--r--odoo/tools/float_utils.py6
-rw-r--r--odoo/tools/func.py2
-rwxr-xr-xodoo/tools/graph.py19
-rw-r--r--odoo/tools/lru.py7
-rw-r--r--odoo/tools/mail.py7
-rw-r--r--odoo/tools/mimetypes.py4
-rw-r--r--odoo/tools/misc.py26
-rw-r--r--odoo/tools/osutil.py2
-rw-r--r--odoo/tools/parse_version.py4
-rw-r--r--odoo/tools/pycompat.py11
-rw-r--r--odoo/tools/safe_eval.py3
-rw-r--r--odoo/tools/test_reports.py2
-rw-r--r--odoo/tools/translate.py10
-rw-r--r--odoo/tools/yaml_import.py36
-rw-r--r--odoo/tools/yaml_tag.py5
20 files changed, 104 insertions, 81 deletions
diff --git a/odoo/tools/amount_to_text_en.py b/odoo/tools/amount_to_text_en.py
index 6a8e7020b4a..b1854b1c4d6 100644
--- a/odoo/tools/amount_to_text_en.py
+++ b/odoo/tools/amount_to_text_en.py
@@ -71,7 +71,7 @@ def amount_to_text(number, currency):
cents_number = int(list[1])
cents_name = (cents_number > 1) and 'Cents' or 'Cent'
- return ' '.join(filter(None, [start_word, units_name, (start_word or units_name) and (end_word or cents_name) and 'and', end_word, cents_name]))
+ return ' '.join(w for w in [start_word, units_name, (start_word or units_name) and (end_word or cents_name) and 'and', end_word, cents_name] if w)
#-------------------------------------------------------------
diff --git a/odoo/tools/appdirs.py b/odoo/tools/appdirs.py
index f98fa1f8ae3..8617dabfa4f 100644
--- a/odoo/tools/appdirs.py
+++ b/odoo/tools/appdirs.py
@@ -15,7 +15,7 @@ from __future__ import print_function
# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
__version_info__ = (1, 3, 0)
-__version__ = '.'.join(map(str, __version_info__))
+__version__ = '.'.join(str(v) for v in __version_info__)
import sys
diff --git a/odoo/tools/cache.py b/odoo/tools/cache.py
index 48dc42ba04c..acc01895a48 100644
--- a/odoo/tools/cache.py
+++ b/odoo/tools/cache.py
@@ -8,6 +8,8 @@ from decorator import decorator
from inspect import formatargspec, getargspec
import logging
+from odoo.tools import pycompat
+
unsafe_eval = eval
_logger = logging.getLogger(__name__)
@@ -112,7 +114,7 @@ class ormcache_context(ormcache):
spec = getargspec(self.method)
args = formatargspec(*spec)[1:-1]
cont_expr = "(context or {})" if 'context' in spec.args else "self._context"
- keys_expr = "tuple(map(%s.get, %r))" % (cont_expr, self.keys)
+ keys_expr = "tuple(%s.get(k) for k in %r)" % (cont_expr, self.keys)
if self.args:
code = "lambda %s: (%s, %s)" % (args, ", ".join(self.args), keys_expr)
else:
@@ -200,10 +202,10 @@ def log_ormcache_stats(sig=None, frame=None):
me = threading.currentThread()
me_dbname = me.dbname
entries = defaultdict(int)
- for dbname, reg in Registry.registries.iteritems():
- for key in reg.cache.iterkeys():
+ for dbname, reg in pycompat.items(Registry.registries):
+ for key in reg.cache:
entries[(dbname,) + key[:2]] += 1
- for key, count in sorted(entries.items()):
+ for key, count in sorted(pycompat.items(entries)):
dbname, model_name, method = key
me.dbname = dbname
stat = STAT[key]
diff --git a/odoo/tools/config.py b/odoo/tools/config.py
index 3a227fb5142..0f51fbcb269 100644
--- a/odoo/tools/config.py
+++ b/odoo/tools/config.py
@@ -13,7 +13,8 @@ import odoo.conf
import odoo.loglevels as loglevels
import logging
import odoo.release as release
-from . import appdirs
+from . import appdirs, pycompat
+
class MyOption (optparse.Option, object):
""" optparse Option with two additional attributes.
@@ -55,7 +56,7 @@ def _deduplicate_loggers(loggers):
# there are no duplicates within the output sequence
return (
'{}:{}'.format(logger, level)
- for logger, level in dict(it.split(':') for it in loggers).iteritems()
+ for logger, level in pycompat.items(dict(it.split(':') for it in loggers))
)
@@ -453,10 +454,10 @@ class configmanager(object):
self.options['demo'] = (dict(self.options['init'])
if not self.options['without_demo'] else {})
self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {}
- self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
+ self.options['translate_modules'] = opt.translate_modules and [m.strip() for m in opt.translate_modules.split(',')] or ['all']
self.options['translate_modules'].sort()
- dev_split = opt.dev_mode and map(str.strip, opt.dev_mode.split(',')) or []
+ dev_split = opt.dev_mode and [s.strip() for s in opt.dev_mode.split(',')] or []
self.options['dev_mode'] = 'all' in dev_split and dev_split + ['pdb', 'reload', 'qweb', 'werkzeug', 'xml'] or dev_split
if opt.pg_path:
@@ -527,9 +528,9 @@ class configmanager(object):
def save(self):
p = ConfigParser.ConfigParser()
- loglevelnames = dict(zip(self._LOGLEVELS.values(), self._LOGLEVELS.keys()))
+ loglevelnames = dict(pycompat.izip(pycompat.values(self._LOGLEVELS), pycompat.keys(self._LOGLEVELS)))
p.add_section('options')
- for opt in sorted(self.options.keys()):
+ for opt in sorted(pycompat.keys(self.options)):
if opt in ('version', 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations', 'init', 'update'):
continue
if opt in self.blacklist_for_save:
@@ -541,9 +542,9 @@ class configmanager(object):
else:
p.set('options', opt, self.options[opt])
- for sec in sorted(self.misc.keys()):
+ for sec in sorted(pycompat.keys(self.misc)):
p.add_section(sec)
- for opt in sorted(self.misc[sec].keys()):
+ for opt in sorted(pycompat.keys(self.misc[sec])):
p.set(sec,opt,self.misc[sec][opt])
# try to create the directories and write the file
diff --git a/odoo/tools/convert.py b/odoo/tools/convert.py
index 815ee9a64f1..7e8e9f6e3ce 100644
--- a/odoo/tools/convert.py
+++ b/odoo/tools/convert.py
@@ -98,7 +98,7 @@ def _eval_xml(self, node, env):
q = safe_eval(f_search, idref2)
ids = env[f_model].search(q).ids
if f_use != 'id':
- ids = map(lambda x: x[f_use], env[f_model].browse(ids).read([f_use]))
+ ids = [x[f_use] for x in env[f_model].browse(ids).read([f_use])]
_fields = env[f_model]._fields
if (f_name in _fields) and _fields[f_name].type == 'many2many':
return ids
@@ -659,7 +659,7 @@ form: module.record_id""" % (xml_id,)
_fields = self.env[rec_model]._fields
# if the current field is many2many
if (f_name in _fields) and _fields[f_name].type == 'many2many':
- f_val = [(6, 0, map(lambda x: x[f_use], s))]
+ f_val = [(6, 0, [x[f_use] for x in s])]
elif len(s):
# otherwise (we are probably in a many2one field),
# take the first element of the search
@@ -707,7 +707,7 @@ form: module.record_id""" % (xml_id,)
'model': 'ir.ui.view',
}
for att in ['forcecreate', 'context']:
- if att in el.keys():
+ if att in el.attrib:
record_attrs[att] = el.attrib.pop(att)
Field = builder.E.field
@@ -733,7 +733,7 @@ form: module.record_id""" % (xml_id,)
record.append(Field(name='customize_show', eval=el.get('customize_show')))
groups = el.attrib.pop('groups', None)
if groups:
- grp_lst = map(lambda x: "ref('%s')" % x, groups.split(','))
+ grp_lst = [("ref('%s')" % x) for x in groups.split(',')]
record.append(Field(name="groups_id", eval="[(6, 0, ["+', '.join(grp_lst)+"])]"))
if el.attrib.pop('page', None) == 'True':
record.append(Field(name="page", eval="True"))
@@ -854,7 +854,7 @@ def convert_csv_import(cr, module, fname, csvcontent, idref=None, mode='init',
if not (line and any(line)):
continue
try:
- datas.append(map(ustr, line))
+ datas.append([ustr(v) for v in line])
except Exception:
_logger.error("Cannot import the line: %s", line)
diff --git a/odoo/tools/float_utils.py b/odoo/tools/float_utils.py
index 8c71d7a29b2..fa150a1d902 100644
--- a/odoo/tools/float_utils.py
+++ b/odoo/tools/float_utils.py
@@ -217,10 +217,10 @@ if __name__ == "__main__":
fractions = [.0, .015, .01499, .675, .67499, .4555, .4555, .45555]
expecteds = ['.00', '.02', '.01', '.68', '.67', '.46', '.456', '.4556']
precisions = [2, 2, 2, 2, 2, 2, 3, 4]
- for magnitude in pycompat.range(7):
- for frac, exp, prec in zip(fractions, expecteds, precisions):
+ for magnitude in range(7):
+ for frac, exp, prec in pycompat.izip(fractions, expecteds, precisions):
for sign in [-1,1]:
- for x in pycompat.range(0, 10000, 97):
+ for x in range(0, 10000, 97):
n = x * 10**magnitude
f = sign * (n + frac)
f_exp = ('-' if f != 0 and sign == -1 else '') + str(n) + exp
diff --git a/odoo/tools/func.py b/odoo/tools/func.py
index 8f49864eda2..2e204206f04 100644
--- a/odoo/tools/func.py
+++ b/odoo/tools/func.py
@@ -33,7 +33,7 @@ class lazy_property(object):
""" Reset all lazy properties on the instance `obj`. """
cls = type(obj)
obj_dict = vars(obj)
- for name in obj_dict.keys():
+ for name in list(obj_dict):
if isinstance(getattr(cls, name, None), lazy_property):
obj_dict.pop(name)
diff --git a/odoo/tools/graph.py b/odoo/tools/graph.py
index 82fc0e0978a..5e3edabf05a 100755
--- a/odoo/tools/graph.py
+++ b/odoo/tools/graph.py
@@ -5,6 +5,9 @@
import operator
import math
+from odoo.tools import pycompat
+
+
class graph(object):
def __init__(self, nodes, transitions, no_ancester=None):
"""Initialize graph's object
@@ -256,7 +259,7 @@ class graph(object):
"""The ranks are normalized by setting the least rank to zero.
"""
- least_rank = min(map(lambda x: x['x'], self.result.values()))
+ least_rank = min(x['x'] for x in pycompat.values(self.result.values))
if least_rank!=0:
for node in self.result:
@@ -380,7 +383,7 @@ class graph(object):
"""Finds actual-order of the nodes with respect to maximum number of nodes in a rank in component
"""
mid_pos = 0.0
- max_level = max(map(lambda x: len(x), self.levels.values()))
+ max_level = max(len(x) for x in pycompat.values(self.levels.values))
for level in self.levels:
if level:
@@ -471,7 +474,7 @@ class graph(object):
"""
if self.Is_Cyclic:
- max_level = max(map(lambda x: len(x), self.levels.values()))
+ max_level = max(len(x) for x in pycompat.values(self.levels.values))
if max_level%2:
self.result[self.start]['y'] = (max_level+1)/2 + self.max_order + (self.max_order and 1)
@@ -483,7 +486,7 @@ class graph(object):
else:
self.result[self.start]['y'] = 0
self.tree_order(self.start, 0)
- min_order = math.fabs(min(map(lambda x: x['y'], self.result.values())))
+ min_order = math.fabs(min(x['y'] for x in pycompat.values(self.result.values)))
index = self.start_nodes.index(self.start)
same = False
@@ -537,7 +540,7 @@ class graph(object):
self.result[start]['y'] = base + factor
factor += 1
- self.max_order = max(map(lambda x: x['y'], self.result.values()))
+ self.max_order = max(x['y'] for x in pycompat.values(self.result.values))
def find_starts(self):
"""Finds other start nodes of the graph in the case when graph is disconneted
@@ -626,7 +629,7 @@ class graph(object):
self.make_chain()
self.preprocess_order()
self.order = {}
- max_rank = max(map(lambda x: x, self.levels.keys()))
+ max_rank = max(x for x in self.levels)
for i in range(max_rank+1):
self.order[i] = 0
@@ -657,7 +660,7 @@ class graph(object):
for node in self.no_ancester:
for sec_node in self.transitions.get(node, []):
- if sec_node in self.partial_order.keys():
+ if sec_node in pycompat.keys(self.partial_order):
self.transitions[self.start_nodes[0]].append(node)
break
@@ -743,7 +746,7 @@ if __name__=='__main__':
for node in nodes:
node_res[node] = result[node]
- for name,node in node_res.items():
+ for name,node in pycompat.items(node_res):
draw.arc( (int(node['y']-radius), int(node['x']-radius),int(node['y']+radius), int(node['x']+radius) ), 0, 360, (128,128,128))
draw.text( (int(node['y']), int(node['x'])), str(name), (128,128,128))
diff --git a/odoo/tools/lru.py b/odoo/tools/lru.py
index 7c485a039d3..a9e4a8b8f18 100644
--- a/odoo/tools/lru.py
+++ b/odoo/tools/lru.py
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
# taken from http://code.activestate.com/recipes/252524-length-limited-o1-lru-cache-implementation/
import threading
+
+from odoo.tools import pycompat
from .func import synchronized
__all__ = ['LRU']
@@ -92,6 +94,7 @@ class LRU(object):
def __len__(self):
return len(self.d)
+ # FIXME: should this have a P2 and a P3 version or something?
@synchronized()
def iteritems(self):
cur = self.first
@@ -106,12 +109,12 @@ class LRU(object):
@synchronized()
def itervalues(self):
- for i,j in self.iteritems():
+ for i,j in pycompat.items(self):
yield j
@synchronized()
def keys(self):
- return self.d.keys()
+ return list(pycompat.keys(self.d))
@synchronized()
def pop(self,key):
diff --git a/odoo/tools/mail.py b/odoo/tools/mail.py
index 490006082d5..9689512df00 100644
--- a/odoo/tools/mail.py
+++ b/odoo/tools/mail.py
@@ -16,6 +16,7 @@ from lxml import etree
import odoo
from odoo.loglevels import ustr
+from odoo.tools import pycompat
_logger = logging.getLogger(__name__)
@@ -84,7 +85,7 @@ class _Cleaner(clean.Cleaner):
new_node.text = text
new_node.tail = tail
if attrs:
- for key, val in attrs.iteritems():
+ for key, val in pycompat.items(attrs):
new_node.set(key, val)
return new_node
@@ -153,7 +154,7 @@ class _Cleaner(clean.Cleaner):
if style[0].lower() in self._style_whitelist:
valid_styles[style[0].lower()] = style[1]
if valid_styles:
- el.attrib['style'] = '; '.join('%s: %s' % (key, val) for (key, val) in valid_styles.iteritems())
+ el.attrib['style'] = '; '.join('%s: %s' % (key, val) for (key, val) in pycompat.items(valid_styles))
else:
del el.attrib['style']
@@ -530,4 +531,4 @@ def decode_smtp_header(smtp_header):
# was mail_thread.decode_header()
def decode_message_header(message, header, separator=' '):
- return separator.join(map(decode_smtp_header, filter(None, message.get_all(header, []))))
+ return separator.join(decode_smtp_header(h) for h in message.get_all(header, []) if h)
diff --git a/odoo/tools/mimetypes.py b/odoo/tools/mimetypes.py
index 370c4b2e85b..3abdc22963d 100644
--- a/odoo/tools/mimetypes.py
+++ b/odoo/tools/mimetypes.py
@@ -10,6 +10,8 @@ import logging
import re
import zipfile
+from odoo.tools import pycompat
+
__all__ = ['guess_mimetype']
_logger = logging.getLogger(__name__)
@@ -33,7 +35,7 @@ def _check_ooxml(data):
# then there is a directory whose name denotes the type of the file:
# word, pt (powerpoint) or xl (excel)
- for dirname, mime in _ooxml_dirs.iteritems():
+ for dirname, mime in pycompat.items(_ooxml_dirs):
if any(entry.startswith(dirname) for entry in filenames):
return mime
diff --git a/odoo/tools/misc.py b/odoo/tools/misc.py
index 492be9951e4..81c27105ff1 100644
--- a/odoo/tools/misc.py
+++ b/odoo/tools/misc.py
@@ -22,7 +22,7 @@ import time
import werkzeug.utils
import zipfile
from collections import defaultdict, Iterable, Mapping, MutableSet, OrderedDict
-from itertools import islice, izip, groupby, repeat
+from itertools import islice, groupby, repeat
from lxml import etree
from .which import which
@@ -282,7 +282,7 @@ def flatten(list):
r = []
for e in list:
if isiterable(e):
- map(r.append, flatten(e))
+ r.extend(flatten(e))
else:
r.append(e)
return r
@@ -304,7 +304,7 @@ def reverse_enumerate(l):
File "<stdin>", line 1, in <module>
StopIteration
"""
- return izip(pycompat.range(len(l)-1, -1, -1), reversed(l))
+ return pycompat.izip(range(len(l)-1, -1, -1), reversed(l))
def partition(pred, elems):
""" Return a pair equivalent to:
@@ -337,10 +337,12 @@ def topological_sort(elems):
visited.add(n)
if n in elems:
# first visit all dependencies of n, then append n to result
- map(visit, elems[n])
+ for it in elems[n]:
+ visit(it)
result.append(n)
- map(visit, elems)
+ for el in elems:
+ visit(el)
return result
@@ -458,7 +460,7 @@ def logged(f):
vector = ['Call -> function: %r' % f]
for i, arg in enumerate(args):
vector.append(' arg %02d: %s' % (i, pformat(arg)))
- for key, value in kwargs.items():
+ for key, value in pycompat.items(kwargs):
vector.append(' kwarg %10s: %s' % (key, pformat(value)))
timeb4 = time.time()
@@ -523,9 +525,9 @@ def detect_ip_addr():
# try 32 bit kernel:
if ip_addr is None:
- ifaces = filter(None, [namestr[i:i+32].split('\0', 1)[0] for i in range(0, outbytes, 32)])
+ ifaces = [namestr[i:i+32].split('\0', 1)[0] for i in range(0, outbytes, 32)]
- for ifname in [iface for iface in ifaces if iface != 'lo']:
+ for ifname in [iface for iface in ifaces if iface if iface != 'lo']:
ip_addr = socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, pack('256s', ifname[:15]))[20:24])
break
@@ -909,7 +911,7 @@ def dumpstacks(sig=None, frame=None):
'uid': getattr(th, 'uid', 'n/a'),
'dbname': getattr(th, 'dbname', 'n/a')}
for th in threading.enumerate()}
- for threadId, stack in sys._current_frames().items():
+ for threadId, stack in pycompat.items(sys._current_frames()):
thread_info = threads_info.get(threadId, {})
code.append("\n# Thread: %s (id:%s) (db:%s) (uid:%s)" %
(thread_info.get('name', 'n/a'),
@@ -939,7 +941,7 @@ def freehash(arg):
if isinstance(arg, Mapping):
return hash(frozendict(arg))
elif isinstance(arg, Iterable):
- return hash(frozenset(map(freehash, arg)))
+ return hash(frozenset(freehash(item) for item in arg))
else:
return id(arg)
@@ -960,7 +962,7 @@ class frozendict(dict):
def update(self, *args, **kwargs):
raise NotImplementedError("'update' not supported on frozendict")
def __hash__(self):
- return hash(frozenset((key, freehash(val)) for key, val in self.iteritems()))
+ return hash(frozenset((key, freehash(val)) for key, val in pycompat.items(self)))
class Collector(Mapping):
""" A mapping from keys to lists. This is essentially a space optimization
@@ -1122,7 +1124,7 @@ def format_date(env, value, lang_code=False, date_format=False):
def _consteq(str1, str2):
""" Constant-time string comparison. Suitable to compare bytestrings of fixed,
known length only, because length difference is optimized. """
- return len(str1) == len(str2) and sum(ord(x)^ord(y) for x, y in zip(str1, str2)) == 0
+ return len(str1) == len(str2) and sum(ord(x)^ord(y) for x, y in pycompat.izip(str1, str2)) == 0
consteq = getattr(passlib.utils, 'consteq', _consteq)
diff --git a/odoo/tools/osutil.py b/odoo/tools/osutil.py
index af12c6558b2..2bb180a1d17 100644
--- a/odoo/tools/osutil.py
+++ b/odoo/tools/osutil.py
@@ -39,7 +39,7 @@ def walksymlinks(top, topdown=True, onerror=None):
if topdown:
yield dirpath, dirnames, filenames
- symlinks = filter(lambda dirname: os.path.islink(os.path.join(dirpath, dirname)), dirnames)
+ symlinks = (dirname for dirname in dirnames if os.path.islink(os.path.join(dirpath, dirname)))
for s in symlinks:
for x in walksymlinks(os.path.join(dirpath, s), topdown, onerror):
yield x
diff --git a/odoo/tools/parse_version.py b/odoo/tools/parse_version.py
index 348fb3664a6..e9ff071c0b6 100644
--- a/odoo/tools/parse_version.py
+++ b/odoo/tools/parse_version.py
@@ -7,6 +7,8 @@
from __future__ import print_function
import re
+from odoo.tools import pycompat
+
component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE)
replace = {'pre':'c', 'preview':'c','-':'final-','_':'final-','rc':'c','dev':'@','saas':'','~':''}.get
@@ -72,7 +74,7 @@ if __name__ == '__main__':
if verbose:
print(v, pv)
- for a, b in zip(pvs, pvs[1:]):
+ for a, b in pycompat.izip(pvs, pvs[1:]):
assert a < b, '%s < %s == %s' % (a, b, a < b)
chk(('0', '4.2', '4.2.3.4', '5.0.0-alpha', '5.0.0-rc1', '5.0.0-rc1.1', '5.0.0_rc2', '5.0.0_rc3', '5.0.0'), False)
diff --git a/odoo/tools/pycompat.py b/odoo/tools/pycompat.py
index 50b6f492a59..3fbb847a9fa 100644
--- a/odoo/tools/pycompat.py
+++ b/odoo/tools/pycompat.py
@@ -6,10 +6,12 @@ import sys
PY2 = sys.version_info[0] == 2
if PY2:
- # pylint: disable=long-builtin,xrange-builtin
+ # pylint: disable=long-builtin,dict-iter-method
integer_types = (int, long)
- range = xrange
+ keys = lambda d: iter(d.iterkeys())
+ values = lambda d: iter(d.itervalues())
+ items = lambda d: iter(d.iteritems())
# noinspection PyUnresolvedReferences
from itertools import imap, izip, ifilter
@@ -19,9 +21,12 @@ if PY2:
del cls.__next__
return cls
else:
+ # pylint: disable=bad-functions
integer_types = (int,)
- range = range
+ keys = lambda d: iter(d.keys())
+ values = lambda d: iter(d.values())
+ items = lambda d: iter(d.items())
imap = map
izip = zip
diff --git a/odoo/tools/safe_eval.py b/odoo/tools/safe_eval.py
index ebe97619e35..1897486eb82 100644
--- a/odoo/tools/safe_eval.py
+++ b/odoo/tools/safe_eval.py
@@ -23,7 +23,6 @@ from types import CodeType
import logging
import werkzeug
-from odoo.tools import pycompat
from .misc import ustr
import odoo
@@ -251,7 +250,7 @@ _BUILTINS = {
'divmod': divmod,
'isinstance': isinstance,
'range': range,
- 'xrange': pycompat.range,
+ 'xrange': range,
'zip': zip,
'Exception': Exception,
}
diff --git a/odoo/tools/test_reports.py b/odoo/tools/test_reports.py
index 17065848129..cad44431c1d 100644
--- a/odoo/tools/test_reports.py
+++ b/odoo/tools/test_reports.py
@@ -174,7 +174,7 @@ def try_report_action(cr, uid, action_id, active_model=None, active_ids=None,
view_data.update(wiz_data)
_logger.debug("View data is: %r", view_data)
- for fk, field in view_res.get('fields',{}).items():
+ for fk, field in pycompat.items(view_res.get('fields',{})):
# Default fields returns list of int, while at create()
# we need to send a [(6,0,[int,..])]
if field['type'] in ('one2many', 'many2many') \
diff --git a/odoo/tools/translate.py b/odoo/tools/translate.py
index dcdb95d0955..b2996af6ae3 100644
--- a/odoo/tools/translate.py
+++ b/odoo/tools/translate.py
@@ -255,7 +255,7 @@ def translate_xml_node(node, callback, method, parser=None):
append_content(result, translate_content(todo) if todo_has_text else todo)
# translate the required attributes
- for name, value in result.items():
+ for name, value in pycompat.items(result.attrib):
if name in TRANSLATED_ATTRS:
result.set(name, translate_text(value) or value)
@@ -660,7 +660,7 @@ def trans_export(lang, modules, buffer, format, cr):
row.setdefault('tnrs', []).append((type, name, res_id))
row.setdefault('comments', set()).update(comments)
- for src, row in sorted(grouped_rows.items()):
+ for src, row in sorted(pycompat.items(grouped_rows)):
if not lang:
# translation template, so no translation value
row['translation'] = ''
@@ -674,7 +674,7 @@ def trans_export(lang, modules, buffer, format, cr):
module = row[0]
rows_by_module.setdefault(module, []).append(row)
tmpdir = tempfile.mkdtemp()
- for mod, modrows in rows_by_module.items():
+ for mod, modrows in pycompat.items(rows_by_module):
tmpmoddir = join(tmpdir, mod, 'i18n')
os.makedirs(tmpmoddir)
pofilename = (lang if lang else mod) + ".po" + ('t' if not lang else '')
@@ -1073,7 +1073,7 @@ def trans_load_data(cr, fileobj, fileformat, lang, lang_name=None, verbose=True,
dic = dict.fromkeys(('type', 'name', 'res_id', 'src', 'value',
'comments', 'imd_model', 'imd_name', 'module'))
dic['lang'] = lang
- dic.update(zip(fields, row))
+ dic.update(pycompat.izip(fields, row))
# discard the target from the POT targets.
src = dic['src']
@@ -1111,7 +1111,7 @@ def trans_load_data(cr, fileobj, fileformat, lang, lang_name=None, verbose=True,
# Then process the entries implied by the POT file (which is more
# correct w.r.t. the targets) if some of them remain.
pot_rows = []
- for src, target in pot_targets.iteritems():
+ for src, target in pycompat.items(pot_targets):
if target.value:
for type, name, res_id in target.targets:
pot_rows.append((type, name, res_id, src, target.value, target.comments))
diff --git a/odoo/tools/yaml_import.py b/odoo/tools/yaml_import.py
index 36c6c248249..4cb2e7ea184 100644
--- a/odoo/tools/yaml_import.py
+++ b/odoo/tools/yaml_import.py
@@ -13,7 +13,7 @@ import yaml
import odoo
from . import assertion_report
-from . import yaml_tag
+from . import yaml_tag, pycompat
from .config import config
from .misc import file_open, DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
from odoo import SUPERUSER_ID
@@ -36,8 +36,8 @@ class YamlImportAbortion(Exception):
def _is_yaml_mapping(node, tag_constructor):
value = isinstance(node, dict) \
- and len(node.keys()) == 1 \
- and isinstance(node.keys()[0], tag_constructor)
+ and len(node) == 1 \
+ and isinstance(next(iter(node)), tag_constructor)
return value
def is_comment(node):
@@ -202,7 +202,7 @@ class YamlInterpreter(object):
def process_assert(self, node):
if isinstance(node, dict):
- assertion, expressions = node.items()[0]
+ assertion, expressions = next(pycompat.items(node))
else:
assertion, expressions = node, []
@@ -275,7 +275,7 @@ class YamlInterpreter(object):
return record_dict
def process_record(self, node):
- record, fields = node.items()[0]
+ record, fields = next(pycompat.items(node))
model = self.env[record.model]
view_id = record.view
if view_id and (view_id is not True) and isinstance(view_id, basestring):
@@ -377,7 +377,7 @@ class YamlInterpreter(object):
def process_vals(fg, vals):
""" sanitize the given field values """
result = {}
- for field_name, field_value in vals.iteritems():
+ for field_name, field_value in pycompat.items(vals):
if field_name not in fg:
continue
if fg[field_name]['type'] == 'many2one' and isinstance(field_value, (tuple, list)):
@@ -391,14 +391,14 @@ class YamlInterpreter(object):
elif isinstance(command, dict):
return process_vals(sub_fg, command)
return command
- field_value = map(process, field_value or [])
+ field_value = [process(v) for v in (field_value or [])]
result[field_name] = field_value
return result
def post_process(fg, elems, vals):
""" filter out readonly fields from vals """
result = {}
- for field_name, field_value in vals.iteritems():
+ for field_name, field_value in pycompat.items(vals):
if is_readonly(elems[field_name]):
continue
if fg[field_name]['type'] in ('one2many', 'many2many'):
@@ -412,7 +412,7 @@ class YamlInterpreter(object):
elif isinstance(command, dict):
return (0, 0, post_process(sub_fg, sub_elems, command))
return command
- field_value = map(process, field_value or [])
+ field_value = [process(v) for v in (field_value or [])]
result[field_name] = field_value
return result
@@ -444,7 +444,7 @@ class YamlInterpreter(object):
record_dict.update(process_vals(fg, result.get('value', {})))
# fill in fields, and execute onchange where necessary
- for field_name, field_elem in elems.iteritems():
+ for field_name, field_elem in pycompat.items(elems):
assert field_name in fg, "The field '%s' is defined in the form view but not on the object '%s'!" % (field_name, model._name)
if is_readonly(field_elem):
# skip readonly fields
@@ -474,7 +474,7 @@ class YamlInterpreter(object):
result = recs.onchange(dict(record_dict, **parent_values), field_name, onchange_spec)
record_dict.update(process_vals(fg, {
key: val
- for key, val in result.get('value', {}).iteritems()
+ for key, val in pycompat.items(result.get('value', {}))
if key not in fields # do not shadow values explicitly set in yaml
}))
@@ -483,7 +483,7 @@ class YamlInterpreter(object):
else:
record_dict = {}
- for field_name, expression in fields.iteritems():
+ for field_name, expression in pycompat.items(fields):
if record_dict.get(field_name):
continue
field_value = self._eval_field(model, field_name, expression, parent=record_dict, default=False, context=context)
@@ -494,7 +494,7 @@ class YamlInterpreter(object):
# should not be sent to create. This bug appears with not stored function fields in the new API.
return {
key: val
- for key, val in record_dict.iteritems()
+ for key, val in pycompat.items(record_dict)
for field in [model._fields[key].base_field]
if field.store or field.inverse
}
@@ -580,7 +580,7 @@ class YamlInterpreter(object):
self.sudo_env = self.env(user=SUPERUSER_ID)
def process_python(self, node):
- python, statements = node.items()[0]
+ python, statements = next(pycompat.items(node))
assert python.model or python.id, "!python node must have attribute `model` or `id`"
if python.id is None:
record = self.env[python.model]
@@ -640,7 +640,7 @@ class YamlInterpreter(object):
return args
def process_function(self, node):
- function, params = node.items()[0]
+ function, params = next(pycompat.items(node))
if self.isnoupdate(function) and self.mode != 'init':
return
model = self.env[function.model]
@@ -770,9 +770,9 @@ class YamlInterpreter(object):
def process_ir_set(self, node):
if not self.mode == 'init':
return False
- _, fields = node.items()[0]
+ _, fields = next(pycompat.items(node))
res = {}
- for fieldname, expression in fields.items():
+ for fieldname, expression in pycompat.items(fields):
if is_eval(expression):
value = safe_eval(expression.expression, self.eval_context)
else:
@@ -878,7 +878,7 @@ class YamlInterpreter(object):
elif not is_preceded_by_comment:
if isinstance(node, dict):
msg = "Creating %s\n with %s"
- args = node.items()[0]
+ args = next(pycompat.items(node))
self._log(msg, *args)
else:
self._log(node)
diff --git a/odoo/tools/yaml_tag.py b/odoo/tools/yaml_tag.py
index f921c598f30..2b7141b6693 100644
--- a/odoo/tools/yaml_tag.py
+++ b/odoo/tools/yaml_tag.py
@@ -1,6 +1,9 @@
import yaml
import logging
+from . import pycompat
+
+
class YamlTag(object):
"""
Superclass for constructors of custom tags defined in yaml file.
@@ -13,7 +16,7 @@ class YamlTag(object):
def __getattr__(self, attr):
return None
def __repr__(self):
- return "<%s %s>" % (self.__class__.__name__, sorted(self.__dict__.items()))
+ return "<%s %s>" % (self.__class__.__name__, sorted(pycompat.items(self.__dict__)))
class Assert(YamlTag):
def __init__(self, model, id=None, severity=logging.WARNING, string="NONAME", **kwargs):