diff options
| author | xmo-odoo <xmo@odoo.com> | 2017-05-10 09:39:55 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-05-10 09:39:55 +0200 |
| commit | fffaf735f58f1ac57781c095eb552de6d1fcdc47 (patch) | |
| tree | 88ab0ab65430d871862a836524e0ecc890d1674d /odoo/addons/base/ir | |
| parent | 10eee18a047d35305dc46282e610e88936d81ffc (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/addons/base/ir')
| -rw-r--r-- | odoo/addons/base/ir/ir_actions.py | 25 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_attachment.py | 10 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_config_parameter.py | 4 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_fields.py | 16 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_http.py | 5 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_mail_server.py | 17 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_model.py | 30 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_qweb/assetsbundle.py | 8 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_qweb/ir_qweb.py | 8 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_qweb/qweb.py | 55 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_sequence.py | 3 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_translation.py | 6 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_ui_view.py | 38 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_values.py | 6 |
14 files changed, 127 insertions, 104 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)) |
