diff --git a/.drone.yml b/.drone.yml index e0bc2f7026..5da6083eb7 100644 --- a/.drone.yml +++ b/.drone.yml @@ -25,12 +25,6 @@ services: matrix: include: - - IMAGE: python:2.7 - TOXENV: py27 - DATABASE: sqlite - - IMAGE: python:2.7 - TOXENV: py27 - DATABASE: postgresql - IMAGE: python:3.4 TOXENV: py34 DATABASE: sqlite diff --git a/CHANGELOG b/CHANGELOG index 8cbe60799d..71ee1a4d98 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Remove support for Python 2.7 * Check Rule also after modification * Check Rule after indirect fields * Allow to define view id in switch client action diff --git a/bin/trytond b/bin/trytond index d6b743834a..e4d74a4319 100755 --- a/bin/trytond +++ b/bin/trytond @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import sys diff --git a/bin/trytond-admin b/bin/trytond-admin index 075fe1c85d..e6d5999a99 100755 --- a/bin/trytond-admin +++ b/bin/trytond-admin @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import sys diff --git a/bin/trytond-cron b/bin/trytond-cron index 9be73f524d..af9ef2499c 100755 --- a/bin/trytond-cron +++ b/bin/trytond-cron @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import sys diff --git a/doc/conf.py b/doc/conf.py index 4b361dee6e..148a5573bc 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -39,8 +39,8 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'trytond' -copyright = (u'2008-2011, Bertrand Chenal, Cédric Krier, Ian Wilson, ' +project = 'trytond' +copyright = ('2008-2011, Bertrand Chenal, Cédric Krier, Ian Wilson, ' 'Udo Spallek') # The version info for the project you're documenting, acts as replacement for @@ -175,8 +175,8 @@ htmlhelp_basename = 'trytonddoc' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]) latex_documents = [ - ('index', 'trytond.tex', u'trytond Documentation', - u'Bertrand Chenal, Cédric Krier, Ian Wilson, Udo Spallek', 'manual'), + ('index', 'trytond.tex', 'trytond Documentation', + 'Bertrand Chenal, Cédric Krier, Ian Wilson, Udo Spallek', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/setup.py b/setup.py index ae6f4d19d2..7f7c8c6fd5 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. @@ -19,6 +19,7 @@ def get_version(): init = read(os.path.join('trytond', '__init__.py')) return re.search('__version__ = "([0-9.]*)"', init).group(1) + version = get_version() major_version, minor_version, _ = version.split('.', 2) major_version = int(major_version) @@ -83,7 +84,6 @@ setup(name=name, 'Natural Language :: Slovenian', 'Natural Language :: Spanish', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', @@ -93,6 +93,7 @@ setup(name=name, ], platforms='any', license='GPL-3', + python_requires='>=3.4', install_requires=[ 'lxml >= 2.0', 'relatorio[fodt] >= 0.7.0', @@ -116,5 +117,4 @@ setup(name=name, test_suite='trytond.tests', test_loader='trytond.test_loader:Loader', tests_require=['mock'], - use_2to3=True, ) diff --git a/tox.ini b/tox.ini index 3f9f17e6bc..16208608a7 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,12 @@ [tox] -envlist = {py27,py34,py35,py36}-{sqlite,postgresql},pypy-{sqlite,postgresql} +envlist = {py34,py35,py36}-{sqlite,postgresql},pypy3-{sqlite,postgresql} [testenv] commands = {envpython} setup.py test deps = mock - {py27,py34,py35,py36}-postgresql: psycopg2 >= 2.5 - pypy-postgresql: psycopg2cffi >= 2.5 + {py34,py35,py36}-postgresql: psycopg2 >= 2.5 + pypy3-postgresql: psycopg2cffi >= 2.5 sqlite: sqlitebck setenv = sqlite: TRYTOND_DATABASE_URI={env:SQLITE_URI:sqlite://} diff --git a/trytond/admin.py b/trytond/admin.py index 53d0821155..e60b32256e 100644 --- a/trytond/admin.py +++ b/trytond/admin.py @@ -80,7 +80,7 @@ def run(options): if options.email is not None: admin.email = options.email elif init[db_name]: - admin.email = raw_input( + admin.email = input( '"admin" email for "%s": ' % db_name) if init[db_name] or options.password: # try to read password from environment variable @@ -91,7 +91,7 @@ def run(options): try: with open(passpath) as passfile: password, = passfile.read().splitlines() - except Exception, err: + except Exception as err: sys.stderr.write('Can not read password ' 'from "%s": "%s"\n' % (passpath, err)) diff --git a/trytond/application.py b/trytond/application.py index 49bae0f7ba..8607949d7e 100644 --- a/trytond/application.py +++ b/trytond/application.py @@ -20,6 +20,6 @@ Pool.start() db_names = os.environ.get('TRYTOND_DATABASE_NAMES') if db_names: # Read with csv so database name can include special chars - reader = csv.reader(StringIO(unicode(db_names))) + reader = csv.reader(StringIO(db_names)) for db_name in next(reader): Pool(db_name).init() diff --git a/trytond/backend/__init__.py b/trytond/backend/__init__.py index b82e8bbd9a..c66a55e957 100644 --- a/trytond/backend/__init__.py +++ b/trytond/backend/__init__.py @@ -1,7 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import sys -import urlparse +import urllib.parse import os import imp try: @@ -15,7 +15,7 @@ __all__ = ['name', 'get'] def name(): - return urlparse.urlparse(config.get('database', 'uri', default='')).scheme + return urllib.parse.urlparse(config.get('database', 'uri', default='')).scheme def get(prop): @@ -24,7 +24,7 @@ def get(prop): if modname not in sys.modules: try: __import__(modname) - except ImportError, exception: + except ImportError as exception: if not pkg_resources: raise exception try: diff --git a/trytond/backend/postgresql/database.py b/trytond/backend/postgresql/database.py index a1ba8ff779..1528e4fad2 100644 --- a/trytond/backend/postgresql/database.py +++ b/trytond/backend/postgresql/database.py @@ -3,7 +3,7 @@ import time import logging import os -import urllib +import urllib.request, urllib.parse, urllib.error import json from decimal import Decimal from threading import RLock @@ -49,7 +49,7 @@ def unescape_quote(s): def replace_special_values(s, **mapping): - for name, value in mapping.iteritems(): + for name, value in mapping.items(): s = s.replace('$' + name, value) return s @@ -111,7 +111,7 @@ class Database(DatabaseInterface): port = uri.port and "port=%s" % uri.port or '' name = "dbname=%s" % name user = uri.username and "user=%s" % uri.username or '' - password = ("password=%s" % urllib.unquote_plus(uri.password) + password = ("password=%s" % urllib.parse.unquote_plus(uri.password) if uri.password else '') return '%s %s %s %s %s' % (host, port, name, user, password) diff --git a/trytond/backend/postgresql/table.py b/trytond/backend/postgresql/table.py index b51bb18de6..e24da61105 100644 --- a/trytond/backend/postgresql/table.py +++ b/trytond/backend/postgresql/table.py @@ -329,7 +329,7 @@ class TableHandler(TableHandlerInterface): self.drop_constraint(column_name + '_fkey', table=table) def index_action(self, column_name, action='add', table=None): - if isinstance(column_name, basestring): + if isinstance(column_name, str): column_name = [column_name] index_name = self.convert_name( ((table or self.table_name) + "_" + '_'.join(column_name) + diff --git a/trytond/backend/sqlite/database.py b/trytond/backend/sqlite/database.py index 7408b17028..82c8c84c1a 100644 --- a/trytond/backend/sqlite/database.py +++ b/trytond/backend/sqlite/database.py @@ -1,14 +1,11 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from trytond.backend.database import DatabaseInterface, SQLType -from trytond.config import config -import os -from decimal import Decimal import datetime -import time -import sys -import threading import logging +import os +import threading +import time +from decimal import Decimal _FIX_ROWCOUNT = False try: @@ -25,6 +22,9 @@ from sql import Flavor, Table, Query, Expression from sql.functions import (Function, Extract, Position, Substring, Overlay, CharLength, CurrentTimestamp, Trim) +from trytond.backend.database import DatabaseInterface, SQLType +from trytond.config import config + __all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError'] logger = logging.getLogger(__name__) @@ -171,7 +171,7 @@ class SQLiteTrim(Trim): }[self.position] def format(arg): - if isinstance(arg, basestring): + if isinstance(arg, str): return param else: return str(arg) @@ -189,7 +189,7 @@ def sign(value): def greatest(*args): - args = filter(lambda a: a is not None, args) + args = [a for a in args if a is not None] if args: return max(args) else: @@ -197,7 +197,7 @@ def greatest(*args): def least(*args): - args = filter(lambda a: a is not None, args) + args = [a for a in args if a is not None] if args: return min(args) else: @@ -440,19 +440,15 @@ class Database(DatabaseInterface): return value sqlite.register_converter('NUMERIC', lambda val: Decimal(val.decode('utf-8'))) -if sys.version_info[0] == 2: - sqlite.register_adapter(Decimal, lambda val: buffer(str(val))) - sqlite.register_adapter(bytearray, lambda val: buffer(val)) -else: - sqlite.register_adapter(Decimal, lambda val: str(val).encode('utf-8')) +sqlite.register_adapter(Decimal, lambda val: str(val).encode('utf-8')) def adapt_datetime(val): return val.replace(tzinfo=None).isoformat(" ") sqlite.register_adapter(datetime.datetime, adapt_datetime) sqlite.register_adapter(datetime.time, lambda val: val.isoformat()) -sqlite.register_converter('TIME', lambda val: datetime.time(*map(int, - val.decode('utf-8').split(':')))) +sqlite.register_converter('TIME', + lambda val: datetime.time(*map(int, val.decode('utf-8').split(':')))) sqlite.register_adapter(datetime.timedelta, lambda val: val.total_seconds()) diff --git a/trytond/backend/sqlite/table.py b/trytond/backend/sqlite/table.py index 76f6520048..1f35e20f45 100644 --- a/trytond/backend/sqlite/table.py +++ b/trytond/backend/sqlite/table.py @@ -97,7 +97,7 @@ class TableHandler(TableHandlerInterface): TableHandler.table_rename(self.table_name, temp_table) new_table = TableHandler(self._model, history=self.history) columns, old_columns = [], [] - for column, values in self._columns.iteritems(): + for column, values in self._columns.items(): typname = new_columns.get(column, {}).get( 'typname', values['typname']) size = new_columns.get(column, {}).get('size', values['size']) @@ -244,7 +244,7 @@ class TableHandler(TableHandlerInterface): warnings.warn('Unable to drop foreign key with SQLite backend') def index_action(self, column_name, action='add', table=None): - if isinstance(column_name, basestring): + if isinstance(column_name, str): column_name = [column_name] index_name = self.convert_name( self.table_name + "_" + '_'.join(column_name) + "_index") @@ -296,13 +296,13 @@ class TableHandler(TableHandlerInterface): temp_table = '_temp_%s' % self.table_name TableHandler.table_rename(self.table_name, temp_table) new_table = TableHandler(self._model, history=self.history) - for name, values in self._columns.iteritems(): + for name, values in self._columns.items(): if name != column_name: typname = values['typname'] size = values['size'] new_table._add_raw_column( name, database.sql_type(typname), field_size=size) - columns_name = [x for x in new_table._columns.keys()] + columns_name = list(new_table._columns.keys()) cursor.execute(('INSERT INTO "%s" (' + ','.join('"%s"' % c for c in columns_name) + ') SELECT ' + diff --git a/trytond/backend/table.py b/trytond/backend/table.py index 12174193c0..1630f95ad6 100644 --- a/trytond/backend/table.py +++ b/trytond/backend/table.py @@ -187,7 +187,7 @@ class TableHandlerInterface(object): :param name: the data name ''' if cls.namedatalen and len(name) >= cls.namedatalen: - if isinstance(name, unicode): + if isinstance(name, str): name = name.encode('utf-8') name = hashlib.sha256(name).hexdigest()[:cls.namedatalen - 1] return name diff --git a/trytond/cache.py b/trytond/cache.py index 2677c89ba8..a1bffe7878 100644 --- a/trytond/cache.py +++ b/trytond/cache.py @@ -17,7 +17,7 @@ def freeze(o): if isinstance(o, (set, tuple, list)): return tuple(freeze(x) for x in o) elif isinstance(o, dict): - return frozenset((x, freeze(y)) for x, y in o.iteritems()) + return frozenset((x, freeze(y)) for x, y in o.items()) else: return o diff --git a/trytond/config.py b/trytond/config.py index 9cdb54bc7b..799190c96d 100644 --- a/trytond/config.py +++ b/trytond/config.py @@ -1,8 +1,8 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import os -import ConfigParser -import urlparse +import configparser +import urllib.parse import logging __all__ = ['config', 'get_hostname', 'get_port', 'split_netloc', @@ -34,13 +34,13 @@ def parse_listen(value): def parse_uri(uri): - return urlparse.urlparse(uri) + return urllib.parse.urlparse(uri) -class TrytonConfigParser(ConfigParser.RawConfigParser): +class TrytonConfigParser(configparser.RawConfigParser): def __init__(self): - ConfigParser.RawConfigParser.__init__(self) + configparser.RawConfigParser.__init__(self) self.add_section('web') self.set('web', 'listen', 'localhost:8000') self.set('web', 'root', os.path.join(os.path.expanduser('~'), 'www')) @@ -77,9 +77,9 @@ class TrytonConfigParser(ConfigParser.RawConfigParser): self.update_etc() def update_etc(self, configfile=os.environ.get('TRYTOND_CONFIG')): - if isinstance(configfile, basestring): + if isinstance(configfile, str): configfile = [configfile] - if not configfile or not filter(None, configfile): + if not configfile or not [_f for _f in configfile if _f]: return configfile = [os.path.expanduser(filename) for filename in configfile] read_files = self.read(configfile) @@ -88,35 +88,35 @@ class TrytonConfigParser(ConfigParser.RawConfigParser): def get(self, section, option, *args, **kwargs): default = kwargs.pop('default', None) try: - return ConfigParser.RawConfigParser.get(self, section, option, + return configparser.RawConfigParser.get(self, section, option, *args, **kwargs) - except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + except (configparser.NoOptionError, configparser.NoSectionError): return default def getint(self, section, option, *args, **kwargs): default = kwargs.pop('default', None) try: - return ConfigParser.RawConfigParser.getint(self, section, option, + return configparser.RawConfigParser.getint(self, section, option, *args, **kwargs) - except (ConfigParser.NoOptionError, ConfigParser.NoSectionError, + except (configparser.NoOptionError, configparser.NoSectionError, TypeError): return default def getfloat(self, section, option, *args, **kwargs): default = kwargs.pop('default', None) try: - return ConfigParser.RawConfigParser.getfloat(self, section, option, + return configparser.RawConfigParser.getfloat(self, section, option, *args, **kwargs) - except (ConfigParser.NoOptionError, ConfigParser.NoSectionError, + except (configparser.NoOptionError, configparser.NoSectionError, TypeError): return default def getboolean(self, section, option, *args, **kwargs): default = kwargs.pop('default', None) try: - return ConfigParser.RawConfigParser.getboolean( + return configparser.RawConfigParser.getboolean( self, section, option, *args, **kwargs) - except (ConfigParser.NoOptionError, ConfigParser.NoSectionError, + except (configparser.NoOptionError, configparser.NoSectionError, AttributeError): return default diff --git a/trytond/convert.py b/trytond/convert.py index 08b33d45ed..b5123f76af 100644 --- a/trytond/convert.py +++ b/trytond/convert.py @@ -5,7 +5,7 @@ import datetime from xml import sax import logging import re -from itertools import izip + from collections import defaultdict from decimal import Decimal @@ -356,13 +356,13 @@ class Fs2bdAccessor: self.browserecord[module].setdefault(model_name, {}) Model = self.pool.get(model_name) if not ids: - ids = self.browserecord[module][model_name].keys() + ids = list(self.browserecord[module][model_name].keys()) models = Model.browse(ids) for model in models: if model.id in self.browserecord[module][model_name]: for cache in Transaction().cache.values(): - for cache in (cache, cache.get('_language_cache', - {}).values()): + for cache in ( + cache, cache.get('_language_cache', {}).values()): if (model_name in cache and model.id in cache[model_name]): cache[model_name][model.id] = {} @@ -396,7 +396,7 @@ class Fs2bdAccessor: ('id', 'in', list(sub_record_ids)), ], order=[('id', 'ASC')]) with Transaction().set_context(language='en'): - models = Model.browse(map(int, records)) + models = Model.browse(list(map(int, records))) for model in models: self.browserecord[module][model_name][model.id] = model self.fetched_modules.append(module) @@ -492,10 +492,10 @@ class TrytondXmlHandler(sax.handler.ContentHandler): def endElement(self, name): if name == 'data' and self.grouped: - for model, values in self.grouped_creations.iteritems(): + for model, values in self.grouped_creations.items(): self.create_records(model, values.values(), values.keys()) self.grouped_creations.clear() - for key, actions in self.grouped_write.iteritems(): + for key, actions in self.grouped_write.items(): module, model = key self.write_records(module, model, *actions) self.grouped_write.clear() @@ -701,7 +701,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler): records = Model.create(vlist) mdata_values = [] - for record, values, fs_id in izip(records, vlist, fs_ids): + for record, values, fs_id in zip(records, vlist, fs_ids): for key in values: values[key] = self._clean_value(key, record) @@ -717,7 +717,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler): models_data = self.ModelData.create(mdata_values) - for record, values, fs_id, mdata in izip( + for record, values, fs_id, mdata in zip( records, vlist, fs_ids, models_data): self.fs2db.set(self.module, fs_id, { 'db_id': record.id, diff --git a/trytond/error.py b/trytond/error.py index 44995c0e12..a4a2594b6f 100644 --- a/trytond/error.py +++ b/trytond/error.py @@ -9,7 +9,7 @@ class WarningErrorMixin(object): @classmethod def _get_error_messages(cls): - return cls._error_messages.values() + return list(cls._error_messages.values()) @classmethod def raise_user_error(cls, error, error_args=None, diff --git a/trytond/exceptions.py b/trytond/exceptions.py index 5b90d7b151..74215dd092 100644 --- a/trytond/exceptions.py +++ b/trytond/exceptions.py @@ -1,13 +1,9 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -import sys class TrytonException(Exception): - - if sys.version_info < (3, ): - def __str__(self): - return unicode(self).encode('utf-8') + pass class UserError(TrytonException): @@ -19,7 +15,7 @@ class UserError(TrytonException): self.code = 1 def __unicode__(self): - return u'%s - %s' % (self.message, self.description) + return '%s - %s' % (self.message, self.description) class UserWarning(TrytonException): @@ -33,7 +29,7 @@ class UserWarning(TrytonException): self.code = 2 def __unicode__(self): - return u'%s - %s' % (self.message, self.description) + return '%s - %s' % (self.message, self.description) class LoginException(TrytonException): diff --git a/trytond/filestore.py b/trytond/filestore.py index 59fa8db442..f14e507ba7 100644 --- a/trytond/filestore.py +++ b/trytond/filestore.py @@ -32,7 +32,7 @@ class FileStore(object): filename = self._filename(id, prefix) dirname = os.path.dirname(filename) if not os.path.exists(dirname): - os.makedirs(dirname, 0770) + os.makedirs(dirname, 0o770) collision = 0 while True: diff --git a/trytond/ir/action.py b/trytond/ir/action.py index 65e767610b..2070cfc7ee 100644 --- a/trytond/ir/action.py +++ b/trytond/ir/action.py @@ -225,7 +225,7 @@ class ActionKeyword(ModelSQL, ModelView): for action_keyword in action_keywords: type_ = action_keyword.action.type types[type_].append(action_keyword.action.id) - for type_, action_ids in types.iteritems(): + for type_, action_ids in types.items(): for value in Action.get_action_values(type_, action_ids): value['keyword'] = keyword keywords.append(value) diff --git a/trytond/ir/attachment.py b/trytond/ir/attachment.py index 7ca225fe15..492224ac62 100644 --- a/trytond/ir/attachment.py +++ b/trytond/ir/attachment.py @@ -17,7 +17,7 @@ __all__ = [ def firstline(description): try: - return (x for x in description.splitlines() if x.strip()).next() + return next((x for x in description.splitlines() if x.strip())) except StopIteration: return '' diff --git a/trytond/ir/gen_time_locale.py b/trytond/ir/gen_time_locale.py index b5a0bbc6ff..bd5e403f8c 100644 --- a/trytond/ir/gen_time_locale.py +++ b/trytond/ir/gen_time_locale.py @@ -1,12 +1,13 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -import time -import locale import os import pprint +import time from lxml import etree +from . import locale + def locale_strftime(lang): time_locale = { @@ -34,6 +35,7 @@ def locale_strftime(lang): time_locale['%p'].append(time.strftime('%p', t).decode('utf-8')) return time_locale + if __name__ == '__main__': base = os.path.dirname(__file__) # Add en as it is not defined in lang.xml diff --git a/trytond/ir/lang.py b/trytond/ir/lang.py index b6e39231d0..fbf78b2be5 100644 --- a/trytond/ir/lang.py +++ b/trytond/ir/lang.py @@ -1,8 +1,6 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from __future__ import absolute_import -import sys import datetime import warnings warnings.filterwarnings('ignore', "", ImportWarning) @@ -213,8 +211,6 @@ class Lang(DeactivableMixin, ModelSQL, ModelView): ''' for lang in langs: date = lang.date - if sys.version_info < (3,): - date = date.encode('utf-8') try: datetime_strftime(datetime.datetime.now(), date) except Exception: @@ -482,13 +478,7 @@ class Lang(DeactivableMixin, ModelSQL, ModelView): format = format.replace('%p', TIME_LOCALE[code]['%p'][datetime.timetuple()[3] < 12 and 0 or 1]) - # Encode and decode under Python2 because strftime use bytes/str. - if sys.version_info < (3,): - format = format.encode('utf-8') - result = datetime_strftime(datetime, format) - if sys.version_info < (3,): - result = result.decode('utf-8') - return result + return datetime_strftime(datetime, format) def get_parent_language(code): diff --git a/trytond/ir/model.py b/trytond/ir/model.py index a69f3b3ec9..0b8d8d35fd 100644 --- a/trytond/ir/model.py +++ b/trytond/ir/model.py @@ -253,7 +253,7 @@ class ModelField(ModelSQL, ModelView): where=ir_model.model == model.__name__)) model_fields = {f['name']: f for f in cursor_dict(cursor)} - for field_name, field in model._fields.iteritems(): + for field_name, field in model._fields.items(): if hasattr(field, 'model_name'): relation = field.model_name elif hasattr(field, 'relation_name'): @@ -339,7 +339,7 @@ class ModelField(ModelSQL, ModelView): to_delete = [] if Transaction().context.get('language'): if fields_names is None: - fields_names = cls._fields.keys() + fields_names = list(cls._fields.keys()) if 'field_description' in fields_names \ or 'help' in fields_names: @@ -514,7 +514,7 @@ class ModelAccess(ModelSQL, ModelView): access.update(dict( (m, {'read': r, 'write': w, 'create': c, 'delete': d}) for m, r, w, c, d in cursor.fetchall())) - for model, maccess in access.iteritems(): + for model, maccess in access.items(): cls._get_access_cache.set((user, model), maccess) return access @@ -558,7 +558,7 @@ class ModelAccess(ModelSQL, ModelView): return True elif field._type == 'reference': selection = field.selection - if isinstance(selection, basestring): + if isinstance(selection, str): sel_func = getattr(Model, field.selection) if not is_instance_method(Model, field.selection): selection = sel_func() @@ -703,7 +703,7 @@ class ModelFieldAccess(ModelSQL, ModelView): group_by=[ir_model.model, model_field.name])) for m, f, r, w, c, d in cursor.fetchall(): accesses[m][f] = {'read': r, 'write': w, 'create': c, 'delete': d} - for model, maccesses in accesses.iteritems(): + for model, maccesses in accesses.items(): cls._get_access_cache.set((user, model), maccesses) return accesses @@ -723,7 +723,7 @@ class ModelFieldAccess(ModelSQL, ModelView): return True accesses = dict((f, a[mode]) - for f, a in cls.get_access([model_name])[model_name].iteritems()) + for f, a in cls.get_access([model_name])[model_name].items()) if access: return accesses for field in fields: @@ -1212,7 +1212,7 @@ class ModelData(ModelSQL, ModelView): @classmethod def dump_values(cls, values): return json.dumps( - sorted(values.iteritems()), cls=JSONEncoder, separators=(',', ':')) + sorted(values.items()), cls=JSONEncoder, separators=(',', ':')) @classmethod def load_values(cls, values): @@ -1245,7 +1245,7 @@ class ModelData(ModelSQL, ModelView): to_write.extend([[data], { 'values': cls.dump_values(fs_values), }]) - for Model, values_to_write in models_to_write.iteritems(): + for Model, values_to_write in models_to_write.items(): Model.write(*values_to_write) if to_write: cls.write(*to_write) diff --git a/trytond/ir/module.py b/trytond/ir/module.py index 414ae14979..ea42116fed 100644 --- a/trytond/ir/module.py +++ b/trytond/ir/module.py @@ -1,6 +1,5 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from __future__ import division from functools import wraps @@ -160,7 +159,7 @@ class Module(ModelSQL, ModelView): child_ids = dict((m.id, []) for m in modules) name2id = dict((m.name, m.id) for m in modules) childs = cls.search([ - ('dependencies.name', 'in', name2id.keys()), + ('dependencies.name', 'in', list(name2id.keys())), ]) for child in childs: for dep in child.dependencies: diff --git a/trytond/ir/rule.py b/trytond/ir/rule.py index e7789034f9..b1abbebcc4 100644 --- a/trytond/ir/rule.py +++ b/trytond/ir/rule.py @@ -237,11 +237,11 @@ class Rule(ModelSQL, ModelView): if fetchone: group_id = fetchone[0] clause[group_id] = [] - clause = clause.values() + clause = list(clause.values()) if clause: clause.insert(0, 'OR') - clause_global = clause_global.values() + clause_global = list(clause_global.values()) if clause_global: clause_global.insert(0, 'AND') diff --git a/trytond/ir/sequence.py b/trytond/ir/sequence.py index 988760451a..0a167a29f7 100644 --- a/trytond/ir/sequence.py +++ b/trytond/ir/sequence.py @@ -2,7 +2,7 @@ # this repository contains the full copyright notices and license terms. from string import Template import time -from itertools import izip + from sql import Literal, For from ..model import ModelView, ModelSQL, DeactivableMixin, fields, Check @@ -184,7 +184,7 @@ class Sequence(DeactivableMixin, ModelSQL, ModelView): @classmethod def create(cls, vlist): sequences = super(Sequence, cls).create(vlist) - for sequence, values in izip(sequences, vlist): + for sequence, values in zip(sequences, vlist): if sql_sequence and not cls._strict: sequence.update_sql_sequence(values.get('number_next', cls.default_number_next())) @@ -351,7 +351,7 @@ class Sequence(DeactivableMixin, ModelSQL, ModelView): ''' if isinstance(domain, cls): domain = domain.id - if isinstance(domain, (int, long)): + if isinstance(domain, int): domain = [('id', '=', domain)] # bypass rules on sequences diff --git a/trytond/ir/time_locale.py b/trytond/ir/time_locale.py index 0548a40f63..0f178ec2b3 100644 --- a/trytond/ir/time_locale.py +++ b/trytond/ir/time_locale.py @@ -2,587 +2,587 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of') # this repository contains the full copyright notices and license terms.') TIME_LOCALE = \ -{'bg': {'%A': [u'\u043f\u043e\u043d\u0435\u0434\u0435\u043b\u043d\u0438\u043a', - u'\u0432\u0442\u043e\u0440\u043d\u0438\u043a', - u'\u0441\u0440\u044f\u0434\u0430', - u'\u0447\u0435\u0442\u0432\u044a\u0440\u0442\u044a\u043a', - u'\u043f\u0435\u0442\u044a\u043a', - u'\u0441\u044a\u0431\u043e\u0442\u0430', - u'\u043d\u0435\u0434\u0435\u043b\u044f'], +{'bg': {'%A': ['\u043f\u043e\u043d\u0435\u0434\u0435\u043b\u043d\u0438\u043a', + '\u0432\u0442\u043e\u0440\u043d\u0438\u043a', + '\u0441\u0440\u044f\u0434\u0430', + '\u0447\u0435\u0442\u0432\u044a\u0440\u0442\u044a\u043a', + '\u043f\u0435\u0442\u044a\u043a', + '\u0441\u044a\u0431\u043e\u0442\u0430', + '\u043d\u0435\u0434\u0435\u043b\u044f'], '%B': [None, - u'\u044f\u043d\u0443\u0430\u0440\u0438', - u'\u0444\u0435\u0432\u0440\u0443\u0430\u0440\u0438', - u'\u043c\u0430\u0440\u0442', - u'\u0430\u043f\u0440\u0438\u043b', - u'\u043c\u0430\u0439', - u'\u044e\u043d\u0438', - u'\u044e\u043b\u0438', - u'\u0430\u0432\u0433\u0443\u0441\u0442', - u'\u0441\u0435\u043f\u0442\u0435\u043c\u0432\u0440\u0438', - u'\u043e\u043a\u0442\u043e\u043c\u0432\u0440\u0438', - u'\u043d\u043e\u0435\u043c\u0432\u0440\u0438', - u'\u0434\u0435\u043a\u0435\u043c\u0432\u0440\u0438'], - '%a': [u'\u043f\u043d', - u'\u0432\u0442', - u'\u0441\u0440', - u'\u0447\u0442', - u'\u043f\u0442', - u'\u0441\u0431', - u'\u043d\u0434'], + '\u044f\u043d\u0443\u0430\u0440\u0438', + '\u0444\u0435\u0432\u0440\u0443\u0430\u0440\u0438', + '\u043c\u0430\u0440\u0442', + '\u0430\u043f\u0440\u0438\u043b', + '\u043c\u0430\u0439', + '\u044e\u043d\u0438', + '\u044e\u043b\u0438', + '\u0430\u0432\u0433\u0443\u0441\u0442', + '\u0441\u0435\u043f\u0442\u0435\u043c\u0432\u0440\u0438', + '\u043e\u043a\u0442\u043e\u043c\u0432\u0440\u0438', + '\u043d\u043e\u0435\u043c\u0432\u0440\u0438', + '\u0434\u0435\u043a\u0435\u043c\u0432\u0440\u0438'], + '%a': ['\u043f\u043d', + '\u0432\u0442', + '\u0441\u0440', + '\u0447\u0442', + '\u043f\u0442', + '\u0441\u0431', + '\u043d\u0434'], '%b': [None, - u'\u044f\u043d\u0443', - u'\u0444\u0435\u0432', - u'\u043c\u0430\u0440', - u'\u0430\u043f\u0440', - u'\u043c\u0430\u0439', - u'\u044e\u043d\u0438', - u'\u044e\u043b\u0438', - u'\u0430\u0432\u0433', - u'\u0441\u0435\u043f', - u'\u043e\u043a\u0442', - u'\u043d\u043e\u0435', - u'\u0434\u0435\u043a'], - '%p': [u'', u'']}, - 'ca': {'%A': [u'dilluns', - u'dimarts', - u'dimecres', - u'dijous', - u'divendres', - u'dissabte', - u'diumenge'], + '\u044f\u043d\u0443', + '\u0444\u0435\u0432', + '\u043c\u0430\u0440', + '\u0430\u043f\u0440', + '\u043c\u0430\u0439', + '\u044e\u043d\u0438', + '\u044e\u043b\u0438', + '\u0430\u0432\u0433', + '\u0441\u0435\u043f', + '\u043e\u043a\u0442', + '\u043d\u043e\u0435', + '\u0434\u0435\u043a'], + '%p': ['', '']}, + 'ca': {'%A': ['dilluns', + 'dimarts', + 'dimecres', + 'dijous', + 'divendres', + 'dissabte', + 'diumenge'], '%B': [None, - u'gener', - u'febrer', - u'mar\xe7', - u'abril', - u'maig', - u'juny', - u'juliol', - u'agost', - u'setembre', - u'octubre', - u'novembre', - u'desembre'], - '%a': [u'dl', u'dt', u'dc', u'dj', u'dv', u'ds', u'dg'], + 'gener', + 'febrer', + 'mar\xe7', + 'abril', + 'maig', + 'juny', + 'juliol', + 'agost', + 'setembre', + 'octubre', + 'novembre', + 'desembre'], + '%a': ['dl', 'dt', 'dc', 'dj', 'dv', 'ds', 'dg'], '%b': [None, - u'gen', - u'feb', - u'mar', - u'abr', - u'mai', - u'jun', - u'jul', - u'ago', - u'set', - u'oct', - u'nov', - u'des'], - '%p': [u'', u'']}, - 'cs': {'%A': [u'Pond\u011bl\xed', - u'\xdater\xfd', - u'St\u0159eda', - u'\u010ctvrtek', - u'P\xe1tek', - u'Sobota', - u'Ned\u011ble'], + 'gen', + 'feb', + 'mar', + 'abr', + 'mai', + 'jun', + 'jul', + 'ago', + 'set', + 'oct', + 'nov', + 'des'], + '%p': ['', '']}, + 'cs': {'%A': ['Pond\u011bl\xed', + '\xdater\xfd', + 'St\u0159eda', + '\u010ctvrtek', + 'P\xe1tek', + 'Sobota', + 'Ned\u011ble'], '%B': [None, - u'leden', - u'\xfanor', - u'b\u0159ezen', - u'duben', - u'kv\u011bten', - u'\u010derven', - u'\u010dervenec', - u'srpen', - u'z\xe1\u0159\xed', - u'\u0159\xedjen', - u'listopad', - u'prosinec'], - '%a': [u'Po', u'\xdat', u'St', u'\u010ct', u'P\xe1', u'So', u'Ne'], + 'leden', + '\xfanor', + 'b\u0159ezen', + 'duben', + 'kv\u011bten', + '\u010derven', + '\u010dervenec', + 'srpen', + 'z\xe1\u0159\xed', + '\u0159\xedjen', + 'listopad', + 'prosinec'], + '%a': ['Po', '\xdat', 'St', '\u010ct', 'P\xe1', 'So', 'Ne'], '%b': [None, - u'led', - u'\xfano', - u'b\u0159e', - u'dub', - u'kv\u011b', - u'\u010den', - u'\u010dec', - u'srp', - u'z\xe1\u0159', - u'\u0159\xedj', - u'lis', - u'pro'], - '%p': [u'', u'']}, - 'de': {'%A': [u'Montag', - u'Dienstag', - u'Mittwoch', - u'Donnerstag', - u'Freitag', - u'Samstag', - u'Sonntag'], + 'led', + '\xfano', + 'b\u0159e', + 'dub', + 'kv\u011b', + '\u010den', + '\u010dec', + 'srp', + 'z\xe1\u0159', + '\u0159\xedj', + 'lis', + 'pro'], + '%p': ['', '']}, + 'de': {'%A': ['Montag', + 'Dienstag', + 'Mittwoch', + 'Donnerstag', + 'Freitag', + 'Samstag', + 'Sonntag'], '%B': [None, - u'Januar', - u'Februar', - u'M\xe4rz', - u'April', - u'Mai', - u'Juni', - u'Juli', - u'August', - u'September', - u'Oktober', - u'November', - u'Dezember'], - '%a': [u'Mo', u'Di', u'Mi', u'Do', u'Fr', u'Sa', u'So'], + 'Januar', + 'Februar', + 'M\xe4rz', + 'April', + 'Mai', + 'Juni', + 'Juli', + 'August', + 'September', + 'Oktober', + 'November', + 'Dezember'], + '%a': ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'], '%b': [None, - u'Jan', - u'Feb', - u'M\xe4r', - u'Apr', - u'Mai', - u'Jun', - u'Jul', - u'Aug', - u'Sep', - u'Okt', - u'Nov', - u'Dez'], - '%p': [u'', u'']}, - 'en': {'%A': [u'Monday', - u'Tuesday', - u'Wednesday', - u'Thursday', - u'Friday', - u'Saturday', - u'Sunday'], + 'Jan', + 'Feb', + 'M\xe4r', + 'Apr', + 'Mai', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Okt', + 'Nov', + 'Dez'], + '%p': ['', '']}, + 'en': {'%A': ['Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday'], '%B': [None, - u'January', - u'February', - u'March', - u'April', - u'May', - u'June', - u'July', - u'August', - u'September', - u'October', - u'November', - u'December'], - '%a': [u'Mon', u'Tue', u'Wed', u'Thu', u'Fri', u'Sat', u'Sun'], + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December'], + '%a': ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], '%b': [None, - u'Jan', - u'Feb', - u'Mar', - u'Apr', - u'May', - u'Jun', - u'Jul', - u'Aug', - u'Sep', - u'Oct', - u'Nov', - u'Dec'], - '%p': [u'AM', u'PM']}, - 'es': {'%A': [u'lunes', - u'martes', - u'mi\xe9rcoles', - u'jueves', - u'viernes', - u's\xe1bado', - u'domingo'], + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec'], + '%p': ['AM', 'PM']}, + 'es': {'%A': ['lunes', + 'martes', + 'mi\xe9rcoles', + 'jueves', + 'viernes', + 's\xe1bado', + 'domingo'], '%B': [None, - u'enero', - u'febrero', - u'marzo', - u'abril', - u'mayo', - u'junio', - u'julio', - u'agosto', - u'septiembre', - u'octubre', - u'noviembre', - u'diciembre'], - '%a': [u'lun', u'mar', u'mi\xe9', u'jue', u'vie', u's\xe1b', u'dom'], + 'enero', + 'febrero', + 'marzo', + 'abril', + 'mayo', + 'junio', + 'julio', + 'agosto', + 'septiembre', + 'octubre', + 'noviembre', + 'diciembre'], + '%a': ['lun', 'mar', 'mi\xe9', 'jue', 'vie', 's\xe1b', 'dom'], '%b': [None, - u'ene', - u'feb', - u'mar', - u'abr', - u'may', - u'jun', - u'jul', - u'ago', - u'sep', - u'oct', - u'nov', - u'dic'], - '%p': [u'', u'']}, - 'fr': {'%A': [u'lundi', - u'mardi', - u'mercredi', - u'jeudi', - u'vendredi', - u'samedi', - u'dimanche'], + 'ene', + 'feb', + 'mar', + 'abr', + 'may', + 'jun', + 'jul', + 'ago', + 'sep', + 'oct', + 'nov', + 'dic'], + '%p': ['', '']}, + 'fr': {'%A': ['lundi', + 'mardi', + 'mercredi', + 'jeudi', + 'vendredi', + 'samedi', + 'dimanche'], '%B': [None, - u'janvier', - u'f\xe9vrier', - u'mars', - u'avril', - u'mai', - u'juin', - u'juillet', - u'ao\xfbt', - u'septembre', - u'octobre', - u'novembre', - u'd\xe9cembre'], - '%a': [u'lun.', u'mar.', u'mer.', u'jeu.', u'ven.', u'sam.', u'dim.'], + 'janvier', + 'f\xe9vrier', + 'mars', + 'avril', + 'mai', + 'juin', + 'juillet', + 'ao\xfbt', + 'septembre', + 'octobre', + 'novembre', + 'd\xe9cembre'], + '%a': ['lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.', 'dim.'], '%b': [None, - u'janv.', - u'f\xe9vr.', - u'mars', - u'avril', - u'mai', - u'juin', - u'juil.', - u'ao\xfbt', - u'sept.', - u'oct.', - u'nov.', - u'd\xe9c.'], - '%p': [u'', u'']}, - 'hu': {'%A': [u'h\xe9tf\u0151', - u'kedd', - u'szerda', - u'cs\xfct\xf6rt\xf6k', - u'p\xe9ntek', - u'szombat', - u'vas\xe1rnap'], + 'janv.', + 'f\xe9vr.', + 'mars', + 'avril', + 'mai', + 'juin', + 'juil.', + 'ao\xfbt', + 'sept.', + 'oct.', + 'nov.', + 'd\xe9c.'], + '%p': ['', '']}, + 'hu': {'%A': ['h\xe9tf\u0151', + 'kedd', + 'szerda', + 'cs\xfct\xf6rt\xf6k', + 'p\xe9ntek', + 'szombat', + 'vas\xe1rnap'], '%B': [None, - u'janu\xe1r', - u'febru\xe1r', - u'm\xe1rcius', - u'\xe1prilis', - u'm\xe1jus', - u'j\xfanius', - u'j\xfalius', - u'augusztus', - u'szeptember', - u'okt\xf3ber', - u'november', - u'december'], - '%a': [u'h', u'k', u'sze', u'cs', u'p', u'szo', u'v'], + 'janu\xe1r', + 'febru\xe1r', + 'm\xe1rcius', + '\xe1prilis', + 'm\xe1jus', + 'j\xfanius', + 'j\xfalius', + 'augusztus', + 'szeptember', + 'okt\xf3ber', + 'november', + 'december'], + '%a': ['h', 'k', 'sze', 'cs', 'p', 'szo', 'v'], '%b': [None, - u'jan', - u'febr', - u'm\xe1rc', - u'\xe1pr', - u'm\xe1j', - u'j\xfan', - u'j\xfal', - u'aug', - u'szept', - u'okt', - u'nov', - u'dec'], - '%p': [u'', u'']}, - 'it': {'%A': [u'luned\xec', - u'marted\xec', - u'mercoled\xec', - u'gioved\xec', - u'venerd\xec', - u'sabato', - u'domenica'], + 'jan', + 'febr', + 'm\xe1rc', + '\xe1pr', + 'm\xe1j', + 'j\xfan', + 'j\xfal', + 'aug', + 'szept', + 'okt', + 'nov', + 'dec'], + '%p': ['', '']}, + 'it': {'%A': ['luned\xec', + 'marted\xec', + 'mercoled\xec', + 'gioved\xec', + 'venerd\xec', + 'sabato', + 'domenica'], '%B': [None, - u'gennaio', - u'febbraio', - u'marzo', - u'aprile', - u'maggio', - u'giugno', - u'luglio', - u'agosto', - u'settembre', - u'ottobre', - u'novembre', - u'dicembre'], - '%a': [u'lun', u'mar', u'mer', u'gio', u'ven', u'sab', u'dom'], + 'gennaio', + 'febbraio', + 'marzo', + 'aprile', + 'maggio', + 'giugno', + 'luglio', + 'agosto', + 'settembre', + 'ottobre', + 'novembre', + 'dicembre'], + '%a': ['lun', 'mar', 'mer', 'gio', 'ven', 'sab', 'dom'], '%b': [None, - u'gen', - u'feb', - u'mar', - u'apr', - u'mag', - u'giu', - u'lug', - u'ago', - u'set', - u'ott', - u'nov', - u'dic'], - '%p': [u'', u'']}, - 'lo': {'%A': [u'\u0e88\u0eb1\u0e99', - u'\u0ead\u0eb1\u0e87\u0e84\u0eb2\u0e99', - u'\u0e9e\u0eb8\u0e94', - u'\u0e9e\u0eb0\u0eab\u0eb1\u0e94', - u'\u0eaa\u0eb8\u0e81', - u'\u0ec0\u0eaa\u0ebb\u0eb2', - u'\u0ead\u0eb2\u0e97\u0eb4\u0e94'], + 'gen', + 'feb', + 'mar', + 'apr', + 'mag', + 'giu', + 'lug', + 'ago', + 'set', + 'ott', + 'nov', + 'dic'], + '%p': ['', '']}, + 'lo': {'%A': ['\u0e88\u0eb1\u0e99', + '\u0ead\u0eb1\u0e87\u0e84\u0eb2\u0e99', + '\u0e9e\u0eb8\u0e94', + '\u0e9e\u0eb0\u0eab\u0eb1\u0e94', + '\u0eaa\u0eb8\u0e81', + '\u0ec0\u0eaa\u0ebb\u0eb2', + '\u0ead\u0eb2\u0e97\u0eb4\u0e94'], '%B': [None, - u'\u0ea1\u0eb1\u0e87\u0e81\u0ead\u0e99', - u'\u0e81\u0eb8\u0ea1\u0e9f\u0eb2', - u'\u0ea1\u0eb5\u0e99\u0eb2', - u'\u0ec0\u0ea1\u0eaa\u0eb2', - u'\u0e9e\u0eb6\u0e94\u0eaa\u0eb0\u0e9e\u0eb2', - u'\u0ea1\u0eb4\u0e96\u0eb8\u0e99\u0eb2', - u'\u0e81\u0ecd\u0ea5\u0eb0\u0e81\u0ebb\u0e94', - u'\u0eaa\u0eb4\u0e87\u0eab\u0eb2', - u'\u0e81\u0eb1\u0e99\u0e8d\u0eb2', - u'\u0e95\u0eb8\u0ea5\u0eb2', - u'\u0e9e\u0eb0\u0e88\u0eb4\u0e81', - u'\u0e97\u0eb1\u0e99\u0ea7\u0eb2'], - '%a': [u'\u0e88.', - u'\u0e84.', - u'\u0e9e.', - u'\u0e9e\u0eab.', - u'\u0eaa.', - u'\u0eaa.', - u'\u0ead\u0eb2.'], + '\u0ea1\u0eb1\u0e87\u0e81\u0ead\u0e99', + '\u0e81\u0eb8\u0ea1\u0e9f\u0eb2', + '\u0ea1\u0eb5\u0e99\u0eb2', + '\u0ec0\u0ea1\u0eaa\u0eb2', + '\u0e9e\u0eb6\u0e94\u0eaa\u0eb0\u0e9e\u0eb2', + '\u0ea1\u0eb4\u0e96\u0eb8\u0e99\u0eb2', + '\u0e81\u0ecd\u0ea5\u0eb0\u0e81\u0ebb\u0e94', + '\u0eaa\u0eb4\u0e87\u0eab\u0eb2', + '\u0e81\u0eb1\u0e99\u0e8d\u0eb2', + '\u0e95\u0eb8\u0ea5\u0eb2', + '\u0e9e\u0eb0\u0e88\u0eb4\u0e81', + '\u0e97\u0eb1\u0e99\u0ea7\u0eb2'], + '%a': ['\u0e88.', + '\u0e84.', + '\u0e9e.', + '\u0e9e\u0eab.', + '\u0eaa.', + '\u0eaa.', + '\u0ead\u0eb2.'], '%b': [None, - u'\u0ea1.\u0e81.', - u'\u0e81.\u0e9e.', - u'\u0ea1.\u0e99.', - u'\u0ea1.\u0eaa.', - u'\u0e9e.\u0e9e.', - u'\u0ea1\u0eb4.\u0e96.', - u'\u0e81.\u0ea5.', - u'\u0eaa.\u0eab.', - u'\u0e81.\u0e8d.', - u'\u0e95.\u0ea5.', - u'\u0e9e.\u0e88.', - u'\u0e97.\u0ea7.'], - '%p': [u'AM', u'PM']}, - 'lt': {'%A': [u'Pirmadienis', - u'Antradienis', - u'Tre\u010diadienis', - u'Ketvirtadienis', - u'Penktadienis', - u'\u0160e\u0161tadienis', - u'Sekmadienis'], + '\u0ea1.\u0e81.', + '\u0e81.\u0e9e.', + '\u0ea1.\u0e99.', + '\u0ea1.\u0eaa.', + '\u0e9e.\u0e9e.', + '\u0ea1\u0eb4.\u0e96.', + '\u0e81.\u0ea5.', + '\u0eaa.\u0eab.', + '\u0e81.\u0e8d.', + '\u0e95.\u0ea5.', + '\u0e9e.\u0e88.', + '\u0e97.\u0ea7.'], + '%p': ['AM', 'PM']}, + 'lt': {'%A': ['Pirmadienis', + 'Antradienis', + 'Tre\u010diadienis', + 'Ketvirtadienis', + 'Penktadienis', + '\u0160e\u0161tadienis', + 'Sekmadienis'], '%B': [None, - u'sausio', - u'vasario', - u'kovo', - u'baland\u017eio', - u'gegu\u017e\u0117s', - u'bir\u017eelio', - u'liepos', - u'rugpj\u016b\u010dio', - u'rugs\u0117jo', - u'spalio', - u'lapkri\u010dio', - u'gruod\u017eio'], - '%a': [u'Pr', u'An', u'Tr', u'Kt', u'Pn', u'\u0160t', u'Sk'], + 'sausio', + 'vasario', + 'kovo', + 'baland\u017eio', + 'gegu\u017e\u0117s', + 'bir\u017eelio', + 'liepos', + 'rugpj\u016b\u010dio', + 'rugs\u0117jo', + 'spalio', + 'lapkri\u010dio', + 'gruod\u017eio'], + '%a': ['Pr', 'An', 'Tr', 'Kt', 'Pn', '\u0160t', 'Sk'], '%b': [None, - u'Sau', - u'Vas', - u'Kov', - u'Bal', - u'Geg', - u'Bir', - u'Lie', - u'Rgp', - u'Rgs', - u'Spa', - u'Lap', - u'Grd'], - '%p': [u'', u'']}, - 'nl': {'%A': [u'maandag', - u'dinsdag', - u'woensdag', - u'donderdag', - u'vrijdag', - u'zaterdag', - u'zondag'], + 'Sau', + 'Vas', + 'Kov', + 'Bal', + 'Geg', + 'Bir', + 'Lie', + 'Rgp', + 'Rgs', + 'Spa', + 'Lap', + 'Grd'], + '%p': ['', '']}, + 'nl': {'%A': ['maandag', + 'dinsdag', + 'woensdag', + 'donderdag', + 'vrijdag', + 'zaterdag', + 'zondag'], '%B': [None, - u'januari', - u'februari', - u'maart', - u'april', - u'mei', - u'juni', - u'juli', - u'augustus', - u'september', - u'oktober', - u'november', - u'december'], - '%a': [u'ma', u'di', u'wo', u'do', u'vr', u'za', u'zo'], + 'januari', + 'februari', + 'maart', + 'april', + 'mei', + 'juni', + 'juli', + 'augustus', + 'september', + 'oktober', + 'november', + 'december'], + '%a': ['ma', 'di', 'wo', 'do', 'vr', 'za', 'zo'], '%b': [None, - u'jan', - u'feb', - u'mrt', - u'apr', - u'mei', - u'jun', - u'jul', - u'aug', - u'sep', - u'okt', - u'nov', - u'dec'], - '%p': [u'', u'']}, - 'pt': {'%A': [u'Segunda', - u'Ter\xe7a', - u'Quarta', - u'Quinta', - u'Sexta', - u'S\xe1bado', - u'Domingo'], + 'jan', + 'feb', + 'mrt', + 'apr', + 'mei', + 'jun', + 'jul', + 'aug', + 'sep', + 'okt', + 'nov', + 'dec'], + '%p': ['', '']}, + 'pt': {'%A': ['Segunda', + 'Ter\xe7a', + 'Quarta', + 'Quinta', + 'Sexta', + 'S\xe1bado', + 'Domingo'], '%B': [None, - u'Janeiro', - u'Fevereiro', - u'Mar\xe7o', - u'Abril', - u'Maio', - u'Junho', - u'Julho', - u'Agosto', - u'Setembro', - u'Outubro', - u'Novembro', - u'Dezembro'], - '%a': [u'Seg', u'Ter', u'Qua', u'Qui', u'Sex', u'S\xe1b', u'Dom'], + 'Janeiro', + 'Fevereiro', + 'Mar\xe7o', + 'Abril', + 'Maio', + 'Junho', + 'Julho', + 'Agosto', + 'Setembro', + 'Outubro', + 'Novembro', + 'Dezembro'], + '%a': ['Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'S\xe1b', 'Dom'], '%b': [None, - u'Jan', - u'Fev', - u'Mar', - u'Abr', - u'Mai', - u'Jun', - u'Jul', - u'Ago', - u'Set', - u'Out', - u'Nov', - u'Dez'], - '%p': [u'', u'']}, - 'ru': {'%A': [u'\u041f\u043e\u043d\u0435\u0434\u0435\u043b\u044c\u043d\u0438\u043a', - u'\u0412\u0442\u043e\u0440\u043d\u0438\u043a', - u'\u0421\u0440\u0435\u0434\u0430', - u'\u0427\u0435\u0442\u0432\u0435\u0440\u0433', - u'\u041f\u044f\u0442\u043d\u0438\u0446\u0430', - u'\u0421\u0443\u0431\u0431\u043e\u0442\u0430', - u'\u0412\u043e\u0441\u043a\u0440\u0435\u0441\u0435\u043d\u044c\u0435'], + 'Jan', + 'Fev', + 'Mar', + 'Abr', + 'Mai', + 'Jun', + 'Jul', + 'Ago', + 'Set', + 'Out', + 'Nov', + 'Dez'], + '%p': ['', '']}, + 'ru': {'%A': ['\u041f\u043e\u043d\u0435\u0434\u0435\u043b\u044c\u043d\u0438\u043a', + '\u0412\u0442\u043e\u0440\u043d\u0438\u043a', + '\u0421\u0440\u0435\u0434\u0430', + '\u0427\u0435\u0442\u0432\u0435\u0440\u0433', + '\u041f\u044f\u0442\u043d\u0438\u0446\u0430', + '\u0421\u0443\u0431\u0431\u043e\u0442\u0430', + '\u0412\u043e\u0441\u043a\u0440\u0435\u0441\u0435\u043d\u044c\u0435'], '%B': [None, - u'\u042f\u043d\u0432\u0430\u0440\u044c', - u'\u0424\u0435\u0432\u0440\u0430\u043b\u044c', - u'\u041c\u0430\u0440\u0442', - u'\u0410\u043f\u0440\u0435\u043b\u044c', - u'\u041c\u0430\u0439', - u'\u0418\u044e\u043d\u044c', - u'\u0418\u044e\u043b\u044c', - u'\u0410\u0432\u0433\u0443\u0441\u0442', - u'\u0421\u0435\u043d\u0442\u044f\u0431\u0440\u044c', - u'\u041e\u043a\u0442\u044f\u0431\u0440\u044c', - u'\u041d\u043e\u044f\u0431\u0440\u044c', - u'\u0414\u0435\u043a\u0430\u0431\u0440\u044c'], - '%a': [u'\u041f\u043d', - u'\u0412\u0442', - u'\u0421\u0440', - u'\u0427\u0442', - u'\u041f\u0442', - u'\u0421\u0431', - u'\u0412\u0441'], + '\u042f\u043d\u0432\u0430\u0440\u044c', + '\u0424\u0435\u0432\u0440\u0430\u043b\u044c', + '\u041c\u0430\u0440\u0442', + '\u0410\u043f\u0440\u0435\u043b\u044c', + '\u041c\u0430\u0439', + '\u0418\u044e\u043d\u044c', + '\u0418\u044e\u043b\u044c', + '\u0410\u0432\u0433\u0443\u0441\u0442', + '\u0421\u0435\u043d\u0442\u044f\u0431\u0440\u044c', + '\u041e\u043a\u0442\u044f\u0431\u0440\u044c', + '\u041d\u043e\u044f\u0431\u0440\u044c', + '\u0414\u0435\u043a\u0430\u0431\u0440\u044c'], + '%a': ['\u041f\u043d', + '\u0412\u0442', + '\u0421\u0440', + '\u0427\u0442', + '\u041f\u0442', + '\u0421\u0431', + '\u0412\u0441'], '%b': [None, - u'\u044f\u043d\u0432', - u'\u0444\u0435\u0432', - u'\u043c\u0430\u0440', - u'\u0430\u043f\u0440', - u'\u043c\u0430\u0439', - u'\u0438\u044e\u043d', - u'\u0438\u044e\u043b', - u'\u0430\u0432\u0433', - u'\u0441\u0435\u043d', - u'\u043e\u043a\u0442', - u'\u043d\u043e\u044f', - u'\u0434\u0435\u043a'], - '%p': [u'', u'']}, - 'sl': {'%A': [u'ponedeljek', - u'torek', - u'sreda', - u'\u010detrtek', - u'petek', - u'sobota', - u'nedelja'], + '\u044f\u043d\u0432', + '\u0444\u0435\u0432', + '\u043c\u0430\u0440', + '\u0430\u043f\u0440', + '\u043c\u0430\u0439', + '\u0438\u044e\u043d', + '\u0438\u044e\u043b', + '\u0430\u0432\u0433', + '\u0441\u0435\u043d', + '\u043e\u043a\u0442', + '\u043d\u043e\u044f', + '\u0434\u0435\u043a'], + '%p': ['', '']}, + 'sl': {'%A': ['ponedeljek', + 'torek', + 'sreda', + '\u010detrtek', + 'petek', + 'sobota', + 'nedelja'], '%B': [None, - u'januar', - u'februar', - u'marec', - u'april', - u'maj', - u'junij', - u'julij', - u'avgust', - u'september', - u'oktober', - u'november', - u'december'], - '%a': [u'pon', u'tor', u'sre', u'\u010det', u'pet', u'sob', u'ned'], + 'januar', + 'februar', + 'marec', + 'april', + 'maj', + 'junij', + 'julij', + 'avgust', + 'september', + 'oktober', + 'november', + 'december'], + '%a': ['pon', 'tor', 'sre', '\u010det', 'pet', 'sob', 'ned'], '%b': [None, - u'jan', - u'feb', - u'mar', - u'apr', - u'maj', - u'jun', - u'jul', - u'avg', - u'sep', - u'okt', - u'nov', - u'dec'], - '%p': [u'', u'']}, - 'zh': {'%A': [u'\u661f\u671f\u4e00', - u'\u661f\u671f\u4e8c', - u'\u661f\u671f\u4e09', - u'\u661f\u671f\u56db', - u'\u661f\u671f\u4e94', - u'\u661f\u671f\u516d', - u'\u661f\u671f\u65e5'], + 'jan', + 'feb', + 'mar', + 'apr', + 'maj', + 'jun', + 'jul', + 'avg', + 'sep', + 'okt', + 'nov', + 'dec'], + '%p': ['', '']}, + 'zh': {'%A': ['\u661f\u671f\u4e00', + '\u661f\u671f\u4e8c', + '\u661f\u671f\u4e09', + '\u661f\u671f\u56db', + '\u661f\u671f\u4e94', + '\u661f\u671f\u516d', + '\u661f\u671f\u65e5'], '%B': [None, - u'\u4e00\u6708', - u'\u4e8c\u6708', - u'\u4e09\u6708', - u'\u56db\u6708', - u'\u4e94\u6708', - u'\u516d\u6708', - u'\u4e03\u6708', - u'\u516b\u6708', - u'\u4e5d\u6708', - u'\u5341\u6708', - u'\u5341\u4e00\u6708', - u'\u5341\u4e8c\u6708'], - '%a': [u'\u4e00', - u'\u4e8c', - u'\u4e09', - u'\u56db', - u'\u4e94', - u'\u516d', - u'\u65e5'], + '\u4e00\u6708', + '\u4e8c\u6708', + '\u4e09\u6708', + '\u56db\u6708', + '\u4e94\u6708', + '\u516d\u6708', + '\u4e03\u6708', + '\u516b\u6708', + '\u4e5d\u6708', + '\u5341\u6708', + '\u5341\u4e00\u6708', + '\u5341\u4e8c\u6708'], + '%a': ['\u4e00', + '\u4e8c', + '\u4e09', + '\u56db', + '\u4e94', + '\u516d', + '\u65e5'], '%b': [None, - u'1\u6708', - u'2\u6708', - u'3\u6708', - u'4\u6708', - u'5\u6708', - u'6\u6708', - u'7\u6708', - u'8\u6708', - u'9\u6708', - u'10\u6708', - u'11\u6708', - u'12\u6708'], - '%p': [u'\u4e0a\u5348', u'\u4e0b\u5348']}} + '1\u6708', + '2\u6708', + '3\u6708', + '4\u6708', + '5\u6708', + '6\u6708', + '7\u6708', + '8\u6708', + '9\u6708', + '10\u6708', + '11\u6708', + '12\u6708'], + '%p': ['\u4e0a\u5348', '\u4e0b\u5348']}} diff --git a/trytond/ir/translation.py b/trytond/ir/translation.py index a57c5ab18d..556f8bc4c8 100644 --- a/trytond/ir/translation.py +++ b/trytond/ir/translation.py @@ -1,14 +1,13 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -import polib +import os import xml.dom.minidom from difflib import SequenceMatcher -import os from hashlib import md5 -from lxml import etree -from itertools import izip from io import BytesIO +from lxml import etree +import polib from sql import Column, Null, Literal from sql.functions import Substring, Position from sql.conditionals import Case @@ -259,7 +258,7 @@ class Translation(ModelSQL, ModelView): [[trans_name, 'en', 'selection', val, val_md5, '', module_name, False, -1]])) - for field_name, field in model._fields.iteritems(): + for field_name, field in model._fields.items(): trans_name = model.__name__ + ',' + field_name update_insert_field(field, trans_name) update_insert_help(field, trans_name) @@ -326,7 +325,7 @@ class Translation(ModelSQL, ModelView): where=ir_translation.id == trans_buttons[trans_name]['id'])) - for state_name, state in wizard.states.iteritems(): + for state_name, state in wizard.states.items(): if not isinstance(state, StateView): continue for button in state.buttons: @@ -395,20 +394,20 @@ class Translation(ModelSQL, ModelView): Model = pool.get('ir.model') translations, to_fetch = {}, [] - name = unicode(name) - ttype = unicode(ttype) - lang = unicode(lang) + name = str(name) + ttype = str(ttype) + lang = str(lang) if name.split(',')[0] in ('ir.model.field', 'ir.model'): field_name = name.split(',')[1] with Transaction().set_context(_check_access=False): if name.split(',')[0] == 'ir.model.field': if field_name == 'field_description': - ttype = u'field' + ttype = 'field' else: - ttype = u'help' + ttype = 'help' records = ModelFields.browse(ids) else: - ttype = u'model' + ttype = 'model' records = Model.browse(ids) trans_args = [] @@ -524,7 +523,7 @@ class Translation(ModelSQL, ModelView): translations[translation.name] = translation to_save = [] - for record, value in izip(records, values): + for record, value in zip(records, values): translation = translations.get(get_name(record)) if not translation: translation = cls() @@ -565,7 +564,7 @@ class Translation(ModelSQL, ModelView): ).append(translation) to_save = [] - for record, value in izip(records, values): + for record, value in zip(records, values): translation = translations.get(record.id) if not translation: translation = cls() @@ -632,11 +631,11 @@ class Translation(ModelSQL, ModelView): to_cache = [] for name, ttype, lang, source in args: - name = unicode(name) - ttype = unicode(ttype) - lang = unicode(lang) + name = str(name) + ttype = str(ttype) + lang = str(lang) if source is not None: - source = unicode(source) + source = str(source) trans = cls._translation_cache.get((name, ttype, lang, source), -1) if trans != -1: res[(name, ttype, lang, source)] = trans @@ -749,7 +748,7 @@ class Translation(ModelSQL, ModelView): def translation_import(cls, lang, module, po_path): pool = Pool() ModelData = pool.get('ir.model.data') - if isinstance(po_path, basestring): + if isinstance(po_path, str): po_path = [po_path] models_data = ModelData.search([ ('module', '=', module), @@ -873,7 +872,7 @@ class Translation(ModelSQL, ModelView): to_save.append(old_translation) else: translations.add(old_translation) - cls.save(filter(None, to_save)) + cls.save([_f for _f in to_save if _f]) translations |= set(to_save) if translations: @@ -939,7 +938,7 @@ class Translation(ModelSQL, ModelView): if pofile: pofile.sort() - return unicode(pofile).encode('utf-8') + return str(pofile).encode('utf-8') else: return @@ -1392,15 +1391,15 @@ class TranslationClean(Wizard): except KeyError: pass if Model: - errors = Model._error_messages.values() + errors = list(Model._error_messages.values()) if issubclass(Model, ModelSQL): - errors += Model._sql_error_messages.values() + errors += list(Model._sql_error_messages.values()) for _, _, error in Model._sql_constraints: errors.append(error) if translation.src not in errors: return True elif Wizard: - errors = Wizard._error_messages.values() + errors = list(Wizard._error_messages.values()) if translation.src not in errors: return True else: diff --git a/trytond/ir/trigger.py b/trytond/ir/trigger.py index 603a138f65..16b9a3a44c 100644 --- a/trytond/ir/trigger.py +++ b/trytond/ir/trigger.py @@ -159,7 +159,7 @@ class Trigger(DeactivableMixin, ModelSQL, ModelView): ('model.model', '=', model_name), ('on_%s' % mode, '=', True), ]) - cls._get_triggers_cache.set(key, map(int, triggers)) + cls._get_triggers_cache.set(key, list(map(int, triggers))) return triggers @staticmethod @@ -185,7 +185,7 @@ class Trigger(DeactivableMixin, ModelSQL, ModelView): ActionModel = pool.get(trigger.action_model.model) cursor = Transaction().connection.cursor() trigger_log = TriggerLog.__table__() - ids = map(int, records) + ids = list(map(int, records)) # Filter on limit_number if trigger.limit_number: @@ -222,12 +222,12 @@ class Trigger(DeactivableMixin, ModelSQL, ModelView): new_ids.append(record_id) continue # SQLite return string for MAX - if isinstance(delay[record_id], basestring): + if isinstance(delay[record_id], str): datepart, timepart = delay[record_id].split(" ") year, month, day = map(int, datepart.split("-")) timepart_full = timepart.split(".") - hours, minutes, seconds = map(int, - timepart_full[0].split(":")) + hours, minutes, seconds = map( + int, timepart_full[0].split(":")) if len(timepart_full) == 2: microseconds = int(timepart_full[1]) else: diff --git a/trytond/ir/ui/icon.py b/trytond/ir/ui/icon.py index 31f148484e..fa824013ad 100644 --- a/trytond/ir/ui/icon.py +++ b/trytond/ir/ui/icon.py @@ -46,7 +46,7 @@ class Icon(ModelSQL, ModelView): order=[('sequence', 'ASC'), ('id', 'ASC')])): if icon.name not in icons: icons[icon.name] = icon.id - return sorted((icon_id, name) for name, icon_id in icons.iteritems()) + return sorted((icon_id, name) for name, icon_id in icons.items()) def get_icon(self, name): path = os.path.join(self.module, self.path.replace('/', os.sep)) diff --git a/trytond/ir/ui/menu.py b/trytond/ir/ui/menu.py index c7cfe6d48e..92bbdde75b 100644 --- a/trytond/ir/ui/menu.py +++ b/trytond/ir/ui/menu.py @@ -165,7 +165,7 @@ class UIMenu(DeactivableMixin, sequence_ordered(), tree(separator=' / '), action2keyword = {k.action.id: k for k in action_keywords} with Transaction().set_context(active_test=False): factions = Action.search([ - ('action', 'in', action2keyword.keys()), + ('action', 'in', list(action2keyword.keys())), ]) for action in factions: model = action2keyword[action.id].model @@ -189,7 +189,7 @@ class UIMenu(DeactivableMixin, sequence_ordered(), tree(separator=' / '), ActionKeyword.delete(action_keywords) if not value: return - if isinstance(value, basestring): + if isinstance(value, str): action_type, action_id = value.split(',') else: action_type, action_id = value diff --git a/trytond/ir/ui/view.py b/trytond/ir/ui/view.py index b70e7f2f59..6ad996a50f 100644 --- a/trytond/ir/ui/view.py +++ b/trytond/ir/ui/view.py @@ -1,7 +1,6 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import os -import sys import logging import json @@ -118,11 +117,7 @@ class View(ModelSQL, ModelView): key = (cls.__name__, type_) rng = cls._get_rng_cache.get(key) if rng is None: - if sys.version_info < (3,): - filename = __file__.decode(sys.getfilesystemencoding()) - else: - filename = __file__ - rng_name = os.path.join(os.path.dirname(filename), type_ + '.rng') + rng_name = os.path.join(os.path.dirname(__file__), type_ + '.rng') with open(rng_name, 'rb') as fp: rng = etree.fromstring(fp.read()) cls._get_rng_cache.set(key, rng) @@ -173,7 +168,7 @@ class View(ModelSQL, ModelView): try: value = PYSONDecoder().decode(element.get(attr)) validates.get(attr, lambda a: True)(value) - except Exception, e: + except Exception as e: error_log = '%s: <%s %s="%s"/>' % ( e, element.get('id') or element.get('name'), attr, element.get(attr)) @@ -298,12 +293,12 @@ class ViewTreeWidth(ModelSQL, ModelView): records = cls.search([ ('user', '=', Transaction().user), ('model', '=', model), - ('field', 'in', fields.keys()), + ('field', 'in', list(fields.keys())), ]) cls.delete(records) to_create = [] - for field in fields.keys(): + for field in list(fields.keys()): to_create.append({ 'model': model, 'field': field, diff --git a/trytond/model/descriptors.py b/trytond/model/descriptors.py index eace007e32..64e29e0ee4 100644 --- a/trytond/model/descriptors.py +++ b/trytond/model/descriptors.py @@ -13,7 +13,7 @@ class dualmethod(object): >>> class Example(object): ... @dualmethod ... def method(cls, instances): - ... print len(instances) + ... print(len(instances)) ... >>> Example.method([Example()]) 1 diff --git a/trytond/model/dictschema.py b/trytond/model/dictschema.py index 16fc34f36d..e84255a21b 100644 --- a/trytond/model/dictschema.py +++ b/trytond/model/dictschema.py @@ -80,7 +80,7 @@ class DictSchemaMixin(object): selection = OrderedDict(json.loads( english_key.selection_json)) selection.update(dict(json.loads(record.selection_json))) - new_key['selection'] = selection.items() + new_key['selection'] = list(selection.items()) new_key['sorted'] = record.selection_sorted elif record.type_ in ('float', 'numeric'): new_key['digits'] = (16, record.digits) diff --git a/trytond/model/fields/binary.py b/trytond/model/fields/binary.py index 55f5249b37..91a77d29ee 100644 --- a/trytond/model/fields/binary.py +++ b/trytond/model/fields/binary.py @@ -86,7 +86,7 @@ class Binary(Field): continue value = i[name] if value: - if isinstance(value, unicode): + if isinstance(value, str): value = value.encode('utf-8') value = converter(value) else: diff --git a/trytond/model/fields/char.py b/trytond/model/fields/char.py index dfef87711d..1231558d53 100644 --- a/trytond/model/fields/char.py +++ b/trytond/model/fields/char.py @@ -1,6 +1,5 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -import sys import warnings from trytond.transaction import Transaction @@ -51,9 +50,7 @@ class Char(FieldTranslate): def sql_format(self, value): if value is None: return None - elif isinstance(value, str) and sys.version_info < (3,): - return unicode(value, 'utf-8') - assert isinstance(value, unicode) + assert isinstance(value, str) return value @property diff --git a/trytond/model/fields/date.py b/trytond/model/fields/date.py index cff91df8bd..f5c214d761 100644 --- a/trytond/model/fields/date.py +++ b/trytond/model/fields/date.py @@ -32,8 +32,8 @@ class Date(Field): def sql_format(self, value): if value is None: return None - if isinstance(value, basestring): - year, month, day = map(int, value.split("-", 2)) + if isinstance(value, str): + year, month, day = list(map(int, value.split("-", 2))) return datetime.date(year, month, day) if (not isinstance(value, datetime.date) @@ -77,7 +77,7 @@ class DateTime(Field): def sql_format(self, value): if not value: return None - if isinstance(value, basestring): + if isinstance(value, str): datepart, timepart = value.split(" ") year, month, day = map(int, datepart.split("-", 2)) hours, minutes, seconds = map(int, timepart.split(":")) @@ -103,7 +103,7 @@ class Timestamp(Field): def sql_format(self, value): if value is None: return None - if isinstance(value, basestring): + if isinstance(value, str): datepart, timepart = value.split(" ") year, month, day = map(int, datepart.split("-", 2)) timepart_full = timepart.split(".", 1) @@ -130,7 +130,7 @@ class Time(DateTime): def sql_format(self, value): if value is None: return None - if isinstance(value, basestring): + if isinstance(value, str): hours, minutes, seconds = map(int, value.split(":")) return datetime.time(hours, minutes, seconds) if not isinstance(value, datetime.time): diff --git a/trytond/model/fields/dict.py b/trytond/model/fields/dict.py index 1f61d26e33..474f2a2e17 100644 --- a/trytond/model/fields/dict.py +++ b/trytond/model/fields/dict.py @@ -28,7 +28,7 @@ class Dict(Field): data = value[name] if data: # If stored as JSON conversion is done on backend - if isinstance(data, basestring): + if isinstance(data, str): data = json.loads(data, object_hook=JSONDecoder()) dicts[value['id']] = data return dicts @@ -86,4 +86,4 @@ class TranslatedDict(object): elif self.type_ == 'values': trans = {k['name']: dict(k['selection']) for k in keys} return {k: v if k not in trans else trans[k].get(v, v) - for k, v in value.iteritems()} + for k, v in value.items()} diff --git a/trytond/model/fields/field.py b/trytond/model/fields/field.py index a4c329338a..5c5ebfa85b 100644 --- a/trytond/model/fields/field.py +++ b/trytond/model/fields/field.py @@ -25,13 +25,13 @@ def domain_validate(value): def test_domain(dom): for arg in dom: - if isinstance(arg, basestring): + if isinstance(arg, str): if arg not in ('AND', 'OR'): return False elif (isinstance(arg, tuple) or (isinstance(arg, list) and len(arg) > 2 - and ((isinstance(arg[1], basestring) + and ((isinstance(arg[1], str) and arg[1] in OPERATORS) or (isinstance(arg[1], PYSON) and arg[1].types() == set([str]))))): diff --git a/trytond/model/fields/float.py b/trytond/model/fields/float.py index 0fb103903d..cf85d885b3 100644 --- a/trytond/model/fields/float.py +++ b/trytond/model/fields/float.py @@ -8,10 +8,10 @@ def digits_validate(value): if value: assert isinstance(value, tuple), 'digits must be a tuple' for i in value: - assert isinstance(i, (int, long, PYSON)), \ + assert isinstance(i, (int, PYSON)), \ 'digits must be tuple of integers or PYSON' if isinstance(i, PYSON): - assert i.types().issubset(set([int, long])), \ + assert i.types().issubset(set([int, int])), \ 'PYSON digits must return an integer' diff --git a/trytond/model/fields/function.py b/trytond/model/fields/function.py index 9b6f62ec9a..9f091ab490 100644 --- a/trytond/model/fields/function.py +++ b/trytond/model/fields/function.py @@ -87,6 +87,8 @@ class Function(Field): with Transaction().set_context(_check_access=False): method = getattr(Model, self.getter) instance_method = is_instance_method(Model, self.getter) + signature = inspect.signature(method) + uses_names = 'names' in signature.parameters def call(name): records = Model.browse(ids) @@ -96,13 +98,11 @@ class Function(Field): return dict((r.id, method(r, name)) for r in records) if isinstance(name, list): names = name - # Test is the function works with a list of names - if 'names' in inspect.getargspec(method)[0]: + if uses_names: return call(names) return dict((name, call(name)) for name in names) else: - # Test is the function works with a list of names - if 'names' in inspect.getargspec(method)[0]: + if uses_names: name = [name] return call(name) diff --git a/trytond/model/fields/many2many.py b/trytond/model/fields/many2many.py index 90645f4d8c..4f905dbded 100644 --- a/trytond/model/fields/many2many.py +++ b/trytond/model/fields/many2many.py @@ -143,7 +143,7 @@ class Many2Many(Field): for relation in relations: origin_id = getattr(relation, self.origin).id res[origin_id].append(getattr(relation, self.target).id) - return dict((key, tuple(value)) for key, value in res.iteritems()) + return dict((key, tuple(value)) for key, value in res.items()) def set(self, Model, name, ids, values, *args): ''' @@ -196,7 +196,7 @@ class Many2Many(Field): target_to_delete.extend(Target.browse(target_ids)) def add(ids, target_ids): - target_ids = map(int, target_ids) + target_ids = list(map(int, target_ids)) if not target_ids: return existing_ids = set() @@ -219,7 +219,7 @@ class Many2Many(Field): }) def remove(ids, target_ids): - target_ids = map(int, target_ids) + target_ids = list(map(int, target_ids)) if not target_ids: return for sub_ids in grouped_slice(target_ids): @@ -229,7 +229,7 @@ class Many2Many(Field): ])) def copy(ids, copy_ids, default=None): - copy_ids = map(int, copy_ids) + copy_ids = list(map(int, copy_ids)) if default is None: default = {} @@ -367,7 +367,7 @@ class Many2Many(Field): if operator.startswith('not'): return ~expression return expression - if isinstance(value, basestring): + if isinstance(value, str): target_domain = [('rec_name', 'ilike', value)] if self.filter: target_domain.append(self.filter) @@ -406,7 +406,7 @@ class Many2Many(Field): return ~expression return expression else: - if isinstance(value, basestring): + if isinstance(value, str): target_name = 'rec_name' else: target_name = 'id' diff --git a/trytond/model/fields/many2one.py b/trytond/model/fields/many2one.py index 3d322d5bbe..a2987ee306 100644 --- a/trytond/model/fields/many2one.py +++ b/trytond/model/fields/many2one.py @@ -101,7 +101,7 @@ class Many2One(Field): Target = self.get_target() if isinstance(value, dict): value = Target(**value) - elif isinstance(value, (int, long)): + elif isinstance(value, int): value = Target(value) assert isinstance(value, (Target, type(None))) super(Many2One, self).__set__(inst, value) @@ -184,7 +184,7 @@ class Many2One(Field): return ~expression return expression - if isinstance(value, basestring): + if isinstance(value, str): targets = Target.search([('rec_name', 'ilike', value)], order=[]) ids = [t.id for t in targets] @@ -212,7 +212,7 @@ class Many2One(Field): return ~expression return expression - if not isinstance(value, basestring): + if not isinstance(value, str): return super(Many2One, self).convert_domain(domain, tables, Model) else: diff --git a/trytond/model/fields/numeric.py b/trytond/model/fields/numeric.py index 3df905b0fd..988b587c99 100644 --- a/trytond/model/fields/numeric.py +++ b/trytond/model/fields/numeric.py @@ -24,7 +24,7 @@ class Numeric(Float): def sql_format(self, value): if value is None: return None - if isinstance(value, (int, long)): + if isinstance(value, int): value = Decimal(str(value)) value = Decimal(value) return value diff --git a/trytond/model/fields/one2many.py b/trytond/model/fields/one2many.py index a248a73e39..87737808e1 100644 --- a/trytond/model/fields/one2many.py +++ b/trytond/model/fields/one2many.py @@ -140,7 +140,7 @@ class One2Many(Field): for target in targets: origin_id = getattr(target, self.field).id res[origin_id].append(target.id) - return dict((key, tuple(value)) for key, value in res.iteritems()) + return dict((key, tuple(value)) for key, value in res.items()) def set(self, Model, name, ids, values, *args): ''' @@ -189,7 +189,7 @@ class One2Many(Field): to_delete.extend(Target.browse(target_ids)) def add(ids, target_ids): - target_ids = map(int, target_ids) + target_ids = list(map(int, target_ids)) if not target_ids: return targets = Target.browse(target_ids) @@ -199,7 +199,7 @@ class One2Many(Field): })) def remove(ids, target_ids): - target_ids = map(int, target_ids) + target_ids = list(map(int, target_ids)) if not target_ids: return for sub_ids in grouped_slice(target_ids): @@ -212,7 +212,7 @@ class One2Many(Field): })) def copy(ids, copy_ids, default=None): - copy_ids = map(int, copy_ids) + copy_ids = list(map(int, copy_ids)) if default is None: default = {} @@ -297,7 +297,7 @@ class One2Many(Field): return ~expression return expression else: - if isinstance(value, basestring): + if isinstance(value, str): target_name = 'rec_name' else: target_name = 'id' diff --git a/trytond/model/fields/one2one.py b/trytond/model/fields/one2one.py index c32ffb0c8a..8bdb6723d4 100644 --- a/trytond/model/fields/one2one.py +++ b/trytond/model/fields/one2one.py @@ -23,7 +23,7 @@ class One2One(Many2Many): :return: a dictionary with ids as key and target id as value ''' res = super(One2One, self).get(ids, model, name, values=values) - for i, vals in res.iteritems(): + for i, vals in res.items(): res[i] = vals[0] if vals else None return res @@ -57,7 +57,7 @@ class One2One(Many2Many): Target = self.get_target() if isinstance(value, dict): value = Target(*value) - elif isinstance(value, (int, long)): + elif isinstance(value, int): value = Target(value) assert isinstance(value, (Target, type(None))) Field.__set__(self, inst, value) diff --git a/trytond/model/fields/reference.py b/trytond/model/fields/reference.py index 08909e9391..858845df58 100644 --- a/trytond/model/fields/reference.py +++ b/trytond/model/fields/reference.py @@ -119,7 +119,7 @@ class Reference(Field, SelectionMixin): # Check if reference ids still exist with Transaction().set_context(active_test=False), \ Transaction().set_context(_check_access=False): - for ref_model, (ref_ids, ids) in ref_to_check.iteritems(): + for ref_model, (ref_ids, ids) in ref_to_check.items(): try: pool.get(ref_model) except KeyError: @@ -129,7 +129,7 @@ class Reference(Field, SelectionMixin): refs = Ref.search([ ('id', 'in', list(ref_ids)), ], order=[]) - refs = map(str, refs) + refs = list(map(str, refs)) for i in ids: if res[i] not in refs: res[i] = None @@ -138,7 +138,7 @@ class Reference(Field, SelectionMixin): def __set__(self, inst, value): from ..model import Model if not isinstance(value, (Model, type(None))): - if isinstance(value, basestring): + if isinstance(value, str): target, value = value.split(',') else: target, value = value @@ -150,7 +150,7 @@ class Reference(Field, SelectionMixin): super(Reference, self).__set__(inst, value) def sql_format(self, value): - if not isinstance(value, (basestring, Query, Expression)): + if not isinstance(value, (str, Query, Expression)): try: value = '%s,%s' % tuple(value) except TypeError: diff --git a/trytond/model/match.py b/trytond/model/match.py index f4e7f89988..c8419dfa0f 100644 --- a/trytond/model/match.py +++ b/trytond/model/match.py @@ -8,7 +8,7 @@ class MatchMixin(object): '''Match on pattern pattern is a dictionary with model field as key and matching value as value''' - for field, pattern_value in pattern.iteritems(): + for field, pattern_value in pattern.items(): value = getattr(self, field) if not match_none and value is None: continue diff --git a/trytond/model/model.py b/trytond/model/model.py index b8236df227..1b36b2582d 100644 --- a/trytond/model/model.py +++ b/trytond/model/model.py @@ -66,7 +66,7 @@ class Model(WarningErrorMixin, URLMixin, PoolBase): # Set _defaults cls._defaults = {} - fields_names = cls._fields.keys() + fields_names = list(cls._fields.keys()) for field_name in fields_names: default_method = getattr(cls, 'default_%s' % field_name, False) if isinstance(default_method, collections.Callable): @@ -78,7 +78,7 @@ class Model(WarningErrorMixin, URLMixin, PoolBase): % (cls.__name__, k,) # Set name to fields - for name, field in cls._fields.iteritems(): + for name, field in cls._fields.items(): if field.name is None: field.name = name else: @@ -148,7 +148,7 @@ class Model(WarningErrorMixin, URLMixin, PoolBase): value[field_name]).rec_name if not with_rec_name: - for field in value.keys(): + for field in list(value.keys()): if field.endswith('.rec_name'): del value[field] return value @@ -306,7 +306,7 @@ class Model(WarningErrorMixin, URLMixin, PoolBase): if res[field]['type'] == 'many2one': target = cls._fields[field].get_target() relation_fields = [] - for target_name, target_field in target._fields.iteritems(): + for target_name, target_field in target._fields.items(): if (target_field._type == 'one2many' and target_field.model_name == cls.__name__ and target_field.field == field): @@ -336,7 +336,7 @@ class Model(WarningErrorMixin, URLMixin, PoolBase): if attr in res[field]: res[field][attr] = encoder.encode(res[field][attr]) - for i in res.keys(): + for i in list(res.keys()): # filter out fields which aren't in the fields_names list if fields_names: if i not in fields_names: @@ -356,7 +356,7 @@ class Model(WarningErrorMixin, URLMixin, PoolBase): if kwargs: self._values = {} parent_values = {} - for name, value in kwargs.iteritems(): + for name, value in kwargs.items(): if not name.startswith('_parent_'): setattr(self, name, value) else: @@ -374,7 +374,7 @@ class Model(WarningErrorMixin, URLMixin, PoolBase): else: setattr(record, parent_name, {field: value}) - for name, value in parent_values.iteritems(): + for name, value in parent_values.items(): set_parent_value(self, name, value) self._init_values = self._values.copy() else: @@ -398,7 +398,7 @@ class Model(WarningErrorMixin, URLMixin, PoolBase): return '%s,%s' % (self.__name__, self.id) def __unicode__(self): - return u'%s,%s' % (self.__name__, self.id) + return '%s,%s' % (self.__name__, self.id) def __repr__(self): if self.id is None or self.id < 0: @@ -425,7 +425,7 @@ class Model(WarningErrorMixin, URLMixin, PoolBase): def __hash__(self): return hash((self.__name__, self.id)) - def __nonzero__(self): + def __bool__(self): return True @property @@ -439,7 +439,7 @@ class Model(WarningErrorMixin, URLMixin, PoolBase): """ values = {} if self._values: - for fname, value in self._values.iteritems(): + for fname, value in self._values.items(): field = self._fields[fname] if field._type in ('many2one', 'one2one', 'reference'): if value: diff --git a/trytond/model/modelsingleton.py b/trytond/model/modelsingleton.py index a10386918c..f025b57458 100644 --- a/trytond/model/modelsingleton.py +++ b/trytond/model/modelsingleton.py @@ -32,14 +32,14 @@ class ModelSingleton(ModelStorage): singleton = cls.get_singleton() if not singleton: if not fields_names: - fields_names = cls._fields.keys() + fields_names = list(cls._fields.keys()) fname_no_rec_name = [f for f in fields_names if '.' not in f] res = cls.default_get(fname_no_rec_name, with_rec_name=len(fname_no_rec_name) != len(fields_names)) for field_name in fields_names: if field_name not in res: res[field_name] = None - for field_name in res.keys(): + for field_name in list(res.keys()): if field_name not in fields_names: del res[field_name] res['id'] = ids[0] @@ -65,7 +65,7 @@ class ModelSingleton(ModelStorage): if local_cache: local_cache.clear() # Clean transaction cache of all ids - for cache in Transaction().cache.itervalues(): + for cache in Transaction().cache.values(): if cls.__name__ in cache: cache[cls.__name__].clear() @@ -75,7 +75,7 @@ class ModelSingleton(ModelStorage): if singleton: super(ModelSingleton, cls).delete([singleton]) # Clean transaction cache of all ids - for cache in Transaction().cache.itervalues(): + for cache in Transaction().cache.values(): if cls.__name__ in cache: cache[cls.__name__].clear() @@ -110,6 +110,6 @@ class ModelSingleton(ModelStorage): if cls._fields[field]._type in ('many2one',): fields_names.append(field + '.rec_name') default, = cls.read([singleton.id], fields_names=fields_names) - for field in (x for x in default.keys() if x not in fields_names): + for field in (x for x in list(default.keys()) if x not in fields_names): del default[field] return default diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index ede0ffa8e4..1d57fd0421 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -1,7 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import datetime -from itertools import islice, izip, chain, ifilter +from itertools import islice, chain from collections import OrderedDict from functools import wraps @@ -208,7 +208,7 @@ class ModelSQL(ModelStorage): history_table = TableHandler(cls, module_name, history=True) history_table.index_action('id', action='add') - for field_name, field in cls._fields.iteritems(): + for field_name, field in cls._fields.items(): if field_name == 'id': continue sql_type = field.sql_type() @@ -266,7 +266,7 @@ class ModelSQL(ModelStorage): table.not_null_action( field_name, action=required and 'add' or 'remove') - for field_name, field in cls._fields.iteritems(): + for field_name, field in cls._fields.items(): if isinstance(field, fields.Many2One) \ and field.model_name == cls.__name__ \ and field.left and field.right: @@ -292,7 +292,7 @@ class ModelSQL(ModelStorage): cursor.execute( *history_table.select(history_table.id, limit=1)) if not cursor.fetchone(): - columns = [n for n, f in cls._fields.iteritems() + columns = [n for n, f in cls._fields.items() if f.sql_type()] cursor.execute(*history_table.insert( [Column(history_table, c) for c in columns], @@ -306,7 +306,7 @@ class ModelSQL(ModelStorage): TableHandler = backend.get('TableHandler') if cls._history: history_table = TableHandler(cls, history=True) - for field_name, field in cls._fields.iteritems(): + for field_name, field in cls._fields.items(): if not field.sql_type(): continue history_table.add_column(field_name, field._sql_type) @@ -314,7 +314,7 @@ class ModelSQL(ModelStorage): @classmethod def _get_error_messages(cls): res = super(ModelSQL, cls)._get_error_messages() - res += cls._sql_error_messages.values() + res += list(cls._sql_error_messages.values()) for _, _, error in cls._sql_constraints: res.append(error) return res @@ -325,7 +325,7 @@ class ModelSQL(ModelStorage): pool = Pool() TableHandler = backend.get('TableHandler') if field_names is None: - field_names = cls._fields.keys() + field_names = list(cls._fields.keys()) if transaction is None: transaction = Transaction() for field_name in field_names: @@ -356,10 +356,10 @@ class ModelSQL(ModelStorage): cls.raise_user_error('foreign_model_missing', error_args=error_args) for name, _, error in cls._sql_constraints: - if TableHandler.convert_name(name) in unicode(exception): + if TableHandler.convert_name(name) in str(exception): cls.raise_user_error(error) - for name, error in cls._sql_error_messages.iteritems(): - if TableHandler.convert_name(name) in unicode(exception): + for name, error in cls._sql_error_messages.items(): + if TableHandler.convert_name(name) in str(exception): cls.raise_user_error(error) @classmethod @@ -387,7 +387,7 @@ class ModelSQL(ModelStorage): revisions = list(chain(*revisions)) revisions.sort(reverse=True) # SQLite uses char for COALESCE - if revisions and isinstance(revisions[0][0], basestring): + if revisions and isinstance(revisions[0][0], str): strptime = datetime.datetime.strptime format_ = '%Y-%m-%d %H:%M:%S.%f' revisions = [(strptime(timestamp, format_), id_, name) @@ -413,7 +413,7 @@ class ModelSQL(ModelStorage): 'write_uid': cls.write_uid, 'write_date': cls.write_date, } - for fname, field in sorted(fields.iteritems()): + for fname, field in sorted(fields.items()): if not field.sql_type(): continue columns.append(Column(table, fname)) @@ -443,7 +443,7 @@ class ModelSQL(ModelStorage): history = cls.__table_history__() columns = [] hcolumns = [] - fnames = sorted(n for n, f in cls._fields.iteritems() + fnames = sorted(n for n, f in cls._fields.items() if f.sql_type()) for fname in fnames: columns.append(Column(table, fname)) @@ -575,7 +575,7 @@ class ModelSQL(ModelStorage): insert_values = [transaction.user, CurrentTimestamp()] # Insert record - for fname, value in values.iteritems(): + for fname, value in values.items(): field = cls._fields[fname] if not hasattr(field, 'set'): insert_columns.append(Column(table, fname)) @@ -599,7 +599,7 @@ class ModelSQL(ModelStorage): [insert_values])) id_new = transaction.database.lastid(cursor) new_ids.append(id_new) - except DatabaseIntegrityError, exception: + except DatabaseIntegrityError as exception: transaction = Transaction() with Transaction().new_transaction(), \ Transaction().set_context(_check_access=False): @@ -612,8 +612,8 @@ class ModelSQL(ModelStorage): translation_values = {} fields_to_set = {} - for values, new_id in izip(vlist, new_ids): - for fname, value in values.iteritems(): + for values, new_id in zip(vlist, new_ids): + for fname, value in values.items(): field = cls._fields[fname] if (getattr(field, 'translate', False) and not hasattr(field, 'set')): @@ -630,9 +630,9 @@ class ModelSQL(ModelStorage): args.extend(([new_id], value)) if translation_values: - for name, translations in translation_values.iteritems(): + for name, translations in translation_values.items(): Translation.set_ids(name, 'model', Transaction().language, - translations.keys(), translations.values()) + list(translations.keys()), list(translations.values())) for fname in sorted(fields_to_set, key=cls.index_set_field): fargs = fields_to_set[fname] @@ -641,7 +641,7 @@ class ModelSQL(ModelStorage): cls._insert_history(new_ids) - field_names = cls._fields.keys() + field_names = list(cls._fields.keys()) cls._update_mptt(field_names, [new_ids] * len(field_names)) cls.__check_domain_rule(new_ids, 'create') @@ -660,7 +660,7 @@ class ModelSQL(ModelStorage): ModelAccess = pool.get('ir.model.access') if not fields_names: fields_names = [] - for field_name in cls._fields.keys(): + for field_name in list(cls._fields.keys()): if ModelAccess.check_relation(cls.__name__, field_name, mode='read'): fields_names.append(field_name) @@ -706,7 +706,7 @@ class ModelSQL(ModelStorage): history_limit = 1 columns = [] - for f in fields_names + fields_related.keys() + datetime_fields: + for f in fields_names + list(fields_related.keys()) + datetime_fields: field = cls._fields.get(f) if field and field.sql_type(): columns.append(field.sql_column(table).as_(f)) @@ -773,7 +773,7 @@ class ModelSQL(ModelStorage): # all fields for which there is a get attribute getter_fields = [f for f in - fields_names + fields_related.keys() + datetime_fields + fields_names + list(fields_related.keys()) + datetime_fields if f in cls._fields and hasattr(cls._fields[f], 'get')] if getter_fields and cachable_fields: @@ -828,7 +828,7 @@ class ModelSQL(ModelStorage): to_del = set() fields_related2values = {} - for fname in fields_related.keys() + datetime_fields: + for fname in list(fields_related.keys()) + datetime_fields: if fname not in fields_names: to_del.add(fname) if fname not in cls._fields: @@ -947,7 +947,7 @@ class ModelSQL(ModelStorage): columns = [table.write_uid, table.write_date] update_values = [transaction.user, CurrentTimestamp()] store_translation = Transaction().language == Config.get_language() - for fname, value in values.iteritems(): + for fname, value in values.items(): field = cls._fields[fname] if not hasattr(field, 'set'): if (not getattr(field, 'translate', False) @@ -960,16 +960,16 @@ class ModelSQL(ModelStorage): try: cursor.execute(*table.update(columns, update_values, where=red_sql)) - except DatabaseIntegrityError, exception: + except DatabaseIntegrityError as exception: transaction = Transaction() with Transaction().new_transaction(), \ Transaction().set_context(_check_access=False): cls.__raise_integrity_error( - exception, values, values.keys(), + exception, values, list(values.keys()), transaction=transaction) raise - for fname, value in values.iteritems(): + for fname, value in values.items(): field = cls._fields[fname] if (getattr(field, 'translate', False) and not hasattr(field, 'set')): @@ -979,7 +979,7 @@ class ModelSQL(ModelStorage): if hasattr(field, 'set'): fields_to_set.setdefault(fname, []).extend((ids, value)) - field_names = values.keys() + field_names = list(values.keys()) cls._update_mptt(field_names, [ids] * len(field_names), values) all_field_names |= set(field_names) @@ -1004,7 +1004,7 @@ class ModelSQL(ModelStorage): cursor = transaction.connection.cursor() pool = Pool() Translation = pool.get('ir.translation') - ids = map(int, records) + ids = list(map(int, records)) if not ids: return @@ -1022,7 +1022,7 @@ class ModelSQL(ModelStorage): has_translation = False tree_ids = {} - for fname, field in cls._fields.iteritems(): + for fname, field in cls._fields.items(): if (isinstance(field, fields.Many2One) and field.model_name == cls.__name__ and field.left and field.right): @@ -1043,7 +1043,7 @@ class ModelSQL(ModelStorage): continue if not issubclass(model, ModelStorage): continue - for field_name, field in model._fields.iteritems(): + for field_name, field in model._fields.items(): if (isinstance(field, fields.Many2One) and field.model_name == cls.__name__): if field.ondelete == 'CASCADE': @@ -1072,7 +1072,7 @@ class ModelSQL(ModelStorage): records = Model.search([(field_name, 'in', sub_ids)]) return records - for sub_ids, sub_records in izip( + for sub_ids, sub_records in zip( grouped_slice(ids), grouped_slice(records)): sub_ids = list(sub_ids) red_sql = reduce_ids(table.id, sub_ids) @@ -1111,7 +1111,7 @@ class ModelSQL(ModelStorage): try: cursor.execute(*table.delete(where=red_sql)) - except DatabaseIntegrityError, exception: + except DatabaseIntegrityError as exception: transaction = Transaction() with Transaction().new_transaction(): cls.__raise_integrity_error( @@ -1123,7 +1123,7 @@ class ModelSQL(ModelStorage): cls._insert_history(ids, deleted=True) - cls._update_mptt(tree_ids.keys(), tree_ids.values()) + cls._update_mptt(list(tree_ids.keys()), list(tree_ids.values())) @classmethod def __check_domain_rule(cls, ids, mode, nodomain=None): @@ -1215,7 +1215,7 @@ class ModelSQL(ModelStorage): columns.append(Column(main_table, '__id').as_('__id')) if not query: columns += [f.sql_column(main_table).as_(n) - for n, f in cls._fields.iteritems() + for n, f in cls._fields.items() if not hasattr(f, 'get') and n != 'id' and not getattr(f, 'translate', False) @@ -1267,14 +1267,14 @@ class ModelSQL(ModelStorage): <= transaction.context['_datetime']))) for deleted_id, delete_date in cursor.fetchall(): history_date, _ = ids_history[deleted_id] - if isinstance(history_date, basestring): + if isinstance(history_date, str): strptime = datetime.datetime.strptime format_ = '%Y-%m-%d %H:%M:%S.%f' history_date = strptime(history_date, format_) if history_date <= delete_date: to_delete.add(deleted_id) - return ifilter(lambda r: history_key(r) == ids_history[r['id']] + return filter(lambda r: history_key(r) == ids_history[r['id']] and r['id'] not in to_delete, rows) # Can not cache the history value if we are not sure to have fetch all @@ -1287,7 +1287,7 @@ class ModelSQL(ModelStorage): if data['id'] in delete_records: continue if keys is None: - keys = data.keys() + keys = list(data.keys()) for k in keys[:]: if k in ('_timestamp', '_datetime', '__id'): keys.remove(k) @@ -1334,7 +1334,7 @@ class ModelSQL(ModelStorage): def is_leaf(expression): return (isinstance(expression, (list, tuple)) and len(expression) > 2 - and isinstance(expression[1], basestring) + and isinstance(expression[1], str) and expression[1] in OPERATORS) # TODO remove OPERATORS test def convert(domain): @@ -1489,7 +1489,7 @@ class ModelSQL(ModelStorage): lock = database.lock cursor = transaction.connection.cursor() # Works only for a single transaction - ids = map(int, records) + ids = list(map(int, records)) for _, sql, error in cls._sql_constraints: if has_constraint(sql): continue @@ -1537,7 +1537,7 @@ def convert_from(table, tables): table = table.join(right, 'LEFT', condition) else: table = right - for k, sub_tables in tables.iteritems(): + for k, sub_tables in tables.items(): if k is None: continue table = convert_from(table, sub_tables) diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py index 9d62378e39..2047cd56de 100644 --- a/trytond/model/modelstorage.py +++ b/trytond/model/modelstorage.py @@ -7,7 +7,7 @@ import csv import warnings from decimal import Decimal -from itertools import islice, ifilter, chain, izip +from itertools import islice, chain from functools import reduce, wraps from operator import itemgetter from collections import defaultdict @@ -60,14 +60,14 @@ class ModelStorage(Model): if issubclass(cls, ModelView): cls.__rpc__.update({ 'create': RPC(readonly=False, - result=lambda r: map(int, r)), + result=lambda r: list(map(int, r))), 'read': RPC(), 'write': RPC(readonly=False, instantiate=slice(0, None, 2)), 'delete': RPC(readonly=False, instantiate=0), 'copy': RPC(readonly=False, instantiate=0, unique=False, - result=lambda r: map(int, r)), - 'search': RPC(result=lambda r: map(int, r)), + result=lambda r: list(map(int, r))), + 'search': RPC(result=lambda r: list(map(int, r))), 'search_count': RPC(), 'search_read': RPC(), 'export_data': RPC(instantiate=0, unique=False), @@ -96,7 +96,7 @@ class ModelStorage(Model): ModelAccess.check(cls.__name__, 'create') - all_fields = list(set(chain(*(v.iterkeys() for v in vlist)))) + all_fields = list(set(chain(*(iter(v.keys()) for v in vlist)))) ModelFieldAccess.check(cls.__name__, all_fields, 'write') # Increase transaction counter @@ -160,7 +160,7 @@ class ModelStorage(Model): cls.raise_user_error('write_xml_record', error_description='xml_record_desc') all_records += records - all_fields.update(values.iterkeys()) + all_fields.update(values.keys()) ModelAccess.check(cls.__name__, 'write') ModelFieldAccess.check(cls.__name__, all_fields, 'write') @@ -175,7 +175,7 @@ class ModelStorage(Model): local_cache.clear() # Clean transaction cache - for cache in Transaction().cache.itervalues(): + for cache in Transaction().cache.values(): if cls.__name__ in cache: for record in all_records: if record.id in cache[cls.__name__]: @@ -206,7 +206,7 @@ class ModelStorage(Model): eligibles is a dictionary of the lists of eligible records by triggers ''' Trigger = Pool().get('ir.trigger') - for trigger, records in eligibles.iteritems(): + for trigger, records in eligibles.items(): triggered = [] for record in records: if Trigger.eval(trigger, record): @@ -235,8 +235,8 @@ class ModelStorage(Model): Transaction().counter += 1 # Clean transaction cache - for cache in Transaction().cache.values(): - for cache in (cache, cache.get('_language_cache', {}).values()): + for cache in list(Transaction().cache.values()): + for cache in (cache, list(cache.get('_language_cache', {}).values())): if cls.__name__ in cache: for record in records: if record.id in cache[cls.__name__]: @@ -319,17 +319,17 @@ class ModelStorage(Model): # Reset MPTT field to the default value mptt = set() - for field in cls._fields.itervalues(): + for field in cls._fields.values(): if (isinstance(field, fields.Many2One) and field.model_name == cls.__name__ and field.left and field.right): mptt.add(field.left) mptt.add(field.right) - fields_names = [n for n, f in cls._fields.iteritems() + fields_names = [n for n, f in cls._fields.items() if (not isinstance(f, fields.Function) or isinstance(f, fields.MultiValue)) and n not in mptt] - ids = map(int, records) + ids = list(map(int, records)) datas = cls.read(ids, fields_names=fields_names) datas = dict((d['id'], d) for d in datas) field_defs = cls.fields_get(fields_names=fields_names) @@ -338,10 +338,10 @@ class ModelStorage(Model): data = convert_data(field_defs, datas[id]) to_create.append(data) new_records = cls.create(to_create) - id2new_record = dict(izip(ids, new_records)) + id2new_record = dict(zip(ids, new_records)) fields_translate = {} - for field_name, field in field_defs.iteritems(): + for field_name, field in field_defs.items(): if field_name in cls._fields and \ getattr(cls._fields[field_name], 'translate', False): fields_translate[field_name] = field @@ -351,13 +351,13 @@ class ModelStorage(Model): ('translatable', '=', True), ]) if langs: + fields_names = list(fields_translate.keys()) + ['id'] for lang in langs: # Prevent fuzzing translations when copying as the terms # should be the same. with Transaction().set_context(language=lang.code, fuzzy_translation=False): - datas = cls.read(ids, - fields_names=fields_translate.keys() + ['id']) + datas = cls.read(ids, fields_names=fields_names) to_write = [] for data in datas: to_write.append([id2new_record[data['id']]]) @@ -395,10 +395,10 @@ class ModelStorage(Model): records = cls.search(domain, offset=offset, limit=limit, order=order) if not fields_names: - fields_names = cls._fields.keys() + fields_names = list(cls._fields.keys()) if 'id' not in fields_names: fields_names.append('id') - rows = cls.read(map(int, records), fields_names) + rows = cls.read(list(map(int, records)), fields_names) index = {r.id: i for i, r in enumerate(records)} rows.sort(key=lambda r: index[r['id']]) return rows @@ -430,9 +430,9 @@ class ModelStorage(Model): domain[i] = process(domain[i]) i += 1 if not active_found: - if (domain and ((isinstance(domain[0], basestring) + if (domain and ((isinstance(domain[0], str) and domain[0] == 'AND') - or (not isinstance(domain[0], basestring)))): + or (not isinstance(domain[0], str)))): domain.append(('active', '=', True)) else: domain = ['AND', domain, ('active', '=', True)] @@ -447,7 +447,7 @@ class ModelStorage(Model): rec_name = self._rec_name if rec_name not in self._fields: rec_name = 'id' - return unicode(getattr(self, rec_name)) + return str(getattr(self, rec_name)) @classmethod def search_rec_name(cls, name, clause): @@ -476,7 +476,7 @@ class ModelStorage(Model): Return a list of instance for the ids ''' transaction = Transaction() - ids = map(int, ids) + ids = list(map(int, ids)) local_cache = LRUDictTransaction(cache_size()) transaction_cache = transaction.get_cache() return [cls(x, _ids=ids, @@ -533,7 +533,7 @@ class ModelStorage(Model): child_lines = ModelStorage.__export_row(child_record, child_fields_names) if first: - for child_fpos in xrange(len(fields_names)): + for child_fpos in range(len(fields_names)): if child_lines and child_lines[0][child_fpos]: data[child_fpos] = \ child_lines[0][child_fpos] @@ -595,8 +595,8 @@ class ModelStorage(Model): return None res = [] Relation = pool.get(relation) - for word in csv.reader(value.splitlines(), delimiter=',', - quoting=csv.QUOTE_NONE, escapechar='\\').next(): + for word in next(csv.reader(value.splitlines(), delimiter=',', + quoting=csv.QUOTE_NONE, escapechar='\\')): res2 = Relation.search([ ('rec_name', '=', word), ], limit=2) @@ -644,8 +644,8 @@ class ModelStorage(Model): relation = None ftype = fields_def[field[-1][:-3]]['type'] if ftype == 'many2many': - value = csv.reader(value.splitlines(), delimiter=',', - quoting=csv.QUOTE_NONE, escapechar='\\').next() + value = next(csv.reader(value.splitlines(), delimiter=',', + quoting=csv.QUOTE_NONE, escapechar='\\')) elif ftype == 'reference': try: relation, value = value.split(',', 1) @@ -739,7 +739,7 @@ class ModelStorage(Model): (newrow, max2, _) = res nbrmax = max(nbrmax, max2) reduce(lambda x, y: x and y, newrow) - row[field] = (reduce(lambda x, y: x or y, newrow.values()) and + row[field] = (reduce(lambda x, y: x or y, list(newrow.values())) and [('create', [newrow])]) or [] i = max2 while (position + i) < len(data): @@ -754,12 +754,12 @@ class ModelStorage(Model): (newrow, max2, _) = \ process_lines(data, prefix + [field], newfd, position + i) - if reduce(lambda x, y: x or y, newrow.values()): + if reduce(lambda x, y: x or y, list(newrow.values())): row[field].append(('create', [newrow])) i += max2 nbrmax = max(nbrmax, i) if prefix_len == 0: - for i in xrange(max(nbrmax, 1)): + for i in range(max(nbrmax, 1)): data.pop(0) return (row, nbrmax, translate) @@ -781,8 +781,8 @@ class ModelStorage(Model): for language in languages: translated = [t.get(language, {}) for t in translations] with Transaction().set_context(language=language): - cls.write(*chain(*ifilter(itemgetter(1), - izip(([r] for r in new_records), translated)))) + cls.write(*chain(*filter(itemgetter(1), + zip(([r] for r in new_records), translated)))) return len(new_records) @classmethod @@ -805,7 +805,7 @@ class ModelStorage(Model): with Transaction().set_context(_check_access=False): models_data = ModelData.search([ ('model', '=', cls.__name__), - ('db_id', 'in', map(int, records)), + ('db_id', 'in', list(map(int, records))), ]) if not models_data: return True @@ -815,7 +815,7 @@ class ModelStorage(Model): if not model_data.values or model_data.noupdate: continue xml_values = ModelData.load_values(model_data.values) - for key, val in values.iteritems(): + for key, val in values.items(): if key in xml_values and val != xml_values[key]: return False return True @@ -885,7 +885,7 @@ class ModelStorage(Model): if is_pyson(i): return True if isinstance(test, dict): - for key, value in test.items(): + for key, value in list(test.items()): if isinstance(value, PYSON): return True if isinstance(value, (list, tuple, dict)): @@ -923,7 +923,7 @@ class ModelStorage(Model): in_max = Transaction().database.IN_MAX count = in_max // 10 new_domains = {} - for sub_domains in grouped_slice(domains.keys(), count): + for sub_domains in grouped_slice(list(domains.keys()), count): grouped_domain = ['OR'] grouped_records = [] for d in sub_domains: @@ -940,7 +940,7 @@ class ModelStorage(Model): else: domains[freeze(field.domain)].extend(records) - for domain, sub_records in domains.iteritems(): + for domain, sub_records in domains.items(): validate_relation_domain(field, sub_records, Relation, domain) def relation_domain(field, records): @@ -971,11 +971,11 @@ class ModelStorage(Model): error_args=cls._get_error_args(field.name)) field_names = set(field_names or []) - function_fields = {name for name, field in cls._fields.iteritems() + function_fields = {name for name, field in cls._fields.items() if isinstance(field, fields.Function)} ctx_pref['active_test'] = False with Transaction().set_context(ctx_pref): - for field_name, field in cls._fields.iteritems(): + for field_name, field in cls._fields.items(): depends = set(field.depends) if (field_names and field_name not in field_names @@ -989,7 +989,7 @@ class ModelStorage(Model): def required_test(value, field_name, field): if (isinstance(value, (type(None), type(False), list, - tuple, basestring, dict)) + tuple, str, dict)) and not value): cls.raise_user_error('required_validation_record', error_args=cls._get_error_args(field_name)) @@ -1244,8 +1244,8 @@ class ModelStorage(Model): FieldAccess = Pool().get('ir.model.field.access') fread_accesses = {} fread_accesses.update(FieldAccess.check(self.__name__, - self._fields.keys(), 'read', access=True)) - to_remove = set(x for x, y in fread_accesses.iteritems() + list(self._fields.keys()), 'read', access=True)) + to_remove = set(x for x, y in fread_accesses.items() if not y and x != name) threshold = config.getint('cache', 'field') @@ -1264,20 +1264,20 @@ class ModelStorage(Model): fname, field = item return fname in self._fields - ifields = ifilter(to_load, - ifilter(not_cached, - self._fields.iteritems())) + ifields = filter(to_load, + filter(not_cached, + iter(self._fields.items()))) ifields = islice(ifields, 0, threshold) ffields.update(ifields) # add datetime_field - for field in ffields.values(): + for field in list(ffields.values()): if hasattr(field, 'datetime_field') and field.datetime_field: datetime_field = self._fields[field.datetime_field] ffields[field.datetime_field] = datetime_field # add depends of field with context - for field in ffields.values(): + for field in list(ffields.values()): if field.context: eval_fields = fields.get_eval_fields(field.context) for context_field_name in eval_fields: @@ -1300,7 +1300,7 @@ class ModelStorage(Model): index = self._ids.index(self.id) ids = chain(islice(self._ids, index, None), islice(self._ids, 0, max(index - 1, 0))) - ids = islice(unique(ifilter(filter_, ids)), + ids = islice(unique(filter(filter_, ids)), self._transaction.database.IN_MAX) def instantiate(field, value, data): @@ -1367,10 +1367,10 @@ class ModelStorage(Model): for i in ids if i in self._cache and name in self._cache[i]] else: - read_data = self.read(list(ids), ffields.keys()) + read_data = self.read(list(ids), list(ffields.keys())) # create browse records for 'remote' models for data in read_data: - for fname, field in ffields.iteritems(): + for fname, field in ffields.items(): fvalue = data[fname] if field._type in ('many2one', 'one2one', 'one2many', 'many2many', 'reference'): @@ -1399,7 +1399,7 @@ class ModelStorage(Model): values = {} if not self._values: return values - for fname, value in self._values.iteritems(): + for fname, value in self._values.items(): field = self._fields[fname] if field._type in ('many2one', 'one2one', 'reference'): if value: @@ -1483,7 +1483,7 @@ class ModelStorage(Model): transaction.set_context(context): if to_create: news = cls.create([save_values[r] for r in to_create]) - for record, new in izip(to_create, news): + for record, new in zip(to_create, news): record._ids.remove(record.id) record._id = new.id record._ids.append(record.id) @@ -1527,7 +1527,7 @@ class EvalEnvironment(dict): def __getattr__(self, item): try: return self.__getitem__(item) - except KeyError, exception: + except KeyError as exception: raise AttributeError(*exception.args) def get(self, item, default=None): @@ -1537,5 +1537,5 @@ class EvalEnvironment(dict): pass return super(EvalEnvironment, self).get(item, default) - def __nonzero__(self): + def __bool__(self): return bool(self._record) diff --git a/trytond/model/modelview.py b/trytond/model/modelview.py index 5db2cbca67..5944087573 100644 --- a/trytond/model/modelview.py +++ b/trytond/model/modelview.py @@ -147,13 +147,13 @@ class ModelView(Model): if parent_value: methods[attr][name] |= parent_value - for name, method in callables.iteritems(): + for name, method in callables.items(): get_callable_attributes(name, method) def setup_field(field_name, field, attribute): if attribute == 'selection_change_with': if isinstance( - getattr(field, 'selection', None), basestring): + getattr(field, 'selection', None), str): function_name = field.selection else: return @@ -184,7 +184,7 @@ class ModelView(Model): # Decorate on_change to always return self setattr(cls, function_name, on_change(function)) - for name, field in fields_.iteritems(): + for name, field in fields_.items(): for attribute in [ 'on_change', 'on_change_with', @@ -198,7 +198,7 @@ class ModelView(Model): super(ModelView, cls).__post_setup__() # Update __rpc__ - for field_name, field in cls._fields.iteritems(): + for field_name, field in cls._fields.items(): field.set_rpc(cls) for button in cls._buttons: @@ -243,7 +243,7 @@ class ModelView(Model): ], ] views = View.search(domain) - views = filter(lambda v: v.rng_type == view_type, views) + views = [v for v in views if v.rng_type == view_type] if views: view = views[0] if view: @@ -391,16 +391,16 @@ class ModelView(Model): # Find field without read access fread_accesses = FieldAccess.check(cls.__name__, - cls._fields.keys(), 'read', access=True) - fields_to_remove = list(x for x, y in fread_accesses.iteritems() + list(cls._fields.keys()), 'read', access=True) + fields_to_remove = list(x for x, y in fread_accesses.items() if not y) # Find relation field without read access - for name, field in cls._fields.iteritems(): + for name, field in cls._fields.items(): if not ModelAccess.check_relation(cls.__name__, name, mode='read'): fields_to_remove.append(name) - for name, field in cls._fields.iteritems(): + for name, field in cls._fields.items(): for field_to_remove in fields_to_remove: if field_to_remove in field.depends: fields_to_remove.append(name) @@ -450,7 +450,7 @@ class ModelView(Model): if hasattr(field, 'field'): fields_def.setdefault(field.field, {'name': field.field}) - for field_name in fields_def.keys(): + for field_name in list(fields_def.keys()): if field_name in cls._fields: field = cls._fields[field_name] else: @@ -460,7 +460,7 @@ class ModelView(Model): arch = etree.tostring( tree, encoding='utf-8', pretty_print=False).decode('utf-8') - fields2 = cls.fields_get(fields_def.keys()) + fields2 = cls.fields_get(list(fields_def.keys())) for field in fields_def: if field in fields2: fields2[field].update(fields_def[field]) @@ -553,7 +553,7 @@ class ModelView(Model): groups = set(User.get_groups()) button_attr = Button.get_view_attributes( cls.__name__, button_name) - for attr, value in button_attr.iteritems(): + for attr, value in button_attr.items(): if not element.get(attr): element.set(attr, value or '') button_groups = Button.get_groups(cls.__name__, button_name) @@ -637,10 +637,8 @@ class ModelView(Model): if button_rules: clicks = ButtonClick.register( cls.__name__, func.__name__, records) - records = filter( - lambda r: all(br.test(r, clicks.get(r.id, [])) - for br in button_rules), - records) + records = [r for r in records if all(br.test(r, clicks.get(r.id, [])) + for br in button_rules)] # Reset click after filtering in case the button also has rules names = Button.get_reset(cls.__name__, func.__name__) if names: @@ -711,7 +709,7 @@ class ModelView(Model): init_values = self._init_values or {} if not self._values: return changed - for fname, value in self._values.iteritems(): + for fname, value in self._values.items(): field = self._fields[fname] # Always test key presence in case value is None if (fname in init_values diff --git a/trytond/model/multivalue.py b/trytond/model/multivalue.py index 3673573e9e..066ff1543a 100644 --- a/trytond/model/multivalue.py +++ b/trytond/model/multivalue.py @@ -17,7 +17,7 @@ class MultiValueMixin(object): def multivalue_records(self, field): Value = self.multivalue_model(field) - for fname, field in self._fields.iteritems(): + for fname, field in self._fields.items(): if (field._type == 'one2many' and field.model_name == Value.__name__): return getattr(self, fname) @@ -25,7 +25,7 @@ class MultiValueMixin(object): def multivalue_record(self, field, **pattern): Value = self.multivalue_model(field) - for fname, field in Value._fields.iteritems(): + for fname, field in Value._fields.items(): if (field._type == 'many2one' and field.model_name == self.__name__): pattern = pattern.copy() @@ -92,4 +92,4 @@ class ValueMixin(MatchMixin): def filter_pattern(pattern, Value): - return {f: v for f, v in pattern.iteritems() if f in Value._fields} + return {f: v for f, v in pattern.items() if f in Value._fields} diff --git a/trytond/model/tree.py b/trytond/model/tree.py index 17a88d6573..5534ac797e 100644 --- a/trytond/model/tree.py +++ b/trytond/model/tree.py @@ -30,7 +30,7 @@ def tree(parent='parent', name='name', separator=None): @classmethod def search_rec_name(cls, _, clause): - if isinstance(clause[2], basestring): + if isinstance(clause[2], str): values = list(reversed(clause[2].split(separator))) else: values = [[]] @@ -60,7 +60,7 @@ def tree(parent='parent', name='name', separator=None): domain.append((top_parent, operator, None)) if (clause[1].endswith('like') and clause[2].replace('%%', '__').endswith('%')): - ids = map(int, cls.search(domain, order=[])) + ids = list(map(int, cls.search(domain, order=[]))) domain = [(parent, 'child_of', ids)] return domain diff --git a/trytond/model/workflow.py b/trytond/model/workflow.py index 02b80b6376..bbed44430c 100644 --- a/trytond/model/workflow.py +++ b/trytond/model/workflow.py @@ -35,7 +35,7 @@ class Workflow(object): result = func(cls, filtered, *args, **kwargs) if to_update: - for record in to_update.keys(): + for record in list(to_update.keys()): current_state = getattr(record, cls._transition_state) if current_state != to_update[record]: del to_update[record] diff --git a/trytond/modules/__init__.py b/trytond/modules/__init__.py index e3d3b5f8be..05c023a158 100644 --- a/trytond/modules/__init__.py +++ b/trytond/modules/__init__.py @@ -5,7 +5,7 @@ import sys import itertools import logging import imp -import ConfigParser +import configparser from glob import iglob from collections import defaultdict @@ -47,9 +47,9 @@ update_egg_modules() def get_module_info(name): "Return the content of the tryton.cfg" - module_config = ConfigParser.ConfigParser() + module_config = configparser.ConfigParser() with tools.file_open(os.path.join(name, 'tryton.cfg')) as fp: - module_config.readfp(fp) + module_config.read_file(fp) directory = os.path.dirname(fp.name) info = dict(module_config.items('tryton')) info['directory'] = directory @@ -74,7 +74,7 @@ class Graph(dict): return node def __iter__(self): - for node in sorted(self.itervalues(), key=lambda n: (n.depth, n.name)): + for node in sorted(self.values(), key=lambda n: (n.depth, n.name)): yield node @@ -174,7 +174,7 @@ def load_module_graph(graph, pool, update=None, lang=None): package_state = 'to activate' for child in node: module2state[child.name] = package_state - for type in classes.keys(): + for type in list(classes.keys()): for cls in classes[type]: logger.info('%s:register %s', module, cls.__name__) cls.__register__(module) @@ -206,7 +206,7 @@ def load_module_graph(graph, pool, update=None, lang=None): continue lang2filenames[lang2].append(filename) base_path_position = len(node.info['directory']) + 1 - for language, files in lang2filenames.iteritems(): + for language, files in lang2filenames.items(): filenames = [f[base_path_position:] for f in files] logger.info('%s:loading %s', module, ','.join(filenames)) Translation = pool.get('ir.translation') diff --git a/trytond/pool.py b/trytond/pool.py index b436b48f8e..0e6c67c71a 100644 --- a/trytond/pool.py +++ b/trytond/pool.py @@ -5,7 +5,7 @@ from threading import RLock import logging from trytond.modules import load_modules, register_classes from trytond.transaction import Transaction -import __builtin__ +import builtins __all__ = ['Pool', 'PoolMeta', 'PoolBase', 'isregisteredby'] @@ -24,9 +24,7 @@ class PoolMeta(type): return new -class PoolBase(object): - __metaclass__ = PoolMeta - +class PoolBase(object, metaclass=PoolMeta): @classmethod def __setup__(cls): pass @@ -99,7 +97,7 @@ class Pool(object): Start/restart the Pool ''' with cls._lock: - for classes in Pool.classes.itervalues(): + for classes in Pool.classes.values(): classes.clear() register_classes() cls._started = True @@ -181,7 +179,7 @@ class Pool(object): if type == 'report': from trytond.report import Report # Keyword argument 'type' conflicts with builtin function - cls = __builtin__.type(str(name), (Report,), {}) + cls = builtins.type(str(name), (Report,), {}) cls.__setup__() self.add(cls, type) return cls @@ -201,7 +199,7 @@ class Pool(object): :param type: the type :return: an iterator ''' - return self._pool[self.database_name][type].iteritems() + return self._pool[self.database_name][type].items() def fill(self, module, modules): ''' @@ -230,8 +228,8 @@ class Pool(object): if classes is None: classes = {} for type_ in self._pool[self.database_name]: - classes[type_] = self._pool[self.database_name][type_].values() - for type_, lst in classes.iteritems(): + classes[type_] = list(self._pool[self.database_name][type_].values()) + for type_, lst in classes.items(): for cls in lst: cls.__setup__() for cls in lst: diff --git a/trytond/protocols/jsonrpc.py b/trytond/protocols/jsonrpc.py index fd166fa9e9..3f03a3aaea 100644 --- a/trytond/protocols/jsonrpc.py +++ b/trytond/protocols/jsonrpc.py @@ -41,7 +41,7 @@ JSONDecoder.register('timedelta', def _bytes_decoder(dct): cast = bytearray if bytes == str else bytes - return cast(base64.decodestring(dct['base64'].encode('utf-8'))) + return cast(base64.decodebytes(dct['base64'].encode('utf-8'))) JSONDecoder.register('bytes', _bytes_decoder) JSONDecoder.register('Decimal', lambda dct: Decimal(dct['decimal'])) @@ -93,7 +93,7 @@ JSONEncoder.register(datetime.timedelta, }) _bytes_encoder = lambda o: { '__class__': 'bytes', - 'base64': base64.encodestring(o).decode('utf-8'), + 'base64': base64.encodebytes(o).decode('utf-8'), } JSONEncoder.register(bytes, _bytes_encoder) JSONEncoder.register(bytearray, _bytes_encoder) diff --git a/trytond/protocols/wrappers.py b/trytond/protocols/wrappers.py index 3a9d930113..2fcec485c7 100644 --- a/trytond/protocols/wrappers.py +++ b/trytond/protocols/wrappers.py @@ -184,7 +184,7 @@ def user_application(name, json=True): transaction.set_context(_check_access=True): try: response = func(request, *args, **kwargs) - except Exception, e: + except Exception as e: if isinstance(e, HTTPException): raise logger.error('%s', request, exc_info=True) diff --git a/trytond/protocols/xmlrpc.py b/trytond/protocols/xmlrpc.py index 53ea6e6a69..bd7d3beba5 100644 --- a/trytond/protocols/xmlrpc.py +++ b/trytond/protocols/xmlrpc.py @@ -1,6 +1,6 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -import xmlrpclib as client +import xmlrpc.client as client import datetime import logging @@ -69,9 +69,9 @@ if bytes == str: def dump_struct(self, value, write, escape=client.escape): converted_value = {} for k, v in value.items(): - if type(k) in (int, long): - k = str(int(k)) - elif type(k) == float: + if isinstance(k, int): + k = str(k) + elif isinstance(k, float): k = repr(k) converted_value[k] = v return self.dump_struct(converted_value, write, escape=escape) diff --git a/trytond/pyson.py b/trytond/pyson.py index 9c4d7a5696..e3e43aa40b 100644 --- a/trytond/pyson.py +++ b/trytond/pyson.py @@ -4,23 +4,7 @@ import json import datetime from decimal import Decimal from dateutil.relativedelta import relativedelta -from functools import reduce, wraps - - -def reduced_type(types): - types = types.copy() - for k, r in [(long, int), (str, basestring), (unicode, basestring)]: - if k in types: - types.remove(k) - types.add(r) - return types - - -def reduce_type(func): - @wraps(func) - def wrapper(*args, **kwargs): - return reduced_type(func(*args, **kwargs)) - return wrapper +from functools import reduce class PYSON(object): @@ -36,20 +20,20 @@ class PYSON(object): raise NotImplementedError def __invert__(self): - if self.types() != set([bool]): + if self.types() != {bool}: return Not(Bool(self)) else: return Not(self) def __and__(self, other): if (isinstance(other, PYSON) - and other.types() != set([bool])): + and other.types() != {bool}): other = Bool(other) if (isinstance(self, And) and not isinstance(self, Or)): self._statements.append(other) return self - if self.types() != set([bool]): + if self.types() != {bool}: return And(Bool(self), other) else: return And(self, other) @@ -58,12 +42,12 @@ class PYSON(object): def __or__(self, other): if (isinstance(other, PYSON) - and other.types() != set([bool])): + and other.types() != {bool}): other = Bool(other) if isinstance(self, Or): self._statements.append(other) return self - if self.types() != set([bool]): + if self.types() != {bool}: return Or(Bool(self), other) else: return Or(self, other) @@ -161,12 +145,11 @@ class Eval(PYSON): 'd': self._default, } - @reduce_type def types(self): if isinstance(self._default, PYSON): return self._default.types() else: - return set([type(self._default)]) + return {type(self._default)} @staticmethod def eval(dct, context): @@ -178,7 +161,7 @@ class Not(PYSON): def __init__(self, v): super(Not, self).__init__() if isinstance(v, PYSON): - assert v.types() == set([bool]), 'value must be boolean' + assert v.types() == {bool}, 'value must be boolean' else: assert isinstance(v, bool), 'value must be boolean' self._value = v @@ -194,7 +177,7 @@ class Not(PYSON): } def types(self): - return set([bool]) + return {bool} @staticmethod def eval(dct, context): @@ -218,7 +201,7 @@ class Bool(PYSON): } def types(self): - return set([bool]) + return {bool} @staticmethod def eval(dct, context): @@ -232,7 +215,7 @@ class And(PYSON): statements = list(statements) + kwargs.get('s', []) for statement in statements: if isinstance(statement, PYSON): - assert statement.types() == set([bool]), \ + assert statement.types() == {bool}, \ 'statement must be boolean' else: assert isinstance(statement, bool), \ @@ -251,7 +234,7 @@ class And(PYSON): } def types(self): - return set([bool]) + return {bool} @staticmethod def eval(dct, context): @@ -278,11 +261,11 @@ class Equal(PYSON): if isinstance(statement1, PYSON): types1 = statement1.types() else: - types1 = reduced_type(set([type(s1)])) + types1 = {type(s1)} if isinstance(statement2, PYSON): types2 = statement2.types() else: - types2 = reduced_type(set([type(s2)])) + types2 = {type(s2)} assert types1 == types2, 'statements must have the same type' self._statement1 = statement1 self._statement2 = statement2 @@ -299,7 +282,7 @@ class Equal(PYSON): } def types(self): - return set([bool]) + return {bool} @staticmethod def eval(dct, context): @@ -313,13 +296,13 @@ class Greater(PYSON): super(Greater, self).__init__() for i in (statement1, statement2): if isinstance(i, PYSON): - assert i.types().issubset({int, long, float, type(None)}), \ + assert i.types().issubset({int, float, type(None)}), \ 'statement must be an integer or a float' else: - assert isinstance(i, (int, long, float, type(None))), \ + assert isinstance(i, (int, float, type(None))), \ 'statement must be an integer or a float' if isinstance(equal, PYSON): - assert equal.types() == set([bool]) + assert equal.types() == {bool} else: assert isinstance(equal, bool) self._statement1 = statement1 @@ -339,14 +322,14 @@ class Greater(PYSON): } def types(self): - return set([bool]) + return {bool} @staticmethod def _convert(dct): for i in ('s1', 's2'): if dct[i] is None: dct[i] = 0.0 - if not isinstance(dct[i], (int, long, float)): + if not isinstance(dct[i], (int, float)): dct = dct.copy() dct[i] = float(dct[i]) return dct @@ -382,18 +365,18 @@ class If(PYSON): condition, then_statement, else_statement = c, t, e super(If, self).__init__() if isinstance(condition, PYSON): - assert condition.types() == set([bool]), \ + assert condition.types() == {bool}, \ 'condition must be boolean' else: assert isinstance(condition, bool), 'condition must be boolean' if isinstance(then_statement, PYSON): then_types = then_statement.types() else: - then_types = reduced_type(set([type(then_statement)])) + then_types = {type(then_statement)} if isinstance(else_statement, PYSON): else_types = else_statement.types() else: - else_types = reduced_type(set([type(else_statement)])) + else_types = {type(else_statement)} assert then_types == else_types, \ 'then and else statements must be the same type' self._condition = condition @@ -412,12 +395,11 @@ class If(PYSON): 'e': self._else_statement, } - @reduce_type def types(self): if isinstance(self._then_statement, PYSON): return self._then_statement.types() else: - return set([type(self._then_statement)]) + return {type(self._then_statement)} @staticmethod def eval(dct, context): @@ -433,14 +415,14 @@ class Get(PYSON): obj, key, default = v, k, d super(Get, self).__init__() if isinstance(obj, PYSON): - assert obj.types() == set([dict]), 'obj must be a dict' + assert obj.types() == {dict}, 'obj must be a dict' else: assert isinstance(obj, dict), 'obj must be a dict' self._obj = obj if isinstance(key, PYSON): - assert key.types() == set([basestring]), 'key must be a string' + assert key.types() == {str}, 'key must be a string' else: - assert isinstance(key, basestring), 'key must be a string' + assert isinstance(key, str), 'key must be a string' self._key = key self._default = default @@ -456,12 +438,11 @@ class Get(PYSON): 'd': self._default, } - @reduce_type def types(self): if isinstance(self._default, PYSON): return self._default.types() else: - return set([type(self._default)]) + return {type(self._default)} @staticmethod def eval(dct, context): @@ -474,20 +455,20 @@ class In(PYSON): key, obj = k, v super(In, self).__init__() if isinstance(key, PYSON): - assert key.types().issubset(set([basestring, int])), \ + assert key.types().issubset({str, int}), \ 'key must be a string or an integer or a long' else: - assert isinstance(key, (basestring, int, long)), \ + assert isinstance(key, (str, int)), \ 'key must be a string or an integer or a long' if isinstance(obj, PYSON): - assert obj.types().issubset(set([dict, list])), \ + assert obj.types().issubset({dict, list}), \ 'obj must be a dict or a list' - if obj.types() == set([dict]): - assert isinstance(key, basestring), 'key must be a string' + if obj.types() == {dict}: + assert isinstance(key, str), 'key must be a string' else: assert isinstance(obj, (dict, list)) if isinstance(obj, dict): - assert isinstance(key, basestring), 'key must be a string' + assert isinstance(key, str), 'key must be a string' self._key = key self._obj = obj @@ -503,7 +484,7 @@ class In(PYSON): } def types(self): - return set([bool]) + return {bool} @staticmethod def eval(dct, context): @@ -523,10 +504,10 @@ class Date(PYSON): super(Date, self).__init__() for i in (year, month, day, delta_years, delta_months, delta_days): if isinstance(i, PYSON): - assert i.types().issubset(set([int, long, type(None)])), \ + assert i.types().issubset({int, type(None)}), \ '%s must be an integer or None' % (i,) else: - assert isinstance(i, (int, long, type(None))), \ + assert isinstance(i, (int, type(None))), \ '%s must be an integer or None' % (i,) self._year = year self._month = month @@ -552,7 +533,7 @@ class Date(PYSON): } def types(self): - return set([datetime.date]) + return {datetime.date} @staticmethod def eval(dct, context): @@ -587,10 +568,10 @@ class DateTime(Date): for i in (hour, minute, second, microsecond, delta_hours, delta_minutes, delta_seconds, delta_microseconds): if isinstance(i, PYSON): - assert i.types() == set([int, type(None)]), \ + assert i.types() == {int, type(None)}, \ '%s must be an integer or None' % (i,) else: - assert isinstance(i, (int, long, type(None))), \ + assert isinstance(i, (int, type(None))), \ '%s must be an integer or None' % (i,) self._hour = hour self._minute = minute @@ -624,7 +605,7 @@ class DateTime(Date): return res def types(self): - return set([datetime.datetime]) + return {datetime.datetime} @staticmethod def eval(dct, context): @@ -651,10 +632,10 @@ class Len(PYSON): def __init__(self, v): super(Len, self).__init__() if isinstance(v, PYSON): - assert v.types().issubset(set([dict, list, basestring])), \ + assert v.types().issubset({dict, list, str}), \ 'value must be a dict or a list or a string' else: - assert isinstance(v, (dict, list, basestring)), \ + assert isinstance(v, (dict, list, str)), \ 'value must be a dict or list or a string' self._value = v @@ -669,7 +650,7 @@ class Len(PYSON): } def types(self): - return set([int]) + return {int} @staticmethod def eval(dct, context): @@ -694,7 +675,8 @@ class Id(PYSON): return ModelData.get_id(self._module, self._fs_id) def types(self): - return set([int]) + return {int} + CONTEXT = { 'Eval': Eval, diff --git a/trytond/report/report.py b/trytond/report/report.py index 276d0b0833..e1cc6ce5e4 100644 --- a/trytond/report/report.py +++ b/trytond/report/report.py @@ -180,7 +180,7 @@ class Report(URLMixin, PoolBase): oext = 'zip' else: oext, content = cls._execute(records, data, action_report) - if not isinstance(content, unicode): + if not isinstance(content, str): content = bytearray(content) if bytes == str else bytes(content) return (oext, content, action_report.direct_print, action_report.name) @@ -230,7 +230,7 @@ class Report(URLMixin, PoolBase): return '%s,%s' % (Model.__name__, self.id) def __unicode__(self): - return u'%s,%s' % (Model.__name__, self.id) + return '%s,%s' % (Model.__name__, self.id) return [TranslateModel(id) for id in ids] @classmethod diff --git a/trytond/res/group.py b/trytond/res/group.py index fd6f8816e9..55dd9941bc 100644 --- a/trytond/res/group.py +++ b/trytond/res/group.py @@ -18,11 +18,11 @@ class MenuMany2Many(fields.Many2Many): menu_ids = list(set(chain(*res.values()))) test_ids = [] for sub_ids in grouped_slice(menu_ids): - test_ids.append(map(int, Menu.search([ + test_ids.append(list(map(int, Menu.search([ ('id', 'in', sub_ids), - ]))) + ])))) menu_ids = set(chain(*test_ids)) - for group_id, ids in res.iteritems(): + for group_id, ids in res.items(): res[group_id] = tuple(id_ for id_ in ids if id_ in menu_ids) return res diff --git a/trytond/res/ir.py b/trytond/res/ir.py index 3c00ed2551..3c2cd37843 100644 --- a/trytond/res/ir.py +++ b/trytond/res/ir.py @@ -166,14 +166,12 @@ class ModelButtonGroup(DeactivableMixin, ModelSQL): pool.get('ir.model.button')._groups_cache.clear() -class ModelButtonRule: - __metaclass__ = PoolMeta +class ModelButtonRule(metaclass=PoolMeta): __name__ = 'ir.model.button.rule' group = fields.Many2One('res.group', "Group", ondelete='CASCADE') -class ModelButtonClick: - __metaclass__ = PoolMeta +class ModelButtonClick(metaclass=PoolMeta): __name__ = 'ir.model.button.click' user = fields.Many2One('res.user', "User", ondelete='CASCADE') @@ -202,8 +200,7 @@ class RuleGroupGroup(ModelSQL): super(RuleGroupGroup, cls).__register__(module_name) -class Lang: - __metaclass__ = PoolMeta +class Lang(metaclass=PoolMeta): __name__ = 'ir.lang' @classmethod @@ -213,8 +210,7 @@ class Lang: Pool().get('res.user')._get_preferences_cache.clear() -class SequenceType: - __metaclass__ = PoolMeta +class SequenceType(metaclass=PoolMeta): __name__ = 'ir.sequence.type' groups = fields.Many2Many('ir.sequence.type-res.group', 'sequence_type', 'group', 'User Groups', @@ -252,8 +248,7 @@ class SequenceTypeGroup(ModelSQL): Rule._domain_get_cache.clear() -class Sequence: - __metaclass__ = PoolMeta +class Sequence(metaclass=PoolMeta): __name__ = 'ir.sequence' groups = fields.Function(fields.Many2Many('res.group', None, None, 'User Groups'), 'get_groups', searcher='search_groups') @@ -266,7 +261,7 @@ class Sequence: code2seq.setdefault(sequence.code, []).append(sequence.id) sequence_types = SequenceType.search([ - ('code', 'in', code2seq.keys()), + ('code', 'in', list(code2seq.keys())), ]) groups = {} @@ -291,8 +286,7 @@ class SequenceStrict(Sequence): __name__ = 'ir.sequence.strict' -class ModuleConfigWizardItem: - __metaclass__ = PoolMeta +class ModuleConfigWizardItem(metaclass=PoolMeta): __name__ = 'ir.module.config_wizard.item' @classmethod diff --git a/trytond/res/user.py b/trytond/res/user.py index a1ea7fe366..b622b909d7 100644 --- a/trytond/res/user.py +++ b/trytond/res/user.py @@ -1,7 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. "User" -from __future__ import division + import copy import string import random @@ -18,7 +18,7 @@ except ImportError: import ipaddress from email.header import Header from functools import wraps -from itertools import groupby, ifilter +from itertools import groupby from operator import attrgetter from ast import literal_eval @@ -277,7 +277,7 @@ class User(DeactivableMixin, ModelSQL, ModelView): @classmethod def validate_password(cls, password, users): password_b = password - if isinstance(password, unicode): + if isinstance(password, str): password_b = password.encode('utf-8') length = config.getint('password', 'length', default=0) if length > 0: @@ -349,7 +349,7 @@ class User(DeactivableMixin, ModelSQL, ModelView): timestamp = session.write_date or session.create_date return abs(timestamp - now) < timeout result.update(dict((i, len(list(g))) - for i, g in groupby(ifilter(filter_, sessions), + for i, g in groupby(filter(filter_, sessions), attrgetter('create_uid.id')))) return result @@ -388,7 +388,7 @@ class User(DeactivableMixin, ModelSQL, ModelView): args.extend((users, cls._convert_vals(values))) super(User, cls).write(*args) # Clean cursor cache as it could be filled by domain_get - for cache in Transaction().cache.itervalues(): + for cache in Transaction().cache.values(): if cls.__name__ in cache: for user in all_users: if user.id in cache[cls.__name__]: @@ -685,35 +685,35 @@ class User(DeactivableMixin, ModelSQL, ModelView): def hash_sha1(cls, password): salt = gen_password() salted_password = password + salt - if isinstance(salted_password, unicode): + if isinstance(salted_password, str): salted_password = salted_password.encode('utf-8') hash_ = hashlib.sha1(salted_password).hexdigest() return '$'.join(['sha1', hash_, salt]) @classmethod def check_sha1(cls, password, hash_): - if isinstance(password, unicode): + if isinstance(password, str): password = password.encode('utf-8') hash_method, hash_, salt = hash_.split('$', 2) salt = salt or '' - if isinstance(salt, unicode): + if isinstance(salt, str): salt = salt.encode('utf-8') assert hash_method == 'sha1' return hash_ == hashlib.sha1(password + salt).hexdigest() @classmethod def hash_bcrypt(cls, password): - if isinstance(password, unicode): + if isinstance(password, str): password = password.encode('utf-8') hash_ = bcrypt.hashpw(password, bcrypt.gensalt()).decode('utf-8') return '$'.join(['bcrypt', hash_]) @classmethod def check_bcrypt(cls, password, hash_): - if isinstance(password, unicode): + if isinstance(password, str): password = password.encode('utf-8') hash_method, hash_ = hash_.split('$', 1) - if isinstance(hash_, unicode): + if isinstance(hash_, str): hash_ = hash_.encode('utf-8') assert hash_method == 'bcrypt' return hash_ == bcrypt.hashpw(password, hash_) @@ -751,11 +751,11 @@ class LoginAttempt(ModelSQL): ip_network = '' if context.get('_request') and context['_request'].get('remote_addr'): ip_address = ipaddress.ip_address( - unicode(context['_request']['remote_addr'])) + str(context['_request']['remote_addr'])) prefix = config.getint( 'session', 'ip_network_%s' % ip_address.version) ip_network = ipaddress.ip_network( - unicode(context['_request']['remote_addr'])) + str(context['_request']['remote_addr'])) ip_network = ip_network.supernet(new_prefix=prefix) return ip_address, ip_network @@ -922,7 +922,7 @@ class UserApplication(Workflow, ModelSQL, ModelView): @classmethod def default_key(cls): - return ''.join(uuid.uuid4().hex for _ in xrange(4)) + return ''.join(uuid.uuid4().hex for _ in range(4)) @classmethod def default_state(cls): diff --git a/trytond/rpc.py b/trytond/rpc.py index e0d3959d49..9000d4b233 100644 --- a/trytond/rpc.py +++ b/trytond/rpc.py @@ -38,7 +38,7 @@ class RPC(object): context = args.pop() context = copy.deepcopy(context) timestamp = None - for key in context.keys(): + for key in list(context.keys()): if key == '_timestamp': timestamp = context[key] # Remove all private keyword but _datetime for history @@ -48,7 +48,7 @@ class RPC(object): def instance(data): with Transaction().set_context(context): - if isinstance(data, (int, long)): + if isinstance(data, int): return obj(data) elif isinstance(data, dict): return obj(**data) diff --git a/trytond/sendmail.py b/trytond/sendmail.py index 1e9c4c1904..0d56d044b9 100644 --- a/trytond/sendmail.py +++ b/trytond/sendmail.py @@ -2,9 +2,8 @@ # this repository contains the full copyright notices and license terms. import logging import smtplib -import urllib from email.message import Message -from urlparse import parse_qs +from urllib.parse import parse_qs, unquote_plus from .config import config, parse_uri from .transaction import Transaction @@ -36,7 +35,7 @@ def sendmail(from_addr, to_addrs, msg, server=None): logger.error('fail to send email', exc_info=True) else: if senderrs: - logger.warn('fail to send email to %s', senderrs) + logger.warning('fail to send email to %s', senderrs) if quit: server.quit() @@ -48,7 +47,7 @@ def get_smtp_server(uri=None): extra = {} if uri.query: cast = {'timeout': int} - for key, value in parse_qs(uri.query, strict_parsing=True).iteritems(): + for key, value in parse_qs(uri.query, strict_parsing=True).items(): extra[key] = cast.get(key, lambda a: a)(value[0]) if uri.scheme.startswith('smtps'): server = smtplib.SMTP_SSL(uri.hostname, uri.port, **extra) @@ -60,8 +59,8 @@ def get_smtp_server(uri=None): if uri.username and uri.password: server.login( - urllib.unquote_plus(uri.username), - urllib.unquote_plus(uri.password)) + unquote_plus(uri.username), + unquote_plus(uri.password)) return server diff --git a/trytond/tests/__init__.py b/trytond/tests/__init__.py index f3e8812809..29724e3236 100644 --- a/trytond/tests/__init__.py +++ b/trytond/tests/__init__.py @@ -17,7 +17,7 @@ from .access import * from .wizard import * from .workflow import * from .copy_ import * -from history import * +from .history import * from .field_context import * from . import model from . import modelstorage diff --git a/trytond/tests/export_data.py b/trytond/tests/export_data.py index 422e6b24b0..a7dabc3f31 100644 --- a/trytond/tests/export_data.py +++ b/trytond/tests/export_data.py @@ -43,9 +43,8 @@ class ExportData(ModelSQL): ]) -class ExportDataTarget2: +class ExportDataTarget2(metaclass=PoolMeta): 'Export Date Target' - __metaclass__ = PoolMeta __name__ = 'test.export_data.target' one2many = fields.Many2One('test.export_data', 'Export Data') diff --git a/trytond/tests/run-tests.py b/trytond/tests/run-tests.py index 66110942ed..b8371af5a3 100755 --- a/trytond/tests/run-tests.py +++ b/trytond/tests/run-tests.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import logging diff --git a/trytond/tests/test_exportdata.py b/trytond/tests/test_exportdata.py index bd72e7f453..e43a45a6a6 100644 --- a/trytond/tests/test_exportdata.py +++ b/trytond/tests/test_exportdata.py @@ -168,7 +168,7 @@ class ExportDataTestCase(unittest.TestCase): export1, = ExportData.create([{ 'date': datetime.date(2010, 1, 1), }]) - self.assert_(ExportData.export_data([export1], + self.assertTrue(ExportData.export_data([export1], ['date']) == [[datetime.date(2010, 1, 1)]]) export2, = ExportData.create([{ diff --git a/trytond/tests/test_field_char.py b/trytond/tests/test_field_char.py index 783e03bca8..476d572c19 100644 --- a/trytond/tests/test_field_char.py +++ b/trytond/tests/test_field_char.py @@ -32,10 +32,10 @@ class CommonTestCaseMixin: Char = self.Char() char, = Char.create([{ - 'char': u"é", + 'char': "é", }]) - self.assertEqual(char.char, u"é") + self.assertEqual(char.char, "é") @with_transaction() def test_search_equals(self): @@ -74,11 +74,11 @@ class CommonTestCaseMixin: "Test search char equals unicode" Char = self.Char() char, = Char.create([{ - 'char': u"é", + 'char': "é", }]) chars = Char.search([ - ('char', '=', u"é"), + ('char', '=', "é"), ]) self.assertListEqual(chars, [char]) @@ -88,7 +88,7 @@ class CommonTestCaseMixin: "Test search char equals non unicode" Char = self.Char() char, = Char.create([{ - 'char': u"é", + 'char': "é", }]) chars = Char.search([ @@ -314,10 +314,10 @@ class CommonTestCaseMixin: }]) Char.write([char], { - 'char': u"é", + 'char': "é", }) - self.assertEqual(char.char, u"é") + self.assertEqual(char.char, "é") class FieldCharTestCase(unittest.TestCase, CommonTestCaseMixin): @@ -498,7 +498,7 @@ class FieldCharUnaccentedTestCase(unittest.TestCase): "Test searches without the unaccented feature" Char = Pool().get('test.char_unaccented_off') char, = Char.create([{ - 'char': u'Stéphanie', + 'char': 'Stéphanie', }]) chars_stephanie = Char.search([ @@ -512,7 +512,7 @@ class FieldCharUnaccentedTestCase(unittest.TestCase): "Test searches of accented value" Char = Pool().get('test.char_unaccented_on') char, = Char.create([{ - 'char': u'Stéphanie', + 'char': 'Stéphanie', }]) chars_stephanie = Char.search([ @@ -526,7 +526,7 @@ class FieldCharUnaccentedTestCase(unittest.TestCase): "Test searches of unaccented value" Char = Pool().get('test.char_unaccented_on') char, = Char.create([{ - 'char': u'Stephanie', + 'char': 'Stephanie', }]) chars_stephanie = Char.search([ @@ -548,12 +548,12 @@ class FieldCharUnaccentedTestCase(unittest.TestCase): lang.translatable = True lang.save() char, = Char.create([{ - 'char': u'School', + 'char': 'School', }]) with Transaction().set_context(lang=lang.code): trans_char = Char(char.id) - trans_char.char = u'École' + trans_char.char = 'École' trans_char.save() chars_ecole = Char.search([ diff --git a/trytond/tests/test_field_text.py b/trytond/tests/test_field_text.py index 6e747ef0fc..c9e8d42a9f 100644 --- a/trytond/tests/test_field_text.py +++ b/trytond/tests/test_field_text.py @@ -30,10 +30,10 @@ class CommonTestCaseMixin: Text = self.Text() text, = Text.create([{ - 'text': u"é", + 'text': "é", }]) - self.assertEqual(text.text, u"é") + self.assertEqual(text.text, "é") @with_transaction() def test_create_multiline(self): @@ -83,11 +83,11 @@ class CommonTestCaseMixin: "Test search text equals unicode" Text = self.Text() text, = Text.create([{ - 'text': u"é", + 'text': "é", }]) texts = Text.search([ - ('text', '=', u"é"), + ('text', '=', "é"), ]) self.assertListEqual(texts, [text]) @@ -97,7 +97,7 @@ class CommonTestCaseMixin: "Test search text equals non unicode" Text = self.Text() text, = Text.create([{ - 'text': u"é", + 'text': "é", }]) texts = Text.search([ @@ -323,10 +323,10 @@ class CommonTestCaseMixin: }]) Text.write([text], { - 'text': u"é", + 'text': "é", }) - self.assertEqual(text.text, u"é") + self.assertEqual(text.text, "é") class FieldTextTestCase(unittest.TestCase, CommonTestCaseMixin): diff --git a/trytond/tests/test_filestore.py b/trytond/tests/test_filestore.py index 46c82a16da..49851b0a90 100644 --- a/trytond/tests/test_filestore.py +++ b/trytond/tests/test_filestore.py @@ -4,7 +4,7 @@ import os import shutil import tempfile import unittest -from mock import patch +from unittest.mock import patch from trytond.config import config from trytond.filestore import filestore @@ -27,14 +27,14 @@ class FileStoreTestCase(unittest.TestCase): "Test set" result = filestore.set(self.data(), prefix='test') self.assertTrue(result) - self.assertIsInstance(result, basestring) + self.assertIsInstance(result, str) def test_setmany(self): "Test setmany" result = filestore.setmany([self.data(), self.data()], prefix='test') self.assertIsInstance(result, list) self.assertEqual(len(result), 2) - self.assertIsInstance(result[0], basestring) + self.assertIsInstance(result[0], str) def test_get(self): "Test get" diff --git a/trytond/tests/test_history.py b/trytond/tests/test_history.py index 7b3c013aa3..9eb93f8ee2 100644 --- a/trytond/tests/test_history.py +++ b/trytond/tests/test_history.py @@ -142,9 +142,9 @@ class HistoryTestCase(unittest.TestCase): revisions = History.history_revisions([history_id]) self.assertEqual(revisions, [ - (third, history_id, u'Administrator'), - (second, history_id, u'Administrator'), - (first, history_id, u'Administrator'), + (third, history_id, 'Administrator'), + (second, history_id, 'Administrator'), + (first, history_id, 'Administrator'), ]) @with_transaction() @@ -465,7 +465,7 @@ class HistoryTestCase(unittest.TestCase): transaction = Transaction() database = transaction.database - for i in xrange(0, 2): + for i in range(0, 2): history = History(value=-1) history.save() diff --git a/trytond/tests/test_ir.py b/trytond/tests/test_ir.py index f64d14979b..e271f9be00 100644 --- a/trytond/tests/test_ir.py +++ b/trytond/tests/test_ir.py @@ -54,9 +54,9 @@ class IrTestCase(ModuleTestCase): pool = Pool() Lang = pool.get('ir.lang') test_data = [ - ((2016, 8, 3), 'en', '%d %B %Y', u"03 August 2016"), - ((2016, 8, 3), 'fr', '%d %B %Y', u"03 ao\xfbt 2016"), - ((2016, 8, 3), 'fr', u'%d %B %Y', u"03 ao\xfbt 2016"), + ((2016, 8, 3), 'en', '%d %B %Y', "03 August 2016"), + ((2016, 8, 3), 'fr', '%d %B %Y', "03 ao\xfbt 2016"), + ((2016, 8, 3), 'fr', '%d %B %Y', "03 ao\xfbt 2016"), ] for date, code, format_, result in test_data: lang = Lang.get(code) diff --git a/trytond/tests/test_mixins.py b/trytond/tests/test_mixins.py index c8ad116a04..abaef90253 100644 --- a/trytond/tests/test_mixins.py +++ b/trytond/tests/test_mixins.py @@ -3,7 +3,7 @@ # this repository contains the full copyright notices and license terms. import unittest -import urllib +import urllib.parse from trytond.tests.test_tryton import activate_module, with_transaction from trytond.transaction import Transaction @@ -30,11 +30,11 @@ class UrlTestCase(unittest.TestCase): self.assertEqual(UrlObject.__url__, 'tryton://%s/%s/model/test.urlobject' % ( - HOSTNAME, urllib.quote(db_name))) + HOSTNAME, urllib.parse.quote(db_name))) self.assertEqual(UrlObject(1).__url__, 'tryton://%s/%s/model/test.urlobject/1' % ( - HOSTNAME, urllib.quote(db_name))) + HOSTNAME, urllib.parse.quote(db_name))) @with_transaction() def testWizardURL(self): @@ -45,7 +45,7 @@ class UrlTestCase(unittest.TestCase): self.assertEqual(UrlWizard.__url__, 'tryton://%s/%s/wizard/test.test_wizard' % ( - HOSTNAME, urllib.quote(db_name))) + HOSTNAME, urllib.parse.quote(db_name))) class MixinTestCase(unittest.TestCase): diff --git a/trytond/tests/test_modelsingleton.py b/trytond/tests/test_modelsingleton.py index f944c64246..576e6bfc14 100644 --- a/trytond/tests/test_modelsingleton.py +++ b/trytond/tests/test_modelsingleton.py @@ -22,12 +22,12 @@ class ModelSingletonTestCase(unittest.TestCase): Singleton = pool.get('test.singleton') singleton, = Singleton.read([1], ['name']) - self.assert_(singleton['name'] == 'test') - self.assert_(singleton['id'] == 1) + self.assertTrue(singleton['name'] == 'test') + self.assertTrue(singleton['id'] == 1) singleton, = Singleton.read([1], ['name']) - self.assert_(singleton['name'] == 'test') - self.assert_(singleton['id'] == 1) + self.assertTrue(singleton['name'] == 'test') + self.assertTrue(singleton['id'] == 1) singleton, = Singleton.read([1], [ 'create_uid', @@ -38,7 +38,7 @@ class ModelSingletonTestCase(unittest.TestCase): ]) self.assertEqual(singleton['create_uid'], Transaction().user) self.assertEqual(singleton['create_uid.rec_name'], 'Administrator') - self.assert_(isinstance(singleton['create_date'], datetime)) + self.assertTrue(isinstance(singleton['create_date'], datetime)) self.assertEqual(singleton['write_uid'], None) self.assertEqual(singleton['write_date'], None) @@ -49,7 +49,7 @@ class ModelSingletonTestCase(unittest.TestCase): Singleton = pool.get('test.singleton') singleton, = Singleton.create([{'name': 'bar'}]) - self.assert_(singleton) + self.assertTrue(singleton) self.assertEqual(singleton.name, 'bar') singleton2, = Singleton.create([{'name': 'foo'}]) @@ -115,10 +115,10 @@ class ModelSingletonTestCase(unittest.TestCase): Singleton = pool.get('test.singleton') singletons = Singleton.search([]) - self.assertEqual(map(int, singletons), [1]) + self.assertEqual(list(map(int, singletons)), [1]) singletons = Singleton.search([]) - self.assertEqual(map(int, singletons), [1]) + self.assertEqual(list(map(int, singletons)), [1]) count = Singleton.search([], count=True) self.assertEqual(count, 1) diff --git a/trytond/tests/test_modelsql.py b/trytond/tests/test_modelsql.py index da77f06ad2..179e9a8174 100644 --- a/trytond/tests/test_modelsql.py +++ b/trytond/tests/test_modelsql.py @@ -4,8 +4,7 @@ import unittest import time - -from mock import patch, call +from unittest.mock import patch, call from trytond import backend from trytond.exceptions import UserError, ConcurrencyException @@ -36,10 +35,10 @@ class ModelSQLTestCase(unittest.TestCase): 'desc': '', 'integer': 0, } - for key, value in fields.iteritems(): + for key, value in fields.items(): try: Modelsql.create([{key: value}]) - except UserError, err: + except UserError as err: # message must not quote key msg = "'%s' not missing but quoted in error: '%s'" % (key, err.message) diff --git a/trytond/tests/test_modelstorage.py b/trytond/tests/test_modelstorage.py index f22e9b4e16..d728a7a350 100644 --- a/trytond/tests/test_modelstorage.py +++ b/trytond/tests/test_modelstorage.py @@ -87,7 +87,7 @@ class ModelStorageTestCase(unittest.TestCase): self.assertDictEqual(record.context, record_context) record = ModelStorageContext(record.id) - self.assertDictContainsSubset({'foo': 'bar'}, record.context) + self.assertEqual(record.context.get('foo'), 'bar') @with_transaction() def test_save_mixed_context(self): diff --git a/trytond/tests/test_modelview.py b/trytond/tests/test_modelview.py index 8ef8720d9e..e62b9eb60e 100644 --- a/trytond/tests/test_modelview.py +++ b/trytond/tests/test_modelview.py @@ -2,7 +2,8 @@ # this repository contains the full copyright notices and license terms. import unittest -from mock import patch +from unittest.mock import patch + from lxml import etree from trytond.tests.test_tryton import activate_module, with_transaction diff --git a/trytond/tests/test_mptt.py b/trytond/tests/test_mptt.py index da8caf231c..2cbe6f9acb 100644 --- a/trytond/tests/test_mptt.py +++ b/trytond/tests/test_mptt.py @@ -3,7 +3,8 @@ # this repository contains the full copyright notices and license terms. import sys import unittest -from mock import patch +from unittest.mock import patch + from trytond.tests.test_tryton import activate_module, with_transaction from trytond.transaction import Transaction from trytond.pool import Pool @@ -16,7 +17,7 @@ class MPTTTestCase(unittest.TestCase): def setUpClass(cls): activate_module('tests') - def check_tree(self, parent_id=None, left=-1, right=sys.maxint): + def check_tree(self, parent_id=None, left=-1, right=sys.maxsize): pool = Pool() Mptt = pool.get('test.mptt') @@ -43,7 +44,7 @@ class MPTTTestCase(unittest.TestCase): (child, child.left, next_left) next_left = child.right childs.reverse() - previous_right = sys.maxint + previous_right = sys.maxsize for child in childs: assert child.right < previous_right, \ '%s: right %d >= previous right %d' \ diff --git a/trytond/tests/test_order.py b/trytond/tests/test_order.py index a97c0b3514..8eb7048b8c 100644 --- a/trytond/tests/test_order.py +++ b/trytond/tests/test_order.py @@ -20,7 +20,7 @@ class SequenceOrderedMixinTestCase(unittest.TestCase): Order = pool.get('test.order.sequence') models = [] - for i in reversed(range(1, 4)): + for i in reversed(list(range(1, 4))): models.append(Order(sequence=i)) Order.save(models) models.reverse() diff --git a/trytond/tests/test_pyson.py b/trytond/tests/test_pyson.py index fbc6f1a937..7664ed56de 100644 --- a/trytond/tests/test_pyson.py +++ b/trytond/tests/test_pyson.py @@ -28,7 +28,7 @@ class PYSONTestCase(unittest.TestCase): 'd': 'foo', }) - self.assertEqual(pyson.Eval('test', 'foo').types(), set([basestring])) + self.assertEqual(pyson.Eval('test', 'foo').types(), set([str])) self.assertEqual(pyson.Eval('test', 1).types(), set([int])) eval = pyson.PYSONEncoder().encode(pyson.Eval('test', 0)) @@ -310,7 +310,7 @@ class PYSONTestCase(unittest.TestCase): self.assertRaises(AssertionError, pyson.If, True, 'foo', False) self.assertEqual(pyson.If(True, 'foo', 'bar').types(), - set([basestring])) + set([str])) self.assertEqual(pyson.If(True, False, True).types(), set([bool])) eval = pyson.PYSONEncoder().encode(pyson.If(True, 'foo', 'bar')) @@ -335,7 +335,7 @@ class PYSONTestCase(unittest.TestCase): self.assertRaises(AssertionError, pyson.Get, {}, 1, 'default') self.assertEqual(pyson.Get({}, 'foo', 'default').types(), - set([basestring])) + set([str])) self.assertEqual(pyson.Get({}, 'foo', True).types(), set([bool])) eval = pyson.PYSONEncoder().encode(pyson.Get( diff --git a/trytond/tests/test_rpc.py b/trytond/tests/test_rpc.py index c2c5d0de26..8601f58cfa 100644 --- a/trytond/tests/test_rpc.py +++ b/trytond/tests/test_rpc.py @@ -1,8 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import unittest - -from mock import Mock, DEFAULT, call +from unittest.mock import Mock, DEFAULT, call from trytond.tests.test_tryton import with_transaction, activate_module from trytond.rpc import RPC diff --git a/trytond/tests/test_sendmail.py b/trytond/tests/test_sendmail.py index 6445518428..14bd364b14 100644 --- a/trytond/tests/test_sendmail.py +++ b/trytond/tests/test_sendmail.py @@ -1,10 +1,9 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -import unittest import smtplib -import sys +import unittest from email.message import Message -from mock import Mock, patch, call +from unittest.mock import Mock, patch, call from trytond.sendmail import ( sendmail_transactional, sendmail, SMTPDataManager, get_smtp_server) @@ -68,7 +67,6 @@ class SendmailTestCase(unittest.TestCase): SMTP.assert_called_once_with('localhost', 25) server.starttls.assert_called_once_with() - @unittest.skipIf(sys.version_info < (2, 7, 4), "python bug #9374") def test_get_smtp_server_extra_parameters(self): 'Test get_smtp_server uri extra parameters' with patch.object(smtplib, 'SMTP') as SMTP: diff --git a/trytond/tests/test_tools.py b/trytond/tests/test_tools.py index 4271c0b624..59b55a77fa 100644 --- a/trytond/tests/test_tools.py +++ b/trytond/tests/test_tools.py @@ -22,26 +22,26 @@ class ToolsTestCase(unittest.TestCase): def test_reduce_ids_continue(self): 'Test reduce_ids continue list' - self.assertEqual(reduce_ids(self.table.id, range(10)), + self.assertEqual(reduce_ids(self.table.id, list(range(10))), sql.operators.Or(((self.table.id >= 0) & (self.table.id <= 9),))) def test_reduce_ids_one_hole(self): 'Test reduce_ids continue list with one hole' - self.assertEqual(reduce_ids(self.table.id, range(10) + range(20, 30)), + self.assertEqual(reduce_ids(self.table.id, list(range(10)) + list(range(20, 30))), ((self.table.id >= 0) & (self.table.id <= 9)) | ((self.table.id >= 20) & (self.table.id <= 29))) def test_reduce_ids_short_continue(self): 'Test reduce_ids short continue list' - self.assertEqual(reduce_ids(self.table.id, range(4)), - sql.operators.Or((self.table.id.in_(range(4)),))) + self.assertEqual(reduce_ids(self.table.id, list(range(4))), + sql.operators.Or((self.table.id.in_(list(range(4))),))) def test_reduce_ids_complex(self): 'Test reduce_ids complex list' self.assertEqual(reduce_ids(self.table.id, - range(10) + range(25, 30) + range(15, 20)), + list(range(10)) + list(range(25, 30)) + list(range(15, 20))), (((self.table.id >= 0) & (self.table.id <= 14)) - | (self.table.id.in_(range(25, 30))))) + | (self.table.id.in_(list(range(25, 30)))))) def test_reduce_ids_complex_small_continue(self): 'Test reduce_ids complex list with small continue' @@ -61,11 +61,11 @@ class ToolsTestCase(unittest.TestCase): def test_datetime_strftime(self): 'Test datetime_strftime' - self.assert_(datetime_strftime(datetime.date(2005, 3, 2), + self.assertTrue(datetime_strftime(datetime.date(2005, 3, 2), '%Y-%m-%d'), '2005-03-02') - self.assert_(datetime_strftime(datetime.date(1805, 3, 2), + self.assertTrue(datetime_strftime(datetime.date(1805, 3, 2), '%Y-%m-%d'), '1805-03-02') - self.assert_(datetime_strftime(datetime.datetime(2005, 3, 2, 0, 0, 0), + self.assertTrue(datetime_strftime(datetime.datetime(2005, 3, 2, 0, 0, 0), '%Y-%m-%d'), '2005-03-02') with self.assertRaises(TypeError): datetime_strftime(None, '%Y-%m-%d') @@ -122,21 +122,27 @@ class ToolsTestCase(unittest.TestCase): def test_file_open(self): "Test file_open" - self.assertTrue(file_open('__init__.py', subdir=None)) - self.assertTrue(file_open('ir/__init__.py')) + with file_open('__init__.py', subdir=None) as fp: + self.assertTrue(fp) - with self.assertRaisesRegexp(IOError, "File not found :"): - file_open('ir/noexist') + with file_open('ir/__init__.py') as fp: + self.assertTrue(fp) - with self.assertRaisesRegexp(IOError, "Permission denied:"): - file_open('/etc/passwd') + with self.assertRaisesRegex(IOError, "File not found :"): + with file_open('ir/noexist'): + pass - with self.assertRaisesRegexp(IOError, "Permission denied:"): - file_open('../../foo') + with self.assertRaisesRegex(IOError, "Permission denied:"): + with file_open('/etc/passwd'): + pass + + with self.assertRaisesRegex(IOError, "Permission denied:"): + with file_open('../../foo'): + pass def test_file_open_suffix(self): "Test file_open from same root name but with a suffix" - with self.assertRaisesRegexp(IOError, "Permission denied:"): + with self.assertRaisesRegex(IOError, "Permission denied:"): file_open('../trytond_suffix', subdir=None) diff --git a/trytond/tests/test_transaction.py b/trytond/tests/test_transaction.py index 8f9fb5baf3..1e8b404016 100644 --- a/trytond/tests/test_transaction.py +++ b/trytond/tests/test_transaction.py @@ -1,7 +1,8 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import unittest -from mock import Mock +from unittest.mock import Mock + from trytond.tests.test_tryton import DB_NAME, USER, CONTEXT, activate_module from trytond.transaction import Transaction diff --git a/trytond/tests/test_trigger.py b/trytond/tests/test_trigger.py index df82491648..b63a017a94 100644 --- a/trytond/tests/test_trigger.py +++ b/trytond/tests/test_trigger.py @@ -44,7 +44,7 @@ class TriggerTestCase(unittest.TestCase): 'action_model': action_model.id, 'action_function': 'test', } - self.assert_(Trigger.create([values])) + self.assertTrue(Trigger.create([values])) transaction.rollback() @@ -387,7 +387,7 @@ class TriggerTestCase(unittest.TestCase): 'name': 'Test', }]) Trigger.trigger_time() - self.assert_(TRIGGER_LOGS == [([triggered], trigger)]) + self.assertTrue(TRIGGER_LOGS == [([triggered], trigger)]) TRIGGER_LOGS.pop() # Trigger with condition @@ -402,7 +402,7 @@ class TriggerTestCase(unittest.TestCase): 'name': 'Bar', }) Trigger.trigger_time() - self.assert_(TRIGGER_LOGS == [([triggered], trigger)]) + self.assertTrue(TRIGGER_LOGS == [([triggered], trigger)]) TRIGGER_LOGS.pop() # Non matching condition @@ -410,7 +410,7 @@ class TriggerTestCase(unittest.TestCase): 'name': 'Foo', }) Trigger.trigger_time() - self.assert_(TRIGGER_LOGS == []) + self.assertTrue(TRIGGER_LOGS == []) # With limit number Trigger.write([trigger], { @@ -419,7 +419,7 @@ class TriggerTestCase(unittest.TestCase): }) Trigger.trigger_time() Trigger.trigger_time() - self.assert_(TRIGGER_LOGS == [([triggered], trigger)]) + self.assertTrue(TRIGGER_LOGS == [([triggered], trigger)]) TRIGGER_LOGS.pop() # Delete trigger logs of limit number test @@ -434,7 +434,7 @@ class TriggerTestCase(unittest.TestCase): }) Trigger.trigger_time() Trigger.trigger_time() - self.assert_(TRIGGER_LOGS == [([triggered], trigger)]) + self.assertTrue(TRIGGER_LOGS == [([triggered], trigger)]) TRIGGER_LOGS.pop() Transaction().delete = {} @@ -449,7 +449,7 @@ class TriggerTestCase(unittest.TestCase): Trigger.trigger_time() time.sleep(1.2) Trigger.trigger_time() - self.assert_(TRIGGER_LOGS == [([triggered], trigger), + self.assertTrue(TRIGGER_LOGS == [([triggered], trigger), ([triggered], trigger)]) TRIGGER_LOGS.pop() TRIGGER_LOGS.pop() diff --git a/trytond/tests/test_tryton.py b/trytond/tests/test_tryton.py index 681ade6f40..23f81e9456 100644 --- a/trytond/tests/test_tryton.py +++ b/trytond/tests/test_tryton.py @@ -1,17 +1,17 @@ # -*- coding: utf-8 -*- # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -import os -import sys -import unittest import doctest -import re -import subprocess -import time -from itertools import chain -import operator -from functools import wraps import inspect +import operator +import os +import subprocess +import sys +import time +import unittest +from functools import reduce +from functools import wraps +from itertools import chain try: import pkg_resources except ImportError: @@ -108,8 +108,7 @@ def backup_db_cache(name): def _db_cache_file(path, name, backend_name): - return os.path.join(path, '%s-%s-py%s.dump' - % (name, backend_name, sys.version_info.major)) + return os.path.join(path, '%s-%s.dump' % (name, backend_name)) def _sqlite_copy(file_, restore=False): @@ -289,7 +288,7 @@ class ModuleTestCase(unittest.TestCase): for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue - for fname, field in model._fields.iteritems(): + for fname, field in model._fields.items(): fields = set() fields |= get_eval_fields(field.domain) if hasattr(field, 'digits'): @@ -309,7 +308,7 @@ class ModuleTestCase(unittest.TestCase): 'Unknown depends %s in "%s"."%s"' % ( list(depends - set(model._fields)), mname, fname)) if issubclass(model, ModelView): - for bname, button in model._buttons.iteritems(): + for bname, button in model._buttons.items(): depends = set(button.get('depends', [])) assert depends <= set(model._fields), ( 'Unknown depends %s in button "%s"."%s"' % ( @@ -321,7 +320,7 @@ class ModuleTestCase(unittest.TestCase): for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue - for fname, field in model._fields.iteritems(): + for fname, field in model._fields.items(): for attribute in ['depends', 'on_change', 'on_change_with', 'selection_change_with', 'autocomplete']: depends = getattr(field, attribute, []) @@ -470,7 +469,7 @@ class ModuleTestCase(unittest.TestCase): 'wizard': wizard_name, }) wizard_instance = wizard(session_id) - for state_name, state in wizard_instance.states.iteritems(): + for state_name, state in wizard_instance.states.items(): if isinstance(state, StateView): # Don't test defaults as they may depend on context state.get_view(wizard_instance, state_name) @@ -484,7 +483,7 @@ class ModuleTestCase(unittest.TestCase): for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue - for field_name, field in model._fields.iteritems(): + for field_name, field in model._fields.items(): selection = getattr(field, 'selection', None) if selection is None: continue @@ -510,7 +509,7 @@ class ModuleTestCase(unittest.TestCase): for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue - for field_name, field in model._fields.iteritems(): + for field_name, field in model._fields.items(): if not isinstance(field, Function): continue for func_name in [field.getter, field.setter, field.searcher]: @@ -661,18 +660,16 @@ def drop_create(name=DB_NAME, lang='en'): drop_db(name) create_db(name, lang) -doctest_setup = lambda test: drop_create() -doctest_teardown = lambda test: drop_db() + +def doctest_setup(test): + return drop_create() -class Py23DocChecker(doctest.OutputChecker): - def check_output(self, want, got, optionflags): - if sys.version_info[0] > 2: - want = re.sub("u'(.*?)'", "'\\1'", want) - want = re.sub('u"(.*?)"', '"\\1"', want) - return doctest.OutputChecker.check_output(self, want, got, optionflags) +def doctest_teardown(test): + return drop_db() -doctest_checker = Py23DocChecker() + +doctest_checker = doctest.OutputChecker() class TestSuite(unittest.TestSuite): @@ -682,7 +679,7 @@ class TestSuite(unittest.TestSuite): try: exist = db_exist() break - except DatabaseOperationalError, err: + except DatabaseOperationalError as err: # Retry on connection error sys.stderr.write(str(err)) time.sleep(1) diff --git a/trytond/tests/test_user.py b/trytond/tests/test_user.py index ab3c0b11fc..547c56dc6f 100644 --- a/trytond/tests/test_user.py +++ b/trytond/tests/test_user.py @@ -116,8 +116,8 @@ class UserTestCase(unittest.TestCase): User = pool.get('res.user') with self.assertRaises(UserError): - User.validate_password(u'123', []) - User.validate_password(u'1234', []) + User.validate_password('123', []) + User.validate_password('1234', []) @with_transaction() def test_validate_password_forbidden(self): @@ -126,7 +126,7 @@ class UserTestCase(unittest.TestCase): User = pool.get('res.user') with self.assertRaises(UserError): - User.validate_password(u'password', []) + User.validate_password('password', []) @with_transaction() def test_validate_password_entropy(self): diff --git a/trytond/tests/test_wizard.py b/trytond/tests/test_wizard.py index 6c25f17888..2203c6290d 100644 --- a/trytond/tests/test_wizard.py +++ b/trytond/tests/test_wizard.py @@ -21,7 +21,7 @@ class WizardTestCase(unittest.TestCase): session_id, start_state, end_state = Wizard.create() self.assertEqual(start_state, 'start') self.assertEqual(end_state, 'end') - self.assert_(session_id) + self.assertTrue(session_id) @with_transaction() def test_delete(self): @@ -78,7 +78,7 @@ class WizardTestCase(unittest.TestCase): session_id, start_state, end_state = Wizard.create() result = Wizard.execute(session_id, {}, start_state) - self.assertEqual(result.keys(), ['view']) + self.assertEqual(list(result.keys()), ['view']) self.assertEqual(result['view']['defaults'], { 'name': 'Test wizard', }) diff --git a/trytond/tests/tools.py b/trytond/tests/tools.py index b038e5df79..de813ac87f 100644 --- a/trytond/tests/tools.py +++ b/trytond/tests/tools.py @@ -8,7 +8,7 @@ __all__ = ['activate_modules', 'set_user'] def activate_modules(modules): - if isinstance(modules, basestring): + if isinstance(modules, str): modules = [modules] cache_name = '-'.join(modules) if restore_db_cache(cache_name): diff --git a/trytond/tools/__init__.py b/trytond/tools/__init__.py index 5854f45760..96f012e929 100644 --- a/trytond/tools/__init__.py +++ b/trytond/tools/__init__.py @@ -1,6 +1,6 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from itertools import izip + from .misc import * from .datetime_strftime import * @@ -19,4 +19,4 @@ def cursor_dict(cursor, size=None): if not rows: break for row in rows: - yield {d[0]: v for d, v in izip(cursor.description, row)} + yield {d[0]: v for d, v in zip(cursor.description, row)} diff --git a/trytond/tools/decimal_.py b/trytond/tools/decimal_.py index 08d36dd75f..72bd5f8360 100644 --- a/trytond/tools/decimal_.py +++ b/trytond/tools/decimal_.py @@ -1,73 +1,43 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -import sys -from tokenize import * -from io import BytesIO, StringIO +from tokenize import tokenize, untokenize, NUMBER, NAME, OP, STRING +from io import BytesIO # code snippet taken from http://docs.python.org/library/tokenize.html -if sys.version_info < (3,): - def decistmt(s): - """Substitute Decimals for floats in a string of statements. +def decistmt(s): + """Substitute Decimals for floats in a string of statements. - >>> from decimal import Decimal - >>> s = 'print +21.3e-5*-.1234/81.7' - >>> decistmt(s) - u"print +Decimal (u'21.3e-5')*-Decimal (u'.1234')/Decimal (u'81.7')" + >>> from decimal import Decimal + >>> s = 'print(+21.3e-5*-.1234/81.7)' + >>> decistmt(s) + "print (+Decimal ('21.3e-5')*-Decimal ('.1234')/Decimal ('81.7'))" - >>> exec(s) - -3.21716034272e-07 - >>> exec(decistmt(s)) - -3.217160342717258261933904529E-7 + The format of the exponent is inherited from the platform C library. + Known cases are "e-007" (Windows) and "e-07" (not Windows). Since + we're only showing 12 digits, and the 13th isn't close to 5, the + rest of the output should be platform-independent. - """ - result = [] - g = generate_tokens(StringIO(s.decode('utf-8')).readline) # tokenize the string - for toknum, tokval, _, _, _ in g: - if toknum == NUMBER and '.' in tokval: # replace NUMBER tokens - result.extend([ - (NAME, 'Decimal'), - (OP, '('), - (STRING, repr(tokval)), - (OP, ')') - ]) - else: - result.append((toknum, tokval)) - return untokenize(result) -else: - def decistmt(s): - """Substitute Decimals for floats in a string of statements. + >>> exec(s) #doctest: +ELLIPSIS + -3.217160342717258e-0...7 - >>> from decimal import Decimal - >>> s = 'print(+21.3e-5*-.1234/81.7)' - >>> decistmt(s) - "print (+Decimal ('21.3e-5')*-Decimal ('.1234')/Decimal ('81.7'))" + Output from calculations with Decimal should be identical across all + platforms. - The format of the exponent is inherited from the platform C library. - Known cases are "e-007" (Windows) and "e-07" (not Windows). Since - we're only showing 12 digits, and the 13th isn't close to 5, the - rest of the output should be platform-independent. - - >>> exec(s) #doctest: +ELLIPSIS - -3.217160342717258e-0...7 - - Output from calculations with Decimal should be identical across all - platforms. - - >>> exec(decistmt(s)) - -3.217160342717258261933904529E-7 - """ - result = [] - g = tokenize(BytesIO(s.encode('utf-8')).readline) # tokenize the string - for toknum, tokval, _, _, _ in g: - if toknum == NUMBER and '.' in tokval: # replace NUMBER tokens - result.extend([ - (NAME, 'Decimal'), - (OP, '('), - (STRING, repr(tokval)), - (OP, ')') - ]) - else: - result.append((toknum, tokval)) - return untokenize(result).decode('utf-8') + >>> exec(decistmt(s)) + -3.217160342717258261933904529E-7 + """ + result = [] + g = tokenize(BytesIO(s.encode('utf-8')).readline) # tokenize the string + for toknum, tokval, _, _, _ in g: + if toknum == NUMBER and '.' in tokval: # replace NUMBER tokens + result.extend([ + (NAME, 'Decimal'), + (OP, '('), + (STRING, repr(tokval)), + (OP, ')') + ]) + else: + result.append((toknum, tokval)) + return untokenize(result).decode('utf-8') diff --git a/trytond/tools/misc.py b/trytond/tools/misc.py index 9808ddb6a7..8c6d1c7c54 100644 --- a/trytond/tools/misc.py +++ b/trytond/tools/misc.py @@ -22,11 +22,7 @@ from trytond.const import OPERATORS def file_open(name, mode="r", subdir='modules', encoding=None): """Open a file from the root dir, using a subdir folder.""" from trytond.modules import EGG_MODULES - if sys.version_info < (3,): - filename = __file__.decode(sys.getfilesystemencoding()) - else: - filename = __file__ - root_path = os.path.dirname(os.path.dirname(os.path.abspath(filename))) + root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) def secure_join(root, *paths): "Join paths and ensure it still below root" @@ -116,11 +112,11 @@ def memoize(maxsize): def wrap(fct): cache = {} - keys = [None for i in xrange(maxsize)] + keys = [None for i in range(maxsize)] seg_size = maxsize // 4 - pointers = [i * seg_size for i in xrange(4)] - max_pointers = [(i + 1) * seg_size for i in xrange(3)] + [maxsize] + pointers = [i * seg_size for i in range(4)] + max_pointers = [(i + 1) * seg_size for i in range(3)] + [maxsize] def wrapper(*args): key = repr(args) @@ -164,7 +160,7 @@ def reduce_ids(field, ids): return Literal(False) assert all(x.is_integer() for x in ids if isinstance(x, float)), \ 'ids must be integer' - ids = map(int, ids) + ids = list(map(int, ids)) ids.sort() prev = ids.pop(0) continue_list = [prev, prev] @@ -200,7 +196,7 @@ def reduce_domain(domain): if not domain: return [] operator = 'AND' - if isinstance(domain[0], basestring): + if isinstance(domain[0], str): operator = domain[0] domain = domain[1:] result = [operator] @@ -229,7 +225,7 @@ def grouped_slice(records, count=None): from trytond.transaction import Transaction if count is None: count = Transaction().database.IN_MAX - for i in xrange(0, len(records), count): + for i in range(0, len(records), count): yield islice(records, i, i + count) diff --git a/trytond/tools/multivalue.py b/trytond/tools/multivalue.py index e0e12db727..3a201daf24 100644 --- a/trytond/tools/multivalue.py +++ b/trytond/tools/multivalue.py @@ -27,9 +27,9 @@ def migrate_property( if fields is None: fields = [] - if isinstance(field_names, basestring): + if isinstance(field_names, str): field_names = [field_names] - if isinstance(value_names, basestring): + if isinstance(value_names, str): value_names = [value_names] def split_value(value): diff --git a/trytond/transaction.py b/trytond/transaction.py index 1e7e321afb..090c9aabc0 100644 --- a/trytond/transaction.py +++ b/trytond/transaction.py @@ -22,7 +22,7 @@ class _AttributeManager(object): return Transaction() def __exit__(self, type, value, traceback): - for name, value in self.kwargs.iteritems(): + for name, value in self.kwargs.items(): setattr(Transaction(), name, value) @@ -207,7 +207,7 @@ class Transaction(object): Cache.resets(self.database.name) def rollback(self): - for cache in self.cache.itervalues(): + for cache in self.cache.values(): cache.clear() for datamanager in self._datamanagers: datamanager.tpc_abort(self) diff --git a/trytond/url.py b/trytond/url.py index a7eccd96df..7ac86ecbe2 100644 --- a/trytond/url.py +++ b/trytond/url.py @@ -2,8 +2,8 @@ # this repository contains the full copyright notices and license terms. import encodings.idna -import urllib import socket +import urllib.parse from trytond.config import config from trytond.transaction import Transaction @@ -36,7 +36,7 @@ class URLAccessor(object): url_part['name'] = cls.__name__ url_part['database'] = Transaction().database.name - local_part = urllib.quote('%(database)s/%(type)s/%(name)s' % url_part) + local_part = urllib.parse.quote('%(database)s/%(type)s/%(name)s' % url_part) if isinstance(inst, Model) and inst.id: local_part += '/%d' % inst.id return 'tryton://%s/%s' % (HOSTNAME, local_part) diff --git a/trytond/wizard/wizard.py b/trytond/wizard/wizard.py index 48e1b6ad20..3686a51f1a 100644 --- a/trytond/wizard/wizard.py +++ b/trytond/wizard/wizard.py @@ -89,7 +89,7 @@ class StateView(State): default = getattr(wizard, 'default_%s' % state_name, None) if default: defaults.update(default(fields)) - for field_name, value in defaults.items(): + for field_name, value in list(defaults.items()): if '.' in field_name: continue field = Model_._fields[field_name] @@ -278,9 +278,9 @@ class Wizard(WarningErrorMixin, URLMixin, PoolBase): ''' cls.check_access() wizard = cls(session_id) - for key, values in data.iteritems(): + for key, values in data.items(): record = getattr(wizard, key) - for field, value in values.iteritems(): + for field, value in values.items(): if field == 'id': continue setattr(record, field, value) @@ -296,7 +296,7 @@ class Wizard(WarningErrorMixin, URLMixin, PoolBase): if isinstance(state, StateView): view = state.get_view(self, state_name) defaults = state.get_defaults(self, state_name, - view['fields'].keys()) + list(view['fields'].keys())) buttons = state.get_buttons(self, state_name) result['view'] = { 'fields_view': view, @@ -327,7 +327,7 @@ class Wizard(WarningErrorMixin, URLMixin, PoolBase): self._session_id = session_id session = Session(session_id) data = json.loads(session.data, object_hook=JSONDecoder()) - for state_name, state in self.states.iteritems(): + for state_name, state in self.states.items(): if isinstance(state, StateView): Target = pool.get(state.model_name) data.setdefault(state_name, {}) @@ -337,7 +337,7 @@ class Wizard(WarningErrorMixin, URLMixin, PoolBase): "Save the session in database" Session = Pool().get('ir.session.wizard') data = {} - for state_name, state in self.states.iteritems(): + for state_name, state in self.states.items(): if isinstance(state, StateView): data[state_name] = getattr(self, state_name)._default_values session = Session(self._session_id) diff --git a/trytond/wsgi.py b/trytond/wsgi.py index 796d4d0ec7..d40ec0070c 100644 --- a/trytond/wsgi.py +++ b/trytond/wsgi.py @@ -1,6 +1,6 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -import httplib +import http.client import logging import os import sys @@ -43,7 +43,7 @@ class TrytondWSGI(object): if request.user_id: return wrapped(*args, **kwargs) else: - abort(httplib.UNAUTHORIZED) + abort(http.client.UNAUTHORIZED) def check_request_size(self, request, size=None): if request.method not in {'POST', 'PUT', 'PATCH'}: @@ -60,9 +60,9 @@ class TrytondWSGI(object): if max_size: content_length = request.content_length if content_length is None: - abort(httplib.LENGTH_REQUIRED) + abort(http.client.LENGTH_REQUIRED) elif content_length > max_size: - abort(httplib.REQUEST_ENTITY_TOO_LARGE) + abort(http.client.REQUEST_ENTITY_TOO_LARGE) def dispatch_request(self, request): adapter = self.url_map.bind_to_environ(request.environ) @@ -71,9 +71,9 @@ class TrytondWSGI(object): max_request_size = getattr(endpoint, 'max_request_size', None) self.check_request_size(request, max_request_size) return endpoint(request, **request.view_args) - except HTTPException, e: + except HTTPException as e: return e - except Exception, e: + except Exception as e: tb_s = ''.join(traceback.format_exception(*sys.exc_info())) for path in sys.path: tb_s = tb_s.replace(path, '')