diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 29fcaa17c8e..daa407d636f 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -5,7 +5,7 @@ import math from odoo.osv import expression from odoo.tools.float_utils import float_round as round -from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, pycompat +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT from odoo.exceptions import UserError, ValidationError from odoo import api, fields, models, _ diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index 87480662784..ee0d1903fd0 100644 --- a/addons/account/models/account_invoice.py +++ b/addons/account/models/account_invoice.py @@ -395,7 +395,7 @@ class AccountInvoice(models.Model): '_onchange_partner_id': ['account_id', 'payment_term_id', 'fiscal_position_id', 'partner_bank_id'], '_onchange_journal_id': ['currency_id'], } - for onchange_method, changed_fields in pycompat.items(onchanges): + for onchange_method, changed_fields in onchanges.items(): if any(f not in vals for f in changed_fields): invoice = self.new(vals) getattr(invoice, onchange_method)() @@ -516,7 +516,7 @@ class AccountInvoice(models.Model): tax_grouped = invoice.get_taxes_values() # Create new tax lines - for tax in pycompat.values(tax_grouped): + for tax in tax_grouped.values(): account_invoice_tax.create(tax) # dummy write on self to trigger recomputations @@ -535,7 +535,7 @@ class AccountInvoice(models.Model): def _onchange_invoice_line_ids(self): taxes_grouped = self.get_taxes_values() tax_lines = self.tax_line_ids.filtered('manual') - for tax in pycompat.values(taxes_grouped): + for tax in taxes_grouped.values(): tax_lines += tax_lines.new(tax) self.tax_line_ids = tax_lines return @@ -886,7 +886,7 @@ class AccountInvoice(models.Model): else: line2[tmp] = l line = [] - for key, val in pycompat.items(line2): + for key, val in line2.items(): line.append((0, 0, val)) return line @@ -1079,7 +1079,7 @@ class AccountInvoice(models.Model): result = [] for line in lines: values = {} - for name, field in pycompat.items(line._fields): + for name, field in line._fields.items(): if name in MAGIC_COLUMNS: continue elif field.type == 'many2one': @@ -1249,7 +1249,7 @@ class AccountInvoice(models.Model): for line in self.tax_line_ids: res.setdefault(line.tax_id.tax_group_id, 0.0) res[line.tax_id.tax_group_id] += line.amount - res = sorted(pycompat.items(res), key=lambda l: l[0].sequence) + res = sorted(res.items(), key=lambda l: l[0].sequence) res = [(l[0].name, l[1]) for l in res] return res diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py index 2fe33e02e3a..d99b3fe99ce 100644 --- a/addons/account/models/chart_template.py +++ b/addons/account/models/chart_template.py @@ -7,8 +7,6 @@ from odoo.exceptions import UserError import logging -from odoo.tools import pycompat - _logger = logging.getLogger(__name__) def migrate_set_tags_and_taxes_updatable(cr, registry, module): @@ -327,7 +325,7 @@ class AccountChartTemplate(models.Model): # writing account values after creation of accounts company.transfer_account_id = account_template_ref[transfer_account_id.id] - for key, value in pycompat.items(generated_tax_res['account_dict']): + for key, value in generated_tax_res['account_dict'].items(): if value['refund_account_id'] or value['account_id'] or value['cash_basis_account']: AccountTaxObj.browse(key).write({ 'refund_account_id': account_ref.get(value['refund_account_id'], False), diff --git a/addons/account/models/partner.py b/addons/account/models/partner.py index f2f0f85f7c0..de32aaa5f3d 100644 --- a/addons/account/models/partner.py +++ b/addons/account/models/partner.py @@ -5,7 +5,7 @@ from operator import itemgetter import time from odoo import api, fields, models, _ -from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, pycompat +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT from odoo.exceptions import ValidationError from odoo.addons.base.res.res_partner import WARNING_MESSAGE, WARNING_HELP @@ -73,7 +73,7 @@ class AccountFiscalPosition(models.Model): ref_dict = {} for line in self.account_ids: ref_dict[line.account_src_id] = line.account_dest_id - for key, acc in list(pycompat.items(accounts)): + for key, acc in accounts.items(): if acc in ref_dict: accounts[key] = ref_dict[acc] return accounts @@ -293,7 +293,7 @@ class ResPartner(models.Model): """ % where_clause self.env.cr.execute(query, where_clause_params) price_totals = self.env.cr.dictfetchall() - for partner, child_ids in pycompat.items(all_partners_and_children): + for partner, child_ids in all_partners_and_children.items(): partner.total_invoiced = sum(price['total'] for price in price_totals if price['partner_id'] in child_ids) @api.multi diff --git a/addons/account/report/account_report_financial.py b/addons/account/report/account_report_financial.py index 01e2080307f..65560736fff 100644 --- a/addons/account/report/account_report_financial.py +++ b/addons/account/report/account_report_financial.py @@ -3,7 +3,6 @@ import time from odoo import api, models, _ from odoo.exceptions import UserError -from odoo.tools import pycompat class ReportFinancial(models.AbstractModel): @@ -28,7 +27,7 @@ class ReportFinancial(models.AbstractModel): if where_clause.strip(): wheres.append(where_clause.strip()) filters = " AND ".join(wheres) - request = "SELECT account_id as id, " + ', '.join(pycompat.values(mapping)) + \ + request = "SELECT account_id as id, " + ', '.join(mapping.values()) + \ " FROM " + tables + \ " WHERE account_id IN %s " \ + filters + \ @@ -55,26 +54,26 @@ class ReportFinancial(models.AbstractModel): if report.type == 'accounts': # it's the sum of the linked accounts res[report.id]['account'] = self._compute_account_balance(report.account_ids) - for value in pycompat.values(res[report.id]['account']): + for value in res[report.id]['account'].values(): for field in fields: res[report.id][field] += value.get(field) elif report.type == 'account_type': # it's the sum the leaf accounts with such an account type accounts = self.env['account.account'].search([('user_type_id', 'in', report.account_type_ids.ids)]) res[report.id]['account'] = self._compute_account_balance(accounts) - for value in pycompat.values(res[report.id]['account']): + for value in res[report.id]['account'].values(): for field in fields: res[report.id][field] += value.get(field) elif report.type == 'account_report' and report.account_report_id: # it's the amount of the linked report res2 = self._compute_report_balance(report.account_report_id) - for key, value in pycompat.items(res2): + for key, value in res2.items(): for field in fields: res[report.id][field] += value[field] elif report.type == 'sum': # it's the sum of the children of this account.report res2 = self._compute_report_balance(report.children_ids) - for key, value in pycompat.items(res2): + for key, value in res2.items(): for field in fields: res[report.id][field] += value[field] return res @@ -86,11 +85,11 @@ class ReportFinancial(models.AbstractModel): res = self.with_context(data.get('used_context'))._compute_report_balance(child_reports) if data['enable_filter']: comparison_res = self.with_context(data.get('comparison_context'))._compute_report_balance(child_reports) - for report_id, value in pycompat.items(comparison_res): + for report_id, value in comparison_res.items(): res[report_id]['comp_bal'] = value['balance'] report_acc = res[report_id].get('account') if report_acc: - for account_id, val in pycompat.items(comparison_res[report_id].get('account')): + for account_id, val in comparison_res[report_id].get('account').items(): report_acc[account_id]['comp_bal'] = val['balance'] for report in child_reports: @@ -115,7 +114,7 @@ class ReportFinancial(models.AbstractModel): if res[report.id].get('account'): sub_lines = [] - for account_id, value in pycompat.items(res[report.id]['account']): + for account_id, value in res[report.id]['account'].items(): #if there are accounts to display, we add them to the lines with a level equals to their level in #the COA + 1 (to avoid having them with a too low level that would conflicts with the level of data #financial reports for Assets, liabilities...) diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py index 0093915eee5..ac2aca16951 100644 --- a/addons/account/tests/test_reconciliation.py +++ b/addons/account/tests/test_reconciliation.py @@ -2,8 +2,6 @@ from odoo.addons.account.tests.account_test_classes import AccountingTestCase import time import unittest -from odoo.tools import pycompat - class TestReconciliation(AccountingTestCase): @@ -339,8 +337,8 @@ class TestReconciliation(AccountingTestCase): self.assertTrue(exchange_loss_line, 'There should be one move line of 0.01 EUR in credit') # The journal items of the reconciliation should have their debit and credit total equal # Besides, the total debit and total credit should be 60.61 EUR (2.00 USD) - self.assertEquals(sum(res['debit'] for res in pycompat.values(result)), 60.61) - self.assertEquals(sum(res['credit'] for res in pycompat.items(result)), 60.61) + self.assertEquals(sum(res['debit'] for res in result.values()), 60.61) + self.assertEquals(sum(res['credit'] for res in result.items()), 60.61) counterpart_exchange_loss_line = None for line in exchange_loss_line.move_id.line_id: if line.account_id.id == self.account_fx_expense_id: diff --git a/addons/account_asset/models/account_asset.py b/addons/account_asset/models/account_asset.py index cc2a29a09a9..15d471301b6 100644 --- a/addons/account_asset/models/account_asset.py +++ b/addons/account_asset/models/account_asset.py @@ -7,7 +7,7 @@ from dateutil.relativedelta import relativedelta from odoo import api, fields, models, _ from odoo.exceptions import UserError, ValidationError -from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DF, pycompat +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DF from odoo.tools import float_compare, float_is_zero @@ -374,7 +374,7 @@ class AccountAssetAsset(models.Model): vals = self.onchange_category_id_values(self.category_id.id) # We cannot use 'write' on an object that doesn't exist yet if vals: - for k, v in pycompat.items(vals['value']): + for k, v in vals['value'].items(): setattr(self, k, v) def onchange_category_id_values(self, category_id): @@ -583,7 +583,7 @@ class AccountAssetDepreciationLine(models.Model): message = '' if message_description: message = '%s' % message_description - for name, values in pycompat.items(tracked_values): + for name, values in tracked_values.items(): message += '
    • %s: ' % name message += '%s
' % values return message diff --git a/addons/account_voucher/models/account_voucher.py b/addons/account_voucher/models/account_voucher.py index e394af36296..40ae66c3395 100644 --- a/addons/account_voucher/models/account_voucher.py +++ b/addons/account_voucher/models/account_voucher.py @@ -5,7 +5,6 @@ from odoo import fields, models, api, _ from odoo.addons import decimal_precision as dp from odoo.exceptions import UserError -from odoo.tools import pycompat class AccountVoucher(models.Model): @@ -358,7 +357,7 @@ class AccountVoucherLine(models.Model): self.company_id.id, self.voucher_id.currency_id.id, self.voucher_id.voucher_type) - for fname, fvalue in pycompat.items(onchange_res['value']): + for fname, fvalue in onchange_res['value'].items(): setattr(self, fname, fvalue) def _get_account(self, product, fpos, type): diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index 8f8db0fab0d..262dfdad86d 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -8,7 +8,6 @@ from odoo.addons.auth_signup.models.res_users import SignupError from odoo.addons.web.controllers.main import ensure_db, Home from odoo.exceptions import UserError from odoo.http import request -from odoo.tools import pycompat _logger = logging.getLogger(__name__) @@ -106,7 +105,7 @@ class AuthSignupHome(Home): try: # retrieve the user info (name, login or email) corresponding to a signup token token_infos = request.env['res.partner'].sudo().signup_retrieve_info(qcontext.get('token')) - for k, v in pycompat.items(token_infos): + for k, v in token_infos.items(): qcontext.setdefault(k, v) except: qcontext['error'] = _("Invalid signup token") diff --git a/addons/auth_signup/models/res_partner.py b/addons/auth_signup/models/res_partner.py index 0cf5c25b7ff..ecf63246580 100644 --- a/addons/auth_signup/models/res_partner.py +++ b/addons/auth_signup/models/res_partner.py @@ -8,8 +8,6 @@ from collections import defaultdict from datetime import datetime, timedelta from odoo import api, exceptions, fields, models, _ -from odoo.tools import pycompat - class SignupError(Exception): pass diff --git a/addons/base_address_extended/models/base_address_extended.py b/addons/base_address_extended/models/base_address_extended.py index 1d49c1c94c4..9a210dde769 100644 --- a/addons/base_address_extended/models/base_address_extended.py +++ b/addons/base_address_extended/models/base_address_extended.py @@ -5,7 +5,6 @@ import re from odoo import api, fields, models, _ from odoo.exceptions import UserError -from odoo.tools import pycompat STREET_FIELDS = ('street_name', 'street_number', 'street_number2') @@ -135,7 +134,7 @@ class Partner(models.Model): vals[field_name] = street_raw # assign the values to the fields # /!\ Note that a write(vals) would cause a recursion since it would bypass the cache - for k, v in pycompat.items(vals): + for k, v in vals.items(): partner[k] = v diff --git a/addons/base_automation/models/base_automation.py b/addons/base_automation/models/base_automation.py index 061828c161e..9896a9706d8 100644 --- a/addons/base_automation/models/base_automation.py +++ b/addons/base_automation/models/base_automation.py @@ -12,7 +12,6 @@ from dateutil.relativedelta import relativedelta from odoo import api, fields, models, SUPERUSER_ID from odoo.modules.registry import Registry -from odoo.tools import pycompat from odoo.tools.safe_eval import safe_eval _logger = logging.getLogger(__name__) @@ -264,7 +263,7 @@ class BaseAutomation(models.Model): if res: if 'value' in res: res['value'].pop('id', None) - self.update({key: val for key, val in pycompat.items(res['value']) if key in self._fields}) + self.update({key: val for key, val in res['value'].items() if key in self._fields}) if 'domain' in res: result.setdefault('domain', {}).update(res['domain']) if 'warning' in res: diff --git a/addons/base_gengo/wizard/base_gengo_translations.py b/addons/base_gengo/wizard/base_gengo_translations.py index 98df9356576..d6e5ce9176a 100644 --- a/addons/base_gengo/wizard/base_gengo_translations.py +++ b/addons/base_gengo/wizard/base_gengo_translations.py @@ -8,7 +8,6 @@ import uuid from odoo import api, fields, models, tools, _ from odoo.exceptions import UserError, ValidationError -from odoo.tools import pycompat _logger = logging.getLogger(__name__) @@ -194,7 +193,7 @@ class BaseGengoTranslations(models.TransientModel): term_ids.write(vals) jobs = response.get('jobs', []) if jobs: - for t_id, job in pycompat.items(jobs): + for t_id, job in jobs.items(): self._update_terms_job(job) return diff --git a/addons/base_import/models/base_import.py b/addons/base_import/models/base_import.py index 09d2dcb2852..19941858dfa 100644 --- a/addons/base_import/models/base_import.py +++ b/addons/base_import/models/base_import.py @@ -42,7 +42,7 @@ FILE_TYPE_DICT = { } EXTENSIONS = { '.' + ext: handler - for mime, (ext, handler, req) in pycompat.items(FILE_TYPE_DICT) + for mime, (ext, handler, req) in FILE_TYPE_DICT.items() } @@ -114,7 +114,7 @@ class Import(models.TransientModel): }] model_fields = Model.fields_get() blacklist = models.MAGIC_COLUMNS + [Model.CONCURRENCY_CHECK_FIELD] - for name, field in pycompat.items(model_fields): + for name, field in model_fields.items(): if name in blacklist: continue # an empty string means the field is deprecated, @deprecated must @@ -127,7 +127,7 @@ class Import(models.TransientModel): continue # states = {state: [(attr, value), (attr2, value2)], state2:...} if not any(attr == 'readonly' and value is False - for attr, value in itertools.chain.from_iterable(pycompat.values(states))): + for attr, value in itertools.chain.from_iterable(states.values())): continue field_value = { 'id': name, @@ -595,7 +595,7 @@ class Import(models.TransientModel): def _parse_import_data(self, data, import_fields, options): # Get fields of type date/datetime all_fields = self.env[self.res_model].fields_get() - for name, field in pycompat.items(all_fields): + for name, field in all_fields.items(): if field['type'] in ('date', 'datetime') and name in import_fields: # Parse date index = import_fields.index(name) diff --git a/addons/base_import/models/odf_ods_reader.py b/addons/base_import/models/odf_ods_reader.py index c2613c6baa5..daa7d2b16e2 100644 --- a/addons/base_import/models/odf_ods_reader.py +++ b/addons/base_import/models/odf_ods_reader.py @@ -19,8 +19,6 @@ from odf import opendocument from odf.table import Table, TableRow, TableCell from odf.text import P -from odoo.tools import pycompat - class ODSReader(object): @@ -97,4 +95,4 @@ class ODSReader(object): return self.SHEETS[name] def getFirstSheet(self): - return next(iter(pycompat.values(self.SHEETS))) + return next(iter(self.SHEETS.values())) diff --git a/addons/base_import_module/models/ir_module.py b/addons/base_import_module/models/ir_module.py index d6bb62a4d46..5bad5ea53eb 100644 --- a/addons/base_import_module/models/ir_module.py +++ b/addons/base_import_module/models/ir_module.py @@ -9,7 +9,7 @@ from os.path import join as opj from odoo import api, fields, models, _ from odoo.exceptions import UserError from odoo.modules import load_information_from_description_file -from odoo.tools import convert_file, exception_to_unicode, pycompat +from odoo.tools import convert_file, exception_to_unicode from odoo.tools.osutil import tempdir _logger = logging.getLogger(__name__) @@ -119,6 +119,6 @@ class IrModule(models.Model): finally: addons.module.ad_paths.remove(module_dir) r = ["Successfully imported module '%s'" % mod for mod in success] - for mod, error in pycompat.items(errors): + for mod, error in errors.items(): r.append("Error while importing module '%s': %r" % (mod, error)) return '\n'.join(r), module_names diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py index 49c87a8e9b9..e80a2727282 100644 --- a/addons/calendar/models/calendar.py +++ b/addons/calendar/models/calendar.py @@ -470,7 +470,7 @@ class Alarm(models.Model): name = fields.Char('Name', required=True) type = fields.Selection([('notification', 'Notification'), ('email', 'Email')], 'Type', required=True, default='email') duration = fields.Integer('Remind Before', required=True, default=1) - interval = fields.Selection(list(pycompat.items(_interval_selection)), 'Unit', required=True, default='hours') + interval = fields.Selection(list(_interval_selection.items()), 'Unit', required=True, default='hours') duration_minutes = fields.Integer('Duration in minutes', compute='_compute_duration_minutes', store=True, help="Duration in minutes") @api.onchange('duration', 'interval') @@ -1292,7 +1292,7 @@ class Meeting(models.Model): @api.multi def _get_message_unread(self): id_map = {x: calendar_id2real_id(x) for x in self.ids} - real = self.browse(set(pycompat.values(id_map))) + real = self.browse(set(id_map.values())) super(Meeting, real)._get_message_unread() for event in self: if event.id == id_map[event.id]: @@ -1304,7 +1304,7 @@ class Meeting(models.Model): @api.multi def _get_message_needaction(self): id_map = {x: calendar_id2real_id(x) for x in self.ids} - real = self.browse(set(pycompat.values(id_map))) + real = self.browse(set(id_map.values())) super(Meeting, real)._get_message_needaction() for event in self: if event.id == id_map[event.id]: diff --git a/addons/event/models/event.py b/addons/event/models/event.py index 95310a67122..c5f7f43915a 100644 --- a/addons/event/models/event.py +++ b/addons/event/models/event.py @@ -5,7 +5,6 @@ import pytz from odoo import _, api, fields, models from odoo.addons.mail.models.mail_template import format_tz from odoo.exceptions import AccessError, UserError, ValidationError -from odoo.tools import pycompat from odoo.tools.translate import html_translate from dateutil.relativedelta import relativedelta @@ -389,7 +388,7 @@ class EventRegistration(models.Model): 'partner_id': partner_id.id, 'event_id': event_id and event_id.id or False, } - data.update({key: value for key, value in pycompat.items(registration) if key in self._fields}) + data.update({key: value for key, value in registration.items() if key in self._fields}) return data @api.one diff --git a/addons/fleet/models/fleet_vehicle_cost.py b/addons/fleet/models/fleet_vehicle_cost.py index 12c6a7d7f2e..827863e3adc 100644 --- a/addons/fleet/models/fleet_vehicle_cost.py +++ b/addons/fleet/models/fleet_vehicle_cost.py @@ -6,8 +6,6 @@ from odoo.exceptions import UserError from dateutil.relativedelta import relativedelta -from odoo.tools import pycompat - class FleetVehicleCost(models.Model): _name = 'fleet.vehicle.cost' @@ -264,7 +262,7 @@ class FleetVehicleLogContract(models.Model): res[contract.vehicle_id.id] = 1 Vehicle = self.env['fleet.vehicle'] - for vehicle, value in pycompat.items(res): + for vehicle, value in res.items(): Vehicle.browse(vehicle).message_post(body=_('%s contract(s) need(s) to be renewed and/or closed!') % value) return contracts.write({'state': 'toclose'}) diff --git a/addons/gamification/models/goal.py b/addons/gamification/models/goal.py index 56c447dd9d4..3c062401662 100644 --- a/addons/gamification/models/goal.py +++ b/addons/gamification/models/goal.py @@ -262,7 +262,7 @@ class Goal(models.Model): for goal in self: goals_by_definition.setdefault(goal.definition_id, []).append(goal) - for definition, goals in pycompat.items(goals_by_definition): + for definition, goals in goals_by_definition.items(): goals_to_write = {} if definition.computation_mode == 'manually': for goal in goals: @@ -307,9 +307,9 @@ class Goal(models.Model): subqueries.setdefault((start_date, end_date), {}).update({goal.id:safe_eval(definition.batch_user_expression, {'user': goal.user_id})}) # the global query should be split by time periods (especially for recurrent goals) - for (start_date, end_date), query_goals in pycompat.items(subqueries): + for (start_date, end_date), query_goals in subqueries.items(): subquery_domain = list(general_domain) - subquery_domain.append((field_name, 'in', list(set(pycompat.values(query_goals))))) + subquery_domain.append((field_name, 'in', list(set(query_goals.values())))) if start_date: subquery_domain.append((field_date_name, '>=', start_date)) if end_date: @@ -353,7 +353,7 @@ class Goal(models.Model): goals_to_write.update(goal._get_write_values(new_value)) - for goal, values in pycompat.items(goals_to_write): + for goal, values in goals_to_write.items(): if not values: continue goal.write(values) diff --git a/addons/google_calendar/models/google_calendar.py b/addons/google_calendar/models/google_calendar.py index 0303dbe5154..ac91835f530 100644 --- a/addons/google_calendar/models/google_calendar.py +++ b/addons/google_calendar/models/google_calendar.py @@ -12,7 +12,7 @@ import pytz from werkzeug import urls from odoo import api, fields, models, tools, _ -from odoo.tools import exception_to_unicode, pycompat +from odoo.tools import exception_to_unicode _logger = logging.getLogger(__name__) @@ -25,15 +25,13 @@ class Meta(type): """ This Meta class allow to define class as a structure, and so instancied variable in __init__ to avoid to have side effect alike 'static' variable """ def __new__(typ, name, parents, attrs): - methods = {k: v for k, v in pycompat.items(attrs) - if callable(v)} - attrs = {k: v for k, v in pycompat.items(attrs) - if not callable(v)} + methods = {k: v for k, v in attrs.items() if callable(v)} + attrs = {k: v for k, v in attrs.items() if not callable(v)} def init(self, **kw): - for key, val in pycompat.items(attrs): + for key, val in attrs.items(): setattr(self, key, val) - for key, val in pycompat.items(kw): + for key, val in kw.items(): assert key in attrs setattr(self, key, val) @@ -161,7 +159,7 @@ class SyncOperation(object): def __init__(self, src, info, **kw): self.src = src self.info = info - for key, val in pycompat.items(kw): + for key, val in kw.items(): setattr(self, key, val) def __str__(self): @@ -689,7 +687,7 @@ class GoogleCalendar(models.AbstractModel): my_google_attendees = CalendarAttendee.with_context(context_novirtual).search([ ('partner_id', '=', my_partner_id), - ('google_internal_event_id', 'in', pycompat.keys(all_event_from_google)) + ('google_internal_event_id', 'in', all_event_from_google.keys()) ]) my_google_att_ids = my_google_attendees.ids @@ -753,7 +751,7 @@ class GoogleCalendar(models.AbstractModel): ev_to_sync.OE.status = event.active ev_to_sync.OE.synchro = att.oe_synchro_date - for event in pycompat.values(all_event_from_google): + for event in all_event_from_google.values(): event_id = event.get('id') base_event_id = event_id.rsplit('_', 1)[0] @@ -788,7 +786,7 @@ class GoogleCalendar(models.AbstractModel): # DO ACTION # ###################### for base_event in event_to_synchronize: - event_to_synchronize[base_event] = sorted(pycompat.items(event_to_synchronize[base_event]), key=operator.itemgetter(0)) + event_to_synchronize[base_event] = sorted(event_to_synchronize[base_event].items(), key=operator.itemgetter(0)) for current_event in event_to_synchronize[base_event]: self.env.cr.commit() event = current_event[1] # event is an Sync Event ! diff --git a/addons/hr/models/hr.py b/addons/hr/models/hr.py index 3e513bb9e5d..d3bb213cb6a 100644 --- a/addons/hr/models/hr.py +++ b/addons/hr/models/hr.py @@ -7,7 +7,6 @@ from odoo import api, fields, models from odoo import tools, _ from odoo.exceptions import ValidationError from odoo.modules.module import get_module_resource -from odoo.tools import pycompat _logger = logging.getLogger(__name__) @@ -250,7 +249,7 @@ class Employee(models.Model): if auto_follow_fields is None: auto_follow_fields = ['user_id'] user_field_lst = [] - for name, field in pycompat.items(self._fields): + for name, field in self._fields.items(): if name in auto_follow_fields and name in updated_fields and field.comodel_name == 'res.users': user_field_lst.append(name) return user_field_lst diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index ce0054a5636..6699dddeb2a 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -5,7 +5,7 @@ import re from odoo import api, fields, models, _ from odoo.exceptions import UserError -from odoo.tools import email_split, float_is_zero, pycompat +from odoo.tools import email_split, float_is_zero from odoo.addons import decimal_precision as dp @@ -243,7 +243,7 @@ class HrExpense(models.Model): expense.sheet_id.write({'account_move_id': move.id}) if expense.payment_mode == 'company_account': expense.sheet_id.paid_expense_sheets() - for move in pycompat.values(move_group_by_sheet): + for move in move_group_by_sheet.values(): move.post() return True diff --git a/addons/hr_payroll/models/hr_payslip.py b/addons/hr_payroll/models/hr_payslip.py index fd30b7a1b23..2f310c3456e 100644 --- a/addons/hr_payroll/models/hr_payslip.py +++ b/addons/hr_payroll/models/hr_payslip.py @@ -11,8 +11,6 @@ import babel from odoo import api, fields, models, tools, _ from odoo.addons import decimal_precision as dp from odoo.exceptions import UserError, ValidationError -from odoo.tools import pycompat - class HrPayslip(models.Model): _name = 'hr.payslip' @@ -205,7 +203,7 @@ class HrPayslip(models.Model): } res.append(attendances) - res.extend(pycompat.values(leaves)) + res.extend(leaves.values()) return res @api.model @@ -369,7 +367,7 @@ class HrPayslip(models.Model): #blacklist this rule and its children blacklist += [id for id, seq in rule._recursive_search_of_rules()] - return [value for code, value in pycompat.items(result_dict)] + return list(result_dict.values()) # YTI TODO To rename. This method is not really an onchange, as it is not in any view # employee_id and contract_id could be browse records diff --git a/addons/hr_payroll/report/report_payslip_details.py b/addons/hr_payroll/report/report_payslip_details.py index 23043d2edb9..ffd5b9d913d 100644 --- a/addons/hr_payroll/report/report_payslip_details.py +++ b/addons/hr_payroll/report/report_payslip_details.py @@ -2,8 +2,6 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import api, models -from odoo.tools import pycompat - class PayslipDetailsReport(models.AbstractModel): _name = 'report.hr_payroll.report_payslipdetails' @@ -38,9 +36,9 @@ class PayslipDetailsReport(models.AbstractModel): result.setdefault(x[2], {}) result[x[2]].setdefault(x[1], []) result[x[2]][x[1]].append(x[0]) - for payslip_id, lines_dict in pycompat.items(result): + for payslip_id, lines_dict in result.items(): res.setdefault(payslip_id, []) - for rule_categ_id, line_ids in pycompat.items(lines_dict): + for rule_categ_id, line_ids in lines_dict.items(): rule_categories = RuleCateg.browse(rule_categ_id) lines = PayslipLine.browse(line_ids) level = 0 @@ -70,9 +68,9 @@ class PayslipDetailsReport(models.AbstractModel): result.setdefault(line.slip_id.id, {}) result[line.slip_id.id].setdefault(line.register_id, line) result[line.slip_id.id][line.register_id] |= line - for payslip_id, lines_dict in pycompat.items(result): + for payslip_id, lines_dict in result.items(): res.setdefault(payslip_id, []) - for register, lines in pycompat.items(lines_dict): + for register, lines in lines_dict.items(): res[payslip_id].append({ 'register_name': register.name, 'total': sum(lines.mapped('total')), diff --git a/addons/hr_recruitment/models/hr_recruitment.py b/addons/hr_recruitment/models/hr_recruitment.py index adec7b15f57..4e4a694e9eb 100644 --- a/addons/hr_recruitment/models/hr_recruitment.py +++ b/addons/hr_recruitment/models/hr_recruitment.py @@ -4,7 +4,6 @@ from datetime import datetime from odoo import api, fields, models, tools, SUPERUSER_ID -from odoo.tools import pycompat from odoo.tools.translate import _ from odoo.exceptions import UserError @@ -251,7 +250,7 @@ class Applicant(models.Model): self = self.with_context(default_department_id=vals.get('department_id')) if vals.get('job_id') or self._context.get('default_job_id'): job_id = vals.get('job_id') or self._context.get('default_job_id') - for key, value in pycompat.items(self._onchange_job_id_internal(job_id)['value']): + for key, value in self._onchange_job_id_internal(job_id)['value'].items(): if key not in vals: vals[key] = value if vals.get('user_id'): diff --git a/addons/im_livechat/models/im_livechat_channel.py b/addons/im_livechat/models/im_livechat_channel.py index a878389913c..c2741fcef92 100644 --- a/addons/im_livechat/models/im_livechat_channel.py +++ b/addons/im_livechat/models/im_livechat_channel.py @@ -6,8 +6,6 @@ import re from datetime import datetime, timedelta from odoo import api, fields, models, modules, tools -from odoo.tools import pycompat - class ImLivechatChannel(models.Model): """ Livechat Channel @@ -96,7 +94,7 @@ class ImLivechatChannel(models.Model): for record in self: dt = fields.Datetime.to_string(datetime.utcnow() - timedelta(days=7)) repartition = record.channel_ids.rating_get_grades([('create_date', '>=', dt)]) - total = sum(pycompat.values(repartition)) + total = sum(repartition.values()) if total > 0: happy = repartition['great'] record.rating_percentage_satisfaction = ((happy*100) / total) if happy > 0 else 0 diff --git a/addons/im_livechat/models/mail_channel.py b/addons/im_livechat/models/mail_channel.py index 32d66a0154c..c64bcd7108c 100644 --- a/addons/im_livechat/models/mail_channel.py +++ b/addons/im_livechat/models/mail_channel.py @@ -2,8 +2,6 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import api, fields, models, _ -from odoo.tools import pycompat - class ChannelPartner(models.Model): _inherit = 'mail.channel.partner' @@ -78,7 +76,7 @@ class MailChannel(models.Model): last_msg = self.env['mail.message'].search([("channel_ids", "in", [channel.id])], limit=1) if last_msg: channel_infos_dict[channel.id]['last_message_date'] = last_msg.date - return list(pycompat.values(channel_infos_dict)) + return list(channel_infos_dict.values()) @api.model def channel_fetch_slot(self): diff --git a/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.py b/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.py index 001a5a782f8..0d26efe9bf5 100644 --- a/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.py +++ b/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.py @@ -5,8 +5,6 @@ from datetime import date from odoo import api, models, _ from odoo.exceptions import UserError -from odoo.tools import pycompat - class EmployeesYearlySalaryReport(models.AbstractModel): _name = 'report.l10n_in_hr_payroll.report_hryearlysalary' @@ -99,7 +97,7 @@ class EmployeesYearlySalaryReport(models.AbstractModel): def salary_list(self, salaries): cat_salary_all = [] - for category_name, amount in pycompat.items(salaries): + for category_name, amount in salaries.items(): cat_salary = [] total = 0.0 cat_salary.append(category_name) diff --git a/addons/l10n_lu/scripts/tax2csv.py b/addons/l10n_lu/scripts/tax2csv.py index f4080284064..23fec3c734c 100644 --- a/addons/l10n_lu/scripts/tax2csv.py +++ b/addons/l10n_lu/scripts/tax2csv.py @@ -59,7 +59,7 @@ class LuxTaxGenerator: if tax_code in tax_codes: raise RuntimeError('duplicate tax code %s' % tax_code) tax_codes[tax_code] = row['id'] - writer.writerow([pycompat.to_text(v) for v in pycompat.values(row)]) + writer.writerow([pycompat.to_text(v) for v in row.values()]) # read taxes and add leaf tax codes new_tax_codes = {} # id: parent_code @@ -162,7 +162,7 @@ class LuxTaxGenerator: cur_seq = seq writer.writerow([ pycompat.to_text(v) - for v in list(pycompat.values(row))[3:] + for v in list(row.values())[3:] ] + [cur_seq]) def fiscal_pos_map_to_csv(self): @@ -173,7 +173,7 @@ class LuxTaxGenerator: keys = next(fiscal_pos_map_iterator) writer.writerow(keys) for row in fiscal_pos_map_iterator: - writer.writerow([pycompat.to_text(s) for s in pycompat.values(row)]) + writer.writerow([pycompat.to_text(s) for s in row.values()]) if __name__ == '__main__': diff --git a/addons/link_tracker/models/link_tracker.py b/addons/link_tracker/models/link_tracker.py index da24f66e905..509fc21e153 100644 --- a/addons/link_tracker/models/link_tracker.py +++ b/addons/link_tracker/models/link_tracker.py @@ -12,7 +12,7 @@ from werkzeug import urls, utils from odoo import models, fields, api, _ -from odoo.tools import ustr, pycompat +from odoo.tools import ustr URL_REGEX = r'(\bhref=[\'"](?!mailto:)([^\'"]+)[\'"])' @@ -165,7 +165,7 @@ class link_tracker(models.Model): create_vals['url'] = VALIDATE_URL(vals['url']) search_domain = [] - for fname, value in pycompat.items(create_vals): + for fname, value in create_vals.items(): search_domain.append((fname, '=', value)) result = self.search(search_domain, limit=1) diff --git a/addons/lunch/models/lunch.py b/addons/lunch/models/lunch.py index ee8bba3492e..e9c13060fc3 100644 --- a/addons/lunch/models/lunch.py +++ b/addons/lunch/models/lunch.py @@ -10,8 +10,6 @@ from odoo import api, fields, models, _ from odoo.exceptions import AccessError, ValidationError from odoo.addons import decimal_precision as dp -from odoo.tools import pycompat - class LunchOrder(models.Model): """ @@ -26,10 +24,10 @@ class LunchOrder(models.Model): prev_order = self.env['lunch.order.line'].search([('user_id', '=', self.env.uid), ('product_id.active', '!=', False)], limit=20, order='id desc') # If we return return prev_order.ids, we will have duplicates (identical orders). # Therefore, this following part removes duplicates based on product_id and note. - return list(pycompat.values({ + return list({ (order.product_id, order.note): order.id for order in prev_order - })) + }.values()) user_id = fields.Many2one('res.users', 'User', readonly=True, states={'new': [('readonly', False)]}, @@ -88,10 +86,10 @@ class LunchOrder(models.Model): prev_order = self.env['lunch.order.line'].search([('user_id', '=', self.env.uid), ('product_id.active', '!=', False)], limit=20, order='date desc, id desc') # If we use prev_order.ids, we will have duplicates (identical orders). # Therefore, this following part removes duplicates based on product_id and note. - self.previous_order_ids = list(pycompat.values({ + self.previous_order_ids = list({ (order.product_id, order.note): order.id for order in prev_order - })) + }.values()) if self.previous_order_ids: lunch_data = {} diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py index a2ac768b772..31e03b99081 100644 --- a/addons/mail/models/mail_channel.py +++ b/addons/mail/models/mail_channel.py @@ -9,7 +9,7 @@ import uuid from odoo import _, api, fields, models, modules, tools from odoo.exceptions import UserError from odoo.osv import expression -from odoo.tools import ormcache, pycompat +from odoo.tools import ormcache from odoo.tools.safe_eval import safe_eval @@ -619,7 +619,7 @@ class Channel(models.Model): channel = channels_preview[message['id']] del(channel['message_id']) channel['last_message'] = message - return list(pycompat.values(channels_preview)) + return list(channels_preview.values()) #------------------------------------------------------ # Commands diff --git a/addons/mail/models/mail_followers.py b/addons/mail/models/mail_followers.py index a6bd60f82c9..3edcfa748c3 100644 --- a/addons/mail/models/mail_followers.py +++ b/addons/mail/models/mail_followers.py @@ -2,7 +2,6 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import api, fields, models -from odoo.tools import pycompat class Followers(models.Model): @@ -42,7 +41,7 @@ class Followers(models.Model): using the subtypes given in the parameters """ res_model_id = self.env['ir.model']._get(res_model).id - force_mode = force or (all(data for data in pycompat.values(partner_data)) and all(data for data in pycompat.values(channel_data))) + force_mode = force or (all(partner_data.values()) and all(channel_data.values())) generic = [] specific = {} existing = {} # {res_id: follower_ids} @@ -71,13 +70,13 @@ class Followers(models.Model): if force_mode: employee_pids = self.env['res.users'].sudo().search([('partner_id', 'in', list(partner_data)), ('share', '=', False)]).mapped('partner_id').ids - for pid, data in pycompat.items(partner_data): + for pid, data in partner_data.items(): if not data: if pid not in employee_pids: partner_data[pid] = external_default_subtypes.ids else: partner_data[pid] = default_subtypes.ids - for cid, data in pycompat.items(channel_data): + for cid, data in channel_data.items(): if not data: channel_data[cid] = default_subtypes.ids diff --git a/addons/mail/models/mail_mail.py b/addons/mail/models/mail_mail.py index e53c24fb176..e723f71bdc6 100644 --- a/addons/mail/models/mail_mail.py +++ b/addons/mail/models/mail_mail.py @@ -13,7 +13,6 @@ from email.utils import formataddr from odoo import _, api, fields, models from odoo import tools from odoo.addons.base.ir.ir_mail_server import MailDeliveryException -from odoo.tools import pycompat from odoo.tools.safe_eval import safe_eval _logger = logging.getLogger(__name__) @@ -205,7 +204,7 @@ class MailMail(models.Model): groups[mail.mail_server_id.id].append(mail.id) sys_params = self.env['ir.config_parameter'].sudo() batch_size = int(sys_params.get_param('mail.session.batch.size', 1000)) - for server_id, record_ids in pycompat.items(groups): + for server_id, record_ids in groups.items(): for mail_batch in tools.split_every(batch_size, record_ids): yield server_id, mail_batch diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index 2970933e704..3675ca26332 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -9,7 +9,6 @@ from email.utils import formataddr from odoo import _, api, fields, models, modules, SUPERUSER_ID, tools from odoo.exceptions import UserError, AccessError from odoo.osv import expression -from odoo.tools import pycompat _logger = logging.getLogger(__name__) _image_dataurl = re.compile(r'(data:image/[a-z]+?);base64,([a-z0-9+/]{3,}=*)([\'"])', re.I) @@ -282,7 +281,7 @@ class Message(models.Model): partners = self.env['res.partner'].sudo() attachments = self.env['ir.attachment'] trackings = self.env['mail.tracking.value'] - for key, message in pycompat.items(message_tree): + for key, message in message_tree.items(): if message.author_id: partners |= message.author_id if message.subtype_id and message.partner_ids: # take notified people of message with a subtype @@ -446,7 +445,7 @@ class Message(models.Model): def _find_allowed_doc_ids(self, model_ids): IrModelAccess = self.env['ir.model.access'] allowed_ids = set() - for doc_model, doc_dict in pycompat.items(model_ids): + for doc_model, doc_dict in model_ids.items(): if not IrModelAccess.check(doc_model, 'read', False): continue allowed_ids |= self._find_allowed_model_wise(doc_model, doc_dict) @@ -607,17 +606,17 @@ class Message(models.Model): # Author condition (READ, WRITE, CREATE (private)) author_ids = [] if operation == 'read' or operation == 'write': - author_ids = [mid for mid, message in pycompat.items(message_values) + author_ids = [mid for mid, message in message_values.items() if message.get('author_id') and message.get('author_id') == self.env.user.partner_id.id] elif operation == 'create': - author_ids = [mid for mid, message in pycompat.items(message_values) + author_ids = [mid for mid, message in message_values.items() if not message.get('model') and not message.get('res_id')] # Parent condition, for create (check for received notifications for the created message parent) notified_ids = [] if operation == 'create': # TDE: probably clean me - parent_ids = [message.get('parent_id') for mid, message in pycompat.items(message_values) + parent_ids = [message.get('parent_id') for message in message_values.values() if message.get('parent_id')] self._cr.execute("""SELECT DISTINCT m.id, partner_rel.res_partner_id, channel_partner.partner_id FROM "%s" m LEFT JOIN "mail_message_res_partner_rel" partner_rel @@ -630,37 +629,37 @@ class Message(models.Model): ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = (%%s) WHERE m.id = ANY (%%s)""" % self._table, (self.env.user.partner_id.id, self.env.user.partner_id.id, parent_ids,)) not_parent_ids = [mid[0] for mid in self._cr.fetchall() if any([mid[1], mid[2]])] - notified_ids += [mid for mid, message in pycompat.items(message_values) + notified_ids += [mid for mid, message in message_values.items() if message.get('parent_id') in not_parent_ids] # Recipients condition, for read and write (partner_ids) and create (message_follower_ids) other_ids = set(self.ids).difference(set(author_ids), set(notified_ids)) model_record_ids = _generate_model_record_ids(message_values, other_ids) if operation in ['read', 'write']: - notified_ids = [mid for mid, message in pycompat.items(message_values) if message.get('notified')] + notified_ids = [mid for mid, message in message_values.items() if message.get('notified')] elif operation == 'create': - for doc_model, doc_ids in pycompat.items(model_record_ids): + for doc_model, doc_ids in model_record_ids.items(): followers = self.env['mail.followers'].sudo().search([ ('res_model', '=', doc_model), ('res_id', 'in', list(doc_ids)), ('partner_id', '=', self.env.user.partner_id.id), ]) fol_mids = [follower.res_id for follower in followers] - notified_ids += [mid for mid, message in pycompat.items(message_values) + notified_ids += [mid for mid, message in message_values.items() if message.get('model') == doc_model and message.get('res_id') in fol_mids] # CRUD: Access rights related to the document other_ids = other_ids.difference(set(notified_ids)) model_record_ids = _generate_model_record_ids(message_values, other_ids) document_related_ids = [] - for model, doc_ids in pycompat.items(model_record_ids): + for model, doc_ids in model_record_ids.items(): DocumentModel = self.env[model] mids = DocumentModel.browse(doc_ids).exists() if hasattr(DocumentModel, 'check_mail_message_access'): DocumentModel.check_mail_message_access(mids.ids, operation) # ?? mids ? else: self.env['mail.thread'].check_mail_message_access(mids.ids, operation, model_name=model) - document_related_ids += [mid for mid, message in pycompat.items(message_values) + document_related_ids += [mid for mid, message in message_values.items() if message.get('model') == model and message.get('res_id') in mids.ids] # Calculate remaining ids: if not void, raise an error diff --git a/addons/mail/models/mail_template.py b/addons/mail/models/mail_template.py index f3a7acb253b..1bd92aff430 100644 --- a/addons/mail/models/mail_template.py +++ b/addons/mail/models/mail_template.py @@ -379,7 +379,7 @@ class MailTemplate(models.Model): 'user': self.env.user, 'ctx': self._context, # context kw would clash with mako internals } - for res_id, record in pycompat.items(res_to_rec): + for res_id, record in res_to_rec.items(): variables['object'] = record try: render_result = template.render(variables) @@ -391,7 +391,7 @@ class MailTemplate(models.Model): results[res_id] = render_result if post_process: - for res_id, result in pycompat.items(results): + for res_id, result in results.items(): results[res_id] = self.render_post_process(result) return multi_mode and results or results[res_ids[0]] @@ -412,7 +412,7 @@ class MailTemplate(models.Model): self.ensure_one() langs = self.render_template(self.lang, self.model, res_ids) - for res_id, lang in pycompat.items(langs): + for res_id, lang in langs.items(): if lang: template = self.with_context(lang=lang) else: @@ -431,11 +431,11 @@ class MailTemplate(models.Model): if self.use_default_to or self._context.get('tpl_force_default_to'): default_recipients = self.env['mail.thread'].message_get_default_recipients(res_model=self.model, res_ids=res_ids) - for res_id, recipients in pycompat.items(default_recipients): + for res_id, recipients in default_recipients.items(): results[res_id].pop('partner_to', None) results[res_id].update(recipients) - for res_id, values in pycompat.items(results): + for res_id, values in results.items(): partner_ids = values.get('partner_ids', list()) if self._context.get('tpl_partners_only'): mails = tools.email_split(values.pop('email_to', '')) + tools.email_split(values.pop('email_cc', '')) @@ -474,11 +474,11 @@ class MailTemplate(models.Model): # templates: res_id -> template; template -> res_ids templates_to_res_ids = {} - for res_id, template in pycompat.items(res_ids_to_templates): + for res_id, template in res_ids_to_templates.items(): templates_to_res_ids.setdefault(template, []).append(res_id) results = dict() - for template, template_res_ids in pycompat.items(templates_to_res_ids): + for template, template_res_ids in templates_to_res_ids.items(): Template = self.env['mail.template'] # generate fields value for all res_ids linked to the current template if template.lang: @@ -488,7 +488,7 @@ class MailTemplate(models.Model): generated_field_values = Template.render_template( getattr(template, field), template.model, template_res_ids, post_process=(field == 'body_html')) - for res_id, field_value in pycompat.items(generated_field_values): + for res_id, field_value in generated_field_values.items(): results.setdefault(res_id, dict())[field] = field_value # compute recipients if any(field in fields for field in ['email_to', 'partner_to', 'email_cc']): diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index 835fd42b5a6..296f79bed14 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -26,7 +26,6 @@ from werkzeug import url_encode from odoo import _, api, exceptions, fields, models, tools from odoo.tools import pycompat -from odoo.tools.pycompat import text_type from odoo.tools.safe_eval import safe_eval @@ -239,7 +238,7 @@ class MailThread(models.AbstractModel): # auto_subscribe: take values and defaults into account create_values = dict(values) - for key, val in pycompat.items(self._context): + for key, val in self._context.items(): if key.startswith('default_') and key[8:] not in create_values: create_values[key[8:]] = val thread.message_auto_subscribe(list(create_values), values=create_values) @@ -389,7 +388,7 @@ class MailThread(models.AbstractModel): always tracked fields and modified on_change fields """ tracked_fields = [] - for name, field in pycompat.items(self._fields): + for name, field in self._fields.items(): if getattr(field, 'track_visibility', False): tracked_fields.append(name) @@ -417,10 +416,10 @@ class MailThread(models.AbstractModel): @api.multi def _message_track_post_template(self, tracking): - if not any(change for rec_id, (change, tracking_value_ids) in pycompat.items(tracking)): + if not any(change for rec_id, (change, tracking_value_ids) in tracking.items()): return True templates = self._track_template(tracking) - for field_name, (template, post_kwargs) in pycompat.items(templates): + for field_name, (template, post_kwargs) in templates.items(): if not template: continue if isinstance(template, pycompat.string_types): @@ -451,7 +450,7 @@ class MailThread(models.AbstractModel): display_values_ids = [] # generate tracked_values data structure: {'col_name': {col_info, new_value, old_value}} - for col_name, col_info in pycompat.items(tracked_fields): + for col_name, col_info in tracked_fields.items(): track_visibility = getattr(self._fields[col_name], 'track_visibility', 'onchange') initial_value = initial[col_name] new_value = getattr(self, col_name) @@ -817,7 +816,7 @@ class MailThread(models.AbstractModel): def message_capable_models(self): """ Used by the plugin addon, based for plugin_outlook and others. """ ret_dict = {} - for model_name, model in pycompat.items(self.env): + for model_name, model in self.env.items(): if hasattr(model, "message_process") and hasattr(model, "message_post"): ret_dict[model_name] = model._description return ret_dict @@ -2057,7 +2056,7 @@ class MailThread(models.AbstractModel): if auto_follow_fields is None: auto_follow_fields = ['user_id'] user_field_lst = [] - for name, field in pycompat.items(self._fields): + for name, field in self._fields.items(): if name in auto_follow_fields and name in updated_fields and getattr(field, 'track_visibility', False) and field.comodel_name == 'res.users': user_field_lst.append(name) return user_field_lst @@ -2153,10 +2152,10 @@ class MailThread(models.AbstractModel): for partner in to_add_users.mapped('partner_id'): new_partners.setdefault(partner.id, None) - for pid, subtypes in pycompat.items(new_partners): + for pid, subtypes in new_partners.items(): subtypes = list(subtypes) if subtypes is not None else None self.message_subscribe(partner_ids=[pid], subtype_ids=subtypes, force=(subtypes != None)) - for cid, subtypes in pycompat.items(new_channels): + for cid, subtypes in new_channels.items(): subtypes = list(subtypes) if subtypes is not None else None self.message_subscribe(channel_ids=[cid], subtype_ids=subtypes, force=(subtypes != None)) diff --git a/addons/mail/models/res_partner.py b/addons/mail/models/res_partner.py index 6379ddb8c42..9140959f89e 100644 --- a/addons/mail/models/res_partner.py +++ b/addons/mail/models/res_partner.py @@ -4,7 +4,6 @@ import logging import threading -from odoo.tools import pycompat from odoo.tools.misc import split_every from odoo import _, api, fields, models, registry, SUPERUSER_ID @@ -175,7 +174,7 @@ class Partner(models.Model): emails = self.env['mail.mail'] recipients_nbr, recipients_max = 0, 50 - for email_type, recipient_template_values in pycompat.items(recipients): + for email_type, recipient_template_values in recipients.items(): if recipient_template_values['followers']: # generate notification email content template_fol_values = dict(base_template_ctx, **recipient_template_values) # fixme: set button_unfollow to none diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 76412ce041c..767462df892 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -140,7 +140,7 @@ class MailComposer(models.TransientModel): for mid, rmod, rid in self._cr.fetchall(): message_values[mid] = {'model': rmod, 'res_id': rid} # remove from the set to check the ids that mail_compose_message accepts - author_ids = [mid for mid, message in pycompat.items(message_values) + author_ids = [mid for mid, message in message_values.items() if message.get('model') and not message.get('res_id')] self = self.browse(list(set(self.ids) - set(author_ids))) # not sure slef = ... @@ -248,7 +248,7 @@ class MailComposer(models.TransientModel): for res_ids in sliced_res_ids: batch_mails = Mail all_mail_values = wizard.get_mail_values(res_ids) - for res_id, mail_values in pycompat.items(all_mail_values): + for res_id, mail_values in all_mail_values.items(): if wizard.composition_mode == 'mass_mail': batch_mails |= Mail.create(mail_values) else: @@ -338,7 +338,7 @@ class MailComposer(models.TransientModel): def onchange_template_id_wrapper(self): self.ensure_one() values = self.onchange_template_id(self.template_id.id, self.composition_mode, self.model, self.res_id)['value'] - for fname, value in pycompat.items(values): + for fname, value in values.items(): setattr(self, fname, value) @api.multi diff --git a/addons/marketing_campaign/models/marketing_campaign.py b/addons/marketing_campaign/models/marketing_campaign.py index 7370633114e..6fea98870d8 100644 --- a/addons/marketing_campaign/models/marketing_campaign.py +++ b/addons/marketing_campaign/models/marketing_campaign.py @@ -9,7 +9,6 @@ import re from odoo import api, fields, models, _ from odoo.exceptions import UserError, ValidationError -from odoo.tools import pycompat from odoo.tools.safe_eval import safe_eval from odoo.addons import decimal_precision as dp @@ -466,7 +465,7 @@ class MarketingCampaignWorkitem(models.Model): matching_workitems = [] for id, res_id, model in res: workitem_map.setdefault(model, {}).setdefault(res_id, set()).add(id) - for model, id_map in pycompat.items(workitem_map): + for model, id_map in workitem_map.items(): Model = self.env[model] condition_name[0] = Model._rec_name condition = [('id', 'in', list(id_map)), condition_name] diff --git a/addons/mrp/models/mrp_workcenter.py b/addons/mrp/models/mrp_workcenter.py index 58226b2e9e8..cc12ec0060b 100644 --- a/addons/mrp/models/mrp_workcenter.py +++ b/addons/mrp/models/mrp_workcenter.py @@ -5,7 +5,6 @@ from dateutil import relativedelta import datetime from odoo import api, exceptions, fields, models, _ -from odoo.tools import pycompat class MrpWorkcenter(models.Model): @@ -74,7 +73,7 @@ class MrpWorkcenter(models.Model): if res_group['state'] in ('pending', 'ready', 'progress'): result_duration_expected[res_group['workcenter_id'][0]] += res_group['duration_expected'] for workcenter in self: - workcenter.workorder_count = sum(count for state, count in pycompat.items(result[workcenter.id]) if state not in ('done', 'cancel')) + workcenter.workorder_count = sum(count for state, count in result[workcenter.id].items() if state not in ('done', 'cancel')) workcenter.workorder_pending_count = result[workcenter.id].get('pending', 0) workcenter.workcenter_load = result_duration_expected[workcenter.id] workcenter.workorder_ready_count = result[workcenter.id].get('ready', 0) diff --git a/addons/mrp_repair/wizard/mrp_repair_make_invoice.py b/addons/mrp_repair/wizard/mrp_repair_make_invoice.py index a659e45e962..03e89da5cd5 100644 --- a/addons/mrp_repair/wizard/mrp_repair_make_invoice.py +++ b/addons/mrp_repair/wizard/mrp_repair_make_invoice.py @@ -2,7 +2,6 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import api, fields, models -from odoo.tools import pycompat class MakeInvoice(models.TransientModel): @@ -25,7 +24,7 @@ class MakeInvoice(models.TransientModel): # but that second call will not do anything, since the repairs are already invoiced. repairs.action_repair_invoice_create() return { - 'domain': [('id', 'in', list(pycompat.values(new_invoice)))], + 'domain': [('id', 'in', list(new_invoice.values()))], 'name': 'Invoices', 'view_type': 'form', 'view_mode': 'tree,form', diff --git a/addons/pad/models/pad.py b/addons/pad/models/pad.py index 945e24f4308..d0c2055c739 100644 --- a/addons/pad/models/pad.py +++ b/addons/pad/models/pad.py @@ -10,7 +10,7 @@ import requests from odoo import api, models, _ from odoo.exceptions import UserError -from odoo.tools import html2plaintext, pycompat +from odoo.tools import html2plaintext from ..py_etherpad import EtherpadLiteClient @@ -106,7 +106,7 @@ class PadCommon(models.AbstractModel): # In case the pad is created programmatically, the content is not filled in yet since it is # normally initialized by the JS layer - for k, field in pycompat.items(self._fields): + for k, field in self._fields.items(): if hasattr(field, 'pad_content_field') and k not in vals: ctx = { 'model': self._name, @@ -121,7 +121,7 @@ class PadCommon(models.AbstractModel): def _set_pad_value(self, vals): # Update the pad if the `pad_content_field` is modified - for k, field in pycompat.items(self._fields): + for k, field in self._fields.items(): if hasattr(field, 'pad_content_field') and vals.get(field.pad_content_field) and self[k]: company = self.env.user.sudo().company_id myPad = EtherpadLiteClient(company.pad_key, company.pad_server + '/api') @@ -129,7 +129,7 @@ class PadCommon(models.AbstractModel): myPad.setText(path, (html2plaintext(vals[field.pad_content_field]).encode('utf-8'))) # Update the `pad_content_field` if the pad is modified - for k, v in list(pycompat.items(vals)): + for k, v in vals.items(): field = self._fields[k] if hasattr(field, 'pad_content_field'): vals[field.pad_content_field] = self.pad_get_content(v) @@ -139,7 +139,7 @@ class PadCommon(models.AbstractModel): self.ensure_one() if not default: default = {} - for k, field in pycompat.items(self._fields): + for k, field in self._fields.items(): if hasattr(field, 'pad_content_field'): pad = self.pad_generate_url() default[k] = pad.get('url') diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py index 909536ac95c..2e36d891344 100644 --- a/addons/payment/models/payment_acquirer.py +++ b/addons/payment/models/payment_acquirer.py @@ -5,7 +5,7 @@ import logging import datetime from odoo import api, exceptions, fields, models, _ -from odoo.tools import consteq, float_round, image_resize_images, ustr, pycompat +from odoo.tools import consteq, float_round, image_resize_images, ustr from odoo.addons.base.module import module from odoo.exceptions import ValidationError @@ -169,7 +169,7 @@ class PaymentAcquirer(models.Model): """ If the field has 'required_if_provider=""' attribute, then it required if record.provider is . """ for acquirer in self: - if any(getattr(f, 'required_if_provider', None) == acquirer.provider and not acquirer[k] for k, f in pycompat.items(self._fields)): + if any(getattr(f, 'required_if_provider', None) == acquirer.provider and not acquirer[k] for k, f in self._fields.items()): return False return True diff --git a/addons/payment_adyen/models/payment.py b/addons/payment_adyen/models/payment.py index 787160fd1ea..dbc24ced0c0 100644 --- a/addons/payment_adyen/models/payment.py +++ b/addons/payment_adyen/models/payment.py @@ -15,8 +15,6 @@ from odoo import api, fields, models, tools, _ from odoo.addons.payment.models.payment_acquirer import ValidationError from odoo.addons.payment_adyen.controllers.main import AdyenController -from odoo.tools import pycompat - _logger = logging.getLogger(__name__) @@ -52,7 +50,7 @@ class AcquirerAdyen(models.Model): def signParams(parms): signing_string = ':'.join( escapeVal(v) - for v in chain(pycompat.keys(parms), pycompat.values(parms)) + for v in chain(parms.keys(), parms.values()) ) hm = hmac.new(hmac_key, signing_string.encode('utf-8'), hashlib.sha256) return base64.b64encode(hm.digest()) @@ -81,7 +79,7 @@ class AcquirerAdyen(models.Model): hmac_key = binascii.a2b_hex(self.adyen_skin_hmac_key.encode('ascii')) raw_values = {k: values.get(k.encode('ascii'), '') for k in keys if k in values} - raw_values_ordered = OrderedDict(sorted(pycompat.items(raw_values), key=lambda t: t[0])) + raw_values_ordered = OrderedDict(sorted(raw_values.items(), key=lambda t: t[0])) return signParams(raw_values_ordered) diff --git a/addons/payment_authorize/tests/test_authorize.py b/addons/payment_authorize/tests/test_authorize.py index 6d3f4244583..59fb401b091 100644 --- a/addons/payment_authorize/tests/test_authorize.py +++ b/addons/payment_authorize/tests/test_authorize.py @@ -11,7 +11,7 @@ import odoo from odoo.addons.payment.models.payment_acquirer import ValidationError from odoo.addons.payment.tests.common import PaymentAcquirerCommon from odoo.addons.payment_authorize.controllers.main import AuthorizeController -from odoo.tools import mute_logger, pycompat +from odoo.tools import mute_logger @odoo.tests.common.at_install(True) @@ -91,7 +91,7 @@ class AuthorizeForm(AuthorizeCommon): tree = objectify.fromstring(res) self.assertEqual(tree.get('action'), 'https://test.authorize.net/gateway/transact.dll', 'Authorize: wrong form POST url') for el in tree.iterfind('input'): - values = list(pycompat.values(el.attrib)) + values = list(el.attrib.values()) if values[1] in ['submit', 'x_fp_hash', 'return_url', 'x_state', 'x_ship_to_state']: continue self.assertEqual( diff --git a/addons/payment_buckaroo/controllers/main.py b/addons/payment_buckaroo/controllers/main.py index 5af6a454b47..40e4ae539e4 100644 --- a/addons/payment_buckaroo/controllers/main.py +++ b/addons/payment_buckaroo/controllers/main.py @@ -6,7 +6,6 @@ import werkzeug from odoo import http from odoo.http import request -from odoo.tools import pycompat _logger = logging.getLogger(__name__) @@ -27,6 +26,6 @@ class BuckarooController(http.Controller): """ Buckaroo.""" _logger.info('Buckaroo: entering form_feedback with post data %s', pprint.pformat(post)) # debug request.env['payment.transaction'].sudo().form_feedback(post, 'buckaroo') - post = {key.upper(): value for key, value in pycompat.items(post)} + post = {key.upper(): value for key, value in post.items()} return_url = post.get('ADD_RETURNDATA') or '/' return werkzeug.utils.redirect(return_url) diff --git a/addons/payment_buckaroo/models/payment.py b/addons/payment_buckaroo/models/payment.py index a07ca83318d..ec0cadfab01 100644 --- a/addons/payment_buckaroo/models/payment.py +++ b/addons/payment_buckaroo/models/payment.py @@ -8,7 +8,6 @@ from odoo import api, fields, models, _ from odoo.addons.payment.models.payment_acquirer import ValidationError from odoo.addons.payment_buckaroo.controllers.main import BuckarooController -from odoo.tools import pycompat from odoo.tools.float_utils import float_compare _logger = logging.getLogger(__name__) @@ -21,7 +20,7 @@ def normalize_keys_upper(data): convert everything to upper case to be able to easily detected the presence of a parameter by checking the uppercase key only """ - return {key.upper(): val for key, val in pycompat.items(data)} + return {key.upper(): val for key, val in data.items()} class AcquirerBuckaroo(models.Model): @@ -73,7 +72,7 @@ class AcquirerBuckaroo(models.Model): del values[key] break - items = sorted(pycompat.items(values), key=lambda pair: pair[0].lower()) + items = sorted(values.items(), key=lambda pair: pair[0].lower()) sign = ''.join('%s=%s' % (k, urls.url_unquote_plus(v)) for k, v in items) else: sign = ''.join('%s=%s' % (k, get_value(k)) for k in keys) diff --git a/addons/payment_ogone/models/payment.py b/addons/payment_ogone/models/payment.py index a5ae46bdcd3..7a2adf37d45 100644 --- a/addons/payment_ogone/models/payment.py +++ b/addons/payment_ogone/models/payment.py @@ -15,7 +15,7 @@ from odoo import api, fields, models, _ from odoo.addons.payment.models.payment_acquirer import ValidationError from odoo.addons.payment_ogone.controllers.main import OgoneController from odoo.addons.payment_ogone.data import ogone -from odoo.tools import float_round, DEFAULT_SERVER_DATE_FORMAT, pycompat +from odoo.tools import float_round, DEFAULT_SERVER_DATE_FORMAT from odoo.tools.float_utils import float_compare, float_repr from odoo.tools.safe_eval import safe_eval @@ -142,7 +142,7 @@ class PaymentAcquirerOgone(models.Model): ] return key.upper() in keys - items = sorted((k.upper(), v) for k, v in pycompat.items(values)) + items = sorted((k.upper(), v) for k, v in values.items()) sign = ''.join('%s=%s%s' % (k, v, key) for k, v in items if v and filter_key(k)) sign = sign.encode("utf-8") shasign = sha1(sign).hexdigest() diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index 5e059a41cfc..9e730f9a2e5 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -8,7 +8,7 @@ import psycopg2 import pytz from odoo import api, fields, models, tools, _ -from odoo.tools import float_is_zero, pycompat +from odoo.tools import float_is_zero from odoo.exceptions import UserError from odoo.http import request from odoo.addons import decimal_precision as dp @@ -333,7 +333,7 @@ class PosOrder(models.Model): # round tax lines per order if rounding_method == 'round_globally': - for group_key, group_value in pycompat.items(grouped_data): + for group_key, group_value in grouped_data.items(): if group_key[0] == 'tax': for line in group_value: line['credit'] = cur.round(line['credit']) @@ -351,7 +351,7 @@ class PosOrder(models.Model): order.write({'state': 'done', 'account_move': move.id}) all_lines = [] - for group_key, group_data in pycompat.items(grouped_data): + for group_key, group_data in grouped_data.items(): for value in group_data: all_lines.append((0, 0, value),) if move: # In case no order was changed @@ -1032,7 +1032,7 @@ class ReportSaleDetails(models.AbstractModel): 'total_paid': user_currency.round(total), 'payments': payments, 'company_name': self.env.user.company_id.name, - 'taxes': list(pycompat.values(taxes)), + 'taxes': list(taxes.values()), 'products': sorted([{ 'product_id': product.id, 'product_name': product.name, @@ -1041,7 +1041,7 @@ class ReportSaleDetails(models.AbstractModel): 'price_unit': price_unit, 'discount': discount, 'uom': product.uom_id.name - } for (product, price_unit, discount), qty in pycompat.items(products_sold)], key=lambda l: l['product_name']) + } for (product, price_unit, discount), qty in products_sold.items()], key=lambda l: l['product_name']) } @api.multi diff --git a/addons/product/models/product_pricelist.py b/addons/product/models/product_pricelist.py index a0457f021ba..392447809f4 100644 --- a/addons/product/models/product_pricelist.py +++ b/addons/product/models/product_pricelist.py @@ -90,7 +90,7 @@ class Pricelist(models.Model): results = {} for pricelist in pricelists: subres = pricelist._compute_price_rule(products_qty_partner, date=date, uom_id=uom_id) - for product_id, price in pycompat.items(subres): + for product_id, price in subres.items(): results.setdefault(product_id, {}) results[product_id][pricelist.id] = price return results @@ -254,11 +254,11 @@ class Pricelist(models.Model): self.ensure_one() return { product_id: res_tuple[0] - for product_id, res_tuple in pycompat.items(self._compute_price_rule( + for product_id, res_tuple in self._compute_price_rule( list(pycompat.izip(products, quantities, partners)), date=date, uom_id=uom_id - )) + ).items() } def get_product_price(self, product, quantity, partner, date=False, uom_id=False): @@ -280,7 +280,7 @@ class Pricelist(models.Model): @api.multi def price_get(self, prod_id, qty, partner=None): """ Multi pricelist, mono product - returns price per pricelist """ - return {key: price[0] for key, price in pycompat.items(self.price_rule_get(prod_id, qty, partner=partner))} + return {key: price[0] for key, price in self.price_rule_get(prod_id, qty, partner=partner).items()} @api.multi def price_rule_get_multi(self, products_by_qty_by_partner): diff --git a/addons/product_expiry/models/production_lot.py b/addons/product_expiry/models/production_lot.py index 89d2a7569ff..6be9a2865dd 100644 --- a/addons/product_expiry/models/production_lot.py +++ b/addons/product_expiry/models/production_lot.py @@ -2,7 +2,6 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import datetime from odoo import api, fields, models -from odoo.tools import pycompat class StockProductionLot(models.Model): @@ -54,5 +53,5 @@ class StockProductionLot(models.Model): @api.onchange('product_id') def _onchange_product(self): dates_dict = self._get_dates() - for field, value in pycompat.items(dates_dict): + for field, value in dates_dict.items(): setattr(self, field, value) diff --git a/addons/product_margin/models/product_product.py b/addons/product_margin/models/product_product.py index f203a9e146b..3dd735b26a4 100644 --- a/addons/product_margin/models/product_product.py +++ b/addons/product_margin/models/product_product.py @@ -4,7 +4,6 @@ import time from odoo import api, fields, models -from odoo.tools import pycompat class ProductProduct(models.Model): @@ -139,6 +138,6 @@ class ProductProduct(models.Model): res[val.id]['expected_margin'] = res[val.id]['sale_expected'] - res[val.id]['normal_cost'] res[val.id]['total_margin_rate'] = res[val.id]['turnover'] and res[val.id]['total_margin'] * 100 / res[val.id]['turnover'] or 0.0 res[val.id]['expected_margin_rate'] = res[val.id]['sale_expected'] and res[val.id]['expected_margin'] * 100 / res[val.id]['sale_expected'] or 0.0 - for k, v in pycompat.items(res[val.id]): + for k, v in res[val.id].items(): setattr(val, k, v) return res diff --git a/addons/rating/models/rating.py b/addons/rating/models/rating.py index a5b54afb3ac..f4bd17b48f3 100644 --- a/addons/rating/models/rating.py +++ b/addons/rating/models/rating.py @@ -6,7 +6,6 @@ import uuid from odoo import api, fields, models, tools, _ from odoo.modules.module import get_resource_path -from odoo.tools import pycompat RATING_LIMIT_SATISFIED = 7 RATING_LIMIT_OK = 3 @@ -275,7 +274,7 @@ class RatingMixin(models.AbstractModel): values.update((d['rating'], d['rating_count']) for d in data) # add other stats if add_stats: - rating_number = sum(pycompat.values(values)) + rating_number = sum(values.values()) result = { 'repartition': values, 'avg': sum(float(key * values[key]) for key in values) / rating_number if rating_number > 0 else 0, diff --git a/addons/rating_project/models/project.py b/addons/rating_project/models/project.py index 915c97848ec..8a4f86d3b96 100644 --- a/addons/rating_project/models/project.py +++ b/addons/rating_project/models/project.py @@ -3,7 +3,6 @@ from datetime import timedelta -from odoo.tools import pycompat from odoo import api, fields, models, _ from odoo.tools.safe_eval import safe_eval @@ -80,13 +79,13 @@ class Project(models.Model): domain = [('create_date', '>=', fields.Datetime.to_string(fields.datetime.now() - timedelta(days=30)))] for project in self: activity = project.tasks.rating_get_grades(domain) - project.percentage_satisfaction_project = activity['great'] * 100 / sum(pycompat.values(activity)) if sum(pycompat.values(activity)) else -1 + project.percentage_satisfaction_project = activity['great'] * 100 / sum(activity.values()) if sum(activity.values()) else -1 @api.one @api.depends('tasks.rating_ids.rating') def _compute_percentage_satisfaction_task(self): activity = self.tasks.rating_get_grades() - self.percentage_satisfaction_task = activity['great'] * 100 / sum(pycompat.values(activity)) if sum(pycompat.values(activity)) else -1 + self.percentage_satisfaction_task = activity['great'] * 100 / sum(activity.values()) if sum(activity.values()) else -1 percentage_satisfaction_task = fields.Integer( compute='_compute_percentage_satisfaction_task', string="Happy % on Task", store=True, default=-1) diff --git a/addons/rating_project_issue/models/project_issue.py b/addons/rating_project_issue/models/project_issue.py index 8116c0f50f1..cd3d541d1ef 100644 --- a/addons/rating_project_issue/models/project_issue.py +++ b/addons/rating_project_issue/models/project_issue.py @@ -4,7 +4,6 @@ from datetime import timedelta from odoo import api, fields, models -from odoo.tools import pycompat from odoo.tools.safe_eval import safe_eval @@ -63,11 +62,11 @@ class Project(models.Model): if project.use_tasks: activity_task = project.tasks.rating_get_grades(domain) activity_great = activity_task['great'] - activity_sum = sum(pycompat.values(activity_task)) + activity_sum = sum(activity_task.values()) if project.use_issues: activity_issue = self.env['project.issue'].search([('project_id', '=', project.id)]).rating_get_grades(domain) activity_great += activity_issue['great'] - activity_sum += sum(pycompat.values(activity_issue)) + activity_sum += sum(activity_issue.values()) project.percentage_satisfaction_project = activity_great * 100 / activity_sum if activity_sum else -1 @api.one @@ -75,7 +74,7 @@ class Project(models.Model): def _compute_percentage_satisfaction_issue(self): project_issue = self.env['project.issue'].search([('project_id', '=', self.id)]) activity = project_issue.rating_get_grades() - self.percentage_satisfaction_issue = activity['great'] * 100 / sum(pycompat.values(activity)) if sum(pycompat.values(activity)) else -1 + self.percentage_satisfaction_issue = activity['great'] * 100 / sum(activity.values()) if sum(activity.values()) else -1 percentage_satisfaction_issue = fields.Integer(compute='_compute_percentage_satisfaction_issue', string="Happy % on Issue", store=True, default=-1) diff --git a/addons/sale/models/account_invoice.py b/addons/sale/models/account_invoice.py index 5163f4baa6c..c4ad56d1277 100644 --- a/addons/sale/models/account_invoice.py +++ b/addons/sale/models/account_invoice.py @@ -3,7 +3,6 @@ from itertools import groupby from odoo import api, fields, models, _ -from odoo.tools import pycompat class AccountInvoice(models.Model): @@ -58,7 +57,7 @@ class AccountInvoice(models.Model): result = super(AccountInvoice, self)._refund_cleanup_lines(lines) if self.env.context.get('mode') == 'modify': for i, line in enumerate(lines): - for name, field in pycompat.items(line._fields): + for name, field in line._fields.items(): if name == 'sale_line_ids': result[i][2][name] = [(6, 0, line[name].ids)] line[name] = False diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index 7e554c6af08..8fe5623e339 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -8,7 +8,7 @@ from datetime import datetime, timedelta from odoo import api, fields, models, _ from odoo.exceptions import UserError -from odoo.tools import float_is_zero, float_compare, DEFAULT_SERVER_DATETIME_FORMAT, pycompat +from odoo.tools import float_is_zero, float_compare, DEFAULT_SERVER_DATETIME_FORMAT from odoo.tools.misc import formatLang from odoo.addons import decimal_precision as dp @@ -400,7 +400,7 @@ class SaleOrder(models.Model): if not invoices: raise UserError(_('There is no invoicable line.')) - for invoice in pycompat.values(invoices): + for invoice in invoices.values(): if not invoice.invoice_line_ids: raise UserError(_('There is no invoicable line.')) # If invoice is negative, do a refund invoice instead @@ -417,7 +417,7 @@ class SaleOrder(models.Model): invoice.message_post_with_view('mail.message_origin_link', values={'self': invoice, 'origin': references[invoice]}, subtype_id=self.env.ref('mail.mt_note').id) - return [inv.id for inv in pycompat.values(invoices)] + return [inv.id for inv in invoices.values()] @api.multi def action_draft(self): @@ -557,7 +557,7 @@ class SaleOrder(models.Model): if tax.include_base_amount: base_tax += tax.compute_all(price_reduce + base_tax, quantity=1, product=line.product_id, partner=self.partner_shipping_id)['taxes'][0]['amount'] - res = sorted(pycompat.items(res), key=lambda l: l[0].sequence) + res = sorted(res.items(), key=lambda l: l[0].sequence) res = [(l[0].name, l[1]) for l in res] return res diff --git a/addons/sale/models/sale_analytic.py b/addons/sale/models/sale_analytic.py index 733bd74bc2a..96f2f896f44 100644 --- a/addons/sale/models/sale_analytic.py +++ b/addons/sale/models/sale_analytic.py @@ -3,7 +3,6 @@ from odoo import api, fields, models, _ from odoo.exceptions import UserError -from odoo.tools import pycompat class SaleOrderLine(models.Model): @@ -41,7 +40,7 @@ class SaleOrderLine(models.Model): qty = d['unit_amount'] lines[line] += qty - for line, qty in pycompat.items(lines): + for line, qty in lines.items(): line.qty_delivered = qty return True diff --git a/addons/sale/tests/test_sale_order.py b/addons/sale/tests/test_sale_order.py index 84dbee6a3fd..2b6c082b150 100644 --- a/addons/sale/tests/test_sale_order.py +++ b/addons/sale/tests/test_sale_order.py @@ -2,7 +2,6 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo.exceptions import UserError, AccessError -from odoo.tools import pycompat from .test_sale_common import TestSale @@ -18,10 +17,10 @@ class TestSaleOrder(TestSale): 'partner_id': self.partner.id, 'partner_invoice_id': self.partner.id, 'partner_shipping_id': self.partner.id, - 'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for (_, p) in pycompat.items(self.products)], + 'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for p in self.products.values()], 'pricelist_id': self.env.ref('product.list0').id, }) - self.assertEqual(so.amount_total, sum([2 * p.list_price for (k, p) in pycompat.items(self.products)]), 'Sale: total amount is wrong') + self.assertEqual(so.amount_total, sum([2 * p.list_price for p in self.products.values()]), 'Sale: total amount is wrong') # send quotation so.force_quotation_send() @@ -36,7 +35,7 @@ class TestSaleOrder(TestSale): inv_id = so.action_invoice_create() inv = inv_obj.browse(inv_id) self.assertEqual(len(inv.invoice_line_ids), 2, 'Sale: invoice is missing lines') - self.assertEqual(inv.amount_total, sum([2 * p.list_price if p.invoice_policy == 'order' else 0 for (k, p) in pycompat.items(self.products)]), 'Sale: invoice total amount is wrong') + self.assertEqual(inv.amount_total, sum([2 * p.list_price if p.invoice_policy == 'order' else 0 for p in self.products.values()]), 'Sale: invoice total amount is wrong') self.assertTrue(so.invoice_status == 'no', 'Sale: SO status after invoicing should be "nothing to invoice"') self.assertTrue(len(so.invoice_ids) == 1, 'Sale: invoice is missing') @@ -47,7 +46,7 @@ class TestSaleOrder(TestSale): inv_id = so.action_invoice_create() inv = inv_obj.browse(inv_id) self.assertEqual(len(inv.invoice_line_ids), 2, 'Sale: second invoice is missing lines') - self.assertEqual(inv.amount_total, sum([2 * p.list_price if p.invoice_policy == 'delivery' else 0 for (k, p) in pycompat.items(self.products)]), 'Sale: second invoice total amount is wrong') + self.assertEqual(inv.amount_total, sum([2 * p.list_price if p.invoice_policy == 'delivery' else 0 for p in self.products.values()]), 'Sale: second invoice total amount is wrong') self.assertTrue(so.invoice_status == 'invoiced', 'Sale: SO status after invoicing everything should be "invoiced"') self.assertTrue(len(so.invoice_ids) == 2, 'Sale: invoice is missing') # go over the sold quantity @@ -72,7 +71,7 @@ class TestSaleOrder(TestSale): 'partner_id': self.partner.id, 'partner_invoice_id': self.partner.id, 'partner_shipping_id': self.partner.id, - 'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for (_, p) in pycompat.items(self.products)], + 'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for p in self.products.values()], 'pricelist_id': self.env.ref('product.list0').id, }) diff --git a/addons/sale_stock/tests/test_sale_stock.py b/addons/sale_stock/tests/test_sale_stock.py index 5a4755d2c16..3dd6d5103ca 100644 --- a/addons/sale_stock/tests/test_sale_stock.py +++ b/addons/sale_stock/tests/test_sale_stock.py @@ -3,7 +3,6 @@ from odoo.addons.sale.tests.test_sale_common import TestSale from odoo.exceptions import UserError -from odoo.tools import pycompat class TestSaleStock(TestSale): @@ -17,7 +16,7 @@ class TestSaleStock(TestSale): 'partner_id': self.partner.id, 'partner_invoice_id': self.partner.id, 'partner_shipping_id': self.partner.id, - 'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for (_, p) in pycompat.items(self.products)], + 'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for p in self.products.values()], 'pricelist_id': self.env.ref('product.list0').id, 'picking_policy': 'direct', }) @@ -74,7 +73,7 @@ class TestSaleStock(TestSale): 'partner_id': self.partner.id, 'partner_invoice_id': self.partner.id, 'partner_shipping_id': self.partner.id, - 'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for (_, p) in pycompat.items(self.products)], + 'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for p in self.products.values()], 'pricelist_id': self.env.ref('product.list0').id, 'picking_policy': 'direct', }) diff --git a/addons/stock/models/procurement.py b/addons/stock/models/procurement.py index 8ac7fc638d7..8d5e419fb90 100644 --- a/addons/stock/models/procurement.py +++ b/addons/stock/models/procurement.py @@ -9,8 +9,7 @@ from psycopg2 import OperationalError from odoo import api, fields, models, registry, _ from odoo.osv import expression -from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, float_compare, \ - float_round, pycompat +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, float_compare, float_round import logging @@ -311,7 +310,7 @@ class ProcurementOrder(models.Model): location_data[key]['orderpoints'] += orderpoint location_data[key]['groups'] = self._procurement_from_orderpoint_get_groups([orderpoint.id]) - for location_id, location_data in pycompat.items(location_data): + for location_id, location_data in location_data.items(): location_orderpoints = location_data['orderpoints'] product_context = dict(self._context, location=location_orderpoints[0].location_id.id) substract_quantity = location_orderpoints.subtract_procurements_from_orderpoints() diff --git a/addons/stock/models/stock_inventory.py b/addons/stock/models/stock_inventory.py index 866f3e2104c..bb14b839d32 100644 --- a/addons/stock/models/stock_inventory.py +++ b/addons/stock/models/stock_inventory.py @@ -4,7 +4,7 @@ from odoo import api, fields, models, _ from odoo.addons import decimal_precision as dp from odoo.exceptions import UserError -from odoo.tools import float_utils, pycompat +from odoo.tools import float_utils class Inventory(models.Model): @@ -251,7 +251,7 @@ class Inventory(models.Model): for product_data in self.env.cr.dictfetchall(): # replace the None the dictionary by False, because falsy values are tested later on - for void_field in [item[0] for item in pycompat.items(product_data) if item[1] is None]: + for void_field in [item[0] for item in product_data.items() if item[1] is None]: product_data[void_field] = False product_data['theoretical_qty'] = product_data['product_qty'] if product_data['product_id']: diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 5eab237011e..6b7b74f5143 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -10,7 +10,7 @@ from odoo import api, fields, models, _ from odoo.addons import decimal_precision as dp from odoo.addons.procurement.models import procurement from odoo.exceptions import UserError -from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, pycompat +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT from odoo.tools.float_utils import float_compare, float_round, float_is_zero @@ -595,7 +595,7 @@ class StockMove(models.Model): (move_waiting | move_create_proc).write({'state': 'waiting'}) # assign picking in batch for all confirmed move that share the same details - for key, moves in pycompat.items(to_assign): + for moves in to_assign.values(): moves.assign_picking() self._push_apply() return self diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index 536ac2a5e6d..de736ef0bcf 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -7,7 +7,7 @@ import time from itertools import groupby from odoo import api, fields, models, _ -from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, pycompat +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT from odoo.tools.float_utils import float_compare, float_round from odoo.addons.procurement.models import procurement from odoo.exceptions import UserError @@ -665,7 +665,7 @@ class Picking(models.Model): self.ensure_one() moves = self.env['stock.move'] for move_line in self.move_line_ids: - for product, remaining_qty in pycompat.items(move_line._get_remaining_prod_quantities()): + for product, remaining_qty in move_line._get_remaining_prod_quantities().items(): if float_compare(remaining_qty, 0, precision_rounding=product.uom_id.rounding) > 0: vals = self._prepare_values_extra_move(move_line, product, remaining_qty) moves |= moves.create(vals) diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py index 5609f5be6a6..f2ebb9d0c87 100644 --- a/addons/stock/models/stock_quant.py +++ b/addons/stock/models/stock_quant.py @@ -5,7 +5,6 @@ from psycopg2 import OperationalError from odoo import api, fields, models, _ from odoo.exceptions import UserError, ValidationError -from odoo.tools import pycompat from odoo.osv import expression @@ -274,7 +273,7 @@ class QuantPackage(models.Model): @api.multi def name_get(self): - return list(pycompat.items(self._compute_complete_name())) + return list(self._compute_complete_name().items()) def _compute_complete_name(self): """ Forms complete name of location from parent location to child location. """ diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py index 1f7e44f23ea..d8bd64253c6 100644 --- a/addons/stock/models/stock_warehouse.py +++ b/addons/stock/models/stock_warehouse.py @@ -8,7 +8,7 @@ from dateutil import relativedelta from odoo import api, fields, models, _ from odoo.addons import decimal_precision as dp from odoo.exceptions import UserError, ValidationError -from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, pycompat +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT import logging @@ -99,7 +99,7 @@ class Warehouse(models.Model): 'wh_output_stock_loc_id': {'name': _('Output'), 'active': delivery_steps != 'ship_only', 'usage': 'internal'}, 'wh_pack_stock_loc_id': {'name': _('Packing Zone'), 'active': delivery_steps == 'pick_pack_ship', 'usage': 'internal'}, } - for field_name, values in pycompat.items(sub_locations): + for field_name, values in sub_locations.items(): values['location_id'] = vals['view_location_id'] if vals.get('company_id'): values['company_id'] = vals.get('company_id') @@ -240,10 +240,10 @@ class Warehouse(models.Model): }, } data = self._get_picking_type_values(self.reception_steps, self.delivery_steps, self.wh_pack_stock_loc_id) - for field_name, values in pycompat.items(data): + for field_name in data: data[field_name].update(create_data[field_name]) - for picking_type, values in pycompat.items(data): + for picking_type, values in data.items(): sequence = IrSequenceSudo.create(sequence_data[picking_type]) values.update(warehouse_id=self.id, color=color, sequence_id=sequence.id) warehouse_data[picking_type] = PickingType.create(values).id @@ -598,8 +598,8 @@ class Warehouse(models.Model): @api.one def _update_picking_type(self): picking_type_values = self._get_picking_type_values(self.reception_steps, self.delivery_steps, self.wh_pack_stock_loc_id) - for field_name, values in pycompat.items(picking_type_values): - getattr(self, field_name).write(values) + for field_name, values in picking_type_values.items(): + self[field_name].write(values) @api.multi def _update_name_and_code(self, new_name=False, new_code=False): diff --git a/addons/stock/wizard/make_procurement.py b/addons/stock/wizard/make_procurement.py index 369995e7a15..2fa3db75b5d 100644 --- a/addons/stock/wizard/make_procurement.py +++ b/addons/stock/wizard/make_procurement.py @@ -2,7 +2,6 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import api, fields, models -from odoo.tools import pycompat class MakeProcurement(models.TransientModel): @@ -51,8 +50,8 @@ class MakeProcurement(models.TransientModel): @api.onchange('product_id') def onchange_product_id(self): if self.product_id: - for key, value in pycompat.items(self.onchange_product_id_dict(self.product_id.id)): - setattr(self, key, value) + for key, value in self.onchange_product_id_dict(self.product_id.id).items(): + self[key] = value @api.model def create(self, values): diff --git a/addons/stock_landed_costs/models/stock_landed_cost.py b/addons/stock_landed_costs/models/stock_landed_cost.py index aeb6e42366f..5fd5ce8f63b 100644 --- a/addons/stock_landed_costs/models/stock_landed_cost.py +++ b/addons/stock_landed_costs/models/stock_landed_cost.py @@ -7,7 +7,6 @@ from odoo import api, fields, models, tools, _ from odoo.addons import decimal_precision as dp from odoo.addons.stock_landed_costs.models import product from odoo.exceptions import UserError -from odoo.tools import pycompat class StockMove(models.Model): @@ -123,7 +122,7 @@ class LandedCost(models.Model): for val_line in landed_cost.valuation_adjustment_lines: val_to_cost_lines[val_line.cost_line_id] += val_line.additional_landed_cost if any(tools.float_compare(cost_line.price_unit, val_amount, precision_digits=prec_digits) != 0 - for cost_line, val_amount in pycompat.items(val_to_cost_lines)): + for cost_line, val_amount in val_to_cost_lines.items()): return False return True @@ -204,9 +203,8 @@ class LandedCost(models.Model): towrite_dict[valuation.id] = value else: towrite_dict[valuation.id] += value - if towrite_dict: - for key, value in pycompat.items(towrite_dict): - AdjustementLines.browse(key).write({'additional_landed_cost': value}) + for key, value in towrite_dict.items(): + AdjustementLines.browse(key).write({'additional_landed_cost': value}) return True diff --git a/addons/survey/models/survey.py b/addons/survey/models/survey.py index 84692c63f9d..7bcf53f67ab 100644 --- a/addons/survey/models/survey.py +++ b/addons/survey/models/survey.py @@ -15,8 +15,6 @@ from odoo.exceptions import UserError, ValidationError from odoo.addons.website.models.website import slug -from odoo.tools import pycompat - email_validator = re.compile(r"[^@]+@[^@]+\.[^@]+") _logger = logging.getLogger(__name__) @@ -26,7 +24,7 @@ def dict_keys_startswith(dictionary, string): .. note:: This function uses dictionary comprehensions (Python >= 2.7) """ - return {k: v for k, v in pycompat.items(dictionary) if k.startswith(string)} + return {k: v for k, v in dictionary.items() if k.startswith(string)} class SurveyStage(models.Model): @@ -240,7 +238,7 @@ class Survey(models.Model): answers[input_line.value_suggested.id]['count'] += 1 if input_line.answer_type == 'text' and (not(current_filters) or input_line.user_input_id.id in current_filters): comments.append(input_line) - result_summary = {'answers': list(pycompat.values(answers)), 'comments': comments} + result_summary = {'answers': list(answers.values()), 'comments': comments} # Calculate and return statistics for matrix if question.type == 'matrix': @@ -640,7 +638,7 @@ class SurveyQuestion(models.Model): if self.comments_allowed: comment_answer = answer_candidates.pop(("%s_%s" % (answer_tag, 'comment')), '').strip() # Preventing answers with blank value - if all(not answer.strip() for answer in pycompat.values(answer_candidates)) and answer_candidates: + if all(not answer.strip() for answer in answer_candidates.values()) and answer_candidates: errors.update({answer_tag: self.constr_error_msg}) # There is no answer neither comments (if comments count as answer) if not answer_candidates and self.comment_count_as_answer and (not comment_flag or not comment_answer): diff --git a/addons/survey/tests/test_survey.py b/addons/survey/tests/test_survey.py index 04a65a02562..b5f9897438b 100644 --- a/addons/survey/tests/test_survey.py +++ b/addons/survey/tests/test_survey.py @@ -13,8 +13,6 @@ from odoo.exceptions import UserError from odoo.tests.common import TransactionCase from odoo.addons.website.models.website import slug -from odoo.tools import pycompat - class TestSurvey(TransactionCase): @@ -179,7 +177,7 @@ class TestSurvey(TransactionCase): base_url = self.env['ir.config_parameter'].get_param('web.base.url') urltypes = {'public': 'start', 'print': 'print', 'result': 'results'} - for urltype, urltxt in pycompat.items(urltypes): + for urltype, urltxt in urltypes.items(): survey_url = getattr(self.survey1, urltype + '_url') survey_url_relative = getattr(self.survey1.with_context({'relative_url': True}), urltype + '_url') self.assertTrue(validate_url(survey_url)) @@ -211,8 +209,8 @@ class TestSurvey(TransactionCase): answers = [input_portal.user_input_line_ids[0], input_public.user_input_line_ids[0]] expected_values = {'answer_type': 'free_text', 'value_free_text': "Test Answer"} for answer in answers: - for field, value in pycompat.items(expected_values): - self.assertEqual(getattr(answer, field), value, msg="Unable to answer the survey. Expected behaviour of %s is not proper." % (field)) + for field, value in expected_values.items(): + self.assertEqual(answer[field], value, msg="Unable to answer the survey. Expected behaviour of %s is not proper." % (field)) def test_10_survey_result_simple_multiple_choice(self): question = self.env['survey.question'].sudo(self.survey_manager).create({ @@ -265,7 +263,7 @@ class TestSurvey(TransactionCase): 'print': {'method': 'print', 'token': '/test', 'text': 'Print'}, 'result': {'method': 'result', 'token': '', 'text': 'Results of the'}, 'test': {'method': 'public', 'token': '/phantom', 'text': 'Results of the'}} - for action, val in pycompat.items(actions): + for action, val in actions.items(): result = getattr(self.survey1.with_context({'survey_token': val['token'][1:]}), 'action_' + action + '_survey')() url = getattr(self.survey1.with_context({'relative_url': True}), val['method'] + '_url') + val['token'] self.assertEqual(result['url'], url) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 114d7ba2e4e..abf019371dc 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -963,7 +963,7 @@ class Binary(http.Controller): def force_contenttype(self, headers, contenttype='image/png'): dictheaders = dict(headers) dictheaders['Content-Type'] = contenttype - return list(pycompat.items(dictheaders)) + return list(dictheaders.items()) @http.route(['/web/content', '/web/content/', @@ -1216,7 +1216,7 @@ class Export(http.Controller): else: fields['.id'] = fields.pop('id', {'string': 'ID'}) - fields_sequence = sorted(pycompat.items(fields), + fields_sequence = sorted(fields.items(), key=lambda field: odoo.tools.ustr(field[1].get('string', ''))) records = [] @@ -1227,7 +1227,7 @@ class Export(http.Controller): if field.get('readonly'): # If none of the field's states unsets readonly, skip the field if all(dict(attrs).get('readonly', True) - for attrs in pycompat.values(field.get('states', {}))): + for attrs in field.get('states', {}).values()): continue if not field.get('exportable', True): continue @@ -1319,7 +1319,7 @@ class Export(http.Controller): export_fields = [field.split('/', 1)[1] for field in fields] return ( (prefix + '/' + k, prefix_string + '/' + v) - for k, v in pycompat.items(self.fields_info(model, export_fields))) + for k, v in self.fields_info(model, export_fields).items()) class ExportFormat(object): raw_data = False diff --git a/addons/web_diagram/controllers/main.py b/addons/web_diagram/controllers/main.py index 56b6c76eefe..d29b60df40e 100644 --- a/addons/web_diagram/controllers/main.py +++ b/addons/web_diagram/controllers/main.py @@ -2,7 +2,6 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import odoo.http as http -from odoo.tools import pycompat from odoo.tools.safe_eval import safe_eval @@ -46,7 +45,7 @@ class DiagramView(http.Controller): isolate_nodes[blnk_node['id']] = blnk_node y = [ t['y'] - for t in pycompat.values(nodes) + for t in nodes.values() if t['x'] == 20 if t['y'] ] @@ -99,11 +98,11 @@ class DiagramView(http.Controller): color='white', options={} ) - for color, expr in pycompat.items(bgcolors): + for color, expr in bgcolors.items(): if safe_eval(expr, act): n['color'] = color - for shape, expr in pycompat.items(shapes): + for shape, expr in shapes.items(): if safe_eval(expr, act): n['shape'] = shape diff --git a/addons/web_editor/models/ir_ui_view.py b/addons/web_editor/models/ir_ui_view.py index 96c27bc923d..977f02f4b44 100644 --- a/addons/web_editor/models/ir_ui_view.py +++ b/addons/web_editor/models/ir_ui_view.py @@ -91,7 +91,7 @@ class IrUiView(models.Model): @api.model def to_field_ref(self, el): # filter out meta-information inserted in the document - attributes = {k: v for k, v in pycompat.items(el.attrib) + attributes = {k: v for k, v in el.attrib.items() if not k.startswith('data-oe-')} attributes['t-field'] = el.get('data-oe-expression') diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index 20e2320b68a..ef8245598a1 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -38,11 +38,11 @@ class QueryURL(object): def __call__(self, path=None, path_args=None, **kw): path = path or self.path - for key, value in pycompat.items(self.args): + for key, value in self.args.items(): kw.setdefault(key, value) path_args = OrderedSet(path_args or []) | self.path_args paths, fragments = {}, [] - for key, value in pycompat.items(kw): + for key, value in kw.items(): if value and key in path_args: if isinstance(value, models.BaseModel): paths[key] = slug(value) diff --git a/addons/website/models/ir_http.py b/addons/website/models/ir_http.py index c06ff77de90..133d0928caa 100644 --- a/addons/website/models/ir_http.py +++ b/addons/website/models/ir_http.py @@ -14,7 +14,7 @@ import odoo from odoo import api, models from odoo import SUPERUSER_ID from odoo.http import request -from odoo.tools import config, pycompat +from odoo.tools import config from odoo.exceptions import QWebException from odoo.tools.safe_eval import safe_eval @@ -244,7 +244,7 @@ class Http(models.AbstractModel): def _postprocess_args(cls, arguments, rule): super(Http, cls)._postprocess_args(arguments, rule) - for key, val in pycompat.items(arguments): + for key, val in arguments.items(): # Replace uid placeholder by the current request.uid if isinstance(val, models.BaseModel) and isinstance(val._uid, RequestUID): arguments[key] = val.sudo(request.uid) diff --git a/addons/website/models/ir_qweb.py b/addons/website/models/ir_qweb.py index e6cedfe07d2..a20db972c0b 100644 --- a/addons/website/models/ir_qweb.py +++ b/addons/website/models/ir_qweb.py @@ -5,7 +5,6 @@ import ast from odoo import models from odoo.http import request -from odoo.tools import pycompat class QWeb(models.AbstractModel): @@ -82,7 +81,7 @@ class QWeb(models.AbstractModel): atts = super(QWeb, self)._get_dynamic_att(tagName, atts, options, values) if options.get('rendering_bundle'): return atts - for name, value in pycompat.items(atts): + for name, value in atts.items(): atts[name] = self._website_build_attribute(tagName, name, value, options, values) return atts diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 77be240cfbe..55f44887f03 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -363,7 +363,7 @@ class Website(models.Model): def get_url_localized(router, lang): arguments = dict(request.endpoint_arguments) - for key, val in list(pycompat.items(arguments)): + for key, val in list(arguments.items()): if isinstance(val, models.BaseModel): arguments[key] = val.with_context(lang=lang) return router.build(request.endpoint, arguments) @@ -496,7 +496,7 @@ class Website(models.Model): endpoint = rule.endpoint methods = endpoint.routing.get('methods') or ['GET'] - converters = list(pycompat.values(rule._converters)) + converters = list(rule._converters.values()) if not ('GET' in methods and endpoint.routing['type'] == 'http' and endpoint.routing['auth'] in ('none', 'public') @@ -541,7 +541,7 @@ class Website(models.Model): values = [{}] # converters with a domain are processed after the other ones convitems = sorted( - pycompat.items(converters), + converters.items(), key=lambda x: hasattr(x[1], 'domain') and (x[1].domain != '[]')) for (i, (name, converter)) in enumerate(convitems): newval = [] @@ -557,7 +557,7 @@ class Website(models.Model): for value in values: domain_part, url = rule.build(value, append_unknown=False) page = {'loc': url} - for key, val in pycompat.items(value): + for key, val in value.items(): if key.startswith('__'): page[key[2:]] = val if url in ('/sitemap.xml',): diff --git a/addons/website/tests/test_converter.py b/addons/website/tests/test_converter.py index 9267826c689..311c035bbac 100644 --- a/addons/website/tests/test_converter.py +++ b/addons/website/tests/test_converter.py @@ -4,8 +4,6 @@ import unittest from odoo.addons.website.models.website import slugify, unslug -from odoo.tools import pycompat - class TestUnslug(unittest.TestCase): @@ -25,7 +23,7 @@ class TestUnslug(unittest.TestCase): 'foo1': (None, None), } - for slug, expected in pycompat.items(tests): + for slug, expected in tests.items(): self.assertEqual(unslug(slug), expected) diff --git a/addons/website/tests/test_views.py b/addons/website/tests/test_views.py index af9d362cfb9..7293eae4c56 100644 --- a/addons/website/tests/test_views.py +++ b/addons/website/tests/test_views.py @@ -11,11 +11,10 @@ from lxml import etree as ET, html from lxml.html import builder as h from odoo.tests import common -from odoo.tools import pycompat def attrs(**kwargs): - return {'data-oe-%s' % key: str(value) for key, value in pycompat.items(kwargs)} + return {'data-oe-%s' % key: str(value) for key, value in kwargs.items()} class TestViewSaving(common.TransactionCase): diff --git a/addons/website_crm_partner_assign/controllers/main.py b/addons/website_crm_partner_assign/controllers/main.py index 3c18c7cf470..6e300e66cd0 100644 --- a/addons/website_crm_partner_assign/controllers/main.py +++ b/addons/website_crm_partner_assign/controllers/main.py @@ -13,7 +13,6 @@ from odoo.http import request from odoo.addons.website.models.website import slug, unslug from odoo.addons.website_partner.controllers.main import WebsitePartnerPage -from odoo.tools import pycompat from odoo.tools.translate import _ from odoo.addons.website_portal.controllers.main import website_account @@ -151,7 +150,7 @@ class WebsiteAccount(website_account): 'pager': pager, 'searchbar_sortings': searchbar_sortings, 'sortby': sortby, - 'searchbar_filters': OrderedDict(sorted(pycompat.items(searchbar_filters))), + 'searchbar_filters': OrderedDict(sorted(searchbar_filters.items())), 'filterby': filterby, }) return request.render("website_crm_partner_assign.portal_my_opportunities", values) diff --git a/addons/website_crm_partner_assign/models/crm_lead.py b/addons/website_crm_partner_assign/models/crm_lead.py index f1f002d5dd0..b73be955c42 100644 --- a/addons/website_crm_partner_assign/models/crm_lead.py +++ b/addons/website_crm_partner_assign/models/crm_lead.py @@ -5,7 +5,6 @@ import random from odoo.addons.base_geolocalize.models.res_partner import geo_find, geo_query_address from odoo import api, fields, models, _ -from odoo.tools import pycompat class CrmLead(models.Model): @@ -45,7 +44,7 @@ class CrmLead(models.Model): if lead.partner_assigned_id and lead.partner_assigned_id.user_id != lead.user_id: salesmans_leads.setdefault(lead.partner_assigned_id.user_id.id, []).append(lead.id) - for salesman_id, leads_ids in pycompat.items(salesmans_leads): + for salesman_id, leads_ids in salesmans_leads.items(): leads = self.browse(leads_ids) leads.write({'user_id': salesman_id}) diff --git a/addons/website_crm_partner_assign/wizard/crm_forward_to_partner.py b/addons/website_crm_partner_assign/wizard/crm_forward_to_partner.py index cb2fbf3ba03..5aa8073192f 100644 --- a/addons/website_crm_partner_assign/wizard/crm_forward_to_partner.py +++ b/addons/website_crm_partner_assign/wizard/crm_forward_to_partner.py @@ -3,7 +3,6 @@ from odoo import api, fields, models, _ from odoo.exceptions import UserError -from odoo.tools import pycompat class CrmLeadForwardToPartner(models.TransientModel): @@ -86,7 +85,7 @@ class CrmLeadForwardToPartner(models.TransientModel): else: partners_leads[partner.id] = {'partner': partner, 'leads': [lead_details]} - for partner_id, partner_leads in pycompat.items(partners_leads): + for partner_id, partner_leads in partners_leads.items(): in_portal = False if portal_group: for contact in (partner.child_ids or partner).filtered(lambda contact: contact.user_ids): diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py index cd020a74372..5aed9a25ffa 100644 --- a/addons/website_event/controllers/main.py +++ b/addons/website_event/controllers/main.py @@ -9,7 +9,6 @@ from dateutil.relativedelta import relativedelta from odoo import fields, http, _ from odoo.addons.website.models.website import slug from odoo.http import request -from odoo.tools import pycompat class WebsiteEventController(http.Controller): @@ -80,7 +79,7 @@ class WebsiteEventController(http.Controller): def dom_without(without): domain = [('state', "in", ['draft', 'confirm', 'done'])] - for key, search in pycompat.items(domain_search): + for key, search in domain_search.items(): if key != without: domain += search return domain @@ -234,16 +233,16 @@ class WebsiteEventController(http.Controller): ''' Process data posted from the attendee details form. ''' registrations = {} global_values = {} - for key, value in pycompat.items(details): + for key, value in details.items(): counter, field_name = key.split('-', 1) if counter == '0': global_values[field_name] = value else: registrations.setdefault(counter, dict())[field_name] = value - for key, value in pycompat.items(global_values): - for registration in pycompat.values(registrations): + for key, value in global_values.items(): + for registration in registrations.values(): registration[key] = value - return list(pycompat.values(registrations)) + return list(registrations.values()) @http.route(['/event//registration/confirm'], type='http', auth="public", methods=['POST'], website=True) def registration_confirm(self, event, **post): diff --git a/addons/website_event_questions/controllers/main.py b/addons/website_event_questions/controllers/main.py index e709e0b3d06..684999a26ff 100644 --- a/addons/website_event_questions/controllers/main.py +++ b/addons/website_event_questions/controllers/main.py @@ -3,8 +3,6 @@ from odoo.addons.website_event.controllers.main import WebsiteEventController -from odoo.tools import pycompat - class WebsiteEvent(WebsiteEventController): @@ -13,7 +11,7 @@ class WebsiteEvent(WebsiteEventController): registrations = super(WebsiteEvent, self)._process_registration_details(details) for registration in registrations: answer_ids = [] - for key, value in pycompat.items(registration): + for key, value in registration.items(): if key.startswith('answer_ids-'): answer_ids.append([4, int(value)]) registration['answer_ids'] = answer_ids diff --git a/addons/website_event_sale/controllers/main.py b/addons/website_event_sale/controllers/main.py index 324fa1026ed..7ccb7f9e4b5 100644 --- a/addons/website_event_sale/controllers/main.py +++ b/addons/website_event_sale/controllers/main.py @@ -4,7 +4,6 @@ from odoo import http, _ from odoo.addons.website_event.controllers.main import WebsiteEventController from odoo.http import request -from odoo.tools import pycompat class WebsiteEventSaleController(WebsiteEventController): @@ -16,7 +15,7 @@ class WebsiteEventSaleController(WebsiteEventController): def _process_tickets_details(self, data): ticket_post = {} - for key, value in pycompat.items(data): + for key, value in data.items(): if not key.startswith('nb_register') or '-' not in key: continue items = key.split('-') diff --git a/addons/website_event_track/controllers/main.py b/addons/website_event_track/controllers/main.py index 1864b28379c..f11aa0bea13 100644 --- a/addons/website_event_track/controllers/main.py +++ b/addons/website_event_track/controllers/main.py @@ -8,7 +8,7 @@ import pytz from odoo import fields, http from odoo.http import request -from odoo.tools import html_escape as escape, html2plaintext, pycompat +from odoo.tools import html_escape as escape, html2plaintext class WebsiteEventTrackController(http.Controller): @@ -75,7 +75,7 @@ class WebsiteEventTrackController(http.Controller): days = {} tracks_by_days = {} - for day, tracks in pycompat.items(days_tracks): + for day, tracks in days_tracks.items(): tracks_by_days[day] = tracks days[day] = self._prepare_calendar(event, tracks) diff --git a/addons/website_form/controllers/main.py b/addons/website_form/controllers/main.py index cdf1034c7a9..978692e894a 100644 --- a/addons/website_form/controllers/main.py +++ b/addons/website_form/controllers/main.py @@ -10,7 +10,7 @@ from psycopg2 import IntegrityError from odoo import http from odoo.http import request -from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, pycompat +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT from odoo.tools.translate import _ from odoo.exceptions import ValidationError from odoo.addons.base.ir.ir_qweb.fields import nl2br @@ -118,7 +118,7 @@ class WebsiteForm(http.Controller): error_fields = [] - for field_name, field_value in pycompat.items(values): + for field_name, field_value in values.items(): # If the value of the field if a file if hasattr(field_value, 'filename'): # Undo file upload field name indexing @@ -164,7 +164,7 @@ class WebsiteForm(http.Controller): if hasattr(dest_model, "website_form_input_filter"): data['record'] = dest_model.website_form_input_filter(request, data['record']) - missing_required_fields = [label for label, field in pycompat.items(authorized_fields) if field['required'] and not label in data['record']] + missing_required_fields = [label for label, field in authorized_fields.items() if field['required'] and not label in data['record']] if any(error_fields): raise ValidationError(error_fields + missing_required_fields) diff --git a/addons/website_form/models/models.py b/addons/website_form/models/models.py index 654fbfa9ee4..67364d59dc2 100644 --- a/addons/website_form/models/models.py +++ b/addons/website_form/models/models.py @@ -5,7 +5,6 @@ import itertools from odoo import models, fields, api from odoo.http import request -from odoo.tools import pycompat class website_form_config(models.Model): @@ -42,7 +41,7 @@ class website_form_model(models.Model): ]) } return { - k: v for k, v in pycompat.items(self.get_authorized_fields(self.model)) + k: v for k, v in self.get_authorized_fields(self.model).items() if k in included } @@ -52,7 +51,7 @@ class website_form_model(models.Model): model = self.env[model_name] fields_get = model.fields_get() - for key, val in pycompat.items(model._inherits): + for key, val in model._inherits.items(): fields_get.pop(val, None) # Unrequire fields with default values diff --git a/addons/website_livechat/controllers/main.py b/addons/website_livechat/controllers/main.py index 675b56e561f..753e4eef1ae 100644 --- a/addons/website_livechat/controllers/main.py +++ b/addons/website_livechat/controllers/main.py @@ -3,7 +3,6 @@ from odoo import http from odoo.http import request -from odoo.tools import pycompat class WebsiteLivechat(http.Controller): @@ -27,7 +26,7 @@ class WebsiteLivechat(http.Controller): # compute percentage percentage = dict.fromkeys(['great', 'okay', 'bad'], 0) for grade in repartition: - percentage[grade] = repartition[grade] * 100 / sum(pycompat.values(repartition)) if sum(pycompat.values(repartition)) else 0 + percentage[grade] = repartition[grade] * 100 / sum(repartition.values()) if sum(repartition.values()) else 0 # the value dict to render the template values = { diff --git a/addons/website_portal/controllers/main.py b/addons/website_portal/controllers/main.py index 3b2fbfc2a2d..865312cc7c6 100644 --- a/addons/website_portal/controllers/main.py +++ b/addons/website_portal/controllers/main.py @@ -4,7 +4,6 @@ from odoo import http from odoo.http import request from odoo import tools -from odoo.tools import pycompat from odoo.tools.translate import _ from odoo.exceptions import ValidationError @@ -134,7 +133,7 @@ class website_account(http.Controller): error["vat"] = 'error' # error message for empty required fields - if [err for err in pycompat.values(error) if err == 'missing']: + if [err for err in error.values() if err == 'missing']: error_message.append(_('Some required fields are empty.')) unknown = [k for k in data if k not in self.MANDATORY_BILLING_FIELDS + self.OPTIONAL_BILLING_FIELDS] diff --git a/addons/website_portal_purchase/controllers/website_portal.py b/addons/website_portal_purchase/controllers/website_portal.py index 874c311f92a..4d8762e4346 100644 --- a/addons/website_portal_purchase/controllers/website_portal.py +++ b/addons/website_portal_purchase/controllers/website_portal.py @@ -6,7 +6,6 @@ from collections import OrderedDict from odoo import http from odoo.exceptions import AccessError from odoo.http import request -from odoo.tools import pycompat from odoo.tools.translate import _ from odoo.addons.website_portal.controllers.main import website_account, get_records_pager @@ -89,7 +88,7 @@ class WebsitePortal(website_account): 'archive_groups': archive_groups, 'searchbar_sortings': searchbar_sortings, 'sortby': sortby, - 'searchbar_filters': OrderedDict(sorted(pycompat.items(searchbar_filters))), + 'searchbar_filters': OrderedDict(sorted(searchbar_filters.items())), 'filterby': filterby, 'default_url': '/my/purchase', }) diff --git a/addons/website_project/controllers/main.py b/addons/website_project/controllers/main.py index d8716efd9ac..720c4f65700 100644 --- a/addons/website_project/controllers/main.py +++ b/addons/website_project/controllers/main.py @@ -8,7 +8,6 @@ from odoo.http import request from odoo.addons.website_portal.controllers.main import website_account, get_records_pager from odoo.osv.expression import OR -from odoo.tools import pycompat class WebsiteAccount(website_account): @@ -156,7 +155,7 @@ class WebsiteAccount(website_account): 'searchbar_inputs': searchbar_inputs, 'search_in': search_in, 'sortby': sortby, - 'searchbar_filters': OrderedDict(sorted(pycompat.items(searchbar_filters))), + 'searchbar_filters': OrderedDict(sorted(searchbar_filters.items())), 'filterby': filterby, }) return request.render("website_project.my_tasks", values) diff --git a/addons/website_project_issue/controllers/main.py b/addons/website_project_issue/controllers/main.py index 1ce21cff8e8..940544a2188 100644 --- a/addons/website_project_issue/controllers/main.py +++ b/addons/website_project_issue/controllers/main.py @@ -105,7 +105,7 @@ class WebsiteAccount(website_account): 'searchbar_sortings': searchbar_sortings, 'searchbar_inputs': searchbar_inputs, 'sortby': sortby, - 'searchbar_filters': OrderedDict(sorted(pycompat.items(searchbar_filters))), + 'searchbar_filters': OrderedDict(sorted(searchbar_filters.items())), 'filterby': filterby, 'search_in': search_in, 'search': search, diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 51462df1de7..40057484efe 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -12,8 +12,6 @@ from odoo.addons.website.controllers.main import QueryURL from odoo.exceptions import ValidationError from odoo.addons.website_form.controllers.main import WebsiteForm -from odoo.tools import pycompat - _logger = logging.getLogger(__name__) PPG = 20 # Products Per Page @@ -78,10 +76,10 @@ class TableCompute(object): index += 1 # Format table according to HTML needs - rows = sorted(pycompat.items(self.table)) + rows = sorted(self.table.items()) rows = [r[1] for r in rows] for col in range(len(rows)): - cols = sorted(pycompat.items(rows[col])) + cols = sorted(rows[col].items()) x += len(cols) rows[col] = [r[1] for r in cols if r[1]] @@ -373,7 +371,7 @@ class WebsiteSale(http.Controller): return request.redirect("/shop/cart") def _filter_attributes(self, **kw): - return {k: v for k, v in pycompat.items(kw) if "attribute" in k} + return {k: v for k, v in kw.items() if "attribute" in k} @http.route(['/shop/cart/update_json'], type='json', auth="public", methods=['POST'], website=True, csrf=False) def cart_update_json(self, product_id, line_id=None, add_qty=None, set_qty=None, display=True): @@ -491,7 +489,7 @@ class WebsiteSale(http.Controller): except ValidationError: error["vat"] = 'error' - if [err for err in pycompat.items(error) if err == 'missing']: + if [err for err in error.items() if err == 'missing']: error_message.append(_('Some required fields are empty.')) return error, error_message @@ -517,7 +515,7 @@ class WebsiteSale(http.Controller): def values_postprocess(self, order, mode, values, errors, error_msg): new_values = {} authorized_fields = request.env['ir.model']._get('res.partner')._get_form_writable_fields() - for k, v in pycompat.items(values): + for k, v in values.items(): # don't drop empty value, it could be a field to reset if k in authorized_fields and v is not None: new_values[k] = v @@ -680,7 +678,7 @@ class WebsiteSale(http.Controller): # if form posted if 'post_values' in post: values = {} - for field_name, field_value in pycompat.items(post): + for field_name, field_value in post.items(): if field_name in request.env['sale.order']._fields and field_name.startswith('x_'): values[field_name] = field_value if values: diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index cffff7f2f3d..724d24b5c03 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -6,7 +6,6 @@ import random from odoo import api, models, fields, _ from odoo.http import request from odoo.exceptions import UserError, ValidationError -from odoo.tools import pycompat _logger = logging.getLogger(__name__) @@ -93,7 +92,7 @@ class SaleOrder(models.Model): # add untracked attributes in the name untracked_attributes = [] - for k, v in pycompat.items(attributes): + for k, v in attributes.items(): # attribute should be like 'attribute-48-1' where 48 is the product_id, 1 is the attribute_id and v is the attribute value attribute_value = self.env['product.attribute.value'].sudo().browse(int(v)) if attribute_value and not attribute_value.attribute_id.create_variant: diff --git a/addons/website_sale/tests/test_website_sale_pricelist.py b/addons/website_sale/tests/test_website_sale_pricelist.py index cf97fd815cc..21accb52814 100644 --- a/addons/website_sale/tests/test_website_sale_pricelist.py +++ b/addons/website_sale/tests/test_website_sale_pricelist.py @@ -2,7 +2,6 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from mock import patch from odoo.tests.common import TransactionCase -from odoo.tools import pycompat class TestWebsitePriceList(TransactionCase): @@ -61,7 +60,7 @@ class TestWebsitePriceList(TransactionCase): 'CA': ['Canada'], 'US': ['USD', 'EUR', 'Benelux', 'Canada'] } - for country, result in pycompat.items(country_list): + for country, result in country_list.items(): pls = self.get_pl(show, current_pl, country) self.assertEquals(len(set(pls.mapped('name')) & set(result)), len(pls), 'Test failed for %s (%s %s vs %s %s)' % (country, len(pls), pls.mapped('name'), len(result), result)) @@ -78,7 +77,7 @@ class TestWebsitePriceList(TransactionCase): 'CA': ['Canada'] } - for country, result in pycompat.items(country_list): + for country, result in country_list.items(): pls = self.get_pl(show, current_pl, country) self.assertEquals(len(set(pls.mapped('name')) & set(result)), len(pls), 'Test failed for %s (%s %s vs %s %s)' % (country, len(pls), pls.mapped('name'), len(result), result)) @@ -99,7 +98,7 @@ class TestWebsitePriceList(TransactionCase): 'CA': False } - for country, result in pycompat.items(country_list): + for country, result in country_list.items(): self.args['country'] = country # mock patch method could not pass env context available = self.website.is_pricelist_available(christmas_pl) @@ -120,7 +119,7 @@ class TestWebsitePriceList(TransactionCase): 'CA': ['EUR', 'Canada'], 'US': ['USD', 'EUR', 'Benelux', 'Canada'] } - for country, result in pycompat.items(country_list): + for country, result in country_list.items(): pls = self.get_pl(show, current_pl, country) self.assertEquals(len(set(pls.mapped('name')) & set(result)), len(pls), 'Test failed for %s (%s %s vs %s %s)' % (country, len(pls), pls.mapped('name'), len(result), result)) diff --git a/addons/website_sale_options/controllers/main.py b/addons/website_sale_options/controllers/main.py index 8349f0e08bc..af48c17f2e1 100644 --- a/addons/website_sale_options/controllers/main.py +++ b/addons/website_sale_options/controllers/main.py @@ -5,8 +5,6 @@ from odoo import http from odoo.http import request from odoo.addons.website_sale.controllers.main import WebsiteSale -from odoo.tools import pycompat - class WebsiteSaleOptions(WebsiteSale): @@ -26,7 +24,7 @@ class WebsiteSaleOptions(WebsiteSale): option_ids = product.optional_product_ids.mapped('product_variant_ids').ids optional_product_ids = [] - for k, v in pycompat.items(kw): + for k, v in kw.items(): if "optional-product-" in k and int(kw.get(k.replace("product", "add"))) and int(v) in option_ids: optional_product_ids.append(int(v)) diff --git a/addons/website_slides/models/slides.py b/addons/website_slides/models/slides.py index a4de9105a1c..ad5df26cadd 100644 --- a/addons/website_slides/models/slides.py +++ b/addons/website_slides/models/slides.py @@ -12,7 +12,7 @@ import re from werkzeug import urls from odoo import api, fields, models, SUPERUSER_ID, _ -from odoo.tools import image, pycompat +from odoo.tools import image from odoo.tools.translate import html_translate from odoo.exceptions import Warning from odoo.addons.website.models.website import slug @@ -323,8 +323,8 @@ class Slide(models.Model): values = res['values'] if not values.get('document_id'): raise Warning(_('Please enter valid Youtube or Google Doc URL')) - for key, value in pycompat.items(values): - setattr(self, key, value) + for key, value in values.items(): + self[key] = value # website date_published = fields.Datetime('Publish Date') @@ -385,7 +385,7 @@ class Slide(models.Model): values['date_published'] = datetime.datetime.now() if values.get('url') and not values.get('document_id'): doc_data = self._parse_document_url(values['url']).get('values', dict()) - for key, value in pycompat.items(doc_data): + for key, value in doc_data.items(): values.setdefault(key, value) # Do not publish slide if user has not publisher rights if not self.user_has_groups('website.group_website_publisher'): @@ -399,7 +399,7 @@ class Slide(models.Model): def write(self, values): if values.get('url') and values['url'] != self.url: doc_data = self._parse_document_url(values['url']).get('values', dict()) - for key, value in pycompat.items(doc_data): + for key, value in doc_data.items(): values.setdefault(key, value) if values.get('channel_id'): custom_channels = self.env['slide.channel'].search([('custom_slide_id', '=', self.id), ('id', '!=', values.get('channel_id'))]) diff --git a/doc/python3.rst b/doc/python3.rst index 7ca86708ced..c8e8269936e 100644 --- a/doc/python3.rst +++ b/doc/python3.rst @@ -233,10 +233,7 @@ methods or functions having been *removed entirely*: .. important:: When possible, use comprehensions (list, generator, ...) rather than - ``map`` or ``filter``, otherwise use the cross-version ``pycompat`` - versions (``pycompat.imap``, ``pycompat.ifilter`` and - ``pycompat.izip``). The ``pycompat`` versions all return *iterators* and - may need to be wrapped in a ``list()`` call to yield a list. + ``map`` or ``filter``. * In Python 3, ``dict.keys``, ``dict.values`` and ``dict.items`` return *views* rather than lists, and the ``iter*`` and ``view*`` methods have @@ -244,11 +241,9 @@ methods or functions having been *removed entirely*: .. important:: - Prefer using :func:`odoo.tools.pycompat.keys`, - :func:`odoo.tools.pycompat.values` and :func:`odoo.tools.pycompat.items` - return cross-version iterators. When needing actual lists (e.g. to - modify a dictionary during iteration), wrap one of the calls above in a - ``list()``. + When the result of the above methods is used for more than a one-shot + loop (e.g. to be included in returned value), or when the dict needs + to be modified during iteration, wrap the calls in a ``list()``. builtins -------- diff --git a/odoo/addons/base/ir/ir_attachment.py b/odoo/addons/base/ir/ir_attachment.py index bd27a675eb0..41b85913ed9 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, pycompat +from odoo.tools import config, human_size, ustr, html_escape 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 pycompat.items(checklist): + for fname, filepath in checklist.items(): 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 pycompat.items(model_ids): + for res_model, res_ids in model_ids.items(): # 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 pycompat.items(model_attachments): + for res_model, targets in model_attachments.items(): 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(*pycompat.values(targets))) + ids.difference_update(itertools.chain(*targets.values())) 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 833185d8d67..a286a8836ce 100644 --- a/odoo/addons/base/ir/ir_config_parameter.py +++ b/odoo/addons/base/ir/ir_config_parameter.py @@ -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 pycompat.items(_default_parameters): + for key, func in _default_parameters.items(): # 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 b8994d9221d..d7521ed6ba1 100644 --- a/odoo/addons/base/ir/ir_fields.py +++ b/odoo/addons/base/ir/ir_fields.py @@ -12,9 +12,9 @@ from odoo.tools import ustr, pycompat REFERENCING_FIELDS = {None, 'id', '.id'} def only_ref_fields(record): - return {k: v for k, v in pycompat.items(record) if k in REFERENCING_FIELDS} + return {k: v for k, v in record.items() if k in REFERENCING_FIELDS} def exclude_ref_fields(record): - return {k: v for k, v in pycompat.items(record) if k not in REFERENCING_FIELDS} + return {k: v for k, v in record.items() if k not in REFERENCING_FIELDS} CREATE = lambda values: (0, False, values) UPDATE = lambda id, values: (1, id, values) @@ -43,7 +43,7 @@ class IrFieldsConverter(models.AbstractModel): if isinstance(error_params, pycompat.string_types): error_params = sanitize(error_params) elif isinstance(error_params, dict): - error_params = {k: sanitize(v) for k, v in pycompat.items(error_params)} + error_params = {k: sanitize(v) for k, v in error_params.items()} elif isinstance(error_params, tuple): error_params = tuple(sanitize(v) for v in error_params) return error_type(error_msg % error_params, error_args) @@ -64,12 +64,12 @@ class IrFieldsConverter(models.AbstractModel): converters = { name: self.to_field(model, field, fromtype) - for name, field in pycompat.items(model._fields) + for name, field in model._fields.items() } def fn(record, log): converted = {} - for field, value in pycompat.items(record): + for field, value in record.items(): if field in REFERENCING_FIELDS: continue if not value: diff --git a/odoo/addons/base/ir/ir_http.py b/odoo/addons/base/ir/ir_http.py index 392cf27d4a1..af9fde0b012 100644 --- a/odoo/addons/base/ir/ir_http.py +++ b/odoo/addons/base/ir/ir_http.py @@ -203,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 list(pycompat.items(arguments)): + for name, arg in list(arguments.items()): 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 eab160b4a84..0ecb895e082 100644 --- a/odoo/addons/base/ir/ir_mail_server.py +++ b/odoo/addons/base/ir/ir_mail_server.py @@ -318,7 +318,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 pycompat.items(headers): + for key, value in headers.items(): msg[pycompat.to_native(ustr(key))] = encode_header(value) if subtype == 'html' and not body_alternative: diff --git a/odoo/addons/base/ir/ir_model.py b/odoo/addons/base/ir/ir_model.py index 2092f4cd0b2..25967c6b212 100644 --- a/odoo/addons/base/ir/ir_model.py +++ b/odoo/addons/base/ir/ir_model.py @@ -194,7 +194,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 pycompat.items(self._context) if k != '__last_update'}) + self = self.with_context({k: v for k, v in self._context.items() 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): @@ -766,7 +766,7 @@ class IrModelFields(models.Model): fields_data[field.name] = dict(params, id=record.id) return record - diff = {key for key, val in pycompat.items(params) if field_data[key] != val} + diff = {key for key, val in params.items() if field_data[key] != val} if diff: cr = self.env.cr # update the entry in this table @@ -784,7 +784,7 @@ class IrModelFields(models.Model): def _reflect_model(self, model): """ Reflect the given model's fields. """ self.clear_caches() - for field in pycompat.values(model._fields): + for field in model._fields.values(): self._reflect_field(field) if not self.pool._init: @@ -867,7 +867,7 @@ class IrModelFields(models.Model): def _add_manual_fields(self, model): """ Add extra fields on model. """ fields_data = self._get_manual_field_data(model._name) - for name, field_data in pycompat.items(fields_data): + for name, field_data in fields_data.items(): if name not in model._fields and field_data['state'] == 'manual': field = self._instanciate(field_data) if field: @@ -1278,7 +1278,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 pycompat.items(model_id_name): + for model, id_name in model_id_name.items(): try: ng = self.env[model].browse(id_name).name_get() id_name.update(ng) @@ -1375,7 +1375,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 pycompat.items(self.env[model]._inherits): + for parent_model, parent_field in self.env[model]._inherits.items(): parent = record[parent_field] parent_xid = '%s_%s' % (xml_id, parent_model.replace('.', '_')) self.loads[(module, parent_xid)] = (parent_model, parent.id) @@ -1430,7 +1430,7 @@ class IrModelData(models.Model): elif record: record.write(values) if xml_id: - for parent_model, parent_field in pycompat.items(record._inherits): + for parent_model, parent_field in record._inherits.items(): self.sudo().create({ 'name': xml_id + '_' + parent_model.replace('.', '_'), 'model': parent_model, @@ -1449,7 +1449,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 pycompat.items(record._inherits): + for parent_model, parent_field in record._inherits.items(): xid = self.sudo().search([ ('module', '=', module), ('name', '=', xml_id + '_' + parent_model.replace('.', '_')), @@ -1469,7 +1469,7 @@ class IrModelData(models.Model): inherit_models = [record] while inherit_models: current_model = inherit_models.pop() - for parent_model_name, parent_field in pycompat.items(current_model._inherits): + for parent_model_name, parent_field in current_model._inherits.items(): inherit_models.append(self.env[parent_model_name]) if parent_model_name in existing_parents: continue @@ -1495,7 +1495,7 @@ class IrModelData(models.Model): if xml_id and record: self.loads[(module, xml_id)] = (model, record.id) - for parent_model, parent_field in pycompat.items(record._inherits): + for parent_model, parent_field in record._inherits.items(): 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 8f894107b81..fbf0d42f8b5 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 import fields, tools from odoo.http import request from odoo.modules.module import get_resource_path import psycopg2 -from odoo.tools import func, misc, pycompat +from odoo.tools import func, misc import logging _logger = logging.getLogger(__name__) @@ -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 pycompat.values(assets)): + if any(asset._content is None for asset in assets.values()): outdated = True if outdated: diff --git a/odoo/addons/base/ir/ir_qweb/fields.py b/odoo/addons/base/ir/ir_qweb/fields.py index 6cc5337f8a6..27d13d3076c 100644 --- a/odoo/addons/base/ir/ir_qweb/fields.py +++ b/odoo/addons/base/ir/ir_qweb/fields.py @@ -427,7 +427,7 @@ class BarcodeConverter(models.AbstractModel): barcode = self.env['ir.actions.report'].barcode( barcode_type, value, - **{key: value for key, value in pycompat.items(options) if key in ['width', 'height', 'humanreadable']}) + **{key: value for key, value in options.items() if key in ['width', 'height', 'humanreadable']}) return u'' % base64.b64encode(barcode).decode('ascii') @api.model diff --git a/odoo/addons/base/ir/ir_qweb/ir_qweb.py b/odoo/addons/base/ir/ir_qweb/ir_qweb.py index 6a23054519e..bd40a2ce76c 100644 --- a/odoo/addons/base/ir/ir_qweb/ir_qweb.py +++ b/odoo/addons/base/ir/ir_qweb/ir_qweb.py @@ -169,7 +169,7 @@ class IrQWeb(models.AbstractModel, QWeb): if field_options and 'monetary' in field_options: try: options = "{'widget': 'monetary'" - for k, v in pycompat.items(json.loads(field_options)): + for k, v in json.loads(field_options).items(): if k in ('display_currency', 'from_currency'): options = "%s, '%s': %s" % (options, k, v) else: diff --git a/odoo/addons/base/ir/ir_qweb/qweb.py b/odoo/addons/base/ir/ir_qweb/qweb.py index 61bd39eab8e..43c10fe19f0 100644 --- a/odoo/addons/base/ir/ir_qweb/qweb.py +++ b/odoo/addons/base/ir/ir_qweb/qweb.py @@ -187,7 +187,7 @@ def foreach_iterator(base_ctx, enum, name): if isinstance(enum, Sized): ctx["%s_size" % name] = size = len(enum) if isinstance(enum, Mapping): - enum = pycompat.items(enum) + enum = enum.items() else: enum = pycompat.izip(*tee(enum)) value_key = '%s_value' % name @@ -240,7 +240,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 pycompat.items(self))) + return hash(frozenset((key, freehash(val)) for key, val in self.items())) #################################### @@ -527,7 +527,6 @@ class QWeb(object): """ return ast.parse(dedent(""" from collections import OrderedDict - from odoo.tools import pycompat from odoo.tools.pycompat import to_text, string_types from odoo.addons.base.ir.ir_qweb.qweb import escape, foreach_iterator """)) @@ -745,7 +744,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(pycompat.items(el.nsmap)) - set(pycompat.items(options['nsmap'])): + for ns_prefix, ns_definition in set(el.nsmap.items()) - set(options['nsmap'].items()): if ns_prefix is None: attrib['xmlns'] = ns_definition else: @@ -754,9 +753,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. - ns = itertools.chain(pycompat.items(options['nsmap']), pycompat.items(el.nsmap)) + ns = itertools.chain(options['nsmap'].items(), el.nsmap.items()) nsprefixmap = {v: k for k, v in ns} - for key, value in pycompat.items(el.attrib): + for key, value in el.attrib.items(): attrib_qname = etree.QName(key) if attrib_qname.namespace: attrib['%s:%s' % (nsprefixmap[attrib_qname.namespace], attrib_qname.localname)] = value @@ -774,7 +773,7 @@ class QWeb(object): if unqualified_el_tag == 't': return content - tag = u'<%s%s' % (el_tag, u''.join([u' %s="%s"' % (name, escape(pycompat.to_text(value))) for name, value in pycompat.items(attrib)])) + tag = u'<%s%s' % (el_tag, u''.join([u' %s="%s"' % (name, escape(pycompat.to_text(value))) for name, value in attrib.items()])) if unqualified_el_tag in self._void_elements: return [self._append(ast.Str(tag + '/>'))] + content else: @@ -786,10 +785,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 itertools.chain(pycompat.items(options['nsmap']), pycompat.items(el.nsmap))} + nsprefixmap = {v: k for k, v in itertools.chain(options['nsmap'].items(), el.nsmap.items())} nodes = [] - for key, value in pycompat.items(el.attrib): + for key, value in el.attrib.items(): if not key.startswith('t-'): attrib_qname = etree.QName(key) if attrib_qname.namespace: @@ -804,7 +803,7 @@ class QWeb(object): We do not support namespaced dynamic attributes. """ nodes = [] - for name, value in pycompat.items(el.attrib): + for name, value in el.attrib.items(): if name.startswith('t-attf-'): nodes.append((name[7:], self._compile_format(value))) elif name.startswith('t-att-'): @@ -830,7 +829,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 pycompat.items(el.attrib)): + if any(name.startswith('t-att') or not name.startswith('t-') for name, value in el.attrib.items()): if not attr_already_created: attr_already_created = True body.append( @@ -871,7 +870,7 @@ class QWeb(object): ))) if attr_already_created: - # for name, value in pycompat.items(t_attrs): + # for name, value in t_attrs.items(): # if value or isinstance(value, basestring)): # append(u' ') # append(name) @@ -882,11 +881,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='pycompat', ctx=ast.Load()), + value=ast.Name(id='t_attrs', ctx=ast.Load()), attr='items', ctx=ast.Load() ), - args=[ast.Name(id='t_attrs', ctx=ast.Load())], keywords=[], + args=[], keywords=[], starargs=None, kwargs=None ), body=[ast.If( @@ -943,7 +942,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(pycompat.items(el.nsmap)) - set(pycompat.items(options['nsmap'])): + for ns_prefix, ns_definition in set(el.nsmap.items()) - set(options['nsmap'].items()): if ns_prefix is None: extra_attrib['xmlns'] = ns_definition else: @@ -952,7 +951,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(pycompat.to_text(value))) for name, value in pycompat.items(extra_attrib)]))))] + body = [self._append(ast.Str(u'<%s%s' % (el_tag, u''.join([u' %s="%s"' % (name, escape(pycompat.to_text(value))) for name, value in extra_attrib.items()]))))] 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'/>'))) @@ -1457,7 +1456,7 @@ class QWeb(object): # make the nsmap an ast dict keys = [] values = [] - for key, value in pycompat.items(options['nsmap']): + for key, value in options['nsmap'].items(): if isinstance(key, pycompat.string_types): 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 aa893e3f316..105f0f9bbf4 100644 --- a/odoo/addons/base/ir/ir_sequence.py +++ b/odoo/addons/base/ir/ir_sequence.py @@ -6,7 +6,6 @@ import pytz from odoo import api, fields, models, _ from odoo.exceptions import UserError -from odoo.tools import pycompat _logger = logging.getLogger(__name__) @@ -195,7 +194,7 @@ class IrSequence(models.Model): 'weekday': '%w', 'h24': '%H', 'h12': '%I', 'min': '%M', 'sec': '%S' } res = {} - for key, format in pycompat.items(sequences): + for key, format in sequences.items(): 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 02c085818d2..f77d84e758b 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 pycompat.items(env[model]._fields): + for field_name, field in env[model]._fields.items(): if hasattr(field, 'translate') and callable(field.translate): src_relevant_fields.append("%s,%s" % (model, field_name)) @@ -531,7 +531,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 pycompat.items(model_ids): + for mname, ids in model_ids.items(): records = self.env[mname].browse(ids) records.check_access_rights(fmode) records.check_field_access_rights(fmode, model_fields[mname]) @@ -646,7 +646,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 pycompat.items(record._fields): + for name, fld in record._fields.items(): 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 e848d88b093..085200a7475 100644 --- a/odoo/addons/base/ir/ir_ui_view.py +++ b/odoo/addons/base/ir/ir_ui_view.py @@ -845,7 +845,7 @@ actual arch. collect(arch, self.env[model_name]) - for field, nodes in pycompat.items(field_nodes): + for field, nodes in field_nodes.items(): # if field should trigger an onchange, add on_change="1" on the # nodes referring to field model = self.env[field.model_name] @@ -1102,12 +1102,12 @@ actual arch. Model = self.env[model] Node = self.env[node_obj] - for model_key, model_value in pycompat.items(Model._fields): + for model_key, model_value in Model._fields.items(): 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 pycompat.items(Node._fields): + for node_key, node_value in Node._fields.items(): if node_value.type == 'one2many': if node_value.comodel_name == conn_obj: # _Source_Field = "Incoming Arrows" (connected via des_node) @@ -1184,7 +1184,7 @@ actual arch. xmlid_filter = "AND md.name IN %s" names = tuple( name - for (xmod, name), (model, res_id) in pycompat.items(self.pool.model_data_reference_ids) + for (xmod, name), (model, res_id) in self.pool.model_data_reference_ids.items() 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 f05241a8c05..851c4705738 100644 --- a/odoo/addons/base/ir/ir_values.py +++ b/odoo/addons/base/ir/ir_values.py @@ -315,7 +315,7 @@ class IrValues(models.Model): for row in self._cr.dictfetchall(): value = pickle.loads(row['value']) defaults.setdefault(row['name'], (row['id'], row['name'], value)) - return list(pycompat.values(defaults)) + return list(defaults.values()) # use ormcache: this is called a lot by BaseModel.default_get()! @api.model @@ -428,4 +428,4 @@ class IrValues(models.Model): results[name] = (id, name, action_def) except (AccessError, MissingError): continue - return sorted(pycompat.values(results)) + return sorted(results.values()) diff --git a/odoo/addons/base/module/module.py b/odoo/addons/base/module/module.py index ccd4be8aa8b..cc77b392a18 100644 --- a/odoo/addons/base/module/module.py +++ b/odoo/addons/base/module/module.py @@ -694,7 +694,7 @@ class Module(models.Model): _logger.debug('Install from url: %r', urls) try: # 1. Download & unzip missing modules - for module_name, url in pycompat.items(urls): + for module_name, url in urls.items(): if not url: continue # nothing to download, local version is already the last one @@ -715,7 +715,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 pycompat.items(urls): + for module_name, url in urls.items(): 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) @@ -747,7 +747,7 @@ class Module(models.Model): self.update_list() - with_urls = [module_name for module_name, url in pycompat.items(urls) if url] + with_urls = [module_name for module_name, url in urls.items() if url] downloaded = self.search([('name', 'in', with_urls)]) installed = self.search([('id', 'in', downloaded.ids), ('state', '=', 'installed')]) 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 a933cdd9430..03632644159 100644 --- a/odoo/addons/base/module/report/ir_module_reference_print.py +++ b/odoo/addons/base/module/report/ir_module_reference_print.py @@ -2,7 +2,6 @@ # 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): @@ -23,7 +22,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(pycompat.items(self.env[model].fields_get(fnames))) + return sorted(self.env[model].fields_get(fnames).items()) return [] @api.model diff --git a/odoo/addons/base/res/ir_property.py b/odoo/addons/base/res/ir_property.py index ec086fb6c61..73f8aafccad 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 pycompat.items(refs): + for ref, id in refs.items(): value = clean(values[id]) if value != default_value: self.create({ diff --git a/odoo/addons/base/res/res_company.py b/odoo/addons/base/res/res_company.py index e81af42efcb..5a711c6b9c0 100644 --- a/odoo/addons/base/res/res_company.py +++ b/odoo/addons/base/res/res_company.py @@ -6,7 +6,6 @@ import re from odoo import api, fields, models, tools, _ from odoo.exceptions import ValidationError -from odoo.tools import pycompat class Company(models.Model): @@ -154,7 +153,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 pycompat.items(values): + for fname, value in values.items(): 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 ec81d2ce882..4176d57ae52 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, pycompat +from odoo.tools import ustr _logger = logging.getLogger(__name__) @@ -247,7 +247,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 pycompat.items(self._fields) + selectable = [name for name, field in self._fields.items() if field.type == 'boolean'] return self.env['ir.module.module'].search([('name', 'in', selectable), ('state', 'in', ['to install', 'installed', 'to upgrade'])]) @@ -272,7 +272,7 @@ class ResConfigInstaller(models.TransientModel, ResConfigModuleInstallationMixin """ base = set(module_name for installer in self.read() - for module_name, to_install in pycompat.items(installer) + for module_name, to_install in installer.items() if self._fields[module_name].type == 'boolean' and to_install) hooks_results = set() @@ -282,7 +282,7 @@ class ResConfigInstaller(models.TransientModel, ResConfigModuleInstallationMixin hooks_results.update(hook() or set()) additionals = set(module - for requirements, consequences in pycompat.items(self._install_if) + for requirements, consequences in self._install_if.items() if base.issuperset(requirements) for module in consequences) @@ -442,7 +442,7 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin) ref = self.env.ref defaults, groups, modules, others = [], [], [], [] - for name, field in pycompat.items(self._fields): + for name, field in self._fields.items(): 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 \ diff --git a/odoo/addons/base/res/res_lang.py b/odoo/addons/base/res/res_lang.py index f4e2d7a1578..1cdc8a5db43 100644 --- a/odoo/addons/base/res/res_lang.py +++ b/odoo/addons/base/res/res_lang.py @@ -8,7 +8,6 @@ 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 @@ -126,7 +125,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 pycompat.items(tools.DATETIME_FORMATS_MAP): + for pattern, replacement in tools.DATETIME_FORMATS_MAP.items(): format = format.replace(pattern, replacement) return str(format) diff --git a/odoo/addons/base/res/res_users.py b/odoo/addons/base/res/res_users.py index 24027c6ec00..fa17d182fd5 100644 --- a/odoo/addons/base/res/res_users.py +++ b/odoo/addons/base/res/res_users.py @@ -761,7 +761,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(pycompat.values(order))) == len(gs): + if len(set(order.values())) == len(gs): return (app, 'selection', gs.sorted(key=order.get)) else: return (app, 'boolean', gs) @@ -775,7 +775,7 @@ class GroupsView(models.Model): others += g # build the result res = [] - for app, gs in sorted(pycompat.items(by_app), key=lambda it: it[0].sequence or 0): + for app, gs in sorted(by_app.items(), key=lambda it: it[0].sequence or 0): res.append(linearize(app, gs)) if others: res.append((self.env['ir.module.category'], 'boolean', others)) @@ -815,7 +815,7 @@ class UsersView(models.Model): add, rem = [], [] values1 = {} - for key, val in pycompat.items(values): + for key, val in values.items(): if is_boolean_group(key): (add if val else rem).append(get_boolean_group(key)) elif is_selection_groups(key): diff --git a/odoo/addons/base/tests/test_api.py b/odoo/addons/base/tests/test_api.py index ad2dd1f0e85..96fba1179b7 100644 --- a/odoo/addons/base/tests/test_api.py +++ b/odoo/addons/base/tests/test_api.py @@ -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 pycompat.items(partners._fields): + for name, field in partners._fields.items(): 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 pycompat.values(country_id_cache) for cid in cids) + country_ids = set(cid for cids in country_id_cache.values() 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 07a087d0842..f27c376c9b5 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, pycompat +from odoo.tools import mute_logger from odoo.osv import expression @@ -85,10 +85,10 @@ class TestExpression(TransactionCase): 'b ab': [cids['B'], cids['AB']], } pids = {} - for name, cat_ids in pycompat.items(partners_config): + for name, cat_ids in partners_config.items(): pids[name] = partners.create({'name': name, 'category_id': [(6, 0, cat_ids)]}).id - base_domain = [('id', 'in', list(pycompat.values(pids)))] + base_domain = [('id', 'in', list(pids.values()))] def test(op, value, expected): found_ids = partners.search(base_domain + [('category_id', op, value)]).ids diff --git a/odoo/addons/base/tests/test_orm.py b/odoo/addons/base/tests/test_orm.py index bf95bee08f7..c24758dc9bf 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 pycompat.items(partners_data): + for name, date in partners_data.items(): p = partners.create(dict(name=name, date=date)) partner_ids.append(p.id) partner_ids_by_day[date].append(p.id) diff --git a/odoo/addons/base/tests/test_qweb.py b/odoo/addons/base/tests/test_qweb.py index cfe65a00f11..ea4ef849b75 100644 --- a/odoo/addons/base/tests/test_qweb.py +++ b/odoo/addons/base/tests/test_qweb.py @@ -11,7 +11,7 @@ from lxml import etree 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, misc, ustr +from odoo.tools import misc, ustr class TestQWebTField(TransactionCase): @@ -406,7 +406,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.assertEqual(set(pycompat.items(result_etree.nsmap)), expected_ns) + self.assertEqual(set(result_etree.nsmap.items()), expected_ns) # 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 97670fdd805..1c2e60291d6 100644 --- a/odoo/addons/base/tests/test_search.py +++ b/odoo/addons/base/tests/test_search.py @@ -2,7 +2,6 @@ # 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): @@ -108,7 +107,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', list(pycompat.values(cron_ids)))], order='user_id').ids + ids = Cron.search([('id', 'in', list(cron_ids.values()))], order='user_id').ids expected_ids = [cron_ids[l] for l in 'ABC'] self.assertEqual(ids, expected_ids) @@ -128,7 +127,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', list(pycompat.values(cat_ids)))]).ids + found_ids = Cats.search([('id', 'in', list(cat_ids.values()))]).ids self.assertEqual(found_ids, expected_ids) def test_13_m2o_order_loop_multi(self): diff --git a/odoo/addons/test_pylint/tests/test_pylint.py b/odoo/addons/test_pylint/tests/test_pylint.py index 92764eaeb84..feba409bf42 100644 --- a/odoo/addons/test_pylint/tests/test_pylint.py +++ b/odoo/addons/test_pylint/tests/test_pylint.py @@ -8,7 +8,6 @@ except ImportError: pylint = None import subprocess from distutils.version import LooseVersion -from os import devnull from os.path import join from odoo.tests.common import TransactionCase diff --git a/odoo/api.py b/odoo/api.py index dc51870f20a..59bd0636b11 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, pycompat +from odoo.tools import frozendict, classproperty _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(pycompat.items(self.kwargs)): + for item in sorted(self.kwargs.items()): 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 list(pycompat.items(attrs)): + for key, value in list(attrs.items()): if not key.startswith('__') and callable(value): # make the method inherit from decorators value = propagate(getattr(parent, key, None), value) @@ -938,13 +938,13 @@ 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 pycompat.items(self.cache) + for field, field_cache in self.cache.items() ) self.invalidate_all() # re-fetch the records, and compare with their former cache invalids = [] - for field, field_dump in pycompat.items(cache_dump): + for field, field_dump in cache_dump.items(): records = self[field.model_name].browse(f for f in field_dump if f) for record in records: try: diff --git a/odoo/fields.py b/odoo/fields.py index 3779041b8e1..43ee8edbad1 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -340,7 +340,7 @@ class Field(MetaField('DummyField', (object,), {})): def __init__(self, string=Default, **kwargs): kwargs['string'] = string self._sequence = kwargs['_sequence'] = next(_global_seq) - args = {key: val for key, val in pycompat.items(kwargs) if val is not Default} + args = {key: val for key, val in kwargs.items() if val is not Default} self.args = args or EMPTY_DICT self._setup_done = None @@ -369,7 +369,7 @@ class Field(MetaField('DummyField', (object,), {})): """ Set all field attributes at once (with slot defaults). """ # optimization: we assign slots only assign = object.__setattr__ - for key, val in pycompat.items(self._slots): + for key, val in self._slots.items(): assign(self, key, attrs.pop(key, val)) if attrs: assign(self, '_attrs', attrs) @@ -547,7 +547,7 @@ class Field(MetaField('DummyField', (object,), {})): if not getattr(self, attr): setattr(self, attr, getattr(field, prop)) - for attr, value in pycompat.items(field._attrs): + for attr, value in field._attrs.items(): if attr not in self._attrs: setattr(self, attr, value) @@ -655,7 +655,7 @@ class Field(MetaField('DummyField', (object,), {})): model = model0.env.get(field.comodel_name) # add self's model dependencies - for mname, fnames in pycompat.items(model0._depends): + for mname, fnames in model0._depends.items(): model = model0.env[mname] for fname in fnames: field = model._fields[fname] @@ -1731,7 +1731,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 = list(pycompat.items(OrderedDict(self.selection + selection_add))) + self.selection = list(OrderedDict(self.selection + selection_add).items()) def _description_selection(self, env): """ return the selection list (pairs (value, label)); labels are @@ -1893,7 +1893,7 @@ class Many2one(_Relational): super(Many2one, self)._setup_attrs(model, name) # determine self.delegate if not self.delegate: - self.delegate = name in pycompat.values(model._inherits) + self.delegate = name in model._inherits.values() def update_db(self, model, columns): comodel = model.env[self.comodel_name] diff --git a/odoo/http.py b/odoo/http.py index cc94ac197ba..0f4d1ad3d97 100644 --- a/odoo/http.py +++ b/odoo/http.py @@ -290,7 +290,7 @@ class WebRequest(object): def set_handler(self, endpoint, arguments, auth): # is this needed ? - arguments ={k: v for k, v in pycompat.items(arguments) + arguments ={k: v for k, v in arguments.items() if not k.startswith("_ignored_")} self.endpoint_arguments = arguments self.endpoint = endpoint @@ -839,7 +839,7 @@ more details. """ response = Response(data, headers=headers) if cookies: - for k, v in pycompat.items(cookies): + for k, v in cookies.items(): response.set_cookie(k, v) return response @@ -880,7 +880,7 @@ class ControllerType(type): super(ControllerType, cls).__init__(name, bases, attrs) # flag old-style methods with req as first argument - for k, v in pycompat.items(attrs): + for k, v in attrs.items(): if inspect.isfunction(v) and hasattr(v, 'original_func'): # Set routing type on original functions routing_type = v.routing.get('type') @@ -1136,7 +1136,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 pycompat.values(req.files): + for f in req.files.values(): 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: @@ -1154,7 +1154,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 pycompat.items(data['files']): + for name, (storename, filename, content_type) in data['files'].items(): path = os.path.join(root.session_store.path, storename) files.add(name, (path, filename, content_type)) yield werkzeug.datastructures.CombinedMultiDict([data['form'], files]) @@ -1162,7 +1162,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session): yield None finally: # cleanup files - for f, _, _ in pycompat.values(files): + for f, _, _ in files.values(): try: os.unlink(f) except IOError: diff --git a/odoo/models.py b/odoo/models.py index a9cd2ded435..792e9f04182 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -148,7 +148,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 pycompat.items(attrs): + for key, val in attrs.items(): 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): @@ -514,7 +514,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): cls._inherits.update(base._inherits) - for mname, fnames in pycompat.items(base._depends): + for mname, fnames in base._depends.items(): cls._depends[mname] = cls._depends.get(mname, []) + fnames for cons in base._constraints: @@ -524,7 +524,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): cls._sql_constraints += base._sql_constraints cls._sequence = cls._sequence or (cls._table + '_id_seq') - cls._constraints = list(pycompat.values(cls._constraints)) + cls._constraints = list(cls._constraints.values()) # update _inherits_children of parent models for parent_name in cls._inherits: @@ -875,7 +875,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): :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 pycompat.items(self._fields)} + field_names = {name: field.string for name, field in self._fields.items()} if self.env.lang: field_names.update(self.env['ir.translation'].get_field_string(self._name)) @@ -1006,7 +1006,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): defaults = self._convert_to_write(defaults) # add default values for inherited fields - for model, names in pycompat.items(parent_fields): + for model, names in parent_fields.items(): defaults.update(self.env[model].default_get(names)) return defaults @@ -1082,7 +1082,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): :rtype: etree._Element """ group = E.group(col="4") - for fname, field in pycompat.items(self._fields): + for fname, field in self._fields.items(): if field.automatic: continue elif field.type in ('one2many', 'many2many', 'text', 'html'): @@ -1504,14 +1504,14 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # avoid overriding inherited values when parent is set avoid_models = { parent_model - for parent_model, parent_field in pycompat.items(self._inherits) + for parent_model, parent_field in self._inherits.items() if parent_field in values } # compute missing fields missing_defaults = { name - for name, field in pycompat.items(self._fields) + for name, field in self._fields.items() if name not in values if self._log_access and name not in MAGIC_COLUMNS if not (field.inherited and field.related_field.model_name in avoid_models) @@ -1522,7 +1522,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # override defaults with the provided values, never allow the other way around defaults = self.default_get(list(missing_defaults)) - for name, value in pycompat.items(defaults): + for name, value in defaults.items(): 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)] @@ -1607,10 +1607,10 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): 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 pycompat.items(result): + for key, line in result.items(): line['__fold'] = fold.get(key, False) - return list(pycompat.values(result)) + return list(result.values()) @api.model def _read_group_prepare(self, orderby, aggregated_fields, annotated_groupbys, query): @@ -1844,7 +1844,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): 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 pycompat.values(self._fields) if f.store] + fields = fields or [f.name for f in self._fields.values() if f.store] groupby = [groupby] if isinstance(groupby, pycompat.string_types) else list(OrderedSet(groupby)) groupby_list = groupby[:1] if lazy else groupby @@ -1918,7 +1918,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): self._read_group_resolve_many2one_fields(fetched_data, annotated_groupbys) - data = ({k: self._read_group_prepare_data(k,v, groupby_dict) for k,v in pycompat.items(r)} for r in fetched_data) + data = ({k: self._read_group_prepare_data(k,v, groupby_dict) for k,v in r.items()} 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). @@ -2024,7 +2024,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # fields which were required but have been removed (or will be added by # another module) cr = self._cr - cols = [name for name, field in pycompat.items(self._fields) + cols = [name for name, field in self._fields.items() if field.store and field.column_type] cr.execute("SELECT a.attname, a.attnotnull" " FROM pg_class c, pg_attribute a" @@ -2124,7 +2124,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): recs = self.with_context(active_test=False).search([]) recs._recompute_todo(field) - for field in pycompat.values(self._fields): + for field in self._fields.values(): if not field.store: continue @@ -2212,9 +2212,9 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): """ Determine inherited fields. """ # determine candidate inherited fields fields = {} - for parent_model, parent_field in pycompat.items(self._inherits): + for parent_model, parent_field in self._inherits.items(): parent = self.env[parent_model] - for name, field in pycompat.items(parent._fields): + for name, field in parent._fields.items(): # inherited fields are implemented as related fields, with the # following specific properties: # - reading inherited fields should not bypass access rights @@ -2227,13 +2227,13 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): ) # add inherited fields that are not redefined locally - for name, field in pycompat.items(fields): + for name, field in fields.items(): if name not in self._fields: self._add_field(name, field) @api.model def _inherits_check(self): - for table, field_name in pycompat.items(self._inherits): + for table, field_name in self._inherits.items(): 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) @@ -2246,7 +2246,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): field.ondelete = "cascade" # reflect fields with delegate=True in dictionary self._inherits - for field in pycompat.values(self._fields): + for field in self._fields.values(): 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) @@ -2334,7 +2334,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # set up fields bad_fields = [] - for name, field in pycompat.items(cls._fields): + for name, field in cls._fields.items(): try: field.setup_full(self) except Exception: @@ -2353,11 +2353,11 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # map each field to the fields computed with the same method groups = defaultdict(list) - for field in pycompat.values(cls._fields): + for field in cls._fields.values(): if field.compute: cls._field_computed[field] = group = groups[field.compute] group.append(field) - for fields in pycompat.values(groups): + for fields in groups.values(): 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", @@ -2370,7 +2370,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): if isinstance(self, Model): # set up field triggers (on database-persisted models only) - for field in pycompat.values(cls._fields): + for field in cls._fields.values(): # dependencies of custom fields may not exist; ignore that case exceptions = (Exception,) if field.manual else () with tools.ignore(*exceptions): @@ -2409,7 +2409,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): readonly = not (has_access('write') or has_access('create')) res = {} - for fname, field in pycompat.items(self._fields): + for fname, field in self._fields.items(): if allfields and fname not in allfields: continue if field.groups and not self.user_has_groups(field.groups): @@ -2421,7 +2421,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): description['states'] = {} if attributes: description = {key: val - for key, val in pycompat.items(description) + for key, val in description.items() if key in attributes} res[fname] = description @@ -2532,7 +2532,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): if self._context.get('prefetch_fields', True) and field.prefetch: fs.update( f - for f in pycompat.values(self._fields) + for f in self._fields.values() # select fields that can be prefetched if f.prefetch # discard fields with groups that the user may not access @@ -2987,7 +2987,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # split up fields into old-style and pure new-style ones old_vals, new_vals, unknown = {}, {}, [] - for key, val in pycompat.items(vals): + for key, val in vals.items(): field = self._fields.get(key) if field: if field.store or field.inherited: @@ -3059,7 +3059,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): 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 pycompat.items(vals): + for name, val in vals.items(): field = self._fields[name] if field and field.deprecated: _logger.warning('Field %s.%s is deprecated: %s', self._name, name, field.deprecated) @@ -3121,7 +3121,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # defaults in context must be removed when call a one2many or many2many rel_context = {key: val - for key, val in pycompat.items(self._context) + for key, val in self._context.items() if not key.startswith('default_')} # call the 'write' method of fields which are not columns @@ -3134,7 +3134,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # write inherited fields on the corresponding parent records unknown_fields = set(updend) - for parent_model, parent_field in pycompat.items(self._inherits): + for parent_model, parent_field in self._inherits.items(): 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 +3258,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # split up fields into old-style and pure new-style ones old_vals, new_vals, unknown = {}, {}, [] - for key, val in pycompat.items(vals): + for key, val in vals.items(): field = self._fields.get(key) if field: if field.store or field.inherited: @@ -3295,7 +3295,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # 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 pycompat.items(self._inherits) + for parent_model, parent_field in self._inherits.items() } # list of column assignments defined as tuples like: @@ -3310,7 +3310,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): upd_todo = [] unknown_fields = [] protected_fields = [] - for name, val in list(pycompat.items(vals)): + for name, val in list(vals.items()): field = self._fields.get(name) if not field: unknown_fields.append(name) @@ -3326,7 +3326,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): _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 pycompat.items(tocreate): + for parent_model, parent_vals in tocreate.items(): parent_id = parent_vals.pop('id') if not parent_id: parent_id = self.env[parent_model].create(parent_vals).id @@ -3335,13 +3335,13 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): vals[self._inherits[parent_model]] = parent_id # set boolean fields to False by default (to make search more powerful) - for name, field in pycompat.items(self._fields): + for name, field in self._fields.items(): if field.type == 'boolean' and field.store and name not in vals: vals[name] = False # determine SQL values self = self.browse() - for name, val in pycompat.items(vals): + for name, val in vals.items(): field = self._fields[name] if field.store and field.column_type: column_val = field.convert_to_column(val, self, vals) @@ -3373,7 +3373,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): if self.env.lang and self.env.lang != 'en_US': # add translations for self.env.lang - for name, val in pycompat.items(vals): + for name, val in vals.items(): field = self._fields[name] if field.store and field.column_type and field.translate is True: tname = "%s,%s" % (self._name, name) @@ -3421,7 +3421,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # defaults in context must be removed when call a one2many or many2many rel_context = {key: val - for key, val in pycompat.items(self._context) + for key, val in self._context.items() if not key.startswith('default_')} # call the 'write' method of fields which are not columns @@ -3726,11 +3726,11 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): # 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 pycompat.items(self._fields) if not field.inherited) + whitelist = set(name for name, field in self._fields.items() if not field.inherited) def blacklist_given_fields(model): # blacklist the fields that are given by inheritance - for parent_model, parent_field in pycompat.items(model._inherits): + for parent_model, parent_field in model._inherits.items(): blacklist.add(parent_field) if parent_field in default: # all the fields of 'parent_model' are given by the record: @@ -3739,17 +3739,17 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): else: blacklist_given_fields(self.env[parent_model]) # blacklist deprecated fields - for name, field in pycompat.items(model._fields): + for name, field in model._fields.items(): if field.deprecated: blacklist.add(name) blacklist_given_fields(self) fields_to_copy = {name: field - for name, field in pycompat.items(self._fields) + for name, field in self._fields.items() if field.copy and name not in default and name not in blacklist} - for name, field in pycompat.items(fields_to_copy): + for name, field in fields_to_copy.items(): if field.type == 'one2many': # duplicate following the order of the ids because we'll rely on # it later for copying translations in copy_translation()! @@ -3787,7 +3787,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): old_wo_lang, new_wo_lang = (old + new).with_context(lang=None) Translation = old.env['ir.translation'] - for name, field in pycompat.items(old._fields): + for name, field in old._fields.items(): if not field.copy: continue @@ -3970,7 +3970,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): """ results = self._get_external_ids() return {key: val[0] if val else '' - for key, val in pycompat.items(results)} + for key, val in results.items()} # backwards compatibility get_xml_id = get_external_id @@ -4343,7 +4343,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): target = self if update else self.browse([], self._prefetch) return { name: fields[name].convert_to_cache(value, target, validate=validate) - for name, value in pycompat.items(values) + for name, value in values.items() if name in fields } @@ -4353,14 +4353,14 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): """ return { name: self._fields[name].convert_to_record(value, self) - for name, value in pycompat.items(values) + for name, value in values.items() } def _convert_to_write(self, values): """ Convert the ``values`` dictionary into the format of :meth:`write`. """ fields = self._fields result = {} - for name, value in pycompat.items(values): + for name, value in values.items(): if name in fields: field = fields[name] value = field.convert_to_cache(value, self, validate=False) @@ -4447,7 +4447,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): def update(self, values): """ Update the records in ``self`` with ``values``. """ for record in self: - for name, value in pycompat.items(values): + for name, value in values.items(): record[name] = value # @@ -4692,7 +4692,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): if fnames is None: if ids is None: return self.env.invalidate_all() - fields = list(pycompat.values(self._fields)) + fields = list(self._fields.values()) else: fields = [self._fields[n] for n in fnames] @@ -4784,7 +4784,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): updates[frozendict(vals)].add(rec.id) # update records in batch when possible with recs.env.norecompute(): - for vals, ids in pycompat.items(updates): + for vals, ids in updates.items(): recs.browse(ids)._write(dict(vals)) # mark computed fields as done for f in fs: @@ -4818,7 +4818,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): if not result.get(names): result[names] = node.attrib.get('on_change') # traverse the subviews included in relational fields - for subinfo in pycompat.values(info['fields'][name].get('views', {})): + for subinfo in info['fields'][name].get('views', {}).values(): process(etree.fromstring(subinfo['arch']), subinfo, names) else: for child in node: @@ -4841,7 +4841,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): return if res.get('value'): res['value'].pop('id', None) - self.update({key: val for key, val in pycompat.items(res['value']) if key in self._fields}) + self.update({key: val for key, val in res['value'].items() if key in self._fields}) if res.get('domain'): result.setdefault('domain', {}).update(res['domain']) if res.get('warning'): @@ -4970,7 +4970,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): record.mapped(field_seq) # determine which fields have been modified - for name, oldval in pycompat.items(values): + for name, oldval in values.items(): field = self._fields[name] newval = record[name] if newval != oldval or ( @@ -5046,7 +5046,7 @@ class RecordCache(MutableMapping): """ if args and isinstance(args[0], SpecialValue): values = dict.fromkeys(self._recs._ids, args[0]) - for name, field in pycompat.items(self._recs._fields): + for name, field in self._recs._fields.items(): if name != 'id': self._recs.env.cache[field].update(values) else: @@ -5064,7 +5064,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 pycompat.items(self._recs._fields): + for name, field in self._recs._fields.items(): if name != 'id' and not isinstance(cache[field].get(id, dummy), SpecialValue): yield name diff --git a/odoo/modules/graph.py b/odoo/modules/graph.py index 731b6f6557a..0f2e329fab9 100644 --- a/odoo/modules/graph.py +++ b/odoo/modules/graph.py @@ -8,7 +8,6 @@ import logging import odoo import odoo.tools as tools -from odoo.tools import pycompat _logger = logging.getLogger(__name__) @@ -36,7 +35,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 = {key: {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'installed_version': None} for key in pycompat.keys(self)} + additional_data = {key: {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'installed_version': None} for key in self.keys()} ## 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 +45,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 pycompat.values(self): - for k, v in pycompat.items(additional_data[package.name]): + for package in self.values(): + for k, v in additional_data[package.name].items(): setattr(package, k, v) def add_module(self, cr, module, force=None): @@ -102,9 +101,9 @@ class Graph(dict): def __iter__(self): level = 0 - done = set(pycompat.keys(self)) + done = set(self.keys()) while done: - level_modules = sorted((name, module) for name, module in pycompat.items(self) if module.depth==level) + level_modules = sorted((name, module) for name, module in self.items() if module.depth==level) for name, module in level_modules: done.remove(name) yield module diff --git a/odoo/modules/loading.py b/odoo/modules/loading.py index bd58376a7b9..c06c219f7eb 100644 --- a/odoo/modules/loading.py +++ b/odoo/modules/loading.py @@ -20,7 +20,6 @@ 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') @@ -298,13 +297,13 @@ def load_modules(db, force_demo=False, status=None, update_module=False): _check_module_names(cr, itertools.chain(tools.config['init'], tools.config['update'])) - module_names = [k for k, v in pycompat.items(tools.config['init']) if v] + module_names = [k for k, v in tools.config['init'].items() 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 pycompat.items(tools.config['update']) if v] + module_names = [k for k, v in tools.config['update'].items() if v] if module_names: modules = Module.search([('state', '=', 'installed'), ('name', 'in', module_names)]) if modules: @@ -392,7 +391,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(pycompat.values(modules_to_remove)).module_uninstall() + Module.browse(modules_to_remove.values()).module_uninstall() # Recursive reload, should only happen once, because there should be no # modules to remove next time cr.commit() @@ -415,7 +414,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 pycompat.values(env): + for model in env.values(): 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 0fa6699cf24..8edc9586415 100644 --- a/odoo/modules/migration.py +++ b/odoo/modules/migration.py @@ -13,7 +13,6 @@ 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 @@ -90,8 +89,8 @@ class MigrationManager(object): def _get_migration_versions(pkg): versions = sorted({ ver - for lv in pycompat.values(self.migrations[pkg.name]) - for ver, lf in pycompat.items(lv) + for lv in self.migrations[pkg.name].values() + for ver, lf in lv.items() if lf }, key=lambda k: parse_version(convert_version(k))) return versions diff --git a/odoo/modules/registry.py b/odoo/modules/registry.py index cade92c23ac..1efc6aa195f 100644 --- a/odoo/modules/registry.py +++ b/odoo/modules/registry.py @@ -17,7 +17,7 @@ import odoo from .. import SUPERUSER_ID from odoo.tools import (assertion_report, config, existing_tables, lazy_classproperty, lazy_property, table_exists, - topological_sort, OrderedSet, pycompat) + topological_sort, OrderedSet) from odoo.tools.lru import LRU _logger = logging.getLogger(__name__) @@ -157,7 +157,7 @@ class Registry(Mapping): def delete_all(cls): """ Delete all the registries. """ with cls._lock: - for db_name in list(pycompat.keys(cls.registries)): + for db_name in list(cls.registries.keys()): cls.delete(db_name) # @@ -193,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 pycompat.values(self) - for field in pycompat.values(model._fields) + for model in self.values() + for field in model._fields.values() } # sort them topologically, and associate a sequence number to each field mapping = { @@ -262,7 +262,7 @@ class Registry(Mapping): env['ir.model']._add_manual_models() # prepare the setup on all models - models = list(pycompat.values(env)) + models = list(env.values()) for model in models: model._prepare_setup() @@ -316,7 +316,7 @@ class Registry(Mapping): missing = {table2model[table] for table in missing_tables} _logger.warning("Models have no table: %s.", ", ".join(missing)) # recreate missing tables following model dependencies - deps = {name: model._depends for name, model in pycompat.items(env)} + deps = {name: model._depends for name, model in env.items()} for name in topological_sort(deps): if name in missing: _logger.info("Recreate table of model %s.", name) @@ -341,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 pycompat.values(self.models): + for model in self.models.values(): model.clear_caches() def setup_signaling(self): diff --git a/odoo/osv/orm.py b/odoo/osv/orm.py index f189638582b..f7cc7ae0701 100644 --- a/odoo/osv/orm.py +++ b/odoo/osv/orm.py @@ -46,12 +46,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 pycompat.items(field.get("states",{})): + for state, modifs in field.get("states",{}).items(): for modif in modifs: if default_values[modif[0]] != modif[1]: state_exceptions[modif[0]].append(state) - for attr, default_value in pycompat.items(default_values): + for attr, default_value in default_values.items(): if state_exceptions[attr]: modifiers[attr] = [("state", "not in" if default_value else "in", state_exceptions[attr])] else: diff --git a/odoo/service/model.py b/odoo/service/model.py index 185d5ce72cc..47eeeab1af1 100644 --- a/odoo/service/model.py +++ b/odoo/service/model.py @@ -10,7 +10,6 @@ import time import odoo from odoo.exceptions import UserError, ValidationError, QWebException from odoo.models import check_method_name -from odoo.tools import pycompat from odoo.tools.translate import translate from odoo.tools.translate import _ @@ -68,10 +67,6 @@ def check(f): except Exception: pass - uid = 1 - if args and isinstance(args[0], pycompat.integer_types): - uid = args[0] - lang = ctx and ctx.get('lang') if not (lang or hasattr(src, '__call__')): return src @@ -119,7 +114,7 @@ def check(f): time.sleep(wait_time) except IntegrityError as inst: registry = odoo.registry(dbname) - for key in pycompat.keys(registry._sql_error): + for key in registry._sql_error.keys(): 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 56acaaed901..b05b93dd825 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 from odoo.tools import config -from odoo.tools import stripped_sys_argv, dumpstacks, log_ormcache_stats, pycompat +from odoo.tools import stripped_sys_argv, dumpstacks, log_ormcache_stats _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 pycompat.items(registries): + for db_name, registry in registries.items(): while registry.ready: try: acquired = ir_cron._acquire_job(db_name) @@ -525,7 +525,7 @@ class PreforkServer(CommonServer): def process_timeout(self): now = time.time() - for (pid, worker) in pycompat.items(self.workers): + for (pid, worker) in self.workers.items(): if worker.watchdog_timeout is not None and \ (now - worker.watchdog_time) >= worker.watchdog_timeout: _logger.error("%s (%s) timeout after %ss", @@ -546,8 +546,8 @@ class PreforkServer(CommonServer): def sleep(self): try: # map of fd -> worker - fds = {w.watchdog_pipe[0]: w for k, w in pycompat.items(self.workers)} - fd_in = list(pycompat.keys(fds)) + [self.pipe[0]] + fds = {w.watchdog_pipe[0]: w for w in self.workers.values()} + fd_in = list(fds) + [self.pipe[0]] # check for ping or internal wakeups ready = select.select(fd_in, [], [], self.beat) # update worker watchdogs @@ -887,7 +887,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 list(pycompat.items(sys.modules)): + for mod_name, mod_mod in list(sys.modules.items()): 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 efc0e51972d..749abc69932 100644 --- a/odoo/sql_db.py +++ b/odoo/sql_db.py @@ -21,8 +21,6 @@ from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_READ from psycopg2.pool import PoolError from werkzeug import urls -from .tools import pycompat - psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) _logger = logging.getLogger(__name__) @@ -43,7 +41,7 @@ def undecimalize(symb, cr): return None return float(symb) -for name, typeoid in pycompat.items(types_mapping): +for name, typeoid in types_mapping.items(): 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)) @@ -269,7 +267,7 @@ class Cursor(object): sqllogs = {'from': self.sql_from_log, 'into': self.sql_into_log} sum = 0 if sqllogs[type]: - sqllogitems = pycompat.items(sqllogs[type]) + sqllogitems = sqllogs[type].items() _logger.debug("SQL LOG %s:", type) for r in sorted(sqllogitems, key=lambda k: k[1]): delay = timedelta(microseconds=r[1][1]) diff --git a/odoo/tools/cache.py b/odoo/tools/cache.py index 1226038a21c..b6b23175773 100644 --- a/odoo/tools/cache.py +++ b/odoo/tools/cache.py @@ -202,10 +202,10 @@ def log_ormcache_stats(sig=None, frame=None): me = threading.currentThread() me_dbname = getattr(me, 'dbname', 'n/a') entries = defaultdict(int) - for dbname, reg in pycompat.items(Registry.registries): + for dbname, reg in Registry.registries.items(): for key in reg.cache: entries[(dbname,) + key[:2]] += 1 - for key, count in sorted(pycompat.items(entries)): + for key, count in sorted(entries.items()): dbname, model_name, method = key me.dbname = dbname stat = STAT[key] diff --git a/odoo/tools/config.py b/odoo/tools/config.py index ee6c03a7e2e..f95e50f7dce 100644 --- a/odoo/tools/config.py +++ b/odoo/tools/config.py @@ -55,7 +55,7 @@ def _deduplicate_loggers(loggers): # there are no duplicates within the output sequence return ( '{}:{}'.format(logger, level) - for logger, level in pycompat.items(dict(it.split(':') for it in loggers)) + for logger, level in dict(it.split(':') for it in loggers).items() ) @@ -527,9 +527,9 @@ class configmanager(object): def save(self): p = ConfigParser.RawConfigParser() - loglevelnames = dict(pycompat.izip(pycompat.values(self._LOGLEVELS), pycompat.keys(self._LOGLEVELS))) + loglevelnames = dict(pycompat.izip(self._LOGLEVELS.values(), self._LOGLEVELS)) p.add_section('options') - for opt in sorted(pycompat.keys(self.options)): + for opt in sorted(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 +541,9 @@ class configmanager(object): else: p.set('options', opt, self.options[opt]) - for sec in sorted(pycompat.keys(self.misc)): + for sec in sorted(self.misc): p.add_section(sec) - for opt in sorted(pycompat.keys(self.misc[sec])): + for opt in sorted(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/graph.py b/odoo/tools/graph.py index 5e3edabf05a..d43088d97ab 100755 --- a/odoo/tools/graph.py +++ b/odoo/tools/graph.py @@ -5,8 +5,6 @@ import operator import math -from odoo.tools import pycompat - class graph(object): def __init__(self, nodes, transitions, no_ancester=None): @@ -259,7 +257,7 @@ class graph(object): """The ranks are normalized by setting the least rank to zero. """ - least_rank = min(x['x'] for x in pycompat.values(self.result.values)) + least_rank = min(x['x'] for x in self.result.values.values()) if least_rank!=0: for node in self.result: @@ -383,7 +381,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(len(x) for x in pycompat.values(self.levels.values)) + max_level = max(len(x) for x in self.levels.values.values()) for level in self.levels: if level: @@ -474,7 +472,7 @@ class graph(object): """ if self.Is_Cyclic: - max_level = max(len(x) for x in pycompat.values(self.levels.values)) + max_level = max(len(x) for x in self.levels.values.values()) if max_level%2: self.result[self.start]['y'] = (max_level+1)/2 + self.max_order + (self.max_order and 1) @@ -486,7 +484,7 @@ class graph(object): else: self.result[self.start]['y'] = 0 self.tree_order(self.start, 0) - min_order = math.fabs(min(x['y'] for x in pycompat.values(self.result.values))) + min_order = math.fabs(min(x['y'] for x in self.result.values.values())) index = self.start_nodes.index(self.start) same = False @@ -540,7 +538,7 @@ class graph(object): self.result[start]['y'] = base + factor factor += 1 - self.max_order = max(x['y'] for x in pycompat.values(self.result.values)) + self.max_order = max(x['y'] for x in self.result.values.values()) def find_starts(self): """Finds other start nodes of the graph in the case when graph is disconneted @@ -660,7 +658,7 @@ class graph(object): for node in self.no_ancester: for sec_node in self.transitions.get(node, []): - if sec_node in pycompat.keys(self.partial_order): + if sec_node in self.partial_order: self.transitions[self.start_nodes[0]].append(node) break @@ -746,7 +744,7 @@ if __name__=='__main__': for node in nodes: node_res[node] = result[node] - for name,node in pycompat.items(node_res): + for name, node in node_res.items(): 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 21ecaf6383a..119fce0e1ee 100644 --- a/odoo/tools/lru.py +++ b/odoo/tools/lru.py @@ -2,7 +2,6 @@ # 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'] @@ -110,12 +109,11 @@ class LRU(object): @synchronized() def itervalues(self): - for i,j in pycompat.items(self): - yield j + return iter(self.d.values()) @synchronized() def keys(self): - return list(pycompat.keys(self.d)) + return list(self.d) @synchronized() def pop(self,key): diff --git a/odoo/tools/mail.py b/odoo/tools/mail.py index b45de63cc71..288defdcf1f 100644 --- a/odoo/tools/mail.py +++ b/odoo/tools/mail.py @@ -85,7 +85,7 @@ class _Cleaner(clean.Cleaner): new_node.text = text new_node.tail = tail if attrs: - for key, val in pycompat.items(attrs): + for key, val in attrs.items(): new_node.set(key, val) return new_node @@ -154,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 pycompat.items(valid_styles)) + el.attrib['style'] = '; '.join('%s: %s' % (key, val) for (key, val) in valid_styles.items()) else: del el.attrib['style'] diff --git a/odoo/tools/mimetypes.py b/odoo/tools/mimetypes.py index 663f35be9b9..59421cf8bb6 100644 --- a/odoo/tools/mimetypes.py +++ b/odoo/tools/mimetypes.py @@ -10,8 +10,6 @@ import logging import re import zipfile -from odoo.tools import pycompat - __all__ = ['guess_mimetype'] _logger = logging.getLogger(__name__) @@ -35,7 +33,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 pycompat.items(_ooxml_dirs): + for dirname, mime in _ooxml_dirs.items(): if any(entry.startswith(dirname) for entry in filenames): return mime diff --git a/odoo/tools/misc.py b/odoo/tools/misc.py index 274ae4a6d24..9116e08cec8 100644 --- a/odoo/tools/misc.py +++ b/odoo/tools/misc.py @@ -479,7 +479,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 pycompat.items(kwargs): + for key, value in kwargs.items(): vector.append(' kwarg %10s: %s' % (key, pformat(value))) timeb4 = time.time() @@ -908,7 +908,7 @@ def dumpstacks(sig=None, frame=None): 'dbname': getattr(th, 'dbname', 'n/a'), 'url': getattr(th, 'url', 'n/a')} for th in threading.enumerate()} - for threadId, stack in pycompat.items(sys._current_frames()): + for threadId, stack in sys._current_frames().items(): thread_info = threads_info.get(threadId, {}) code.append("\n# Thread: %s (id:%s) (db:%s) (uid:%s) (url:%s)" % (thread_info.get('name', 'n/a'), @@ -960,7 +960,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 pycompat.items(self))) + return hash(frozenset((key, freehash(val)) for key, val in self.items())) class Collector(Mapping): """ A mapping from keys to lists. This is essentially a space optimization diff --git a/odoo/tools/pycompat.py b/odoo/tools/pycompat.py index 89bf96c3dd6..feb65da7a67 100644 --- a/odoo/tools/pycompat.py +++ b/odoo/tools/pycompat.py @@ -27,10 +27,6 @@ if PY2: integer_types = (int, long) - 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 @@ -74,10 +70,6 @@ else: integer_types = (int,) - keys = lambda d: iter(d.keys()) - values = lambda d: iter(d.values()) - items = lambda d: iter(d.items()) - imap = map izip = zip ifilter = filter diff --git a/odoo/tools/test_reports.py b/odoo/tools/test_reports.py index 6a6401eb896..a3588577c08 100644 --- a/odoo/tools/test_reports.py +++ b/odoo/tools/test_reports.py @@ -173,7 +173,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 pycompat.items(view_res.get('fields',{})): + for fk, field in view_res.get('fields',{}).items(): # 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 f958772514e..a9f8c8fd57f 100644 --- a/odoo/tools/translate.py +++ b/odoo/tools/translate.py @@ -258,7 +258,7 @@ def translate_xml_node(node, callback, parse, serialize): append_content(result, translate_content(todo) if todo_has_text else todo) # translate the required attributes - for name, value in pycompat.items(result.attrib): + for name, value in result.attrib.items(): if name in TRANSLATED_ATTRS: result.set(name, translate_text(value) or value) @@ -678,7 +678,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(pycompat.items(grouped_rows)): + for src, row in sorted(grouped_rows.items()): if not lang: # translation template, so no translation value row['translation'] = '' @@ -692,7 +692,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 pycompat.items(rows_by_module): + for mod, modrows in rows_by_module.items(): tmpmoddir = join(tmpdir, mod, 'i18n') os.makedirs(tmpmoddir) pofilename = (lang if lang else mod) + ".po" + ('t' if not lang else '') @@ -1125,7 +1125,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 pycompat.items(pot_targets): + for src, target in pot_targets.items(): 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 6cf4cc357eb..79f997045a2 100644 --- a/odoo/tools/yaml_import.py +++ b/odoo/tools/yaml_import.py @@ -198,7 +198,7 @@ class YamlInterpreter(object): def process_assert(self, node): if isinstance(node, dict): - assertion, expressions = next(pycompat.items(node)) + assertion, expressions = list(node.items())[0] else: assertion, expressions = node, [] @@ -271,7 +271,7 @@ class YamlInterpreter(object): return record_dict def process_record(self, node): - record, fields = next(pycompat.items(node)) + record, fields = list(node.items())[0] model = self.env[record.model] view_id = record.view if view_id and (view_id is not True) and isinstance(view_id, pycompat.string_types): @@ -376,7 +376,7 @@ class YamlInterpreter(object): def process_vals(fg, vals): """ sanitize the given field values """ result = {} - for field_name, field_value in pycompat.items(vals): + for field_name, field_value in vals.items(): if field_name not in fg: continue if fg[field_name]['type'] == 'many2one' and isinstance(field_value, (tuple, list)): @@ -397,7 +397,7 @@ class YamlInterpreter(object): def post_process(fg, elems, vals): """ filter out readonly fields from vals """ result = {} - for field_name, field_value in pycompat.items(vals): + for field_name, field_value in vals.items(): if is_readonly(elems[field_name]): continue if fg[field_name]['type'] in ('one2many', 'many2many'): @@ -443,7 +443,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 pycompat.items(elems): + for field_name, field_elem in elems.items(): 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 @@ -473,7 +473,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 pycompat.items(result.get('value', {})) + for key, val in result.get('value', {}).items() if key not in fields # do not shadow values explicitly set in yaml })) @@ -482,7 +482,7 @@ class YamlInterpreter(object): else: record_dict = {} - for field_name, expression in pycompat.items(fields): + for field_name, expression in fields.items(): if record_dict.get(field_name): continue field_value = self._eval_field(model, field_name, expression, parent=record_dict, default=False, context=context) @@ -493,7 +493,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 pycompat.items(record_dict) + for key, val in record_dict.items() for field in [model._fields[key].base_field] if field.store or field.inverse } @@ -579,7 +579,7 @@ class YamlInterpreter(object): self.sudo_env = self.env(user=SUPERUSER_ID) def process_python(self, node): - python, statements = next(pycompat.items(node)) + python, statements = list(node.items())[0] assert python.model or python.id, "!python node must have attribute `model` or `id`" if python.id is None: record = self.env[python.model] @@ -639,7 +639,7 @@ class YamlInterpreter(object): return args def process_function(self, node): - function, params = next(pycompat.items(node)) + function, params = list(node.items())[0] if self.isnoupdate(function) and self.mode != 'init': return model = self.env[function.model] @@ -769,9 +769,9 @@ class YamlInterpreter(object): def process_ir_set(self, node): if not self.mode == 'init': return False - _, fields = next(pycompat.items(node)) + _, fields = list(node.items())[0] res = {} - for fieldname, expression in pycompat.items(fields): + for fieldname, expression in fields.items(): if is_eval(expression): value = safe_eval(expression.expression, self.eval_context) else: @@ -877,7 +877,7 @@ class YamlInterpreter(object): elif not is_preceded_by_comment: if isinstance(node, dict): msg = "Creating %s\n with %s" - args = next(pycompat.items(node)) + args = list(node.items())[0] self._log(msg, *args) else: self._log(node) diff --git a/odoo/tools/yaml_tag.py b/odoo/tools/yaml_tag.py index 2b7141b6693..9f57111bf37 100644 --- a/odoo/tools/yaml_tag.py +++ b/odoo/tools/yaml_tag.py @@ -1,8 +1,6 @@ import yaml import logging -from . import pycompat - class YamlTag(object): """ @@ -16,7 +14,7 @@ class YamlTag(object): def __getattr__(self, attr): return None def __repr__(self): - return "<%s %s>" % (self.__class__.__name__, sorted(pycompat.items(self.__dict__))) + return "<%s %s>" % (self.__class__.__name__, sorted(self.__dict__.items())) class Assert(YamlTag): def __init__(self, model, id=None, severity=logging.WARNING, string="NONAME", **kwargs):