summaryrefslogtreecommitdiff
path: root/odoo
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
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')
-rw-r--r--odoo/addons/base/ir/ir_actions.py25
-rw-r--r--odoo/addons/base/ir/ir_attachment.py10
-rw-r--r--odoo/addons/base/ir/ir_config_parameter.py4
-rw-r--r--odoo/addons/base/ir/ir_fields.py16
-rw-r--r--odoo/addons/base/ir/ir_http.py5
-rw-r--r--odoo/addons/base/ir/ir_mail_server.py17
-rw-r--r--odoo/addons/base/ir/ir_model.py30
-rw-r--r--odoo/addons/base/ir/ir_qweb/assetsbundle.py8
-rw-r--r--odoo/addons/base/ir/ir_qweb/ir_qweb.py8
-rw-r--r--odoo/addons/base/ir/ir_qweb/qweb.py55
-rw-r--r--odoo/addons/base/ir/ir_sequence.py3
-rw-r--r--odoo/addons/base/ir/ir_translation.py6
-rw-r--r--odoo/addons/base/ir/ir_ui_view.py38
-rw-r--r--odoo/addons/base/ir/ir_values.py6
-rw-r--r--odoo/addons/base/module/module.py16
-rw-r--r--odoo/addons/base/module/report/ir_module_reference_print.py4
-rw-r--r--odoo/addons/base/res/ir_property.py8
-rw-r--r--odoo/addons/base/res/res_company.py3
-rw-r--r--odoo/addons/base/res/res_config.py14
-rw-r--r--odoo/addons/base/res/res_lang.py7
-rw-r--r--odoo/addons/base/res/res_partner.py2
-rw-r--r--odoo/addons/base/res/res_users.py34
-rw-r--r--odoo/addons/base/tests/test_api.py8
-rw-r--r--odoo/addons/base/tests/test_expression.py6
-rw-r--r--odoo/addons/base/tests/test_float.py6
-rw-r--r--odoo/addons/base/tests/test_ir_filters.py27
-rw-r--r--odoo/addons/base/tests/test_orm.py8
-rw-r--r--odoo/addons/base/tests/test_qweb.py3
-rw-r--r--odoo/addons/base/tests/test_search.py5
-rw-r--r--odoo/addons/base/tests/test_views.py2
-rw-r--r--odoo/addons/test_impex/tests/test_export.py5
-rw-r--r--odoo/addons/test_limits/models.py3
-rw-r--r--odoo/addons/test_new_api/models.py5
-rw-r--r--odoo/addons/test_new_api/tests/test_one2many.py2
-rw-r--r--odoo/addons/test_pylint/tests/test_pylint.py39
-rw-r--r--odoo/api.py15
-rw-r--r--odoo/cli/command.py2
-rw-r--r--odoo/fields.py20
-rw-r--r--odoo/http.py22
-rw-r--r--odoo/models.py163
-rw-r--r--odoo/modules/db.py2
-rw-r--r--odoo/modules/graph.py19
-rw-r--r--odoo/modules/loading.py11
-rw-r--r--odoo/modules/migration.py12
-rw-r--r--odoo/modules/module.py13
-rw-r--r--odoo/modules/registry.py19
-rw-r--r--odoo/osv/expression.py4
-rw-r--r--odoo/osv/orm.py5
-rw-r--r--odoo/release.py4
-rw-r--r--odoo/service/db.py2
-rw-r--r--odoo/service/model.py2
-rw-r--r--odoo/service/server.py16
-rw-r--r--odoo/sql_db.py10
-rw-r--r--odoo/tests/common.py2
-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
74 files changed, 521 insertions, 445 deletions
diff --git a/odoo/addons/base/ir/ir_actions.py b/odoo/addons/base/ir/ir_actions.py
index 918e5d90880..4979c898e08 100644
--- a/odoo/addons/base/ir/ir_actions.py
+++ b/odoo/addons/base/ir/ir_actions.py
@@ -5,6 +5,7 @@ import odoo
from odoo import api, fields, models, tools, SUPERUSER_ID, _
from odoo.exceptions import MissingError, UserError, ValidationError, AccessError
from odoo.tools.safe_eval import safe_eval, test_python_expr
+from odoo.tools import pycompat
from odoo.http import request
import datetime
@@ -649,17 +650,19 @@ class IrActionsTodo(models.Model):
"""
user_groups = self.env.user.groups_id
- def groups_match(todo):
- """ Checks if the todo's groups match those of the current user """
- return not todo.groups_id or bool(todo.groups_id & user_groups)
-
- done = filter(groups_match, self.browse(self.search([('state', '!=', 'open')])))
- total = filter(groups_match, self.browse(self.search([])))
-
+ done_count = self.search_count([
+ ('state', '!=', open),
+ '|', ('groups_id', '=', False),
+ ('groups_id', 'in', user_groups.ids),
+ ])
+ total_count = self.search_count([
+ '|', ('groups_id', '=', False),
+ ('groups_id', 'in', user_groups.ids),
+ ])
return {
- 'done': len(done),
- 'total': len(total),
- 'todo': len(total) - len(done),
+ 'done': done_count,
+ 'total': total_count,
+ 'todo': total_count - done_count,
}
@@ -688,7 +691,7 @@ class IrActionsActClient(models.Model):
@api.depends('params_store')
def _compute_params(self):
self_bin = self.with_context(bin_size=False, bin_size_params_store=False)
- for record, record_bin in zip(self, self_bin):
+ for record, record_bin in pycompat.izip(self, self_bin):
record.params = record_bin.params_store and safe_eval(record_bin.params_store, {'uid': self._uid})
def _inverse_params(self):
diff --git a/odoo/addons/base/ir/ir_attachment.py b/odoo/addons/base/ir/ir_attachment.py
index b6bd9a08ee7..9d19d076438 100644
--- a/odoo/addons/base/ir/ir_attachment.py
+++ b/odoo/addons/base/ir/ir_attachment.py
@@ -11,7 +11,7 @@ from collections import defaultdict
from odoo import api, fields, models, tools, SUPERUSER_ID, _
from odoo.exceptions import AccessError
-from odoo.tools import config, human_size, ustr, html_escape
+from odoo.tools import config, human_size, ustr, html_escape, pycompat
from odoo.tools.mimetypes import guess_mimetype
_logger = logging.getLogger(__name__)
@@ -167,7 +167,7 @@ class IrAttachment(models.Model):
# remove garbage files, and clean up checklist
removed = 0
- for fname, filepath in checklist.iteritems():
+ for fname, filepath in pycompat.items(checklist):
if fname not in whitelist:
try:
os.unlink(self._full_path(fname))
@@ -318,7 +318,7 @@ class IrAttachment(models.Model):
model_ids[values['res_model']].add(values['res_id'])
# check access rights on the records
- for res_model, res_ids in model_ids.iteritems():
+ for res_model, res_ids in pycompat.items(model_ids):
# ignore attachments that are not attached to a resource anymore
# when checking access rights (resource was deleted but attachment
# was not)
@@ -374,12 +374,12 @@ class IrAttachment(models.Model):
# To avoid multiple queries for each attachment found, checks are
# performed in batch as much as possible.
- for res_model, targets in model_attachments.iteritems():
+ for res_model, targets in pycompat.items(model_attachments):
if res_model not in self.env:
continue
if not self.env[res_model].check_access_rights('read', False):
# remove all corresponding attachment ids
- ids.difference_update(itertools.chain(*targets.itervalues()))
+ ids.difference_update(itertools.chain(*pycompat.values(targets)))
continue
# filter ids according to what access rules permit
target_ids = list(targets)
diff --git a/odoo/addons/base/ir/ir_config_parameter.py b/odoo/addons/base/ir/ir_config_parameter.py
index cbb44ca6457..8e4ea20b5a5 100644
--- a/odoo/addons/base/ir/ir_config_parameter.py
+++ b/odoo/addons/base/ir/ir_config_parameter.py
@@ -8,7 +8,7 @@ import uuid
import logging
from odoo import api, fields, models
-from odoo.tools import config, ormcache, mute_logger
+from odoo.tools import config, ormcache, mute_logger, pycompat
_logger = logging.getLogger(__name__)
@@ -42,7 +42,7 @@ class IrConfigParameter(models.Model):
Initializes the parameters listed in _default_parameters.
It overrides existing parameters if force is ``True``.
"""
- for key, func in _default_parameters.iteritems():
+ for key, func in pycompat.items(_default_parameters):
# force=True skips search and always performs the 'if' body (because ids=False)
params = self.sudo().search([('key', '=', key)])
if force or not params:
diff --git a/odoo/addons/base/ir/ir_fields.py b/odoo/addons/base/ir/ir_fields.py
index 26c351d6fa8..204d4599425 100644
--- a/odoo/addons/base/ir/ir_fields.py
+++ b/odoo/addons/base/ir/ir_fields.py
@@ -8,13 +8,13 @@ import psycopg2
import pytz
from odoo import api, fields, models, _
-from odoo.tools import ustr
+from odoo.tools import ustr, pycompat
REFERENCING_FIELDS = {None, 'id', '.id'}
def only_ref_fields(record):
- return {k: v for k, v in record.iteritems() if k in REFERENCING_FIELDS}
+ return {k: v for k, v in pycompat.items(record) if k in REFERENCING_FIELDS}
def exclude_ref_fields(record):
- return {k: v for k, v in record.iteritems() if k not in REFERENCING_FIELDS}
+ return {k: v for k, v in pycompat.items(record) if k not in REFERENCING_FIELDS}
CREATE = lambda values: (0, False, values)
UPDATE = lambda id, values: (1, id, values)
@@ -43,9 +43,9 @@ class IrFieldsConverter(models.AbstractModel):
if isinstance(error_params, basestring):
error_params = sanitize(error_params)
elif isinstance(error_params, dict):
- error_params = {k: sanitize(v) for k, v in error_params.iteritems()}
+ error_params = {k: sanitize(v) for k, v in pycompat.items(error_params)}
elif isinstance(error_params, tuple):
- error_params = tuple(map(sanitize, error_params))
+ error_params = tuple(sanitize(v) for v in error_params)
return error_type(error_msg % error_params, error_args)
@api.model
@@ -64,12 +64,12 @@ class IrFieldsConverter(models.AbstractModel):
converters = {
name: self.to_field(model, field, fromtype)
- for name, field in model._fields.iteritems()
+ for name, field in pycompat.items(model._fields)
}
def fn(record, log):
converted = {}
- for field, value in record.iteritems():
+ for field, value in pycompat.items(record):
if field in REFERENCING_FIELDS:
continue
if not value:
@@ -369,7 +369,7 @@ class IrFieldsConverter(models.AbstractModel):
:rtype: str, list
"""
# Can import by name_get, external id or database id
- fieldset = set(record.iterkeys())
+ fieldset = set(record)
if fieldset - REFERENCING_FIELDS:
raise ValueError(
_(u"Can not create Many-To-One records indirectly, import the field separately"))
diff --git a/odoo/addons/base/ir/ir_http.py b/odoo/addons/base/ir/ir_http.py
index 3d9c72aec60..aa23531055e 100644
--- a/odoo/addons/base/ir/ir_http.py
+++ b/odoo/addons/base/ir/ir_http.py
@@ -21,6 +21,7 @@ import odoo
from odoo import api, http, models, tools, SUPERUSER_ID
from odoo.exceptions import AccessDenied, AccessError
from odoo.http import request, STATIC_CACHE, content_disposition
+from odoo.tools import pycompat
from odoo.tools.mimetypes import guess_mimetype
from odoo.modules.module import get_resource_path, get_module_path
@@ -54,7 +55,7 @@ class ModelsConverter(werkzeug.routing.BaseConverter):
def to_python(self, value):
env = api.Environment(request.cr, UID_PLACEHOLDER, request.context)
- return env[self.model].browse(map(int, value.split(',')))
+ return env[self.model].browse(int(v) for v in value.split(','))
def to_url(self, value):
return ",".join(value.ids)
@@ -202,7 +203,7 @@ class IrHttp(models.AbstractModel):
@classmethod
def _postprocess_args(cls, arguments, rule):
""" post process arg to set uid on browse records """
- for name, arg in arguments.items():
+ for name, arg in list(pycompat.items(arguments)):
if isinstance(arg, models.BaseModel) and arg._uid is UID_PLACEHOLDER:
arguments[name] = arg.sudo(request.uid)
if not arg.exists():
diff --git a/odoo/addons/base/ir/ir_mail_server.py b/odoo/addons/base/ir/ir_mail_server.py
index 22f78da6c18..737e9806d68 100644
--- a/odoo/addons/base/ir/ir_mail_server.py
+++ b/odoo/addons/base/ir/ir_mail_server.py
@@ -13,9 +13,11 @@ import re
import smtplib
import threading
+import itertools
+
from odoo import api, fields, models, tools, _
from odoo.exceptions import except_orm, UserError
-from odoo.tools import html2text, ustr
+from odoo.tools import html2text, ustr, pycompat
_logger = logging.getLogger(__name__)
_test_logger = logging.getLogger('odoo.tests')
@@ -110,7 +112,7 @@ def extract_rfc2822_addresses(text):
if not text:
return []
candidates = address_pattern.findall(ustr(text).encode('utf-8'))
- return filter(try_coerce_ascii, candidates)
+ return [c for c in candidates if try_coerce_ascii(c)]
def encode_rfc2822_address_header(header_text):
@@ -127,7 +129,7 @@ def encode_rfc2822_address_header(header_text):
return formataddr((name, email))
addresses = getaddresses([ustr(header_text).encode('utf-8')])
- return COMMASPACE.join(map(encode_addr, addresses))
+ return COMMASPACE.join(encode_addr(a) for a in addresses)
class IrMailServer(models.Model):
@@ -333,7 +335,7 @@ class IrMailServer(models.Model):
msg['Bcc'] = encode_rfc2822_address_header(COMMASPACE.join(email_bcc))
msg['Date'] = formatdate()
# Custom headers may override normal headers or provide additional ones
- for key, value in headers.iteritems():
+ for key, value in pycompat.items(headers):
msg[ustr(key).encode('utf-8')] = encode_header(value)
if subtype == 'html' and not body_alternative and html2text:
@@ -442,7 +444,12 @@ class IrMailServer(models.Model):
email_cc = message['Cc']
email_bcc = message['Bcc']
- smtp_to_list = filter(None, tools.flatten(map(extract_rfc2822_addresses, [email_to, email_cc, email_bcc])))
+ smtp_to_list = [
+ address
+ for base in [email_to, email_cc, email_bcc]
+ for address in extract_rfc2822_addresses(base)
+ if address
+ ]
assert smtp_to_list, self.NO_VALID_RECIPIENT
x_forge_to = message['X-Forge-To']
diff --git a/odoo/addons/base/ir/ir_model.py b/odoo/addons/base/ir/ir_model.py
index ad72694d410..9272a790fbc 100644
--- a/odoo/addons/base/ir/ir_model.py
+++ b/odoo/addons/base/ir/ir_model.py
@@ -45,7 +45,7 @@ def query_insert(cr, table, values):
query = INSERT_QUERY.format(
table=table,
cols=",".join(values),
- vals=",".join(map("%({0})s".format, values)),
+ vals=",".join("%({0})s".format(v) for v in values),
)
cr.execute(query, values)
@@ -53,8 +53,8 @@ def query_update(cr, table, values, selectors):
setters = set(values) - set(selectors)
query = UPDATE_QUERY.format(
table=table,
- assignment=",".join(map("{0}=%({0})s".format, setters)),
- condition=" AND ".join(map("{0}=%({0})s".format, selectors)),
+ assignment=",".join("{0}=%({0})s".format(s) for s in setters),
+ condition=" AND ".join("{0}=%({0})s".format(s) for s in selectors),
)
cr.execute(query, values)
@@ -188,7 +188,7 @@ class IrModel(models.Model):
@api.multi
def write(self, vals):
if '__last_update' in self._context:
- self = self.with_context({k: v for k, v in self._context.iteritems() if k != '__last_update'})
+ self = self.with_context({k: v for k, v in pycompat.items(self._context) if k != '__last_update'})
if 'model' in vals and any(rec.model != vals['model'] for rec in self):
raise UserError(_('Field "Model" cannot be modified on models.'))
if 'state' in vals and any(rec.state != vals['state'] for rec in self):
@@ -559,8 +559,8 @@ class IrModelFields(models.Model):
except Exception:
raise UserError("\n".join([
_("Cannot rename/delete fields that are still present in views:"),
- _("Fields:") + " " + ", ".join(map(str, fields)),
- _("View:") + " " + view.name,
+ _("Fields: %s") % ", ".join(str(f) for f in fields),
+ _("View: %s") % view.name,
]))
finally:
# the registry has been modified, restore it
@@ -760,7 +760,7 @@ class IrModelFields(models.Model):
fields_data[field.name] = dict(params, id=record.id)
return record
- diff = {key for key, val in params.items() if field_data[key] != val}
+ diff = {key for key, val in pycompat.items(params) if field_data[key] != val}
if diff:
cr = self.env.cr
# update the entry in this table
@@ -778,7 +778,7 @@ class IrModelFields(models.Model):
def _reflect_model(self, model):
""" Reflect the given model's fields. """
self.clear_caches()
- for field in model._fields.itervalues():
+ for field in pycompat.values(model._fields):
self._reflect_field(field)
if not self.pool._init:
@@ -1228,7 +1228,7 @@ class IrModelData(models.Model):
@api.depends('module', 'name')
def _compute_complete_name(self):
for res in self:
- res.complete_name = ".".join(filter(None, [res.module, res.name]))
+ res.complete_name = ".".join(n for n in [res.module, res.name] if n)
@api.depends('model', 'res_id')
def _compute_reference(self):
@@ -1259,7 +1259,7 @@ class IrModelData(models.Model):
model_id_name[xid.model][xid.res_id] = None
# fill in model_id_name with name_get() of corresponding records
- for model, id_name in model_id_name.iteritems():
+ for model, id_name in pycompat.items(model_id_name):
try:
ng = self.env[model].browse(id_name).name_get()
id_name.update(ng)
@@ -1356,7 +1356,7 @@ class IrModelData(models.Model):
record = self.get_object(module, xml_id)
if record:
self.loads[(module, xml_id)] = (model, record.id)
- for parent_model, parent_field in self.env[model]._inherits.iteritems():
+ for parent_model, parent_field in pycompat.items(self.env[model]._inherits):
parent = record[parent_field]
parent_xid = '%s_%s' % (xml_id, parent_model.replace('.', '_'))
self.loads[(module, parent_xid)] = (parent_model, parent.id)
@@ -1411,7 +1411,7 @@ class IrModelData(models.Model):
elif record:
record.write(values)
if xml_id:
- for parent_model, parent_field in record._inherits.iteritems():
+ for parent_model, parent_field in pycompat.items(record._inherits):
self.sudo().create({
'name': xml_id + '_' + parent_model.replace('.', '_'),
'model': parent_model,
@@ -1430,7 +1430,7 @@ class IrModelData(models.Model):
elif mode == 'init' or (mode == 'update' and xml_id):
existing_parents = set() # {parent_model, ...}
if xml_id:
- for parent_model, parent_field in record._inherits.iteritems():
+ for parent_model, parent_field in pycompat.items(record._inherits):
xid = self.sudo().search([
('module', '=', module),
('name', '=', xml_id + '_' + parent_model.replace('.', '_')),
@@ -1450,7 +1450,7 @@ class IrModelData(models.Model):
inherit_models = [record]
while inherit_models:
current_model = inherit_models.pop()
- for parent_model_name, parent_field in current_model._inherits.iteritems():
+ for parent_model_name, parent_field in pycompat.items(current_model._inherits):
inherit_models.append(self.env[parent_model_name])
if parent_model_name in existing_parents:
continue
@@ -1476,7 +1476,7 @@ class IrModelData(models.Model):
if xml_id and record:
self.loads[(module, xml_id)] = (model, record.id)
- for parent_model, parent_field in record._inherits.iteritems():
+ for parent_model, parent_field in pycompat.items(record._inherits):
parent_xml_id = xml_id + '_' + parent_model.replace('.', '_')
self.loads[(module, parent_xml_id)] = (parent_model, record[parent_field].id)
diff --git a/odoo/addons/base/ir/ir_qweb/assetsbundle.py b/odoo/addons/base/ir/ir_qweb/assetsbundle.py
index 5bc2dec8a9b..19902e78d1b 100644
--- a/odoo/addons/base/ir/ir_qweb/assetsbundle.py
+++ b/odoo/addons/base/ir/ir_qweb/assetsbundle.py
@@ -13,7 +13,7 @@ from odoo.http import request
from odoo.modules.module import get_resource_path
import psycopg2
import werkzeug
-from odoo.tools import func, misc
+from odoo.tools import func, misc, pycompat
import logging
_logger = logging.getLogger(__name__)
@@ -308,7 +308,7 @@ class AssetsBundle(object):
outdated = False
assets = dict((asset.html_url, asset) for asset in self.stylesheets if isinstance(asset, atype))
if assets:
- assets_domain = [('url', 'in', assets.keys())]
+ assets_domain = [('url', 'in', list(assets))]
attachments = self.env['ir.attachment'].sudo().search(assets_domain)
for attachment in attachments:
asset = assets[attachment.url]
@@ -320,7 +320,7 @@ class AssetsBundle(object):
if not asset._content and attachment.file_size > 0:
asset._content = None # file missing, force recompile
- if any(asset._content is None for asset in assets.itervalues()):
+ if any(asset._content is None for asset in pycompat.values(assets)):
outdated = True
if outdated:
@@ -452,7 +452,7 @@ class WebAsset(object):
def stat(self):
if not (self.inline or self._filename or self._ir_attach):
- path = filter(None, self.url.split('/'))
+ path = (segment for segment in self.url.split('/') if segment)
self._filename = get_resource_path(*path)
if self._filename:
return
diff --git a/odoo/addons/base/ir/ir_qweb/ir_qweb.py b/odoo/addons/base/ir/ir_qweb/ir_qweb.py
index 68c4bf048d7..5548fcd47d8 100644
--- a/odoo/addons/base/ir/ir_qweb/ir_qweb.py
+++ b/odoo/addons/base/ir/ir_qweb/ir_qweb.py
@@ -75,7 +75,7 @@ class IrQWeb(models.AbstractModel, QWeb):
# apply ormcache_context decorator unless in dev mode...
@tools.conditional(
'xml' not in tools.config['dev_mode'],
- tools.ormcache('id_or_xml_id', 'tuple(map(options.get, self._get_template_cache_keys()))'),
+ tools.ormcache('id_or_xml_id', 'tuple(options.get(k) for k in self._get_template_cache_keys())'),
)
def compile(self, id_or_xml_id, options):
return super(IrQWeb, self).compile(id_or_xml_id, options=options)
@@ -173,7 +173,7 @@ class IrQWeb(models.AbstractModel, QWeb):
if field_options and 'monetary' in field_options:
try:
options = "{'widget': 'monetary'"
- for k, v in json.loads(field_options).iteritems():
+ for k, v in pycompat.items(json.loads(field_options)):
if k in ('display_currency', 'from_currency'):
options = "%s, '%s': %s" % (options, k, v)
else:
@@ -238,12 +238,12 @@ class IrQWeb(models.AbstractModel, QWeb):
atype = 'text/less'
if atype not in ('text/less', 'text/sass'):
atype = 'text/css'
- path = filter(None, href.split('/'))
+ path = [segment for segment in href.split('/') if segment]
filename = get_resource_path(*path) if path else None
files.append({'atype': atype, 'url': href, 'filename': filename, 'content': el.text, 'media': media})
elif el.tag == 'script':
atype = 'text/javascript'
- path = filter(None, src.split('/'))
+ path = [segment for segment in src.split('/') if segment]
filename = get_resource_path(*path) if path else None
files.append({'atype': atype, 'url': src, 'filename': filename, 'content': el.text, 'media': media})
else:
diff --git a/odoo/addons/base/ir/ir_qweb/qweb.py b/odoo/addons/base/ir/ir_qweb/qweb.py
index 14a0a54fbd9..06aba8ccdb4 100644
--- a/odoo/addons/base/ir/ir_qweb/qweb.py
+++ b/odoo/addons/base/ir/ir_qweb/qweb.py
@@ -6,14 +6,15 @@ import traceback
from collections import OrderedDict, Sized, Mapping, defaultdict
from functools import reduce
-from itertools import chain, izip, tee, count
+from itertools import tee, count
from textwrap import dedent
+import itertools
from lxml import etree, html
import werkzeug
from werkzeug.utils import escape as _escape
-from odoo.tools import pycompat
+from odoo.tools import pycompat, freehash
try:
import builtins
@@ -82,7 +83,7 @@ class Contextifier(ast.NodeTransformer):
return ast.copy_location(ast.Lambda(
args=ast.arguments(
args=args.args,
- defaults=map(self.visit, args.defaults),
+ defaults=[self.visit(default) for default in args.defaults],
vararg=args.vararg,
kwarg=args.kwarg,
),
@@ -109,7 +110,7 @@ class Contextifier(ast.NodeTransformer):
for field, value in ast.iter_fields(node):
# map transformation of comprehensions
if isinstance(value, list):
- setattr(newnode, field, map(transformer.visit, value))
+ setattr(newnode, field, [transformer.visit(v) for v in value])
else: # set transformation of key/value/expr fields
setattr(newnode, field, transformer.visit(value))
return newnode
@@ -168,14 +169,14 @@ def foreach_iterator(base_ctx, enum, name):
if not enum:
return
if isinstance(enum, int):
- enum = pycompat.range(enum)
+ enum = range(enum)
size = None
if isinstance(enum, Sized):
ctx["%s_size" % name] = size = len(enum)
if isinstance(enum, Mapping):
- enum = enum.iteritems()
+ enum = pycompat.items(enum)
else:
- enum = izip(*tee(enum))
+ enum = pycompat.izip(*tee(enum))
value_key = '%s_value' % name
index_key = '%s_index' % name
first_key = '%s_first' % name
@@ -201,7 +202,7 @@ def foreach_iterator(base_ctx, enum, name):
yield ctx
# copy changed items back into source context (?)
# FIXME: maybe values could provide a ChainMap-style clone?
- for k in base_ctx.keys():
+ for k in list(base_ctx):
base_ctx[k] = ctx[k]
_FORMAT_REGEX = re.compile(
@@ -226,7 +227,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)))
####################################
@@ -510,6 +511,7 @@ class QWeb(object):
"""
return ast.parse(dedent("""
from collections import OrderedDict
+ from odoo.tools import pycompat
from odoo.addons.base.ir.ir_qweb.qweb import escape, unicodifier, foreach_iterator
"""))
@@ -665,7 +667,7 @@ class QWeb(object):
# all directives have been compiled, there should be none left
if any(att.startswith('t-') for att in el.attrib):
- raise "Unknown directive on %s" % ("', '".join(directives), etree.tostring(el))
+ raise NameError("Unknown directive on %s" % etree.tostring(el))
return []
def _values_var(self, varname, ctx):
@@ -726,7 +728,7 @@ class QWeb(object):
attrib = {}
# If `el` introduced new namespaces, write them as attribute by using the
# `attrib` dict.
- for ns_prefix, ns_definition in set(el.nsmap.items()) - set(options['nsmap'].items()):
+ for ns_prefix, ns_definition in set(pycompat.items(el.nsmap)) - set(pycompat.items(options['nsmap'])):
if ns_prefix is None:
attrib['xmlns'] = ns_definition
else:
@@ -735,8 +737,9 @@ class QWeb(object):
# Etree will also remove the ns prefixes indirection in the attributes. As we only have
# the namespace definition, we'll use an nsmap where the keys are the definitions and
# the values the prefixes in order to get back the right prefix and restore it.
- nsprefixmap = {v: k for k, v in options['nsmap'].items() + el.nsmap.items()}
- for key, value in el.attrib.items():
+ ns = itertools.chain(pycompat.items(options['nsmap']), pycompat.items(el.nsmap))
+ nsprefixmap = {v: k for k, v in ns}
+ for key, value in pycompat.items(el.attrib):
attrib_qname = etree.QName(key)
if attrib_qname.namespace:
attrib['%s:%s' % (nsprefixmap[attrib_qname.namespace], attrib_qname.localname)] = value
@@ -754,7 +757,7 @@ class QWeb(object):
if unqualified_el_tag == 't':
return content
- tag = u'<%s%s' % (el_tag, u''.join([u' %s="%s"' % (name, escape(unicodifier(value))) for name, value in attrib.iteritems()]))
+ tag = u'<%s%s' % (el_tag, u''.join([u' %s="%s"' % (name, escape(unicodifier(value))) for name, value in pycompat.items(attrib)]))
if unqualified_el_tag in self._void_elements:
return [self._append(ast.Str(tag + '/>'))] + content
else:
@@ -766,10 +769,10 @@ class QWeb(object):
# Etree will also remove the ns prefixes indirection in the attributes. As we only have
# the namespace definition, we'll use an nsmap where the keys are the definitions and
# the values the prefixes in order to get back the right prefix and restore it.
- nsprefixmap = {v: k for k, v in options['nsmap'].items() + el.nsmap.items()}
+ nsprefixmap = {v: k for k, v in itertools.chain(pycompat.items(options['nsmap']), pycompat.items(el.nsmap))}
nodes = []
- for key, value in el.attrib.iteritems():
+ for key, value in pycompat.items(el.attrib):
if not key.startswith('t-'):
attrib_qname = etree.QName(key)
if attrib_qname.namespace:
@@ -784,7 +787,7 @@ class QWeb(object):
We do not support namespaced dynamic attributes.
"""
nodes = []
- for name, value in el.attrib.iteritems():
+ for name, value in pycompat.items(el.attrib):
if name.startswith('t-attf-'):
nodes.append((name[7:], self._compile_format(value)))
elif name.startswith('t-att-'):
@@ -810,7 +813,7 @@ class QWeb(object):
def _compile_all_attributes(self, el, options, attr_already_created=False):
""" Compile the attributes of the given elements into a list of AST nodes. """
body = []
- if any(name.startswith('t-att') or not name.startswith('t-') for name, value in el.attrib.iteritems()):
+ if any(name.startswith('t-att') or not name.startswith('t-') for name, value in pycompat.items(el.attrib)):
if not attr_already_created:
attr_already_created = True
body.append(
@@ -851,7 +854,7 @@ class QWeb(object):
)))
if attr_already_created:
- # for name, value in t_attrs.iteritems():
+ # for name, value in pycompat.items(t_attrs):
# if value or isinstance(value, basestring)):
# append(u' ')
# append(name)
@@ -862,11 +865,11 @@ class QWeb(object):
target=ast.Tuple(elts=[ast.Name(id='name', ctx=ast.Store()), ast.Name(id='value', ctx=ast.Store())], ctx=ast.Store()),
iter=ast.Call(
func=ast.Attribute(
- value=ast.Name(id='t_attrs', ctx=ast.Load()),
- attr='iteritems',
+ value=ast.Name(id='pycompat', ctx=ast.Load()),
+ attr='items',
ctx=ast.Load()
),
- args=[], keywords=[],
+ args=[ast.Name(id='t_attrs', ctx=ast.Load())], keywords=[],
starargs=None, kwargs=None
),
body=[ast.If(
@@ -923,7 +926,7 @@ class QWeb(object):
# If `el` introduced new namespaces, write them as attribute by using the
# `extra_attrib` dict.
- for ns_prefix, ns_definition in set(el.nsmap.items()) - set(options['nsmap'].items()):
+ for ns_prefix, ns_definition in set(pycompat.items(el.nsmap)) - set(pycompat.items(options['nsmap'])):
if ns_prefix is None:
extra_attrib['xmlns'] = ns_definition
else:
@@ -932,7 +935,7 @@ class QWeb(object):
if unqualified_el_tag == 't':
return content
- body = [self._append(ast.Str(u'<%s%s' % (el_tag, u''.join([u' %s="%s"' % (name, escape(unicodifier(value))) for name, value in extra_attrib.iteritems()]))))]
+ body = [self._append(ast.Str(u'<%s%s' % (el_tag, u''.join([u' %s="%s"' % (name, escape(unicodifier(value))) for name, value in pycompat.items(extra_attrib)]))))]
body.extend(self._compile_all_attributes(el, options, attr_already_created))
if unqualified_el_tag in self._void_elements:
body.append(self._append(ast.Str(u'/>')))
@@ -1054,7 +1057,7 @@ class QWeb(object):
def _compile_directive_if(self, el, options):
orelse = []
next_el = el.getnext()
- if next_el is not None and {'t-else', 't-elif'} & set(next_el.attrib.keys()):
+ if next_el is not None and {'t-else', 't-elif'} & set(next_el.attrib):
if el.tail and not el.tail.isspace():
raise ValueError("Unexpected non-whitespace characters between t-if and t-else directives")
el.tail = None
@@ -1437,7 +1440,7 @@ class QWeb(object):
# make the nsmap an ast dict
keys = []
values = []
- for key, value in options['nsmap'].items():
+ for key, value in pycompat.items(options['nsmap']):
if isinstance(key, basestring):
keys.append(ast.Str(s=key))
elif key is None:
diff --git a/odoo/addons/base/ir/ir_sequence.py b/odoo/addons/base/ir/ir_sequence.py
index a46f5e95eca..58961e921ba 100644
--- a/odoo/addons/base/ir/ir_sequence.py
+++ b/odoo/addons/base/ir/ir_sequence.py
@@ -6,6 +6,7 @@ import pytz
from odoo import api, fields, models, _
from odoo.exceptions import UserError
+from odoo.tools import pycompat
_logger = logging.getLogger(__name__)
@@ -178,7 +179,7 @@ class IrSequence(models.Model):
'weekday': '%w', 'h24': '%H', 'h12': '%I', 'min': '%M', 'sec': '%S'
}
res = {}
- for key, format in sequences.iteritems():
+ for key, format in pycompat.items(sequences):
res[key] = effective_date.strftime(format)
res['range_' + key] = range_date.strftime(format)
res['current_' + key] = now.strftime(format)
diff --git a/odoo/addons/base/ir/ir_translation.py b/odoo/addons/base/ir/ir_translation.py
index 7d73f40d65b..219ebcc471d 100644
--- a/odoo/addons/base/ir/ir_translation.py
+++ b/odoo/addons/base/ir/ir_translation.py
@@ -128,7 +128,7 @@ class IrTranslationImport(object):
env = api.Environment(cr, SUPERUSER_ID, {})
src_relevant_fields = []
for model in env:
- for field_name, field in env[model]._fields.items():
+ for field_name, field in pycompat.items(env[model]._fields):
if hasattr(field, 'translate') and callable(field.translate):
src_relevant_fields.append("%s,%s" % (model, field_name))
@@ -530,7 +530,7 @@ class IrTranslation(models.Model):
# check for read/write access on translated field records
fmode = 'read' if mode == 'read' else 'write'
- for mname, ids in model_ids.iteritems():
+ for mname, ids in pycompat.items(model_ids):
records = self.env[mname].browse(ids)
records.check_access_rights(fmode)
records.check_field_access_rights(fmode, model_fields[mname])
@@ -640,7 +640,7 @@ class IrTranslation(models.Model):
return ['&', ('res_id', '=', rec.id), ('name', '=', name)]
# insert missing translations, and extend domain for related fields
- for name, fld in record._fields.items():
+ for name, fld in pycompat.items(record._fields):
if not fld.translate:
continue
diff --git a/odoo/addons/base/ir/ir_ui_view.py b/odoo/addons/base/ir/ir_ui_view.py
index c557d15ee6b..0a709985ff6 100644
--- a/odoo/addons/base/ir/ir_ui_view.py
+++ b/odoo/addons/base/ir/ir_ui_view.py
@@ -8,6 +8,8 @@ import logging
import os
import re
import time
+
+import itertools
from dateutil.relativedelta import relativedelta
from operator import itemgetter
@@ -49,7 +51,7 @@ def keep_query(*keep_params, **additional_params):
if not keep_params and not additional_params:
keep_params = ('*',)
params = additional_params.copy()
- qs_keys = request.httprequest.args.keys()
+ qs_keys = list(request.httprequest.args)
for keep_param in keep_params:
for param in fnmatch.filter(qs_keys, keep_param):
if param not in additional_params and param in qs_keys:
@@ -251,11 +253,11 @@ actual arch.
@api.depends('arch')
def _compute_arch_base(self):
# 'arch_base' is the same as 'arch' without translation
- for view, view_wo_lang in zip(self, self.with_context(lang=None)):
+ for view, view_wo_lang in pycompat.izip(self, self.with_context(lang=None)):
view.arch_base = view_wo_lang.arch
def _inverse_arch_base(self):
- for view, view_wo_lang in zip(self, self.with_context(lang=None)):
+ for view, view_wo_lang in pycompat.izip(self, self.with_context(lang=None)):
view_wo_lang.arch = view.arch_base
@api.depends('write_date')
@@ -458,7 +460,7 @@ actual arch.
# not required. The root cause is the INNER JOIN
# used to implement it.
views = self.search(conditions + [('model_ids.module', 'in', tuple(self.pool._init_modules))])
- views = self.search(conditions + [('id', 'in', list(self._context.get('check_view_ids') or (0,)) + map(int, views))])
+ views = self.search(conditions + [('id', 'in', list(self._context.get('check_view_ids') or (0,)) + views.ids)])
else:
views = self.search(conditions)
@@ -581,10 +583,16 @@ actual arch.
separator = child.get('separator', ',')
if separator == ' ':
separator = None # squash spaces
- to_add = filter(bool, map(str.strip, child.get('add', '').split(separator)))
- to_remove = map(str.strip, child.get('remove', '').split(separator))
- values = map(str.strip, node.get(attribute, '').split(separator))
- value = (separator or ' ').join(filter(lambda s: s not in to_remove, values) + to_add)
+ to_add = (
+ s for s in (s.strip() for s in child.get('add', '').split(separator))
+ if s
+ )
+ to_remove = {s.strip() for s in child.get('remove', '').split(separator)}
+ values = (s.strip() for s in node.get(attribute, '').split(separator))
+ value = (separator or ' ').join(itertools.chain(
+ (v for v in values if v not in to_remove),
+ to_add
+ ))
if value:
node.set(attribute, value)
elif attribute in node.attrib:
@@ -834,7 +842,7 @@ actual arch.
collect(arch, self.env[model_name])
- for field, nodes in field_nodes.iteritems():
+ for field, nodes in pycompat.items(field_nodes):
# if field should trigger an onchange, add on_change="1" on the
# nodes referring to field
model = self.env[field.model_name]
@@ -900,7 +908,7 @@ actual arch.
node.set(action, 'false')
arch = etree.tostring(node, encoding="utf-8").replace('\t', '')
- for k in fields.keys():
+ for k in list(fields):
if k not in fields_def:
del fields[k]
for field in fields_def:
@@ -924,7 +932,7 @@ actual arch.
@tools.conditional(
'xml' not in config['dev_mode'],
tools.ormcache('frozenset(self.env.user.groups_id.ids)', 'view_id',
- 'tuple(map(self._context.get, self._read_template_keys()))'),
+ 'tuple(self._context.get(k) for k in self._read_template_keys())'),
)
def _read_template(self, view_id):
arch = self.browse(view_id).read_combined(['arch'])['arch']
@@ -1091,12 +1099,12 @@ actual arch.
Model = self.env[model]
Node = self.env[node_obj]
- for model_key, model_value in Model._fields.iteritems():
+ for model_key, model_value in pycompat.items(Model._fields):
if model_value.type == 'one2many':
if model_value.comodel_name == node_obj:
_Node_Field = model_key
_Model_Field = model_value.inverse_name
- for node_key, node_value in Node._fields.iteritems():
+ for node_key, node_value in pycompat.items(Node._fields):
if node_value.type == 'one2many':
if node_value.comodel_name == conn_obj:
# _Source_Field = "Incoming Arrows" (connected via des_node)
@@ -1159,7 +1167,7 @@ actual arch.
GROUP BY coalesce(v.inherit_id, v.id)"""
self._cr.execute(query, [model])
- rec = self.browse(map(itemgetter(0), self._cr.fetchall()))
+ rec = self.browse(it[0] for it in self._cr.fetchall())
return rec.with_context({'load_all_views': True})._check_xml()
@api.model
@@ -1173,7 +1181,7 @@ actual arch.
xmlid_filter = "AND md.name IN %s"
names = tuple(
name
- for (xmod, name), (model, res_id) in self.pool.model_data_reference_ids.items()
+ for (xmod, name), (model, res_id) in pycompat.items(self.pool.model_data_reference_ids)
if xmod == module and model == self._name
)
if not names:
diff --git a/odoo/addons/base/ir/ir_values.py b/odoo/addons/base/ir/ir_values.py
index 3672d7811c2..ace2998b2b3 100644
--- a/odoo/addons/base/ir/ir_values.py
+++ b/odoo/addons/base/ir/ir_values.py
@@ -5,7 +5,7 @@ from ast import literal_eval
from odoo import api, fields, models, tools, _
from odoo.exceptions import AccessError, MissingError, ValidationError
-from odoo.tools import pickle
+from odoo.tools import pickle, pycompat
import logging
_logger = logging.getLogger(__name__)
@@ -315,7 +315,7 @@ class IrValues(models.Model):
for row in self._cr.dictfetchall():
value = pickle.loads(row['value'].encode('utf-8'))
defaults.setdefault(row['name'], (row['id'], row['name'], value))
- return defaults.values()
+ return list(pycompat.values(defaults))
# use ormcache: this is called a lot by BaseModel.default_get()!
@api.model
@@ -426,4 +426,4 @@ class IrValues(models.Model):
results[name] = (id, name, action_def)
except (AccessError, MissingError):
continue
- return sorted(results.values())
+ return sorted(pycompat.values(results))
diff --git a/odoo/addons/base/module/module.py b/odoo/addons/base/module/module.py
index ae36ccd1727..c8a83a01bf1 100644
--- a/odoo/addons/base/module/module.py
+++ b/odoo/addons/base/module/module.py
@@ -10,6 +10,8 @@ import shutil
import tempfile
import zipfile
+from odoo.tools import pycompat
+
try:
from urllib import parse as urlparse
from urllib.request import urlopen
@@ -209,9 +211,9 @@ class Module(models.Model):
def format_view(v):
return '%s%s (%s)' % (v.inherit_id and '* INHERIT ' or '', v.name, v.type)
- module.views_by_module = "\n".join(sorted(map(format_view, browse('ir.ui.view'))))
- module.reports_by_module = "\n".join(sorted(map(attrgetter('name'), browse('ir.actions.report'))))
- module.menus_by_module = "\n".join(sorted(map(attrgetter('complete_name'), browse('ir.ui.menu'))))
+ module.views_by_module = "\n".join(sorted(format_view(v) for v in browse('ir.ui.view')))
+ module.reports_by_module = "\n".join(sorted(r.name for r in browse('ir.actions.report')))
+ module.menus_by_module = "\n".join(sorted(m.complete_name for m in browse('ir.ui.menu')))
@api.depends('icon')
def _get_icon_image(self):
@@ -668,7 +670,7 @@ class Module(models.Model):
_logger.debug('Install from url: %r', urls)
try:
# 1. Download & unzip missing modules
- for module_name, url in urls.iteritems():
+ for module_name, url in pycompat.items(urls):
if not url:
continue # nothing to download, local version is already the last one
@@ -687,7 +689,7 @@ class Module(models.Model):
assert os.path.isdir(os.path.join(tmp, module_name))
# 2a. Copy/Replace module source in addons path
- for module_name, url in urls.iteritems():
+ for module_name, url in pycompat.items(urls):
if module_name == OPENERP or not url:
continue # OPENERP is special case, handled below, and no URL means local module
module_path = modules.get_module_path(module_name, downloaded=True, display_warning=False)
@@ -719,11 +721,11 @@ class Module(models.Model):
self.update_list()
- with_urls = [module_name for module_name, url in urls.iteritems() if url]
+ with_urls = [module_name for module_name, url in pycompat.items(urls) if url]
downloaded = self.search([('name', 'in', with_urls)])
installed = self.search([('id', 'in', downloaded.ids), ('state', '=', 'installed')])
- to_install = self.search([('name', 'in', urls.keys()), ('state', '=', 'uninstalled')])
+ to_install = self.search([('name', 'in', list(urls)), ('state', '=', 'uninstalled')])
post_install_action = to_install.button_immediate_install()
if installed or to_install:
diff --git a/odoo/addons/base/module/report/ir_module_reference_print.py b/odoo/addons/base/module/report/ir_module_reference_print.py
index 1f2dff53243..a933cdd9430 100644
--- a/odoo/addons/base/module/report/ir_module_reference_print.py
+++ b/odoo/addons/base/module/report/ir_module_reference_print.py
@@ -2,6 +2,8 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models
+from odoo.tools import pycompat
+
class IrModelReferenceReport(models.AbstractModel):
_name = 'report.base.report_irmodulereference'
@@ -21,7 +23,7 @@ class IrModelReferenceReport(models.AbstractModel):
if data:
res_ids = data.mapped('res_id')
fnames = self.env['ir.model.fields'].browse(res_ids).mapped('name')
- return sorted(self.env[model].fields_get(fnames).iteritems())
+ return sorted(pycompat.items(self.env[model].fields_get(fnames)))
return []
@api.model
diff --git a/odoo/addons/base/res/ir_property.py b/odoo/addons/base/res/ir_property.py
index 8e1b7eda860..ec086fb6c61 100644
--- a/odoo/addons/base/res/ir_property.py
+++ b/odoo/addons/base/res/ir_property.py
@@ -217,7 +217,7 @@ class Property(models.Model):
prop.write({'value': value})
# create new properties for records that do not have one yet
- for ref, id in refs.iteritems():
+ for ref, id in pycompat.items(refs):
value = clean(values[id])
if value != default_value:
self.create({
@@ -248,13 +248,13 @@ class Property(models.Model):
elif operator in ('!=', '<=', '<', '>', '>='):
value = makeref(value)
elif operator in ('in', 'not in'):
- value = map(makeref, value)
+ value = [makeref(v) for v in value]
elif operator in ('=like', '=ilike', 'like', 'not like', 'ilike', 'not ilike'):
# most probably inefficient... but correct
target = self.env[comodel]
target_names = target.name_search(value, operator=operator, limit=None)
- target_ids = map(itemgetter(0), target_names)
- operator, value = 'in', map(makeref, target_ids)
+ target_ids = [n[0] for n in target_names]
+ operator, value = 'in', [makeref(v) for v in target_ids]
elif field.type in ('integer', 'float'):
# No record is created in ir.property if the field's type is float or integer with a value
# equal to 0. Then to match with the records that are linked to a property field equal to 0,
diff --git a/odoo/addons/base/res/res_company.py b/odoo/addons/base/res/res_company.py
index 96e922f5e77..9dfdba96cba 100644
--- a/odoo/addons/base/res/res_company.py
+++ b/odoo/addons/base/res/res_company.py
@@ -6,6 +6,7 @@ import re
from odoo import api, fields, models, tools, _
from odoo.exceptions import ValidationError
+from odoo.tools import pycompat
class Company(models.Model):
@@ -146,7 +147,7 @@ class Company(models.Model):
if self.country_id:
res['domain']['state_id'] = [('country_id', '=', self.country_id.id)]
values = self.on_change_country(self.country_id.id)['value']
- for fname, value in values.iteritems():
+ for fname, value in pycompat.items(values):
setattr(self, fname, value)
return res
diff --git a/odoo/addons/base/res/res_config.py b/odoo/addons/base/res/res_config.py
index 59764fca1e8..a19c6406dcb 100644
--- a/odoo/addons/base/res/res_config.py
+++ b/odoo/addons/base/res/res_config.py
@@ -9,7 +9,7 @@ from lxml import etree
from odoo import api, models, registry, SUPERUSER_ID, _
from odoo.exceptions import AccessError, RedirectWarning, UserError
-from odoo.tools import ustr
+from odoo.tools import ustr, pycompat
_logger = logging.getLogger(__name__)
@@ -257,7 +257,7 @@ class ResConfigInstaller(models.TransientModel, ResConfigModuleInstallationMixin
installer
:rtype: [str]
"""
- return map(attrgetter('name'), self._already_installed())
+ return [m.name for m in self._already_installed()]
def _already_installed(self):
""" For each module (boolean fields in a res.config.installer),
@@ -267,7 +267,7 @@ class ResConfigInstaller(models.TransientModel, ResConfigModuleInstallationMixin
:returns: a list of all installed modules in this installer
:rtype: recordset (collection of Record)
"""
- selectable = [name for name, field in self._fields.iteritems()
+ selectable = [name for name, field in pycompat.items(self._fields)
if field.type == 'boolean']
return self.env['ir.module.module'].search([('name', 'in', selectable),
('state', 'in', ['to install', 'installed', 'to upgrade'])])
@@ -292,7 +292,7 @@ class ResConfigInstaller(models.TransientModel, ResConfigModuleInstallationMixin
"""
base = set(module_name
for installer in self.read()
- for module_name, to_install in installer.iteritems()
+ for module_name, to_install in pycompat.items(installer)
if self._fields[module_name].type == 'boolean' and to_install)
hooks_results = set()
@@ -302,7 +302,7 @@ class ResConfigInstaller(models.TransientModel, ResConfigModuleInstallationMixin
hooks_results.update(hook() or set())
additionals = set(module
- for requirements, consequences in self._install_if.iteritems()
+ for requirements, consequences in pycompat.items(self._install_if)
if base.issuperset(requirements)
for module in consequences)
@@ -462,13 +462,13 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin)
ref = self.env.ref
defaults, groups, modules, others = [], [], [], []
- for name, field in self._fields.iteritems():
+ for name, field in pycompat.items(self._fields):
if name.startswith('default_') and hasattr(field, 'default_model'):
defaults.append((name, field.default_model, name[8:]))
elif name.startswith('group_') and field.type in ('boolean', 'selection') and \
hasattr(field, 'implied_group'):
field_group_xmlids = getattr(field, 'group', 'base.group_user').split(',')
- field_groups = Groups.concat(*map(ref, field_group_xmlids))
+ field_groups = Groups.concat(*(ref(it) for it in field_group_xmlids))
groups.append((name, field_groups, ref(field.implied_group)))
elif name.startswith('module_') and field.type in ('boolean', 'selection'):
module = IrModule.sudo().search([('name', '=', name[7:])], limit=1)
diff --git a/odoo/addons/base/res/res_lang.py b/odoo/addons/base/res/res_lang.py
index 19a5b9be339..f4e2d7a1578 100644
--- a/odoo/addons/base/res/res_lang.py
+++ b/odoo/addons/base/res/res_lang.py
@@ -8,6 +8,7 @@ import re
from operator import itemgetter
from odoo import api, fields, models, tools, _
+from odoo.tools import pycompat
from odoo.tools.safe_eval import safe_eval
from odoo.exceptions import UserError, ValidationError
@@ -22,7 +23,7 @@ class Lang(models.Model):
_description = "Languages"
_order = "active desc,name"
- _disallowed_datetime_patterns = tools.DATETIME_FORMATS_MAP.keys()
+ _disallowed_datetime_patterns = list(tools.DATETIME_FORMATS_MAP)
_disallowed_datetime_patterns.remove('%y') # this one is in fact allowed, just not good practice
name = fields.Char(required=True)
@@ -125,7 +126,7 @@ class Lang(models.Model):
# For some locales, nl_langinfo returns a D_FMT/T_FMT that contains
# unsupported '%-' patterns, e.g. for cs_CZ
format = format.replace('%-', '%')
- for pattern, replacement in tools.DATETIME_FORMATS_MAP.iteritems():
+ for pattern, replacement in pycompat.items(tools.DATETIME_FORMATS_MAP):
format = format.replace(pattern, replacement)
return str(format)
@@ -318,5 +319,5 @@ def intersperse(string, counts, separator=''):
left, rest, right = intersperse_pat.match(string).groups()
def reverse(s): return s[::-1]
splits = split(reverse(rest), counts)
- res = separator.join(map(reverse, reverse(splits)))
+ res = separator.join(reverse(s) for s in reverse(splits))
return left + res + right, len(splits) > 0 and len(splits) -1 or 0
diff --git a/odoo/addons/base/res/res_partner.py b/odoo/addons/base/res/res_partner.py
index fe758d36de1..17e6507a01e 100644
--- a/odoo/addons/base/res/res_partner.py
+++ b/odoo/addons/base/res/res_partner.py
@@ -680,7 +680,7 @@ class Partner(models.Model):
query += ' limit %s'
where_clause_params.append(limit)
self.env.cr.execute(query, where_clause_params)
- partner_ids = map(lambda x: x[0], self.env.cr.fetchall())
+ partner_ids = [row[0] for row in self.env.cr.fetchall()]
if partner_ids:
return self.browse(partner_ids).name_get()
diff --git a/odoo/addons/base/res/res_users.py b/odoo/addons/base/res/res_users.py
index ae8863eee4d..106fc32d56f 100644
--- a/odoo/addons/base/res/res_users.py
+++ b/odoo/addons/base/res/res_users.py
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import pytz
import datetime
+import itertools
import logging
from collections import defaultdict
@@ -13,7 +14,7 @@ from odoo import api, fields, models, tools, SUPERUSER_ID, _
from odoo.exceptions import AccessDenied, AccessError, UserError, ValidationError
from odoo.osv import expression
from odoo.service.db import check_super
-from odoo.tools import partition
+from odoo.tools import partition, pycompat
_logger = logging.getLogger(__name__)
@@ -29,7 +30,7 @@ def name_boolean_group(id):
return 'in_group_' + str(id)
def name_selection_groups(ids):
- return 'sel_groups_' + '_'.join(map(str, ids))
+ return 'sel_groups_' + '_'.join(str(it) for it in ids)
def is_boolean_group(name):
return name.startswith('in_group_')
@@ -44,7 +45,7 @@ def get_boolean_group(name):
return int(name[9:])
def get_selection_groups(name):
- return map(int, name[11:].split('_'))
+ return [int(v) for v in name[11:].split('_')]
def parse_m2m(commands):
"return a list of ids corresponding to a many2many value"
@@ -92,7 +93,7 @@ class Groups(models.Model):
@api.depends('category_id.name', 'name')
def _compute_full_name(self):
# Important: value must be stored in environment of group, not group1!
- for group, group1 in zip(self, self.sudo()):
+ for group, group1 in pycompat.izip(self, self.sudo()):
if group1.category_id:
group.full_name = '%s / %s' % (group1.category_id.name, group1.name)
else:
@@ -111,7 +112,7 @@ class Groups(models.Model):
operand = [operand]
where = []
for group in operand:
- values = filter(bool, group.split('/'))
+ values = [v for v in group.split('/') if v]
group_name = values.pop().strip()
category_name = values and '/'.join(values).strip() or group_name
group_domain = [('name', operator, lst and [group_name] or group_name)]
@@ -299,13 +300,11 @@ class Users(models.Model):
canwrite = self.env['ir.model.access'].check('res.users', 'write', False)
if not canwrite:
- def override_password(vals):
- if (vals['id'] != self._uid):
+ for vals in result:
+ if vals['id'] != self._uid:
for key in USER_PRIVATE_FIELDS:
if key in vals:
vals[key] = '********'
- return vals
- result = map(override_password, result)
return result
@@ -343,7 +342,7 @@ class Users(models.Model):
raise UserError(_("You cannot deactivate the user you're currently logged in as."))
if self == self.env.user:
- for key in values.keys():
+ for key in list(values):
if not (key in self.SELF_WRITEABLE_FIELDS or key.startswith('context_')):
break
else:
@@ -624,7 +623,7 @@ class GroupsImplied(models.Model):
if values.get('users') or values.get('implied_ids'):
# add all implied groups (to all users of each group)
for group in self:
- vals = {'users': zip(repeat(4), group.with_context(active_test=False).users.ids)}
+ vals = {'users': list(pycompat.izip(repeat(4), group.with_context(active_test=False).users.ids))}
super(GroupsImplied, group.trans_implied_ids).write(vals)
return res
@@ -766,7 +765,7 @@ class GroupsView(models.Model):
# determine sequence order: a group appears after its implied groups
order = {g: len(g.trans_implied_ids & gs) for g in gs}
# check whether order is total, i.e., sequence orders are distinct
- if len(set(order.itervalues())) == len(gs):
+ if len(set(pycompat.values(order))) == len(gs):
return (app, 'selection', gs.sorted(key=order.get))
else:
return (app, 'boolean', gs)
@@ -780,7 +779,7 @@ class GroupsView(models.Model):
others += g
# build the result
res = []
- for app, gs in sorted(by_app.iteritems(), key=lambda it: it[0].sequence or 0):
+ for app, gs in sorted(pycompat.items(by_app), key=lambda it: it[0].sequence or 0):
res.append(linearize(app, gs))
if others:
res.append((self.env['ir.module.category'], 'boolean', others))
@@ -820,7 +819,7 @@ class UsersView(models.Model):
add, rem = [], []
values1 = {}
- for key, val in values.iteritems():
+ for key, val in pycompat.items(values):
if is_boolean_group(key):
(add if val else rem).append(get_boolean_group(key))
elif is_selection_groups(key):
@@ -832,7 +831,10 @@ class UsersView(models.Model):
if 'groups_id' not in values and (add or rem):
# remove group ids in `rem` and add group ids in `add`
- values1['groups_id'] = zip(repeat(3), rem) + zip(repeat(4), add)
+ values1['groups_id'] = list(itertools.chain(
+ pycompat.izip(repeat(3), rem),
+ pycompat.izip(repeat(4), add)
+ ))
return values1
@@ -847,7 +849,7 @@ class UsersView(models.Model):
@api.multi
def read(self, fields=None, load='_classic_read'):
# determine whether reified groups fields are required, and which ones
- fields1 = fields or self.fields_get().keys()
+ fields1 = fields or list(self.fields_get())
group_fields, other_fields = partition(is_reified_group, fields1)
# read regular fields (other_fields); add 'groups_id' if necessary
diff --git a/odoo/addons/base/tests/test_api.py b/odoo/addons/base/tests/test_api.py
index 8270d6086d5..55cf1ff26b5 100644
--- a/odoo/addons/base/tests/test_api.py
+++ b/odoo/addons/base/tests/test_api.py
@@ -79,11 +79,11 @@ class TestAPI(common.TransactionCase):
domain = [('name', 'ilike', 'j')]
partners = self.env['res.partner'].search(domain)
self.assertTrue(partners)
- ids = map(int, partners)
+ ids = partners.ids
# modify those partners, and check that partners has not changed
partners.write({'active': False})
- self.assertEqual(ids, map(int, partners))
+ self.assertEqual(ids, partners.ids)
# redo the search, and check that the result is now empty
partners2 = self.env['res.partner'].search(domain)
@@ -98,7 +98,7 @@ class TestAPI(common.TransactionCase):
self.assertIsRecordset(user.groups_id, 'res.groups')
partners = self.env['res.partner'].search([])
- for name, field in partners._fields.iteritems():
+ for name, field in pycompat.items(partners._fields):
if field.type == 'many2one':
for p in partners:
self.assertIsRecord(p[name], field.comodel_name)
@@ -296,7 +296,7 @@ class TestAPI(common.TransactionCase):
self.assertItemsEqual(partners.ids, country_id_cache)
# partners' countries are ready for prefetching
- country_ids = set(cid for cids in country_id_cache.itervalues() for cid in cids)
+ country_ids = set(cid for cids in pycompat.values(country_id_cache) for cid in cids)
self.assertTrue(len(country_ids) > 1)
self.assertItemsEqual(country_ids, partners._prefetch['res.country'])
diff --git a/odoo/addons/base/tests/test_expression.py b/odoo/addons/base/tests/test_expression.py
index 4dea4797b0c..87099e78754 100644
--- a/odoo/addons/base/tests/test_expression.py
+++ b/odoo/addons/base/tests/test_expression.py
@@ -5,7 +5,7 @@ import psycopg2
from odoo.models import BaseModel
from odoo.tests.common import TransactionCase
-from odoo.tools import mute_logger
+from odoo.tools import mute_logger, pycompat
import odoo.osv.expression as expression
@@ -85,10 +85,10 @@ class TestExpression(TransactionCase):
'b ab': [cids['B'], cids['AB']],
}
pids = {}
- for name, cat_ids in partners_config.iteritems():
+ for name, cat_ids in pycompat.items(partners_config):
pids[name] = partners.create({'name': name, 'category_id': [(6, 0, cat_ids)]}).id
- base_domain = [('id', 'in', pids.values())]
+ base_domain = [('id', 'in', list(pycompat.values(pids)))]
def test(op, value, expected):
found_ids = partners.search(base_domain + [('category_id', op, value)]).ids
diff --git a/odoo/addons/base/tests/test_float.py b/odoo/addons/base/tests/test_float.py
index 8c4ee380bd2..725ba48365b 100644
--- a/odoo/addons/base/tests/test_float.py
+++ b/odoo/addons/base/tests/test_float.py
@@ -98,10 +98,10 @@ class TestFloatPrecision(TransactionCase):
precisions = [2, 2, 2, 2, 2, 2, 3, 4]
# Note: max precision for double floats is 53 bits of precision or
# 17 significant decimal digits
- 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/addons/base/tests/test_ir_filters.py b/odoo/addons/base/tests/test_ir_filters.py
index d932f16be11..b8de7590ad2 100644
--- a/odoo/addons/base/tests/test_ir_filters.py
+++ b/odoo/addons/base/tests/test_ir_filters.py
@@ -4,11 +4,12 @@
from odoo import exceptions
from odoo.tests.common import TransactionCase, ADMIN_USER_ID
-def noid(d):
+def noid(seq):
""" Removes values that are not relevant for the test comparisons """
- d.pop('id', None)
- d.pop('action_id', None)
- return d
+ for d in seq:
+ d.pop('id', None)
+ d.pop('action_id', None)
+ return seq
class FiltersCase(TransactionCase):
@@ -34,7 +35,7 @@ class TestGetFilters(FiltersCase):
filters = self.env['ir.filters'].sudo(self.USER_ID).get_filters('ir.filters')
- self.assertItemsEqual(map(noid, filters), [
+ self.assertItemsEqual(noid(filters), [
dict(name='a', is_default=False, user_id=self.USER_NG, domain='[]', context='{}', sort='[]'),
dict(name='b', is_default=False, user_id=self.USER_NG, domain='[]', context='{}', sort='[]'),
dict(name='c', is_default=False, user_id=self.USER_NG, domain='[]', context='{}', sort='[]'),
@@ -52,7 +53,7 @@ class TestGetFilters(FiltersCase):
filters = self.env['ir.filters'].sudo(self.USER_ID).get_filters('ir.filters')
- self.assertItemsEqual(map(noid, filters), [
+ self.assertItemsEqual(noid(filters), [
dict(name='a', is_default=False, user_id=False, domain='[]', context='{}', sort='[]'),
dict(name='b', is_default=False, user_id=False, domain='[]', context='{}', sort='[]'),
dict(name='c', is_default=False, user_id=False, domain='[]', context='{}', sort='[]'),
@@ -69,7 +70,7 @@ class TestGetFilters(FiltersCase):
filters = self.env['ir.filters'].sudo(self.USER_ID).get_filters('ir.filters')
- self.assertItemsEqual(map(noid, filters), [
+ self.assertItemsEqual(noid(filters), [
dict(name='a', is_default=False, user_id=False, domain='[]', context='{}', sort='[]'),
dict(name='c', is_default=False, user_id=self.USER_NG, domain='[]', context='{}', sort='[]'),
])
@@ -95,7 +96,7 @@ class TestOwnDefaults(FiltersCase):
})
filters = Filters.get_filters('ir.filters')
- self.assertItemsEqual(map(noid, filters), [
+ self.assertItemsEqual(noid(filters), [
dict(name='a', user_id=self.USER_NG, is_default=True,
domain='[]', context='{}', sort='[]')
])
@@ -120,7 +121,7 @@ class TestOwnDefaults(FiltersCase):
})
filters = Filters.get_filters('ir.filters')
- self.assertItemsEqual(map(noid, filters), [
+ self.assertItemsEqual(noid(filters), [
dict(name='a', user_id=self.USER_NG, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='b', user_id=self.USER_NG, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='c', user_id=self.USER_NG, is_default=True, domain='[]', context='{}', sort='[]'),
@@ -146,7 +147,7 @@ class TestOwnDefaults(FiltersCase):
})
filters = Filters.get_filters('ir.filters')
- self.assertItemsEqual(map(noid, filters), [
+ self.assertItemsEqual(noid(filters), [
dict(name='a', user_id=self.USER_NG, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='b', user_id=self.USER_NG, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='c', user_id=self.USER_NG, is_default=True, domain='[]', context='{}', sort='[]'),
@@ -172,7 +173,7 @@ class TestOwnDefaults(FiltersCase):
})
filters = Filters.get_filters('ir.filters')
- self.assertItemsEqual(map(noid, filters), [
+ self.assertItemsEqual(noid(filters), [
dict(name='a', user_id=self.USER_NG, is_default=True, domain='[]', context='{}', sort='[]'),
dict(name='b', user_id=self.USER_NG, is_default=False, domain='[]', context='{}', sort='[]'),
])
@@ -204,7 +205,7 @@ class TestGlobalDefaults(FiltersCase):
})
filters = Filters.get_filters('ir.filters')
- self.assertItemsEqual(map(noid, filters), [
+ self.assertItemsEqual(noid(filters), [
dict(name='a', user_id=False, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='b', user_id=False, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='c', user_id=False, is_default=True, domain='[]', context='{}', sort='[]'),
@@ -271,7 +272,7 @@ class TestGlobalDefaults(FiltersCase):
})
filters = Filters.get_filters('ir.filters')
- self.assertItemsEqual(map(noid, filters), [
+ self.assertItemsEqual(noid(filters), [
dict(name='a', user_id=False, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='b', user_id=False, is_default=True, domain='[]', context=context_value, sort='[]'),
])
diff --git a/odoo/addons/base/tests/test_orm.py b/odoo/addons/base/tests/test_orm.py
index 0da71a84ad5..bf95bee08f7 100644
--- a/odoo/addons/base/tests/test_orm.py
+++ b/odoo/addons/base/tests/test_orm.py
@@ -141,7 +141,7 @@ class TestORM(TransactionCase):
partner_ids_by_year = defaultdict(list)
partners = self.env['res.partner']
- for name, date in partners_data.items():
+ for name, date in pycompat.items(partners_data):
p = partners.create(dict(name=name, date=date))
partner_ids.append(p.id)
partner_ids_by_day[date].append(p.id)
@@ -297,7 +297,7 @@ class TestO2MSerialization(TransactionCase):
def test_CREATE_commands(self):
" returns the VALUES dict as-is "
values = [{'foo': 'bar'}, {'foo': 'baz'}, {'foo': 'baq'}]
- results = self.env['res.partner'].resolve_2many_commands('child_ids', map(CREATE, values))
+ results = self.env['res.partner'].resolve_2many_commands('child_ids', [CREATE(v) for v in values])
self.assertEqual(results, values)
def test_LINK_TO_command(self):
@@ -307,7 +307,7 @@ class TestO2MSerialization(TransactionCase):
self.env['res.partner'].create({'name': 'bar'}).id,
self.env['res.partner'].create({'name': 'baz'}).id,
]
- commands = map(LINK_TO, ids)
+ commands = [LINK_TO(v) for v in ids]
results = self.env['res.partner'].resolve_2many_commands('child_ids', commands, ['name'])
self.assertItemsEqual(results, [
@@ -356,7 +356,7 @@ class TestO2MSerialization(TransactionCase):
self.env['res.partner'].create({'name': 'bar'}).id,
self.env['res.partner'].create({'name': 'baz'}).id,
]
- commands = map(DELETE, ids)
+ commands = [DELETE(v) for v in ids]
results = self.env['res.partner'].resolve_2many_commands('child_ids', commands, ['name'])
self.assertEqual(results, [])
diff --git a/odoo/addons/base/tests/test_qweb.py b/odoo/addons/base/tests/test_qweb.py
index dcf41b092d6..cd459fe7669 100644
--- a/odoo/addons/base/tests/test_qweb.py
+++ b/odoo/addons/base/tests/test_qweb.py
@@ -13,6 +13,7 @@ from itertools import chain
from odoo.modules import get_module_resource
from odoo.tests.common import TransactionCase
from odoo.addons.base.ir.ir_qweb import QWebException
+from odoo.tools import pycompat
def dedent_and_strip(string):
@@ -411,7 +412,7 @@ class TestQWebNS(TransactionCase):
'cac': 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2',
'cbc': 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2'
}
- self.assertSetEqual(set(expected_ns.items()) - set(result_etree.nsmap.items()), set())
+ self.assertSetEqual(set(pycompat.items(expected_ns)) - set(pycompat.items(result_etree.nsmap)), set())
# check that the t-call did its work
cac_lines = result_etree.findall('.//cac:line', namespaces={'cac': 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'})
diff --git a/odoo/addons/base/tests/test_search.py b/odoo/addons/base/tests/test_search.py
index d52cd9db9c4..97670fdd805 100644
--- a/odoo/addons/base/tests/test_search.py
+++ b/odoo/addons/base/tests/test_search.py
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests.common import TransactionCase
+from odoo.tools import pycompat
class test_search(TransactionCase):
@@ -107,7 +108,7 @@ class test_search(TransactionCase):
user_ids[u] = Users.create({'name': u, 'login': u}).id
cron_ids[u] = Cron.create({'name': u, 'model_id': self.env.ref('base.model_res_partner').id, 'user_id': user_ids[u]}).id
- ids = Cron.search([('id', 'in', cron_ids.values())], order='user_id').ids
+ ids = Cron.search([('id', 'in', list(pycompat.values(cron_ids)))], order='user_id').ids
expected_ids = [cron_ids[l] for l in 'ABC']
self.assertEqual(ids, expected_ids)
@@ -127,7 +128,7 @@ class test_search(TransactionCase):
create('F', parent_id=cat_ids['D'])
expected_ids = [cat_ids[x] for x in 'ADEFBC']
- found_ids = Cats.search([('id', 'in', cat_ids.values())]).ids
+ found_ids = Cats.search([('id', 'in', list(pycompat.values(cat_ids)))]).ids
self.assertEqual(found_ids, expected_ids)
def test_13_m2o_order_loop_multi(self):
diff --git a/odoo/addons/base/tests/test_views.py b/odoo/addons/base/tests/test_views.py
index bd462932e5f..40cd922340c 100644
--- a/odoo/addons/base/tests/test_views.py
+++ b/odoo/addons/base/tests/test_views.py
@@ -638,7 +638,7 @@ class TestViews(ViewCase):
kw.setdefault('mode', 'extension' if kw.get('inherit_id') else 'primary')
kw.setdefault('active', True)
- keys = sorted(kw.keys())
+ keys = sorted(kw)
fields = ','.join('"%s"' % (k.replace('"', r'\"'),) for k in keys)
params = ','.join('%%(%s)s' % (k,) for k in keys)
diff --git a/odoo/addons/test_impex/tests/test_export.py b/odoo/addons/test_impex/tests/test_export.py
index 226ba9943f6..72a3b2a6f75 100644
--- a/odoo/addons/test_impex/tests/test_export.py
+++ b/odoo/addons/test_impex/tests/test_export.py
@@ -4,6 +4,7 @@
import itertools
from odoo.tests import common
+from odoo.tools import pycompat
class CreatorCase(common.TransactionCase):
@@ -494,9 +495,9 @@ class test_o2m_multiple(CreatorCase):
"""
fields = ['const', 'child1/value', 'child2/value']
child1 = [(0, False, {'value': v, 'str': 'record%.02d' % index})
- for index, v in zip(itertools.count(), [4, 42, 36, 4, 13])]
+ for index, v in pycompat.izip(itertools.count(), [4, 42, 36, 4, 13])]
child2 = [(0, False, {'value': v, 'str': 'record%.02d' % index})
- for index, v in zip(itertools.count(10), [8, 12, 8, 55, 33, 13])]
+ for index, v in pycompat.izip(itertools.count(10), [8, 12, 8, 55, 33, 13])]
self.assertEqual(
self.export(child1=child1, child2=False, fields=fields),
diff --git a/odoo/addons/test_limits/models.py b/odoo/addons/test_limits/models.py
index 506578a811b..957e3c3d6f9 100644
--- a/odoo/addons/test_limits/models.py
+++ b/odoo/addons/test_limits/models.py
@@ -5,7 +5,6 @@ import time
import sys
from odoo import models, api
-from odoo.tools import pycompat
class m(models.Model):
""" This model exposes a few methods that will consume between 'almost no
@@ -39,7 +38,7 @@ class m(models.Model):
t0 = time.clock()
t1 = time.clock()
while t1 - t0 < seconds:
- for i in pycompat.range(10000000):
+ for i in range(10000000):
x = i * i
t1 = time.clock()
return True
diff --git a/odoo/addons/test_new_api/models.py b/odoo/addons/test_new_api/models.py
index c84827d47ce..2f288e89f74 100644
--- a/odoo/addons/test_new_api/models.py
+++ b/odoo/addons/test_new_api/models.py
@@ -5,6 +5,7 @@ import datetime
from odoo import models, fields, api, _
from odoo.exceptions import AccessError, ValidationError
+from odoo.tools import pycompat
class Category(models.Model):
@@ -45,7 +46,7 @@ class Category(models.Model):
categories.append(category[0])
categories.append(self)
# assign parents following sequence
- for parent, child in zip(categories, categories[1:]):
+ for parent, child in pycompat.izip(categories, categories[1:]):
if parent and child:
child.parent = parent
# assign name of last category, and reassign display_name (to normalize it)
@@ -364,4 +365,4 @@ class ComputeRecursive(models.Model):
if rec.parent:
rec.display_name = rec.parent.display_name + " / " + rec.name
else:
- rec.display_name = rec.name \ No newline at end of file
+ rec.display_name = rec.name
diff --git a/odoo/addons/test_new_api/tests/test_one2many.py b/odoo/addons/test_new_api/tests/test_one2many.py
index 9ade2c0fe19..e1ad547e95e 100644
--- a/odoo/addons/test_new_api/tests/test_one2many.py
+++ b/odoo/addons/test_new_api/tests/test_one2many.py
@@ -45,7 +45,7 @@ class One2manyCase(TransactionCase):
# Check the lines first
self.assertItemsEqual(
self.multi.lines.mapped('name'),
- map(str, range(10)))
+ [str(i) for i in range(10)])
# Modify the first line and drop the last one
self.multi.lines[0].name = "hello"
self.multi.lines = self.multi.lines[:-1]
diff --git a/odoo/addons/test_pylint/tests/test_pylint.py b/odoo/addons/test_pylint/tests/test_pylint.py
index 1ae544e56a6..f98dcf88b3c 100644
--- a/odoo/addons/test_pylint/tests/test_pylint.py
+++ b/odoo/addons/test_pylint/tests/test_pylint.py
@@ -30,22 +30,33 @@ class TestPyLint(TransactionCase):
'relative-import',
'deprecated-module',
'import-star-module-level',
- # 'bad-python3-import', # TODO: more stuff used in report
+
+ 'bad-builtin',
+
+ 'dict-iter-method',
+ 'dict-view-method',
'long-suffix',
- 'apply-builtin',
- 'cmp-builtin',
- 'coerce-builtin',
- 'execfile-builtin',
- 'input-builtin',
- 'intern-builtin',
- 'long-builtin',
- 'raw_input-builtin',
- 'reload-builtin',
- 'xrange-builtin',
+ ]
+
+ BAD_FUNCTIONS = [
+ 'apply',
+ 'cmp',
+ 'coerce',
+ 'execfile',
+ 'input',
+ 'intern',
+ 'long',
+ 'raw_input',
+ 'reload',
+ 'xrange',
+ 'long',
+ 'map',
+ 'filter',
+ 'zip',
# TODO: enable once report has been removed
- # 'file-builtin',
- # 'reduce-builtin',
+ # 'file',
+ # 'reduce',
]
def _skip_test(self, reason):
@@ -69,6 +80,8 @@ class TestPyLint(TransactionCase):
'--enable=%s' % ','.join(self.ENABLED_CODES),
'--reports=n',
"--msg-template='{msg} ({msg_id}) at {path}:{line}'",
+ '--load-plugins=pylint.extensions.bad_builtin',
+ '--bad-functions=%s' % ','.join(self.BAD_FUNCTIONS),
]
try:
diff --git a/odoo/api.py b/odoo/api.py
index 1adbd08f6e5..dc51870f20a 100644
--- a/odoo/api.py
+++ b/odoo/api.py
@@ -55,7 +55,7 @@ from weakref import WeakSet
from decorator import decorator
from werkzeug.local import Local, release_local
-from odoo.tools import frozendict, classproperty
+from odoo.tools import frozendict, classproperty, pycompat
_logger = logging.getLogger(__name__)
@@ -85,7 +85,7 @@ class Params(object):
params = []
for arg in self.args:
params.append(repr(arg))
- for item in sorted(self.kwargs.iteritems()):
+ for item in sorted(pycompat.items(self.kwargs)):
params.append("%s=%r" % item)
return ', '.join(params)
@@ -100,7 +100,7 @@ class Meta(type):
# dummy parent class to catch overridden methods decorated with 'returns'
parent = type.__new__(meta, name, bases, {})
- for key, value in attrs.items():
+ for key, value in list(pycompat.items(attrs)):
if not key.startswith('__') and callable(value):
# make the method inherit from decorators
value = propagate(getattr(parent, key, None), value)
@@ -916,7 +916,7 @@ class Environment(Mapping):
def remove_todo(self, field, records):
""" Mark ``field`` as recomputed on ``records``. """
recs_list = [recs - records for recs in self.all.todo.pop(field, [])]
- recs_list = filter(None, recs_list)
+ recs_list = [r for r in recs_list if r]
if recs_list:
self.all.todo[field] = recs_list
@@ -938,15 +938,14 @@ class Environment(Mapping):
# make a full copy of the cache, and invalidate it
cache_dump = dict(
(field, dict(field_cache))
- for field, field_cache in self.cache.iteritems()
+ for field, field_cache in pycompat.items(self.cache)
)
self.invalidate_all()
# re-fetch the records, and compare with their former cache
invalids = []
- for field, field_dump in cache_dump.iteritems():
- ids = filter(None, field_dump)
- records = self[field.model_name].browse(ids)
+ for field, field_dump in pycompat.items(cache_dump):
+ records = self[field.model_name].browse(f for f in field_dump if f)
for record in records:
try:
cached = field_dump[record.id]
diff --git a/odoo/cli/command.py b/odoo/cli/command.py
index 2ab042f58e8..503ffcb360a 100644
--- a/odoo/cli/command.py
+++ b/odoo/cli/command.py
@@ -28,7 +28,7 @@ class Help(Command):
"""Display the list of available commands"""
def run(self, args):
print("Available commands:\n")
- names = commands.keys()
+ names = list(commands)
padding = max([len(k) for k in names]) + 2
for k in sorted(names):
name = k.ljust(padding, ' ')
diff --git a/odoo/fields.py b/odoo/fields.py
index e200d7b32dc..8d5d865968b 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -335,7 +335,7 @@ class Field(object):
def __init__(self, string=Default, **kwargs):
kwargs['string'] = string
- args = {key: val for key, val in kwargs.iteritems() if val is not Default}
+ args = {key: val for key, val in pycompat.items(kwargs) if val is not Default}
self.args = args or EMPTY_DICT
self._setup_done = None
@@ -364,7 +364,7 @@ class Field(object):
""" Set all field attributes at once (with slot defaults). """
# optimization: we assign slots only
assign = object.__setattr__
- for key, val in self._slots.iteritems():
+ for key, val in pycompat.items(self._slots):
assign(self, key, attrs.pop(key, val))
if attrs:
assign(self, '_attrs', attrs)
@@ -542,7 +542,7 @@ class Field(object):
if not getattr(self, attr):
setattr(self, attr, getattr(field, prop))
- for attr, value in field._attrs.iteritems():
+ for attr, value in pycompat.items(field._attrs):
if attr not in self._attrs:
setattr(self, attr, value)
@@ -565,7 +565,7 @@ class Field(object):
""" Compute the related field ``self`` on ``records``. """
# when related_sudo, bypass access rights checks when reading values
others = records.sudo() if self.related_sudo else records
- for record, other in zip(records, others):
+ for record, other in pycompat.izip(records, others):
if not record.id and record.env != other.env:
# draft records: copy record's cache to other's cache first
copy_cache(record, other.env)
@@ -650,7 +650,7 @@ class Field(object):
model = model0.env.get(field.comodel_name)
# add self's model dependencies
- for mname, fnames in model0._depends.iteritems():
+ for mname, fnames in pycompat.items(model0._depends):
model = model0.env[mname]
for fname in fnames:
field = model._fields[fname]
@@ -1002,7 +1002,7 @@ class Field(object):
# HACK: if result is in the wrong cache, copy values
if recs.env != env:
computed = record._field_computed[self]
- for source, target in zip(recs, recs.with_env(env)):
+ for source, target in pycompat.izip(recs, recs.with_env(env)):
try:
values = target._convert_to_cache({
f.name: source[f.name] for f in computed
@@ -1071,8 +1071,8 @@ class Field(object):
for field, path in records._field_triggers[self]:
bymodel[field.model_name][path].append(field)
- for model_name, bypath in bymodel.iteritems():
- for path, fields in bypath.iteritems():
+ for model_name, bypath in pycompat.items(bymodel):
+ for path, fields in pycompat.items(bypath):
if path and any(field.compute and field.store for field in fields):
# process stored fields
stored = set(field for field in fields if field.compute and field.store)
@@ -1755,7 +1755,7 @@ class Selection(Field):
if 'selection_add' in field.args:
# use an OrderedDict to update existing values
selection_add = field.args['selection_add']
- self.selection = OrderedDict(self.selection + selection_add).items()
+ self.selection = list(pycompat.items(OrderedDict(self.selection + selection_add)))
def _description_selection(self, env):
""" return the selection list (pairs (value, label)); labels are
@@ -1934,7 +1934,7 @@ class Many2one(_Relational):
super(Many2one, self)._setup_attrs(model, name)
# determine self.delegate
if not self.delegate:
- self.delegate = name in model._inherits.values()
+ self.delegate = name in pycompat.values(model._inherits)
def update_db(self, model, columns):
comodel = model.env[self.comodel_name]
diff --git a/odoo/http.py b/odoo/http.py
index 0b3ad105e39..fa5b44d1226 100644
--- a/odoo/http.py
+++ b/odoo/http.py
@@ -52,7 +52,7 @@ import odoo
from odoo.service.server import memory_info
from odoo.service import security, model as service_model
from odoo.tools.func import lazy_property
-from odoo.tools import ustr, consteq, frozendict
+from odoo.tools import ustr, consteq, frozendict, pycompat, unique
from odoo.modules.module import module_manifest
@@ -288,8 +288,8 @@ class WebRequest(object):
def set_handler(self, endpoint, arguments, auth):
# is this needed ?
- arguments = dict((k, v) for k, v in arguments.iteritems()
- if not k.startswith("_ignored_"))
+ arguments ={k: v for k, v in pycompat.items(arguments)
+ if not k.startswith("_ignored_")}
self.endpoint_arguments = arguments
self.endpoint = endpoint
self.auth_method = auth
@@ -837,7 +837,7 @@ more details.
"""
response = Response(data, headers=headers)
if cookies:
- for k, v in cookies.iteritems():
+ for k, v in pycompat.items(cookies):
response.set_cookie(k, v)
return response
@@ -878,7 +878,7 @@ class ControllerType(type):
super(ControllerType, cls).__init__(name, bases, attrs)
# flag old-style methods with req as first argument
- for k, v in attrs.items():
+ for k, v in pycompat.items(attrs):
if inspect.isfunction(v) and hasattr(v, 'original_func'):
# Set routing type on original functions
routing_type = v.routing.get('type')
@@ -941,14 +941,12 @@ def routing_map(modules, nodb_only, converters=None):
result = [klass]
return result
- uniq = lambda it: collections.OrderedDict((id(x), x) for x in it).values()
-
for module in modules:
if module not in controllers_per_module:
continue
for _, cls in controllers_per_module[module]:
- subclasses = uniq(c for c in get_subclasses(cls) if c is not cls)
+ subclasses = list(unique(c for c in get_subclasses(cls) if c is not cls))
if subclasses:
name = "%s (extended by %s)" % (cls.__name__, ', '.join(sub.__name__ for sub in subclasses))
cls = type(name, tuple(reversed(subclasses)), {})
@@ -1050,7 +1048,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
security.check(self.db, self.uid, self.password)
def logout(self, keep_db=False):
- for k in self.keys():
+ for k in list(self):
if not (keep_db and k == 'db'):
del self[k]
self._default_values()
@@ -1137,7 +1135,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
# NOTE we do not store files in the session itself to avoid loading them in memory.
# By storing them in the session store, we ensure every worker (even ones on other
# servers) can access them. It also allow stale files to be deleted by `session_gc`.
- for f in req.files.values():
+ for f in pycompat.values(req.files):
storename = 'werkzeug_%s_%s.file' % (self.sid, uuid.uuid4().hex)
path = os.path.join(root.session_store.path, storename)
with open(path, 'w') as fp:
@@ -1155,7 +1153,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
try:
if data:
# regenerate files filenames with the current session store
- for name, (storename, filename, content_type) in data['files'].iteritems():
+ for name, (storename, filename, content_type) in pycompat.items(data['files']):
path = os.path.join(root.session_store.path, storename)
files.add(name, (path, filename, content_type))
yield werkzeug.datastructures.CombinedMultiDict([data['form'], files])
@@ -1163,7 +1161,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
yield None
finally:
# cleanup files
- for f, _, _ in files.values():
+ for f, _, _ in pycompat.values(files):
try:
os.unlink(f)
except IOError:
diff --git a/odoo/models.py b/odoo/models.py
index a1535123c9d..a9448b3ca05 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -108,9 +108,6 @@ def check_method_name(name):
if regex_private.match(name):
raise AccessError(_('Private methods (such as %s) cannot be called remotely.') % (name,))
-def intersect(la, lb):
- return filter(lambda x: x in lb, la)
-
def same_name(f, g):
""" Test whether functions ``f`` and ``g`` are identical or have the same name """
return f == g or getattr(f, '__name__', 0) == getattr(g, '__name__', 1)
@@ -150,7 +147,7 @@ class MetaModel(api.Meta):
self.module_to_models[self._module].append(self)
# check for new-api conversion error: leave comma after field definition
- for key, val in attrs.iteritems():
+ for key, val in pycompat.items(attrs):
if type(val) is tuple and len(val) == 1 and isinstance(val[0], Field):
_logger.error("Trailing comma after field definition: %s.%s", self, key)
if isinstance(val, Field):
@@ -515,7 +512,7 @@ class BaseModel(object):
cls._inherits.update(base._inherits)
- for mname, fnames in base._depends.iteritems():
+ for mname, fnames in pycompat.items(base._depends):
cls._depends[mname] = cls._depends.get(mname, []) + fnames
for cons in base._constraints:
@@ -525,7 +522,7 @@ class BaseModel(object):
cls._sql_constraints += base._sql_constraints
cls._sequence = cls._sequence or (cls._table + '_id_seq')
- cls._constraints = cls._constraints.values()
+ cls._constraints = list(pycompat.values(cls._constraints))
# update _inherits_children of parent models
for parent_name in cls._inherits:
@@ -707,7 +704,7 @@ class BaseModel(object):
This method is used when exporting data via client menu
"""
- fields_to_export = map(fix_import_export_id_paths, fields_to_export)
+ fields_to_export = [fix_import_export_id_paths(f) for f in fields_to_export]
if raw_data:
self = self.with_context(export_raw_data=True)
return {'datas': self._export_rows(fields_to_export)}
@@ -740,7 +737,7 @@ class BaseModel(object):
cr = self._cr
cr.execute('SAVEPOINT model_load')
- fields = map(fix_import_export_id_paths, fields)
+ fields = [fix_import_export_id_paths(f) for f in fields]
fg = self.fields_get()
ids = []
@@ -835,7 +832,7 @@ class BaseModel(object):
# copy non-relational fields to record dict
record = {fnames[0]: value
- for fnames, value in itertools.izip(fields_, row)
+ for fnames, value in pycompat.izip(fields_, row)
if not is_relational(fnames[0])}
# Get all following rows which have relational values attached to
@@ -849,13 +846,13 @@ class BaseModel(object):
# get only cells for this sub-field, should be strictly
# non-empty, field path [None] is for name_get field
- indices, subfields = zip(*((index, fnames[1:] or [None])
+ indices, subfields = pycompat.izip(*((index, fnames[1:] or [None])
for index, fnames in enumerate(fields_)
if fnames[0] == relfield))
# return all rows which have at least one value for the
# subfields of relfield
- relfield_data = filter(any, map(itemgetter_tuple(indices), record_span))
+ relfield_data = [it for it in pycompat.imap(itemgetter_tuple(indices), record_span) if any(it)]
record[relfield] = [
subrecord
for subrecord, _subinfo in comodel._extract_records(subfields, relfield_data, log=log)
@@ -876,7 +873,7 @@ class BaseModel(object):
:returns: a list of triplets of (id, xid, record)
:rtype: list((int|None, str|None, dict))
"""
- field_names = {name: field.string for name, field in self._fields.iteritems()}
+ field_names = {name: field.string for name, field in pycompat.items(self._fields)}
if self.env.lang:
field_names.update(self.env['ir.translation'].get_field_string(self._name))
@@ -1007,7 +1004,7 @@ class BaseModel(object):
defaults = self._convert_to_write(defaults)
# add default values for inherited fields
- for model, names in parent_fields.iteritems():
+ for model, names in pycompat.items(parent_fields):
defaults.update(self.env[model].default_get(names))
return defaults
@@ -1083,7 +1080,7 @@ class BaseModel(object):
:rtype: etree._Element
"""
group = E.group(col="4")
- for fname, field in self._fields.iteritems():
+ for fname, field in pycompat.items(self._fields):
if field.automatic:
continue
elif field.type in ('one2many', 'many2many', 'text', 'html'):
@@ -1500,14 +1497,14 @@ class BaseModel(object):
# avoid overriding inherited values when parent is set
avoid_models = {
parent_model
- for parent_model, parent_field in self._inherits.iteritems()
+ for parent_model, parent_field in pycompat.items(self._inherits)
if parent_field in values
}
# compute missing fields
missing_defaults = {
name
- for name, field in self._fields.iteritems()
+ for name, field in pycompat.items(self._fields)
if name not in values
if name not in MAGIC_COLUMNS
if not (field.inherited and field.related_field.model_name in avoid_models)
@@ -1518,7 +1515,7 @@ class BaseModel(object):
# override defaults with the provided values, never allow the other way around
defaults = self.default_get(list(missing_defaults))
- for name, value in defaults.iteritems():
+ for name, value in pycompat.items(defaults):
if self._fields[name].type == 'many2many' and value and isinstance(value[0], pycompat.integer_types):
# convert a list of ids into a list of commands
defaults[name] = [(6, 0, value)]
@@ -1603,10 +1600,10 @@ class BaseModel(object):
if field.relational and groups._fold_name in groups._fields:
fold = {group.id: group[groups._fold_name]
for group in groups.browse([key for key in result if key])}
- for key, line in result.iteritems():
+ for key, line in pycompat.items(result):
line['__fold'] = fold.get(key, False)
- return result.values()
+ return list(pycompat.values(result))
@api.model
def _read_group_prepare(self, orderby, aggregated_fields, annotated_groupbys, query):
@@ -1840,7 +1837,7 @@ class BaseModel(object):
def _read_group_raw(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
self.check_access_rights('read')
query = self._where_calc(domain)
- fields = fields or [f.name for f in self._fields.itervalues() if f.store]
+ fields = fields or [f.name for f in pycompat.values(self._fields) if f.store]
groupby = [groupby] if isinstance(groupby, basestring) else list(OrderedSet(groupby))
groupby_list = groupby[:1] if lazy else groupby
@@ -1914,7 +1911,7 @@ class BaseModel(object):
self._read_group_resolve_many2one_fields(fetched_data, annotated_groupbys)
- data = map(lambda r: {k: self._read_group_prepare_data(k,v, groupby_dict) for k,v in r.iteritems()}, fetched_data)
+ data = ({k: self._read_group_prepare_data(k,v, groupby_dict) for k,v in pycompat.items(r)} for r in fetched_data)
result = [self._read_group_format_result(d, annotated_groupbys, groupby, domain) for d in data]
if lazy:
# Right now, read_group only fill results in lazy mode (by default).
@@ -2020,7 +2017,7 @@ class BaseModel(object):
# fields which were required but have been removed (or will be added by
# another module)
cr = self._cr
- cols = [name for name, field in self._fields.iteritems()
+ cols = [name for name, field in pycompat.items(self._fields)
if field.store and field.column_type]
cr.execute("SELECT a.attname, a.attnotnull"
" FROM pg_class c, pg_attribute a"
@@ -2120,7 +2117,7 @@ class BaseModel(object):
recs = self.with_context(active_test=False).search([])
recs._recompute_todo(field)
- for field in self._fields.itervalues():
+ for field in pycompat.values(self._fields):
if not field.store:
continue
@@ -2208,9 +2205,9 @@ class BaseModel(object):
""" Determine inherited fields. """
# determine candidate inherited fields
fields = {}
- for parent_model, parent_field in self._inherits.iteritems():
+ for parent_model, parent_field in pycompat.items(self._inherits):
parent = self.env[parent_model]
- for name, field in parent._fields.iteritems():
+ for name, field in pycompat.items(parent._fields):
# inherited fields are implemented as related fields, with the
# following specific properties:
# - reading inherited fields should not bypass access rights
@@ -2223,13 +2220,13 @@ class BaseModel(object):
)
# add inherited fields that are not redefined locally
- for name, field in fields.iteritems():
+ for name, field in pycompat.items(fields):
if name not in self._fields:
self._add_field(name, field)
@api.model
def _inherits_check(self):
- for table, field_name in self._inherits.items():
+ for table, field_name in pycompat.items(self._inherits):
field = self._fields.get(field_name)
if not field:
_logger.info('Missing many2one field definition for _inherits reference "%s" in "%s", using default one.', field_name, self._name)
@@ -2242,7 +2239,7 @@ class BaseModel(object):
field.ondelete = "cascade"
# reflect fields with delegate=True in dictionary self._inherits
- for field in self._fields.itervalues():
+ for field in pycompat.values(self._fields):
if field.type == 'many2one' and not field.related and field.delegate:
if not field.required:
_logger.warning("Field %s with delegate=True must be required.", field)
@@ -2330,7 +2327,7 @@ class BaseModel(object):
# set up fields
bad_fields = []
- for name, field in cls._fields.iteritems():
+ for name, field in pycompat.items(cls._fields):
try:
field.setup_full(self)
except Exception:
@@ -2349,11 +2346,11 @@ class BaseModel(object):
# map each field to the fields computed with the same method
groups = defaultdict(list)
- for field in cls._fields.itervalues():
+ for field in pycompat.values(cls._fields):
if field.compute:
cls._field_computed[field] = group = groups[field.compute]
group.append(field)
- for fields in groups.itervalues():
+ for fields in pycompat.values(groups):
compute_sudo = fields[0].compute_sudo
if not all(field.compute_sudo == compute_sudo for field in fields):
_logger.warning("%s: inconsistent 'compute_sudo' for computed fields: %s",
@@ -2366,7 +2363,7 @@ class BaseModel(object):
if isinstance(self, Model):
# set up field triggers (on database-persisted models only)
- for field in cls._fields.itervalues():
+ for field in pycompat.values(cls._fields):
# dependencies of custom fields may not exist; ignore that case
exceptions = (Exception,) if field.manual else ()
with tools.ignore(*exceptions):
@@ -2405,7 +2402,7 @@ class BaseModel(object):
readonly = not (has_access('write') or has_access('create'))
res = {}
- for fname, field in self._fields.iteritems():
+ for fname, field in pycompat.items(self._fields):
if allfields and fname not in allfields:
continue
if field.groups and not self.user_has_groups(field.groups):
@@ -2417,7 +2414,7 @@ class BaseModel(object):
description['states'] = {}
if attributes:
description = {key: val
- for key, val in description.iteritems()
+ for key, val in pycompat.items(description)
if key in attributes}
res[fname] = description
@@ -2452,9 +2449,9 @@ class BaseModel(object):
return True
if not fields:
- fields = filter(valid, self._fields)
+ fields = [name for name in self._fields if valid(name)]
else:
- invalid_fields = set(filter(lambda name: not valid(name), fields))
+ invalid_fields = {name for name in fields if not valid(name)}
if invalid_fields:
_logger.info('Access Denied by ACLs for operation: %s, uid: %s, model: %s, fields: %s',
operation, self._uid, self._name, ', '.join(invalid_fields))
@@ -2528,7 +2525,7 @@ class BaseModel(object):
if self._context.get('prefetch_fields', True) and field.prefetch:
fs.update(
f
- for f in self._fields.itervalues()
+ for f in pycompat.values(self._fields)
# select fields that can be prefetched
if f.prefetch
# discard fields with groups that the user may not access
@@ -2591,7 +2588,7 @@ class BaseModel(object):
order_str = self._generate_order_by(None, query)
# determine the fields that are stored as columns in tables;
- fields = map(self._fields.get, field_names + inherited_field_names)
+ fields = [self._fields[n] for n in (field_names + inherited_field_names)]
fields_pre = [
field
for field in fields
@@ -2608,7 +2605,7 @@ class BaseModel(object):
res = 'pg_size_pretty(length(%s)::bigint)' % res
return '%s as "%s"' % (res, col)
- qual_names = map(qualify, set(fields_pre + [self._fields['id']]))
+ qual_names = [qualify(name) for name in set(fields_pre + [self._fields['id']])]
# determine the actual query to execute
from_clause, where_clause, params = query.get_sql()
@@ -2666,8 +2663,7 @@ class BaseModel(object):
if extras:
raise AccessError(
_("Database fetch misses ids ({}) and has extra ids ({}), may be caused by a type incoherence in a previous request").format(
- ', '.join(map(repr, missing._ids)),
- ', '.join(map(repr, extras._ids)),
+ missing._ids, extras._ids,
))
# mark non-existing records in missing
forbidden = missing.exists()
@@ -2990,7 +2986,7 @@ class BaseModel(object):
# split up fields into old-style and pure new-style ones
old_vals, new_vals, unknown = {}, {}, []
- for key, val in vals.iteritems():
+ for key, val in pycompat.items(vals):
field = self._fields.get(key)
if field:
if field.store or field.inherited:
@@ -3003,7 +2999,7 @@ class BaseModel(object):
if unknown:
_logger.warning("%s.write() with unknown fields: %s", self._name, ', '.join(sorted(unknown)))
- protected_fields = map(self._fields.get, new_vals)
+ protected_fields = [self._fields[n] for n in new_vals]
with self.env.protecting(protected_fields, self):
# write old-style fields with (low-level) method _write
if old_vals:
@@ -3054,7 +3050,7 @@ class BaseModel(object):
query = "SELECT id FROM %s WHERE id IN %%s AND (%s IS NOT NULL) ORDER BY %s" % \
(self._table, self._parent_name, self._parent_order)
cr.execute(query, (tuple(self.ids),))
- parents_changed = map(operator.itemgetter(0), cr.fetchall())
+ parents_changed = [x[0] for x in cr.fetchall()]
updates = [] # list of (column, expr) or (column, pattern, value)
upd_todo = [] # list of column names to set explicitly
@@ -3062,7 +3058,7 @@ class BaseModel(object):
direct = [] # list of direcly updated columns
has_trans = self.env.lang and self.env.lang != 'en_US'
single_lang = len(self.env['res.lang'].get_installed()) <= 1
- for name, val in vals.iteritems():
+ for name, val in pycompat.items(vals):
field = self._fields[name]
if field and field.deprecated:
_logger.warning('Field %s.%s is deprecated: %s', self._name, name, field.deprecated)
@@ -3124,7 +3120,7 @@ class BaseModel(object):
# defaults in context must be removed when call a one2many or many2many
rel_context = {key: val
- for key, val in self._context.iteritems()
+ for key, val in pycompat.items(self._context)
if not key.startswith('default_')}
# call the 'write' method of fields which are not columns
@@ -3137,7 +3133,7 @@ class BaseModel(object):
# write inherited fields on the corresponding parent records
unknown_fields = set(updend)
- for parent_model, parent_field in self._inherits.iteritems():
+ for parent_model, parent_field in pycompat.items(self._inherits):
parent_ids = []
for sub_ids in cr.split_for_in_conditions(self.ids):
query = "SELECT DISTINCT %s FROM %s WHERE id IN %%s" % (parent_field, self._table)
@@ -3258,7 +3254,7 @@ class BaseModel(object):
# split up fields into old-style and pure new-style ones
old_vals, new_vals, unknown = {}, {}, []
- for key, val in vals.iteritems():
+ for key, val in pycompat.items(vals):
field = self._fields.get(key)
if field:
if field.store or field.inherited:
@@ -3274,7 +3270,7 @@ class BaseModel(object):
# create record with old-style fields
record = self.browse(self._create(old_vals))
- protected_fields = map(self._fields.get, new_vals)
+ protected_fields = [self._fields[n] for n in new_vals]
with self.env.protecting(protected_fields, record):
# put the values of pure new-style fields into cache, and inverse them
record.modified(set(new_vals) - set(old_vals))
@@ -3299,7 +3295,7 @@ class BaseModel(object):
# data of parent records to create or update, by model
tocreate = {
parent_model: {'id': vals.pop(parent_field, None)}
- for parent_model, parent_field in self._inherits.iteritems()
+ for parent_model, parent_field in pycompat.items(self._inherits)
}
# list of column assignments defined as tuples like:
@@ -3314,7 +3310,7 @@ class BaseModel(object):
upd_todo = []
unknown_fields = []
protected_fields = []
- for name, val in vals.items():
+ for name, val in list(pycompat.items(vals)):
field = self._fields.get(name)
if not field:
unknown_fields.append(name)
@@ -3330,7 +3326,7 @@ class BaseModel(object):
_logger.warning('No such field(s) in model %s: %s.', self._name, ', '.join(unknown_fields))
# create or update parent records
- for parent_model, parent_vals in tocreate.iteritems():
+ for parent_model, parent_vals in pycompat.items(tocreate):
parent_id = parent_vals.pop('id')
if not parent_id:
parent_id = self.env[parent_model].create(parent_vals).id
@@ -3339,12 +3335,12 @@ class BaseModel(object):
updates.append((self._inherits[parent_model], '%s', parent_id))
# set boolean fields to False by default (to make search more powerful)
- for name, field in self._fields.iteritems():
+ for name, field in pycompat.items(self._fields):
if field.type == 'boolean' and field.store and name not in vals:
vals[name] = False
# determine SQL values
- for name, val in vals.iteritems():
+ for name, val in pycompat.items(vals):
field = self._fields[name]
if field.store and field.column_type:
updates.append((name, field.column_format, field.convert_to_column(val, self)))
@@ -3375,7 +3371,7 @@ class BaseModel(object):
if self.env.lang and self.env.lang != 'en_US':
# add translations for self.env.lang
- for name, val in vals.iteritems():
+ for name, val in pycompat.items(vals):
field = self._fields[name]
if field.store and field.column_type and field.translate is True:
tname = "%s,%s" % (self._name, name)
@@ -3423,7 +3419,7 @@ class BaseModel(object):
# defaults in context must be removed when call a one2many or many2many
rel_context = {key: val
- for key, val in self._context.iteritems()
+ for key, val in pycompat.items(self._context)
if not key.startswith('default_')}
# call the 'write' method of fields which are not columns
@@ -3728,11 +3724,11 @@ class BaseModel(object):
# build a black list of fields that should not be copied
blacklist = set(MAGIC_COLUMNS + ['parent_left', 'parent_right'])
- whitelist = set(name for name, field in self._fields.iteritems() if not field.inherited)
+ whitelist = set(name for name, field in pycompat.items(self._fields) if not field.inherited)
def blacklist_given_fields(model):
# blacklist the fields that are given by inheritance
- for parent_model, parent_field in model._inherits.items():
+ for parent_model, parent_field in pycompat.items(model._inherits):
blacklist.add(parent_field)
if parent_field in default:
# all the fields of 'parent_model' are given by the record:
@@ -3741,17 +3737,17 @@ class BaseModel(object):
else:
blacklist_given_fields(self.env[parent_model])
# blacklist deprecated fields
- for name, field in model._fields.iteritems():
+ for name, field in pycompat.items(model._fields):
if field.deprecated:
blacklist.add(name)
blacklist_given_fields(self)
fields_to_copy = {name: field
- for name, field in self._fields.iteritems()
+ for name, field in pycompat.items(self._fields)
if field.copy and name not in default and name not in blacklist}
- for name, field in fields_to_copy.iteritems():
+ for name, field in pycompat.items(fields_to_copy):
if field.type == 'one2many':
# duplicate following the order of the ids because we'll rely on
# it later for copying translations in copy_translation()!
@@ -3789,7 +3785,7 @@ class BaseModel(object):
old_wo_lang, new_wo_lang = (old + new).with_context(lang=None)
Translation = old.env['ir.translation']
- for name, field in old._fields.iteritems():
+ for name, field in pycompat.items(old._fields):
if not field.copy:
continue
@@ -3799,7 +3795,7 @@ class BaseModel(object):
# foreseen in copy_data()
old_lines = old[name].sorted(key='id')
new_lines = new[name].sorted(key='id')
- for (old_line, new_line) in zip(old_lines, new_lines):
+ for (old_line, new_line) in pycompat.izip(old_lines, new_lines):
old_line.copy_translations(new_line)
elif field.translate:
@@ -3969,7 +3965,7 @@ class BaseModel(object):
"""
results = self._get_external_ids()
return {key: val[0] if val else ''
- for key, val in results.iteritems()}
+ for key, val in pycompat.items(results)}
# backwards compatibility
get_xml_id = get_external_id
@@ -4236,7 +4232,7 @@ class BaseModel(object):
""" List of actual record ids in this recordset (ignores placeholder
ids for records to create)
"""
- return filter(None, list(self._ids))
+ return [it for it in self._ids if it]
# backward-compatibility with former browse records
_cr = property(lambda self: self.env.cr)
@@ -4342,7 +4338,7 @@ class BaseModel(object):
target = self if update else self.browse([], self._prefetch)
return {
name: fields[name].convert_to_cache(value, target, validate=validate)
- for name, value in values.iteritems()
+ for name, value in pycompat.items(values)
if name in fields
}
@@ -4352,14 +4348,14 @@ class BaseModel(object):
"""
return {
name: self._fields[name].convert_to_record(value, self)
- for name, value in values.iteritems()
+ for name, value in pycompat.items(values)
}
def _convert_to_write(self, values):
""" Convert the ``values`` dictionary into the format of :meth:`write`. """
fields = self._fields
result = {}
- for name, value in values.iteritems():
+ for name, value in pycompat.items(values):
if name in fields:
field = fields[name]
value = field.convert_to_cache(value, self, validate=False)
@@ -4423,7 +4419,7 @@ class BaseModel(object):
"""
if isinstance(func, basestring):
name = func
- func = lambda rec: filter(None, rec.mapped(name))
+ func = lambda rec: any(rec.mapped(name))
return self.browse([rec.id for rec in self if func(rec)])
def sorted(self, key=None, reverse=False):
@@ -4440,13 +4436,13 @@ class BaseModel(object):
return self.browse(reversed(recs._ids)) if reverse else recs
if isinstance(key, basestring):
key = itemgetter(key)
- return self.browse(map(attrgetter('id'), sorted(self, key=key, reverse=reverse)))
+ return self.browse(item.id for item in sorted(self, key=key, reverse=reverse))
@api.multi
def update(self, values):
""" Update the records in ``self`` with ``values``. """
for record in self:
- for name, value in values.iteritems():
+ for name, value in pycompat.items(values):
record[name] = value
#
@@ -4667,7 +4663,7 @@ class BaseModel(object):
the records of model ``self`` in cache that have no value for ``field``
(:class:`Field` instance).
"""
- ids = filter(None, self._prefetch[self._name] - set(self.env.cache[field]))
+ ids = [it for it in self._prefetch[self._name] - set(self.env.cache[field]) if it]
return self.browse(ids)
@api.model
@@ -4690,9 +4686,9 @@ class BaseModel(object):
if fnames is None:
if ids is None:
return self.env.invalidate_all()
- fields = self._fields.values()
+ fields = list(pycompat.values(self._fields))
else:
- fields = map(self._fields.__getitem__, fnames)
+ fields = [self._fields[n] for n in fnames]
# invalidate fields and inverse fields, too
spec = [(f, ids) for f in fields] + \
@@ -4746,10 +4742,11 @@ class BaseModel(object):
updates[frozendict(vals)].add(rec.id)
# update records in batch when possible
with recs.env.norecompute():
- for vals, ids in updates.iteritems():
+ for vals, ids in pycompat.items(updates):
recs.browse(ids)._write(dict(vals))
# mark computed fields as done
- map(recs._recompute_done, fs)
+ for f in fs:
+ recs._recompute_done(f)
#
# Generic onchange method
@@ -4779,7 +4776,7 @@ class BaseModel(object):
if not result.get(names):
result[names] = node.attrib.get('on_change')
# traverse the subviews included in relational fields
- for subinfo in info['fields'][name].get('views', {}).itervalues():
+ for subinfo in pycompat.values(info['fields'][name].get('views', {})):
process(etree.fromstring(subinfo['arch']), subinfo, names)
else:
for child in node:
@@ -4802,19 +4799,19 @@ class BaseModel(object):
return
if res.get('value'):
res['value'].pop('id', None)
- self.update({key: val for key, val in res['value'].iteritems() if key in self._fields})
+ self.update({key: val for key, val in pycompat.items(res['value']) if key in self._fields})
if res.get('domain'):
result.setdefault('domain', {}).update(res['domain'])
if res.get('warning'):
if result.get('warning'):
# Concatenate multiple warnings
warning = result['warning']
- warning['message'] = '\n\n'.join(filter(None, [
+ warning['message'] = '\n\n'.join(s for s in [
warning.get('title'),
warning.get('message'),
res['warning'].get('title'),
res['warning'].get('message'),
- ]))
+ ] if s)
warning['title'] = _('Warnings')
else:
result['warning'] = res['warning']
@@ -4931,7 +4928,7 @@ class BaseModel(object):
record.mapped(field_seq)
# determine which fields have been modified
- for name, oldval in values.iteritems():
+ for name, oldval in pycompat.items(values):
field = self._fields[name]
newval = record[name]
if newval != oldval or (
@@ -5005,7 +5002,7 @@ class RecordCache(MutableMapping):
"""
if args and isinstance(args[0], SpecialValue):
values = dict.fromkeys(self._recs._ids, args[0])
- for name, field in self._recs._fields.iteritems():
+ for name, field in pycompat.items(self._recs._fields):
if name != 'id':
self._recs.env.cache[field].update(values)
else:
@@ -5023,7 +5020,7 @@ class RecordCache(MutableMapping):
""" Iterate over the field names with a regular value in cache. """
cache, id = self._recs.env.cache, self._recs.id
dummy = SpecialValue(None)
- for name, field in self._recs._fields.iteritems():
+ for name, field in pycompat.items(self._recs._fields):
if name != 'id' and not isinstance(cache[field].get(id, dummy), SpecialValue):
yield name
diff --git a/odoo/modules/db.py b/odoo/modules/db.py
index 7106ff82b69..bde759843cf 100644
--- a/odoo/modules/db.py
+++ b/odoo/modules/db.py
@@ -99,7 +99,7 @@ def create_categories(cr, categories):
category = []
while categories:
category.append(categories[0])
- xml_id = 'module_category_' + ('_'.join(map(lambda x: x.lower(), category))).replace('&', 'and').replace(' ', '_')
+ xml_id = 'module_category_' + ('_'.join(x.lower() for x in category)).replace('&', 'and').replace(' ', '_')
# search via xml_id (because some categories are renamed)
cr.execute("SELECT res_id FROM ir_model_data WHERE name=%s AND module=%s AND model=%s",
(xml_id, "base", "ir.module.category"))
diff --git a/odoo/modules/graph.py b/odoo/modules/graph.py
index d962ea78bdc..731b6f6557a 100644
--- a/odoo/modules/graph.py
+++ b/odoo/modules/graph.py
@@ -8,7 +8,7 @@ import logging
import odoo
import odoo.tools as tools
-
+from odoo.tools import pycompat
_logger = logging.getLogger(__name__)
@@ -36,7 +36,7 @@ class Graph(dict):
return
# update the graph with values from the database (if exist)
## First, we set the default values for each package in graph
- additional_data = dict((key, {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'installed_version': None}) for key in self.keys())
+ additional_data = {key: {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'installed_version': None} for key in pycompat.keys(self)}
## Then we get the values from the database
cr.execute('SELECT name, id, state, demo AS dbdemo, latest_version AS installed_version'
' FROM ir_module_module'
@@ -46,8 +46,8 @@ class Graph(dict):
## and we update the default values with values from the database
additional_data.update((x['name'], x) for x in cr.dictfetchall())
- for package in self.values():
- for k, v in additional_data[package.name].items():
+ for package in pycompat.values(self):
+ for k, v in pycompat.items(additional_data[package.name]):
setattr(package, k, v)
def add_module(self, cr, module, force=None):
@@ -94,7 +94,7 @@ class Graph(dict):
self.update_from_db(cr)
for package in later:
- unmet_deps = filter(lambda p: p not in self, dependencies[package])
+ unmet_deps = [p for p in dependencies[package] if p not in self]
_logger.error('module %s: Unmet dependencies: %s', package, ', '.join(unmet_deps))
return len(self) - len_graph
@@ -102,9 +102,9 @@ class Graph(dict):
def __iter__(self):
level = 0
- done = set(self.keys())
+ done = set(pycompat.keys(self))
while done:
- level_modules = sorted((name, module) for name, module in self.items() if module.depth==level)
+ level_modules = sorted((name, module) for name, module in pycompat.items(self) if module.depth==level)
for name, module in level_modules:
done.remove(name)
yield module
@@ -164,7 +164,10 @@ class Node(object):
setattr(child, name, value + 1)
def __iter__(self):
- return itertools.chain(iter(self.children), *map(iter, self.children))
+ return itertools.chain(
+ self.children,
+ itertools.chain.from_iterable(self.children)
+ )
def __str__(self):
return self._pprint()
diff --git a/odoo/modules/loading.py b/odoo/modules/loading.py
index f8c65d40177..bd58376a7b9 100644
--- a/odoo/modules/loading.py
+++ b/odoo/modules/loading.py
@@ -20,6 +20,7 @@ import odoo.tools as tools
from odoo import api, SUPERUSER_ID
from odoo.modules.module import adapt_version, initialize_sys_path, load_openerp_module
+from odoo.tools import pycompat
_logger = logging.getLogger(__name__)
_test_logger = logging.getLogger('odoo.tests')
@@ -295,15 +296,15 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
_logger.info('updating modules list')
Module.update_list()
- _check_module_names(cr, itertools.chain(tools.config['init'].keys(), tools.config['update'].keys()))
+ _check_module_names(cr, itertools.chain(tools.config['init'], tools.config['update']))
- module_names = [k for k, v in tools.config['init'].items() if v]
+ module_names = [k for k, v in pycompat.items(tools.config['init']) if v]
if module_names:
modules = Module.search([('state', '=', 'uninstalled'), ('name', 'in', module_names)])
if modules:
modules.button_install()
- module_names = [k for k, v in tools.config['update'].items() if v]
+ module_names = [k for k, v in pycompat.items(tools.config['update']) if v]
if module_names:
modules = Module.search([('state', '=', 'installed'), ('name', 'in', module_names)])
if modules:
@@ -391,7 +392,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
getattr(py_module, uninstall_hook)(cr, registry)
Module = env['ir.module.module']
- Module.browse(modules_to_remove.values()).module_uninstall()
+ Module.browse(pycompat.values(modules_to_remove)).module_uninstall()
# Recursive reload, should only happen once, because there should be no
# modules to remove next time
cr.commit()
@@ -414,7 +415,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
_logger.info('Modules loaded.')
# STEP 8: call _register_hook on every model
- for model in env.values():
+ for model in pycompat.values(env):
model._register_hook()
# STEP 9: save installed/updated modules for post-install tests
diff --git a/odoo/modules/migration.py b/odoo/modules/migration.py
index 5b1dcf09ab2..0fa6699cf24 100644
--- a/odoo/modules/migration.py
+++ b/odoo/modules/migration.py
@@ -13,6 +13,7 @@ from os.path import join as opj
from odoo.modules.module import get_resource_path
import odoo.release as release
import odoo.tools as tools
+from odoo.tools import pycompat
from odoo.tools.parse_version import parse_version
@@ -87,13 +88,12 @@ class MigrationManager(object):
return "%s.%s" % (release.major_version, version)
def _get_migration_versions(pkg):
- versions = list(set(
+ versions = sorted({
ver
- for lv in self.migrations[pkg.name].values()
- for ver, lf in lv.items()
+ for lv in pycompat.values(self.migrations[pkg.name])
+ for ver, lf in pycompat.items(lv)
if lf
- ))
- versions.sort(key=lambda k: parse_version(convert_version(k)))
+ }, key=lambda k: parse_version(convert_version(k)))
return versions
def _get_migration_files(pkg, version, stage):
@@ -107,7 +107,7 @@ class MigrationManager(object):
'maintenance': opj('base', 'maintenance', 'migrations', pkg.name),
}
- for x in mapping.keys():
+ for x in mapping:
if version in m.get(x):
for f in m[x][version]:
if not f.startswith(stage + '-'):
diff --git a/odoo/modules/module.py b/odoo/modules/module.py
index dd1485e1d8a..e6194a5fb10 100644
--- a/odoo/modules/module.py
+++ b/odoo/modules/module.py
@@ -23,6 +23,7 @@ import odoo
import odoo.tools as tools
import odoo.release as release
from odoo import SUPERUSER_ID, api
+from odoo.tools import pycompat
MANIFEST_NAMES = ('__manifest__.py', '__openerp__.py')
README = ['README.rst', 'README.md', 'README.txt']
@@ -323,7 +324,7 @@ def load_information_from_description_file(module, mod_path=None):
'sequence': 100,
'summary': '',
}
- info.update(itertools.izip(
+ info.update(pycompat.izip(
'depends data demo test init_xml update_xml demo_xml'.split(),
iter(list, None)))
@@ -395,7 +396,11 @@ def get_modules():
for mname in MANIFEST_NAMES:
if os.path.isfile(opj(dir, name, mname)):
return True
- return map(clean, filter(is_really_module, os.listdir(dir)))
+ return [
+ clean(it)
+ for it in os.listdir(dir)
+ if is_really_module(it)
+ ]
plist = []
initialize_sys_path()
@@ -492,7 +497,7 @@ def run_unit_tests(module_name, dbname, position=runs_at_install):
r = True
for m in mods:
tests = unwrap_suite(unittest.TestLoader().loadTestsFromModule(m))
- suite = unittest.TestSuite(itertools.ifilter(position, tests))
+ suite = unittest.TestSuite(t for t in tests if position(t))
if suite.countTestCases():
t0 = time.time()
@@ -532,5 +537,5 @@ def unwrap_suite(test):
return
for item in itertools.chain.from_iterable(
- itertools.imap(unwrap_suite, subtests)):
+ unwrap_suite(t) for t in subtests):
yield item
diff --git a/odoo/modules/registry.py b/odoo/modules/registry.py
index e0cfb532fd1..71d7b5d9fbe 100644
--- a/odoo/modules/registry.py
+++ b/odoo/modules/registry.py
@@ -16,7 +16,8 @@ import threading
import odoo
from .. import SUPERUSER_ID
from odoo.tools import (assertion_report, lazy_classproperty, config,
- lazy_property, table_exists, topological_sort, OrderedSet)
+ lazy_property, table_exists, topological_sort,
+ OrderedSet, pycompat)
from odoo.tools.lru import LRU
_logger = logging.getLogger(__name__)
@@ -156,7 +157,7 @@ class Registry(Mapping):
def delete_all(cls):
""" Delete all the registries. """
with cls._lock:
- for db_name in cls.registries.keys():
+ for db_name in list(pycompat.keys(cls.registries)):
cls.delete(db_name)
#
@@ -192,8 +193,8 @@ class Registry(Mapping):
# map fields on their dependents
dependents = {
field: set(dep for dep, _ in model._field_triggers[field] if dep != field)
- for model in self.itervalues()
- for field in model._fields.itervalues()
+ for model in pycompat.values(self)
+ for field in pycompat.values(model._fields)
}
# sort them topologically, and associate a sequence number to each field
mapping = {
@@ -261,7 +262,7 @@ class Registry(Mapping):
env['ir.model']._add_manual_models()
# prepare the setup on all models
- models = env.values()
+ models = list(pycompat.values(env))
for model in models:
model._prepare_setup()
@@ -310,18 +311,18 @@ class Registry(Mapping):
# make sure all tables are present
missing = [name
- for name, model in env.items()
+ for name, model in pycompat.items(env)
if not model._abstract and not table_exists(cr, model._table)]
if missing:
_logger.warning("Models have no table: %s.", ", ".join(missing))
# recreate missing tables following model dependencies
- deps = {name: model._depends for name, model in env.items()}
+ deps = {name: model._depends for name, model in pycompat.items(env)}
for name in topological_sort(deps):
if name in missing:
_logger.info("Recreate table of model %s.", name)
env[name].init()
# check again, and log errors if tables are still missing
- for name, model in env.items():
+ for name, model in pycompat.items(env):
if not model._abstract and not table_exists(cr, model._table):
_logger.error("Model %s has no table.", name)
@@ -340,7 +341,7 @@ class Registry(Mapping):
""" Clear the caches associated to methods decorated with
``tools.ormcache`` or ``tools.ormcache_multi`` for all the models.
"""
- for model in self.models.itervalues():
+ for model in pycompat.values(self.models):
model.clear_caches()
def setup_signaling(self):
diff --git a/odoo/osv/expression.py b/odoo/osv/expression.py
index b98fca44b39..8df300a2bd4 100644
--- a/odoo/osv/expression.py
+++ b/odoo/osv/expression.py
@@ -1018,7 +1018,7 @@ class expression(object):
subop = 'not inselect' if operator in NEGATIVE_TERM_OPERATORS else 'inselect'
subquery = 'SELECT "%s" FROM "%s" WHERE "%s" IN %%s' % (rel_id1, rel_table, rel_id2)
# avoid flattening of argument in to_sql()
- subquery = cr.mogrify(subquery, [tuple(filter(None, res_ids))])
+ subquery = cr.mogrify(subquery, [tuple(it for it in res_ids if it)])
push(create_substitution_leaf(leaf, ('id', subop, (subquery, [])), internal=True))
if call_null_m2m:
@@ -1200,7 +1200,7 @@ class expression(object):
else:
field = model._fields[left]
instr = ','.join([field.column_format] * len(params))
- params = map(partial(field.convert_to_column, record=model), params)
+ params = [field.convert_to_column(p, record=model) for p in params]
query = '(%s."%s" %s (%s))' % (table_alias, left, operator, instr)
else:
# The case for (left, 'in', []) or (left, 'not in', []).
diff --git a/odoo/osv/orm.py b/odoo/osv/orm.py
index 6a815c9577a..c06c4e66496 100644
--- a/odoo/osv/orm.py
+++ b/odoo/osv/orm.py
@@ -1,6 +1,7 @@
import json
from lxml import etree
+from odoo.tools import pycompat
from ..exceptions import except_orm
from ..models import (
MetaModel,
@@ -35,12 +36,12 @@ def transfer_field_to_modifiers(field, modifiers):
for attr in ('invisible', 'readonly', 'required'):
state_exceptions[attr] = []
default_values[attr] = bool(field.get(attr))
- for state, modifs in (field.get("states",{})).items():
+ for state, modifs in pycompat.items(field.get("states",{})):
for modif in modifs:
if default_values[modif[0]] != modif[1]:
state_exceptions[modif[0]].append(state)
- for attr, default_value in default_values.items():
+ for attr, default_value in pycompat.items(default_values):
if state_exceptions[attr]:
modifiers[attr] = [("state", "not in" if default_value else "in", state_exceptions[attr])]
else:
diff --git a/odoo/release.py b/odoo/release.py
index 51385a7a02b..c563d9ff4cd 100644
--- a/odoo/release.py
+++ b/odoo/release.py
@@ -13,8 +13,8 @@ RELEASE_LEVELS_DISPLAY = {ALPHA: ALPHA,
# (6,1,0,'beta',0) < (6,1,0,'candidate',1) < (6,1,0,'candidate',2)
# (6,1,0,'candidate',2) < (6,1,0,'final',0) < (6,1,2,'final',0)
version_info = (11, 0, 0, ALPHA, 1, '')
-version = '.'.join(map(str, version_info[:2])) + RELEASE_LEVELS_DISPLAY[version_info[3]] + str(version_info[4] or '') + version_info[5]
-series = serie = major_version = '.'.join(map(str, version_info[:2]))
+version = '.'.join(str(s) for s in version_info[:2]) + RELEASE_LEVELS_DISPLAY[version_info[3]] + str(version_info[4] or '') + version_info[5]
+series = serie = major_version = '.'.join(str(s) for s in version_info[:2])
product_name = 'Odoo'
description = 'Odoo Server'
diff --git a/odoo/service/db.py b/odoo/service/db.py
index e20dcb7442a..e451b151d31 100644
--- a/odoo/service/db.py
+++ b/odoo/service/db.py
@@ -351,7 +351,7 @@ def list_db_incompatible(databases):
:return: A list of databases that are incompatible
"""
incompatible_databases = []
- server_version = '.'.join(map(str, version_info[:2]))
+ server_version = '.'.join(str(v) for v in version_info[:2])
for database_name in databases:
with closing(db_connect(database_name).cursor()) as cr:
if odoo.tools.table_exists(cr, 'ir_module_module'):
diff --git a/odoo/service/model.py b/odoo/service/model.py
index ba243bf432c..78a07460a38 100644
--- a/odoo/service/model.py
+++ b/odoo/service/model.py
@@ -137,7 +137,7 @@ def check(f):
time.sleep(wait_time)
except IntegrityError as inst:
registry = odoo.registry(dbname)
- for key in registry._sql_error.keys():
+ for key in pycompat.keys(registry._sql_error):
if key in inst[0]:
raise ValidationError(tr(registry._sql_error[key], 'sql_constraint') or inst[0])
if inst.pgcode in (errorcodes.NOT_NULL_VIOLATION, errorcodes.FOREIGN_KEY_VIOLATION, errorcodes.RESTRICT_VIOLATION):
diff --git a/odoo/service/server.py b/odoo/service/server.py
index 5de54d8188e..fa6ed422f81 100644
--- a/odoo/service/server.py
+++ b/odoo/service/server.py
@@ -40,7 +40,7 @@ from odoo.modules.module import run_unit_tests, runs_post_install
from odoo.modules.registry import Registry
from odoo.release import nt_service_name
import odoo.tools.config as config
-from odoo.tools import stripped_sys_argv, dumpstacks, log_ormcache_stats
+from odoo.tools import stripped_sys_argv, dumpstacks, log_ormcache_stats, pycompat
_logger = logging.getLogger(__name__)
@@ -216,7 +216,7 @@ class ThreadedServer(CommonServer):
time.sleep(SLEEP_INTERVAL + number) # Steve Reich timing style
registries = odoo.modules.registry.Registry.registries
_logger.debug('cron%d polling for jobs', number)
- for db_name, registry in registries.iteritems():
+ for db_name, registry in pycompat.items(registries):
while registry.ready:
try:
acquired = odoo.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name)
@@ -513,7 +513,7 @@ class PreforkServer(CommonServer):
def process_timeout(self):
now = time.time()
- for (pid, worker) in self.workers.items():
+ for (pid, worker) in pycompat.items(self.workers):
if worker.watchdog_timeout is not None and \
(now - worker.watchdog_time) >= worker.watchdog_timeout:
_logger.error("%s (%s) timeout after %ss",
@@ -534,8 +534,8 @@ class PreforkServer(CommonServer):
def sleep(self):
try:
# map of fd -> worker
- fds = dict([(w.watchdog_pipe[0], w) for k, w in self.workers.items()])
- fd_in = fds.keys() + [self.pipe[0]]
+ fds = {w.watchdog_pipe[0]: w for k, w in pycompat.items(self.workers)}
+ fd_in = list(pycompat.keys(fds)) + [self.pipe[0]]
# check for ping or internal wakeups
ready = select.select(fd_in, [], [], self.beat)
# update worker watchdogs
@@ -584,7 +584,7 @@ class PreforkServer(CommonServer):
if graceful:
_logger.info("Stopping gracefully")
limit = time.time() + self.timeout
- for pid in self.workers.keys():
+ for pid in self.workers:
self.worker_kill(pid, signal.SIGINT)
while self.workers and time.time() < limit:
try:
@@ -596,7 +596,7 @@ class PreforkServer(CommonServer):
time.sleep(0.1)
else:
_logger.info("Stopping forcefully")
- for pid in self.workers.keys():
+ for pid in self.workers:
self.worker_kill(pid, signal.SIGTERM)
if self.socket:
self.socket.close()
@@ -874,7 +874,7 @@ def load_test_file_yml(registry, test_file):
def load_test_file_py(registry, test_file):
# Locate python module based on its filename and run the tests
test_path, _ = os.path.splitext(os.path.abspath(test_file))
- for mod_name, mod_mod in sys.modules.items():
+ for mod_name, mod_mod in pycompat.items(sys.modules):
if mod_mod:
mod_path, _ = os.path.splitext(getattr(mod_mod, '__file__', ''))
if test_path == mod_path:
diff --git a/odoo/sql_db.py b/odoo/sql_db.py
index 9a438d387de..a722c3ea41b 100644
--- a/odoo/sql_db.py
+++ b/odoo/sql_db.py
@@ -47,7 +47,7 @@ def undecimalize(symb, cr):
return None
return float(symb)
-for name, typeoid in types_mapping.items():
+for name, typeoid in pycompat.items(types_mapping):
psycopg2.extensions.register_type(psycopg2.extensions.new_type(typeoid, name, lambda x, cr: x))
psycopg2.extensions.register_type(psycopg2.extensions.new_type((700, 701, 1700,), 'float', undecimalize))
@@ -190,9 +190,9 @@ class Cursor(object):
row = self._obj.fetchone()
return row and self.__build_dict(row)
def dictfetchmany(self, size):
- return map(self.__build_dict, self._obj.fetchmany(size))
+ return [self.__build_dict(row) for row in self._obj.fetchmany(size)]
def dictfetchall(self):
- return map(self.__build_dict, self._obj.fetchall())
+ return [self.__build_dict(row) for row in self._obj.fetchall()]
def __del__(self):
if not self._closed and not self._cnx.closed:
@@ -260,7 +260,7 @@ class Cursor(object):
sqllogs = {'from': self.sql_from_log, 'into': self.sql_into_log}
sum = 0
if sqllogs[type]:
- sqllogitems = sqllogs[type].items()
+ sqllogitems = pycompat.items(sqllogs[type])
_logger.debug("SQL LOG %s:", type)
for r in sorted(sqllogitems, key=lambda k: k[1]):
delay = timedelta(microseconds=r[1][1])
@@ -473,7 +473,7 @@ class LazyCursor(object):
if cr is None:
from odoo import registry
cr = self._cursor = registry(self.dbname).cursor()
- for _ in pycompat.range(self._depth):
+ for _ in range(self._depth):
cr.__enter__()
return getattr(cr, name)
diff --git a/odoo/tests/common.py b/odoo/tests/common.py
index 78cfbfbdbf9..a491c23ef76 100644
--- a/odoo/tests/common.py
+++ b/odoo/tests/common.py
@@ -135,7 +135,7 @@ class BaseCase(unittest.TestCase):
def shortDescription(self):
doc = self._testMethodDoc
- return doc and ' '.join(filter(None, map(str.strip, doc.splitlines()))) or None
+ return doc and ' '.join(l.strip() for l in doc.splitlines() if not l.isspace()) or None
class TransactionCase(BaseCase):
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):