diff --git a/CHANGELOG b/CHANGELOG
index 3d430014a2..42492af1a1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,4 @@
+* Use Python 3
* Improve loading strategy by using views
* Use non conflicting keyboard shortcuts in sync with sao
* Use the context to get the suffix of window name
diff --git a/bin/tryton b/bin/tryton
index 4523016f6f..06cea2e45c 100755
--- a/bin/tryton
+++ b/bin/tryton
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
import sys
import os
try:
diff --git a/doc/conf.py b/doc/conf.py
index fc8d5ba0ed..140a219838 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -38,7 +38,7 @@ master_doc = 'index'
# General information about the project.
project = 'tryton'
-copyright = u'2008-2011, Anne Krings, Bertrand Chenal, Cédric Krier, \
+copyright = '2008-2011, Anne Krings, Bertrand Chenal, Cédric Krier, \
Mathias Behrle, Tobias Paepke, Udo Spallek'
# The version info for the project you're documenting, acts as replacement for
@@ -174,7 +174,7 @@ htmlhelp_basename = 'trytonddoc'
# (source start file, target name, title, author, documentclass [howto/manual])
latex_documents = [
('index', 'tryton.tex', 'tryton Documentation',
- u'Anne Krings, Bertrand Chenal, Cédric Krier, Mathias Behrle, '
+ 'Anne Krings, Bertrand Chenal, Cédric Krier, Mathias Behrle, '
'Tobias Paepke, Udo Spallek', 'manual'),
]
diff --git a/doc/installation.rst b/doc/installation.rst
index 99c1c4edb6..fe0601127b 100644
--- a/doc/installation.rst
+++ b/doc/installation.rst
@@ -4,12 +4,12 @@ Installing tryton
Prerequisites
-------------
- * Python 2.7.9 or later (http://www.python.org/)
+ * Python 3.4 or later (http://www.python.org/)
* gtk+ 3.20 or later and py-gobject3 (http://www.gtk.org/)
* librsvg (http://librsvg.sourceforge.net/)
* python-dateutil (http://labix.org/python-dateutil)
* Optional: cdecimal (http://www.bytereef.org/mpdecimal/index.html)
- * Optional: GooCalendar (https://pypi.python.org/pypi/GooCalendar)
+ * Optional: GooCalendar 0.4 or later (https://pypi.python.org/pypi/GooCalendar)
Installation
------------
diff --git a/setup.py b/setup.py
old mode 100644
new mode 100755
index 29f99cf94f..670c90a524
--- a/setup.py
+++ b/setup.py
@@ -1,17 +1,20 @@
-#!/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.
from setuptools import setup, find_packages
+import io
import os
import re
def read(fname):
- return open(os.path.join(os.path.dirname(__file__), fname)).read()
+ return io.open(
+ os.path.join(os.path.dirname(__file__), fname),
+ 'r', encoding='utf-8').read()
+
args = {}
-
try:
from babel.messages import frontend as babel
@@ -45,6 +48,7 @@ def get_version():
init = read(os.path.join('tryton', '__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)
@@ -95,18 +99,21 @@ dist = setup(name=name,
'Natural Language :: Spanish',
'Natural Language :: Japanese',
'Operating System :: OS Independent',
- 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
'Topic :: Office/Business',
],
platforms='any',
license='GPL-3',
+ python_requires='>=3.4',
install_requires=[
# "py-gobject3",
"python-dateutil",
],
extras_require={
'cdecimal': ['cdecimal'],
- 'calendar': ['GooCalendar'],
+ 'calendar': ['GooCalendar>=0.4'],
},
zip_safe=False,
**args
diff --git a/tryton/action/__init__.py b/tryton/action/__init__.py
index ed2a492011..0dad8d0854 100644
--- a/tryton/action/__init__.py
+++ b/tryton/action/__init__.py
@@ -1,3 +1,3 @@
# 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 main import *
+from .main import *
diff --git a/tryton/action/main.py b/tryton/action/main.py
index 76f8047d90..90d730dddb 100644
--- a/tryton/action/main.py
+++ b/tryton/action/main.py
@@ -93,7 +93,7 @@ class Action(object):
context=context)
name_suffix = _(', ').join([x['rec_name'] for x in rec_names])
if len(data['ids']) > max_records:
- name_suffix += _(u',\u2026')
+ name_suffix += _(',\u2026')
return _('%s (%s)') % (name, name_suffix)
data['action_id'] = action['id']
diff --git a/tryton/client.py b/tryton/client.py
index ba8833bce1..4d2c57d5ed 100644
--- a/tryton/client.py
+++ b/tryton/client.py
@@ -189,7 +189,7 @@ try:
except ImportError:
pass
gobject.threads_init()
-from urlparse import urlparse
+from urllib.parse import urlparse
import threading
import tryton.common as common
@@ -217,7 +217,7 @@ if sys.platform == 'win32':
return super(Dialog, self).run()
gtk.Dialog = Dialog
-CSS = """
+CSS = b"""
.readonly entry {
background-color: @insensitive_bg_color;
}
@@ -284,7 +284,7 @@ class TrytonClient(object):
lambda signum, frame: main.sig_quit())
def excepthook(*args):
- import common
+ from . import common
import traceback
detail = ''.join(traceback.format_exception(*args))
common.error(str(args[1]), detail)
diff --git a/tryton/common/__init__.py b/tryton/common/__init__.py
index 032fa6e6fb..bf2d9fabed 100644
--- a/tryton/common/__init__.py
+++ b/tryton/common/__init__.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.
-from common import *
-from datetime_strftime import *
-from domain_inversion import domain_inversion, eval_domain, localize_domain, \
+from .common import *
+from .datetime_strftime import *
+from .domain_inversion import domain_inversion, eval_domain, localize_domain, \
merge, inverse_leaf, filter_leaf, concat, simplify, unique_value
-from environment import EvalEnvironment
-import timedelta
+from .environment import EvalEnvironment
+from . import timedelta
diff --git a/tryton/common/button.py b/tryton/common/button.py
index ce532f3ed0..19d586edf8 100644
--- a/tryton/common/button.py
+++ b/tryton/common/button.py
@@ -54,7 +54,7 @@ class Button(gtk.Button):
label += ' (%s)' % len(clicks)
if tip:
tip += '\n'
- tip += _('By: ') + _(', ').join(clicks.itervalues())
+ tip += _('By: ') + _(', ').join(iter(clicks.values()))
self.set_label(label)
self.set_tooltip_text(tip)
diff --git a/tryton/common/cellrendererbinary.py b/tryton/common/cellrendererbinary.py
index 1f4efae990..1f18e0c4eb 100644
--- a/tryton/common/cellrendererbinary.py
+++ b/tryton/common/cellrendererbinary.py
@@ -70,7 +70,7 @@ class CellRendererBinary(gtk.GenericCellRenderer):
return getattr(self, pspec.name)
def button_width(self):
- return (sum(width for n, (_, _, width, _) in self.images.iteritems()
+ return (sum(width for n, (_, _, width, _) in self.images.items()
if n in self.buttons)
+ (2 * (BUTTON_BORDER + BUTTON_SPACING) * len(self.buttons))
- 2 * BUTTON_SPACING)
diff --git a/tryton/common/cellrendererfloat.py b/tryton/common/cellrendererfloat.py
index b12d28a17b..551920789a 100644
--- a/tryton/common/cellrendererfloat.py
+++ b/tryton/common/cellrendererfloat.py
@@ -3,7 +3,7 @@
import gtk
import gobject
import locale
-from cellrendererinteger import CellRendererInteger
+from .cellrendererinteger import CellRendererInteger
class CellRendererFloat(CellRendererInteger):
diff --git a/tryton/common/cellrendererinteger.py b/tryton/common/cellrendererinteger.py
index a811c24f8d..fbc3445a05 100644
--- a/tryton/common/cellrendererinteger.py
+++ b/tryton/common/cellrendererinteger.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 gobject
-from cellrenderertext import CellRendererText
+from .cellrenderertext import CellRendererText
import locale
diff --git a/tryton/common/common.py b/tryton/common/common.py
index d91d8bcc75..e394719c68 100644
--- a/tryton/common/common.py
+++ b/tryton/common/common.py
@@ -17,7 +17,8 @@ from functools import partial
from tryton.config import CONFIG
from tryton.config import TRYTON_ICON, PIXMAPS_DIR
import sys
-import xmlrpclib
+import xmlrpc.client
+from functools import reduce
try:
import hashlib
except ImportError:
@@ -27,10 +28,10 @@ import webbrowser
import traceback
import tryton.rpc as rpc
import socket
-import thread
-import urllib
-import urllib2
-import urlparse
+import _thread
+import urllib.request
+import urllib.parse
+import urllib.error
from string import Template
import shlex
try:
@@ -67,7 +68,7 @@ class TrytonIconFactory(gtk.IconFactory):
continue
try:
pixbuf = gtk.gdk.pixbuf_new_from_file(
- os.path.join(PIXMAPS_DIR, fname).decode('utf-8'))
+ os.path.join(PIXMAPS_DIR, fname))
except (IOError, glib.GError):
continue
finally:
@@ -117,13 +118,14 @@ class TrytonIconFactory(gtk.IconFactory):
except TrytonServerError:
icons = []
for icon in icons:
- pixbuf = _data2pixbuf(icon['icon'])
+ pixbuf = _data2pixbuf(icon['icon'].encode('utf-8'))
self._tryton_icons.remove((icon['id'], icon['name']))
del self._name2id[icon['name']]
self._loaded_icons.add(icon['name'])
iconset = gtk.IconSet(pixbuf)
self.add(icon['name'], iconset)
+
ICONFACTORY = TrytonIconFactory()
ICONFACTORY.add_default()
@@ -161,6 +163,7 @@ class ModelAccess(object):
self._access.update(access)
return self._access[model]
+
MODELACCESS = ModelAccess()
@@ -178,6 +181,7 @@ class ModelHistory(object):
def __contains__(self, model):
return model in self._models
+
MODELHISTORY = ModelHistory()
@@ -224,6 +228,7 @@ class ViewSearch(object):
del self.searches[model][i]
break
+
VIEW_SEARCH = ViewSearch()
@@ -341,7 +346,7 @@ def selection(title, values, alwaysask=False):
if not values or len(values) == 0:
return None
elif len(values) == 1 and (not alwaysask):
- key = values.keys()[0]
+ key = list(values.keys())[0]
return (key, values[key])
parent = get_toplevel_window()
@@ -373,7 +378,7 @@ def selection(title, values, alwaysask=False):
treeview.set_search_column(0)
model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_INT)
- keys = values.keys()
+ keys = list(values.keys())
keys.sort()
i = 0
for val in keys:
@@ -446,19 +451,13 @@ def file_selection(title, filename='',
win.set_preview_widget(img_preview)
win.connect('update-preview', update_preview_cb, img_preview)
- if os.name == 'nt':
- encoding = 'utf-8'
- else:
- encoding = sys.getfilesystemencoding()
button = win.run()
if button != gtk.RESPONSE_OK:
result = None
elif not multi:
result = win.get_filename()
- if result:
- result = unicode(result, encoding)
else:
- result = [unicode(path, encoding) for path in win.get_filenames()]
+ result = win.get_filenames()
parent.present()
win.destroy()
return result
@@ -469,10 +468,10 @@ _slugify_hyphenate_re = re.compile(r'[-\s]+')
def slugify(value):
- if not isinstance(value, unicode):
- value = unicode(value)
- value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
- value = unicode(_slugify_strip_re.sub('', value).strip())
+ if not isinstance(value, str):
+ value = str(value)
+ value = unicodedata.normalize('NFKD', value)
+ value = str(_slugify_strip_re.sub('', value).strip())
return _slugify_hyphenate_re.sub('-', value)
@@ -539,27 +538,17 @@ def mailto(to=None, cc=None, subject=None, body=None, attachment=None):
# http://www.faqs.org/rfcs/rfc2368.html
url = "mailto:"
if to:
- if isinstance(to, unicode):
- to = to.encode('utf-8')
- url += urllib.quote(to.strip(), "@,")
+ url += urllib.parse.quote(to.strip(), "@,")
url += '?'
if cc:
- if isinstance(cc, unicode):
- cc = cc.encode('utf-8')
- url += "&cc=" + urllib.quote(cc, "@,")
+ url += "&cc=" + urllib.parse.quote(cc, "@,")
if subject:
- if isinstance(subject, unicode):
- subject = subject.encode('utf-8')
- url += "&subject=" + urllib.quote(subject, "")
+ url += "&subject=" + urllib.parse.quote(subject, "")
if body:
- if isinstance(body, unicode):
- body = body.encode('utf-8')
body = "\r\n".join(body.splitlines())
- url += "&body=" + urllib.quote(body, "")
+ url += "&body=" + urllib.parse.quote(body, "")
if attachment:
- if isinstance(attachment, unicode):
- attachment = attachment.encode('utf-8')
- url += "&attachment=" + urllib.quote(attachment, "")
+ url += "&attachment=" + urllib.parse.quote(attachment, "")
webbrowser.open(url, new=1)
@@ -598,6 +587,7 @@ class MessageDialog(UniqueDialog):
def __call__(self, message, msg_type=gtk.MESSAGE_INFO):
super(MessageDialog, self).__call__(message, msg_type)
+
message = MessageDialog()
@@ -610,6 +600,7 @@ class WarningDialog(UniqueDialog):
dialog.format_secondary_markup(to_xml(message))
return dialog
+
warning = WarningDialog()
@@ -643,6 +634,7 @@ class UserWarningDialog(WarningDialog):
return 'ok'
return 'cancel'
+
userwarning = UserWarningDialog()
@@ -680,6 +672,7 @@ class SurDialog(ConfirmationDialog):
response = super(SurDialog, self).__call__(message)
return response == gtk.RESPONSE_OK
+
sur = SurDialog()
@@ -707,6 +700,7 @@ class Sur3BDialog(ConfirmationDialog):
response = super(Sur3BDialog, self).__call__(message)
return self.response_mapping.get(response, 'cancel')
+
sur_3b = Sur3BDialog()
@@ -754,6 +748,7 @@ class AskDialog(UniqueDialog):
self.running = False
return result
+
ask = AskDialog()
@@ -814,6 +809,7 @@ class ConcurrencyDialog(UniqueDialog):
mode=['form', 'tree'])
return False
+
concurrency = ConcurrencyDialog()
@@ -898,6 +894,7 @@ class ErrorDialog(UniqueDialog):
if response == gtk.RESPONSE_OK:
send_bugtracker(title, details)
+
error = ErrorDialog()
@@ -958,17 +955,18 @@ def send_bugtracker(title, msg):
if response == gtk.RESPONSE_OK:
try:
msg = msg.encode('ascii', 'replace')
+ title = title.encode('ascii', 'replace')
protocol = 'http'
if ssl or hasattr(socket, 'ssl'):
protocol = 'https'
- quote = partial(urllib.quote, safe="!$&'()*+,;=:")
- server = xmlrpclib.Server(
+ quote = partial(urllib.parse.quote, safe="!$&'()*+,;=:")
+ server = xmlrpc.client.Server(
('%s://%s:%s@' + CONFIG['roundup.xmlrpc'])
% (protocol, quote(user), quote(password)), allow_none=True)
if hashlib:
- msg_md5 = hashlib.md5(msg + '\n' + title).hexdigest()
+ msg_md5 = hashlib.md5(msg + b'\n' + title).hexdigest()
else:
- msg_md5 = md5.new(msg + '\n' + title).hexdigest()
+ msg_md5 = md5.new(msg + b'\n' + title).hexdigest()
if not title:
title = '[no title]'
issue_id = None
@@ -1001,15 +999,15 @@ def send_bugtracker(title, msg):
+ 'issue%s' % issue_id)
webbrowser.open(CONFIG['roundup.url'] + 'issue%s' % issue_id,
new=2)
- except (socket.error, xmlrpclib.Fault), exception:
- if (isinstance(exception, xmlrpclib.Fault)
+ except (socket.error, xmlrpc.client.Fault) as exception:
+ if (isinstance(exception, xmlrpc.client.Fault)
and 'roundup.cgi.exceptions.Unauthorised' in
exception.faultString):
message(_('Connection error.\nBad username or password.'))
return send_bugtracker(title, msg)
tb_s = reduce(lambda x, y: x + y,
- traceback.format_exception(sys.exc_type,
- sys.exc_value, sys.exc_traceback))
+ traceback.format_exception(sys.exc_info()[0],
+ sys.exc_info()[1], sys.exc_info()[2]))
message(_('Exception:') + '\n' + tb_s, msg_type=gtk.MESSAGE_ERROR)
@@ -1019,7 +1017,7 @@ def check_version(box, version=__version__):
webbrowser.open(url)
box.remove(info_bar)
- class HeadRequest(urllib2.Request):
+ class HeadRequest(urllib.request.Request):
def get_method(self):
return 'HEAD'
@@ -1033,14 +1031,15 @@ def check_version(box, version=__version__):
filename = 'tryton-setup-%s.exe' % version
elif sys.platform == 'darwin':
filename = 'tryton-%s.dmg' % version
- url = list(urlparse.urlparse(CONFIG['download.url']))
+ url = list(urllib.parse.urlparse(CONFIG['download.url']))
url[2] = '/%s/%s' % (series, filename)
- url = urlparse.urlunparse(url)
+ url = urllib.parse.urlunparse(url)
logger.info(_("Check URL: %s"), url)
try:
- urllib2.urlopen(HeadRequest(url), timeout=5, cafile=rpc._CA_CERTS)
- except (urllib2.HTTPError, socket.timeout):
+ urllib.request.urlopen(
+ HeadRequest(url), timeout=5, cafile=rpc._CA_CERTS)
+ except (urllib.error.HTTPError, socket.timeout):
return True
except Exception:
logger.error(
@@ -1063,6 +1062,7 @@ def to_xml(string):
return string.replace('&', '&'
).replace('<', '<').replace('>', '>')
+
PLOCK = Lock()
@@ -1098,12 +1098,14 @@ def process_exception(exception, *args, **kwargs):
from tryton.gui.main import Main
if PLOCK.acquire(False):
language = CONFIG['client.lang']
- func = lambda parameters: rpc.login(
- rpc._HOST, rpc._PORT, rpc._DATABASE, rpc._USERNAME,
- parameters, language)
+
+ def login(parameters):
+ return rpc.login(
+ rpc._HOST, rpc._PORT, rpc._DATABASE, rpc._USERNAME,
+ parameters, language)
try:
- Login(func)
- except TrytonError, exception:
+ Login(login)
+ except TrytonError as exception:
if exception.faultCode == 'QueryCanceled':
Main.get_main().sig_quit()
raise
@@ -1124,7 +1126,7 @@ class Login(object):
while True:
try:
func(parameters)
- except TrytonServerError, exception:
+ except TrytonServerError as exception:
if exception.faultCode.startswith('403'):
parameters.clear()
continue
@@ -1216,7 +1218,7 @@ class RPCProgress(object):
def start(self):
try:
self.res = getattr(rpc, self.method)(*self.args)
- except Exception, exception:
+ except Exception as exception:
self.error = True
self.res = False
self.exception = exception
@@ -1239,7 +1241,7 @@ class RPCProgress(object):
if self.parent.get_window():
watch = gtk.gdk.Cursor(gtk.gdk.WATCH)
self.parent.get_window().set_cursor(watch)
- thread.start_new_thread(self.start, ())
+ _thread.start_new_thread(self.start, ())
return
else:
self.start()
@@ -1256,7 +1258,7 @@ class RPCProgress(object):
try:
return process_exception(
self.exception, *self.args, rpc_execute=rpc_execute)
- except RPCException, exception:
+ except RPCException as exception:
self.exception = exception
def return_():
@@ -1313,6 +1315,7 @@ class Tooltips(object):
if self._tooltips:
self._tooltips.disable()
+
COLOR_SCHEMES = {
'red': '#cf1d1d',
'green': '#3fb41b',
@@ -1329,7 +1332,7 @@ def filter_domain(domain):
'''
res = []
for arg in domain:
- if isinstance(arg, basestring):
+ if isinstance(arg, str):
if arg == 'OR':
res = []
break
@@ -1401,10 +1404,11 @@ def resize_pixbuf(pixbuf, width, height):
def _data2pixbuf(data):
loader = gtk.gdk.PixbufLoader()
- loader.write(bytes(data))
+ loader.write(data)
loader.close()
return loader.get_pixbuf()
+
BIG_IMAGE_SIZE = 10 ** 6
with open(os.path.join(PIXMAPS_DIR, 'tryton-noimage.png'), 'rb') as no_image:
NO_IMG_PIXBUF = _data2pixbuf(no_image.read())
diff --git a/tryton/common/completion.py b/tryton/common/completion.py
index dd1a35b16d..d6f56f7f00 100644
--- a/tryton/common/completion.py
+++ b/tryton/common/completion.py
@@ -33,7 +33,7 @@ def update_completion(entry, record, field, model, domain=None):
def update(search_text, domain):
if not entry.props.window:
return False
- if search_text != entry.get_text().decode('utf-8'):
+ if search_text != entry.get_text():
return False
completion_model = entry.get_completion().get_model()
if not search_text or not model:
@@ -53,7 +53,7 @@ def update_completion(entry, record, field, model, domain=None):
results = results()
except (TrytonError, TrytonServerError):
results = []
- if search_text != entry.get_text().decode('utf-8'):
+ if search_text != entry.get_text():
return False
completion_model.clear()
for result in results:
@@ -70,5 +70,5 @@ def update_completion(entry, record, field, model, domain=None):
_("Unable to search for completion of %s") % model,
exc_info=True)
return False
- search_text = entry.get_text().decode('utf-8')
+ search_text = entry.get_text()
gobject.timeout_add(300, update, search_text, domain)
diff --git a/tryton/common/datetime_.py b/tryton/common/datetime_.py
index 70d1d2fcaf..e947e02b4a 100644
--- a/tryton/common/datetime_.py
+++ b/tryton/common/datetime_.py
@@ -3,17 +3,13 @@
import gettext
import datetime
-import pygtk
-
-pygtk.require('2.0')
-
import gobject
import gtk
from dateutil.relativedelta import relativedelta
from dateutil.parser import parse
-from datetime_strftime import datetime_strftime as strftime
+from .datetime_strftime import datetime_strftime as strftime
__all__ = ['Date', 'CellRendererDate', 'Time', 'CellRendererTime', 'DateTime']
@@ -165,7 +161,7 @@ class Date(gtk.Entry):
def do_set_property(self, prop, value):
if prop.name == 'value':
- if isinstance(value, basestring):
+ if isinstance(value, str):
self.set_text(value)
self.parse()
value = self.__date
@@ -185,6 +181,7 @@ class Date(gtk.Entry):
elif prop.name == 'format':
return self.__format
+
gobject.type_register(Date)
@@ -226,6 +223,7 @@ class CellRendererDate(gtk.CellRendererText):
widget.show()
return widget
+
gobject.type_register(CellRendererDate)
@@ -314,7 +312,7 @@ class Time(gtk.ComboBoxEntry):
def do_set_property(self, prop, value):
if prop.name == 'value':
- if isinstance(value, basestring):
+ if isinstance(value, str):
self.__entry.set_text(value)
self.parse()
value = self.__time
@@ -334,6 +332,7 @@ class Time(gtk.ComboBoxEntry):
elif prop.name == 'format':
return self.__format
+
gobject.type_register(Time)
@@ -377,6 +376,7 @@ class CellRendererTime(gtk.CellRendererText):
widget.show()
return widget
+
gobject.type_register(CellRendererTime)
@@ -457,6 +457,7 @@ class DateTime(gtk.HBox):
self.__date.modify_text(state, color)
self.__time.child.modify_text(state, color)
+
gobject.type_register(DateTime)
@@ -480,13 +481,13 @@ def popup_show(popup):
(gtk.gdk.BUTTON_PRESS_MASK
| gtk.gdk.BUTTON_RELEASE_MASK
| gtk.gdk.POINTER_MOTION_MASK),
- None, cursor, 0L)
+ None, cursor, 0)
def popup_hide(popup):
popup.hide()
popup.grab_remove()
- gtk.gdk.pointer_ungrab(0L)
+ gtk.gdk.pointer_ungrab(0)
def timelist_set_list(model, min_, max_, format_):
@@ -496,10 +497,10 @@ def timelist_set_list(model, min_, max_, format_):
model.append((time.strftime(format_), time))
hour = time.hour
minute = time.minute + delta
- hour += minute / 60
+ hour, minute = divmod(minute, 60)
+ hour += time.hour
if hour >= 24:
break
- minute = minute % 60
time = datetime.time(hour, minute)
@@ -534,6 +535,7 @@ def add_operators(widget):
editable.connect('key-press-event', key_press)
return widget
+
OPERATORS = {
gtk.keysyms.S: relativedelta(seconds=-1),
gtk.keysyms.s: relativedelta(seconds=1),
diff --git a/tryton/common/domain_inversion.py b/tryton/common/domain_inversion.py
index c88540599d..c57eba6cdb 100644
--- a/tryton/common/domain_inversion.py
+++ b/tryton/common/domain_inversion.py
@@ -2,9 +2,9 @@
# this repository contains the full copyright notices and license terms.
import operator
-import types
import datetime
from collections import defaultdict
+from functools import reduce
def in_(a, b):
@@ -16,6 +16,7 @@ def in_(a, b):
else:
return operator.contains(b, a)
+
OPERATORS = defaultdict(lambda: lambda a, b: True)
OPERATORS.update({
'=': operator.eq,
@@ -41,7 +42,7 @@ def locale_part(expression, field_name, locale_name='id'):
def is_leaf(expression):
return (isinstance(expression, (list, tuple))
and len(expression) > 2
- and isinstance(expression[1], basestring))
+ and isinstance(expression[1], str))
def constrained_leaf(part, boolop=operator.and_):
@@ -72,14 +73,14 @@ def eval_leaf(part, context, boolop=operator.and_):
context_field = datetime.date.min
if isinstance(context_field, (list, tuple)) and value is None:
value = type(context_field)()
- if (isinstance(context_field, basestring)
+ if (isinstance(context_field, str)
and isinstance(value, (list, tuple))):
try:
value = '%s,%s' % tuple(value)
except TypeError:
pass
elif (isinstance(context_field, (list, tuple))
- and isinstance(value, basestring)):
+ and isinstance(value, str)):
try:
context_field = '%s,%s' % tuple(context_field)
except TypeError:
@@ -90,7 +91,7 @@ def eval_leaf(part, context, boolop=operator.and_):
value = list(value)
if (operand in ('=', '!=')
and isinstance(context_field, (list, tuple))
- and isinstance(value, (int, long))):
+ and isinstance(value, int)):
operand = {
'=': 'in',
'!=': 'not in',
@@ -112,7 +113,7 @@ def inverse_leaf(domain):
return [domain[3]] + list(domain[1:])
return domain
else:
- return map(inverse_leaf, domain)
+ return list(map(inverse_leaf, domain))
def filter_leaf(domain, field, model):
@@ -155,7 +156,7 @@ def localize_domain(domain, field_name=None, strip_target=False):
else:
return [domain[3]] + list(domain[1:-1])
locale_name = 'id'
- if isinstance(domain[2], basestring):
+ if isinstance(domain[2], str):
locale_name = 'rec_name'
n = 3 if strip_target else 4
return [locale_part(domain[0], field_name, locale_name)] \
@@ -246,7 +247,7 @@ def domain_inversion(domain, symbol, context=None):
class And(object):
def __init__(self, expressions):
- self.branches = map(parse, expressions)
+ self.branches = list(map(parse, expressions))
self.variables = set()
for expression in self.branches:
if is_leaf(expression):
@@ -265,7 +266,7 @@ class And(object):
for part in self.branches:
if isinstance(part, And):
part_inversion = part.inverse(symbol, context)
- evaluated = isinstance(part_inversion, types.BooleanType)
+ evaluated = isinstance(part_inversion, bool)
if not evaluated:
result.append(part_inversion)
elif part_inversion:
@@ -284,7 +285,7 @@ class And(object):
else:
return False
- result = filter(lambda e: e is not True, result)
+ result = [e for e in result if e is not True]
if result == []:
return True
else:
@@ -305,7 +306,7 @@ class Or(And):
for part in self.branches:
if isinstance(part, And):
part_inversion = part.inverse(symbol, context)
- evaluated = isinstance(part_inversion, types.BooleanType)
+ evaluated = isinstance(part_inversion, bool)
if symbol not in part.variables:
if evaluated and part_inversion:
return True
@@ -329,7 +330,7 @@ class Or(And):
and not eval_leaf(part, context, operator.or_)):
result.append(False)
- result = filter(lambda e: e is not False, result)
+ result = [e for e in result if e is not False]
if result == []:
return False
else:
@@ -390,7 +391,7 @@ def test_or_inversion():
assert domain_inversion(domain, 'y', {'z': 4}) == [['y', '<', 3]]
assert domain_inversion(domain, 'y', {'x': 3}) is True
- domain = [u'OR', [u'length', u'>', 5], [u'language.code', u'=', u'de_DE']]
+ domain = ['OR', ['length', '>', 5], ['language.code', '=', 'de_DE']]
assert domain_inversion(domain, 'length', {'length': 0, 'name': 'n'}) ==\
[['length', '>', 5]]
@@ -636,6 +637,7 @@ def test_localize():
assert localize_domain(domain, 'x', False) == [['b.c', '=', 1, 'y', 'z']]
assert localize_domain(domain, 'x', True) == [['b.c', '=', 1, 'z']]
+
if __name__ == '__main__':
test_simple_inversion()
test_and_inversion()
diff --git a/tryton/common/domain_parser.py b/tryton/common/domain_parser.py
index 74c68fd761..2d28e8581c 100644
--- a/tryton/common/domain_parser.py
+++ b/tryton/common/domain_parser.py
@@ -42,8 +42,8 @@ class udlex(shlex):
class DummyWordchars(object):
"Simulate str that contains all chars except somes"
def __contains__(self, item):
- return item not in (u':', u'>', u'<', u'=', u'!', u'"', u';',
- u'(', u')')
+ return item not in (':', '>', '<', '=', '!', '"', ';',
+ '(', ')')
self.wordchars = DummyWordchars()
@@ -116,7 +116,7 @@ def likify(value):
def quote(value):
"Quote string if needed"
- if not isinstance(value, basestring):
+ if not isinstance(value, str):
return value
if '\\' in value:
value = value.replace('\\', '\\\\')
@@ -196,7 +196,7 @@ def split_target_value(field, value):
"Split the reference value into target and value"
assert field['type'] == 'reference'
target = None
- if isinstance(value, basestring):
+ if isinstance(value, str):
for key, text in field['selection']:
if value.lower().startswith(text.lower() + ','):
target = key
@@ -230,8 +230,8 @@ def convert_value(field, value, context=None):
context = {}
def convert_boolean():
- if isinstance(value, basestring):
- return any(test.decode('utf-8').lower().startswith(value.lower())
+ if isinstance(value, str):
+ return any(test.lower().startswith(value.lower())
for test in (
_('y'), _('Yes'), _('True'), _('t'), '1'))
else:
@@ -256,7 +256,7 @@ def convert_value(field, value, context=None):
return
def convert_selection():
- if isinstance(value, basestring):
+ if isinstance(value, str):
for key, text in field['selection']:
if value.lower() == text.lower():
return key
@@ -883,10 +883,10 @@ class DomainParser(object):
def __init__(self, fields, context=None):
self.fields = OrderedDict((name, f)
- for name, f in fields.iteritems()
+ for name, f in fields.items()
if f.get('searchable', True))
self.strings = dict((f['string'].lower(), f)
- for f in fields.itervalues()
+ for f in fields.values()
if f.get('searchable', True))
self.context = context
@@ -901,8 +901,8 @@ class DomainParser(object):
tokens = operatorize(tokens, 'and')
tokens = self.parse_clause(tokens)
return simplify(rlist(tokens))
- except ValueError, exception:
- if exception.message == 'No closing quotation':
+ except ValueError as exception:
+ if str(exception) == 'No closing quotation':
return self.parse(input_ + '"')
def stringable(self, domain):
@@ -930,7 +930,7 @@ class DomainParser(object):
def string_(clause):
if not clause:
return ''
- if (not isinstance(clause[0], basestring)
+ if (not isinstance(clause[0], str)
or clause[0] in ('AND', 'OR')):
return '(%s)' % self.string(clause)
name, operator, value = clause[:3]
@@ -995,7 +995,7 @@ class DomainParser(object):
"Return completion for the input string"
domain = self.parse(input_)
closing = 0
- for i in xrange(1, len(input_)):
+ for i in range(1, len(input_)):
if input_[-i] not in (')', ' '):
break
if input_[-i] == ')':
@@ -1018,7 +1018,7 @@ class DomainParser(object):
return
if len(input_) >= 2 and input_[-2] == ':':
return
- for field in self.strings.itervalues():
+ for field in self.strings.values():
operator = default_operator(field)
value = ''
if 'ilike' in operator:
@@ -1052,7 +1052,7 @@ class DomainParser(object):
name = ''
if (name.lower() not in self.strings
and name not in self.fields):
- for field in self.strings.itervalues():
+ for field in self.strings.values():
if field['string'].lower().startswith(name.lower()):
operator = default_operator(field)
value = ''
@@ -1310,81 +1310,81 @@ def test_group():
'string': '(Sur)Name',
},
})
- assert rlist(dom.group(udlex(u'Name: Doe'))) == [('Name', None, 'Doe')]
- assert rlist(dom.group(udlex(u'"(Sur)Name": Doe'))) == [
+ assert rlist(dom.group(udlex('Name: Doe'))) == [('Name', None, 'Doe')]
+ assert rlist(dom.group(udlex('"(Sur)Name": Doe'))) == [
('(Sur)Name', None, 'Doe'),
]
- assert rlist(dom.group(udlex(u'Name: Doe Name: John'))) == [
+ assert rlist(dom.group(udlex('Name: Doe Name: John'))) == [
('Name', None, 'Doe'),
('Name', None, 'John')]
- assert rlist(dom.group(udlex(u'Name: Name: John'))) == [
+ assert rlist(dom.group(udlex('Name: Name: John'))) == [
('Name', None, None),
('Name', None, 'John')]
- assert rlist(dom.group(udlex(u'First Name: John'))) == [
+ assert rlist(dom.group(udlex('First Name: John'))) == [
('First Name', None, 'John'),
]
- assert rlist(dom.group(udlex(u'Name: Doe First Name: John'))) == [
+ assert rlist(dom.group(udlex('Name: Doe First Name: John'))) == [
('Name', None, 'Doe'),
('First Name', None, 'John'),
]
- assert rlist(dom.group(udlex(u'First Name: John Name: Doe'))) == [
+ assert rlist(dom.group(udlex('First Name: John Name: Doe'))) == [
('First Name', None, 'John'),
('Name', None, 'Doe'),
]
- assert rlist(dom.group(udlex(u'First Name: John First Name: Jane'))) == [
+ assert rlist(dom.group(udlex('First Name: John First Name: Jane'))) == [
('First Name', None, 'John'),
('First Name', None, 'Jane'),
]
- assert rlist(dom.group(udlex(u'Name: John Doe'))) == [
+ assert rlist(dom.group(udlex('Name: John Doe'))) == [
('Name', None, 'John'),
('Doe',),
]
- assert rlist(dom.group(udlex(u'Name: "John Doe"'))) == [
+ assert rlist(dom.group(udlex('Name: "John Doe"'))) == [
('Name', None, 'John Doe'),
]
- assert rlist(dom.group(udlex(u'Name: =Doe'))) == [('Name', '=', 'Doe')]
- assert rlist(dom.group(udlex(u'Name: =Doe Name: >John'))) == [
+ assert rlist(dom.group(udlex('Name: =Doe'))) == [('Name', '=', 'Doe')]
+ assert rlist(dom.group(udlex('Name: =Doe Name: >John'))) == [
('Name', '=', 'Doe'),
('Name', '>', 'John'),
]
- assert rlist(dom.group(udlex(u'First Name: =John First Name: =Jane'))) == [
+ assert rlist(dom.group(udlex('First Name: =John First Name: =Jane'))) == [
('First Name', '=', 'John'),
('First Name', '=', 'Jane'),
]
- assert rlist(dom.group(udlex(u'Name: John;Jane'))) == [
+ assert rlist(dom.group(udlex('Name: John;Jane'))) == [
('Name', None, ['John', 'Jane'])
]
- assert rlist(dom.group(udlex(u'Name: John;'))) == [
+ assert rlist(dom.group(udlex('Name: John;'))) == [
('Name', None, ['John'])
]
- assert rlist(dom.group(udlex(u'Name: John;Jane Name: Doe'))) == [
+ assert rlist(dom.group(udlex('Name: John;Jane Name: Doe'))) == [
('Name', None, ['John', 'Jane']),
('Name', None, 'Doe'),
]
- assert rlist(dom.group(udlex(u'Name: John; Name: Doe'))) == [
+ assert rlist(dom.group(udlex('Name: John; Name: Doe'))) == [
('Name', None, ['John']),
('Name', None, 'Doe'),
]
- assert rlist(dom.group(udlex(u'Name:'))) == [
+ assert rlist(dom.group(udlex('Name:'))) == [
('Name', None, None),
]
- assert rlist(dom.group(udlex(u'Name: ='))) == [
+ assert rlist(dom.group(udlex('Name: ='))) == [
('Name', '=', None),
]
- assert rlist(dom.group(udlex(u'Name: =""'))) == [
+ assert rlist(dom.group(udlex('Name: =""'))) == [
('Name', '=', ''),
]
- assert rlist(dom.group(udlex(u'Name: = ""'))) == [
+ assert rlist(dom.group(udlex('Name: = ""'))) == [
('Name', '=', ''),
]
- assert rlist(dom.group(udlex(u'Name: = Name: Doe'))) == [
+ assert rlist(dom.group(udlex('Name: = Name: Doe'))) == [
('Name', '=', None),
('Name', None, 'Doe'),
]
- assert rlist(dom.group(udlex(u'Name: \\"foo\\"'))) == [
+ assert rlist(dom.group(udlex('Name: \\"foo\\"'))) == [
('Name', None, '"foo"'),
]
- assert rlist(dom.group(udlex(u'Name: "" <'))) == [
+ assert rlist(dom.group(udlex('Name: "" <'))) == [
('Name', '', '<'),
]
@@ -1497,10 +1497,10 @@ def test_completion():
'type': 'char',
},
})
- assert list(dom.completion(u'Nam')) == ['Name: ']
- assert list(dom.completion(u'Name:')) == ['Name: ']
- assert list(dom.completion(u'Name: foo')) == []
- assert list(dom.completion(u'Name: !=')) == []
- assert list(dom.completion(u'Name: !=foo')) == []
- assert list(dom.completion(u'')) == ['Name: ']
- assert list(dom.completion(u' ')) == ['', 'Name: ']
+ assert list(dom.completion('Nam')) == ['Name: ']
+ assert list(dom.completion('Name:')) == ['Name: ']
+ assert list(dom.completion('Name: foo')) == []
+ assert list(dom.completion('Name: !=')) == []
+ assert list(dom.completion('Name: !=foo')) == []
+ assert list(dom.completion('')) == ['Name: ']
+ assert list(dom.completion(' ')) == ['', 'Name: ']
diff --git a/tryton/common/environment.py b/tryton/common/environment.py
index 042ab87b04..c0491e8a78 100644
--- a/tryton/common/environment.py
+++ b/tryton/common/environment.py
@@ -35,7 +35,7 @@ class EvalEnvironment(dict):
pass
return super(EvalEnvironment, self).get(item, default)
- def __nonzero__(self):
+ def __bool__(self):
return True
def __str__(self):
diff --git a/tryton/common/htmltextbuffer.py b/tryton/common/htmltextbuffer.py
index b76f1f51b4..2f6cdcedd5 100644
--- a/tryton/common/htmltextbuffer.py
+++ b/tryton/common/htmltextbuffer.py
@@ -1,11 +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 __future__ import division
+
import sys
-from io import BytesIO
+from io import StringIO
import xml.etree.ElementTree as ET
from xml.sax.saxutils import escape, unescape
-from HTMLParser import HTMLParser
+from html.parser import HTMLParser
from gi.repository import Gtk, Gdk, Pango
@@ -16,7 +16,7 @@ use_serialize_func = False
def _reverse_dict(dct):
- return {j: i for i, j in dct.iteritems()}
+ return {j: i for i, j in dct.items()}
SIZE2SCALE = {
'1': 1 / (1.2 * 1.2 * 1.2),
@@ -41,7 +41,7 @@ FAMILIES = ['normal', 'sans', 'serif', 'monospace']
def gdk_to_hex(gdk_color):
"Convert color to 2 digit hex"
colors = [gdk_color.red, gdk_color.green, gdk_color.blue]
- return "#" + "".join(["%02x" % (color / 256) for color in colors])
+ return "#" + "".join(["%02x" % (color // 256) for color in colors])
def _markup(text):
@@ -49,7 +49,7 @@ def _markup(text):
def _strip_markup(text):
- return text[len(u''):-len(u'')]
+ return text[len(''):-len('')]
def _strip_newline(text):
@@ -101,7 +101,7 @@ class MarkupHTMLParse(HTMLParser):
def parse_markup(markup_text):
'Return plain text and a list of start, end TextTag'
- markup_text = BytesIO(_markup(markup_text))
+ markup_text = StringIO(_markup(markup_text))
plain_text = ''
tag_stack = []
tags = []
@@ -129,8 +129,6 @@ def parse_markup(markup_text):
def normalize_markup(markup_text, method='html'):
- if isinstance(markup_text, unicode):
- markup_text = markup_text.encode('utf-8')
parser = MarkupHTMLParse()
parser.feed(_strip_newline(markup_text))
root = parser.root
@@ -177,7 +175,8 @@ def normalize_markup(markup_text, method='html'):
if em.text is None and len(em) == 0:
em.append(ET.Element('br'))
# TODO order attributes
- return _strip_markup(ET.tostring(root, encoding='utf-8', method=method))
+ return _strip_markup(
+ ET.tostring(root, encoding='utf-8', method=method).decode('utf-8'))
def find_list_delta(old, new):
@@ -268,7 +267,7 @@ def serialize(register, content, start, end, data):
def deserialize(register, content, iter_, text, create_tags, data):
- if not isinstance(text, unicode):
+ if not isinstance(text, str):
for encoding in [sys.getfilesystemencoding(),
'utf-8', 'utf-16', 'utf-32']:
try:
@@ -278,14 +277,13 @@ def deserialize(register, content, iter_, text, create_tags, data):
break
else:
text = text.decode('ascii', errors='replace')
- text = text.encode('utf-8')
text, tags = parse_markup(normalize_markup(text, method='xml'))
offset = iter_.get_offset()
content.insert(iter_, text)
def sort_key(tag):
start, end, element = tag
- return start, -end, element
+ return start, -end, element.tag
for start, end, element in sorted(tags, key=sort_key):
for tag in _get_tags(content, element):
@@ -306,9 +304,9 @@ _TAGS = [
_TAGS.extend([('family %s' % family, {'family': family})
for family in FAMILIES])
_TAGS.extend([('size %s' % size, {'scale': scale})
- for size, scale in SIZE2SCALE.iteritems()])
+ for size, scale in SIZE2SCALE.items()])
_TAGS.extend([('justification %s' % align, {'justification': justification})
- for align, justification in ALIGN2JUSTIFICATION.iteritems()])
+ for align, justification in ALIGN2JUSTIFICATION.items()])
def register_foreground(text_buffer, color):
@@ -374,7 +372,7 @@ def _get_html(texttag, close=False, div_only=False):
if close:
return ''.join('%s>' % t for t in tags)
return ''.join('<%s %s>' % (t, ' '.join('%s="%s"' % (a, v)
- for a, v in attrib.get(t, {}).iteritems()))
+ for a, v in attrib.get(t, {}).items()))
for t in tags)
@@ -403,7 +401,7 @@ def remove_tags(content, start, end, *names):
if __name__ == '__main__':
- html = u'''Bold
+ html = '''Bold
Italic
Underline
@@ -416,13 +414,13 @@ if __name__ == '__main__':
def cb(window, event):
if use_serialize_func:
- print text_buffer.serialize(
+ print(text_buffer.serialize(
text_buffer, MIME, text_buffer.get_start_iter(),
- text_buffer.get_end_iter())
+ text_buffer.get_end_iter()))
else:
- print serialize(
+ print(serialize(
text_buffer, text_buffer, text_buffer.get_start_iter(),
- text_buffer.get_end_iter(), None)
+ text_buffer.get_end_iter(), None))
Gtk.main_quit()
win.connect('delete-event', cb)
vbox = Gtk.VBox()
@@ -439,8 +437,7 @@ if __name__ == '__main__':
if use_serialize_func:
text_buffer.deserialize(
- text_buffer, MIME, text_buffer.get_start_iter(),
- html.encode('utf-8'))
+ text_buffer, MIME, text_buffer.get_start_iter(), html)
result = text_buffer.serialize(
text_buffer, MIME, text_buffer.get_start_iter(),
@@ -455,7 +452,7 @@ if __name__ == '__main__':
text_buffer, text_buffer, text_buffer.get_start_iter(),
text_buffer.get_end_iter(), None)
- assert normalize_markup(html) == result
+ assert normalize_markup(html) == result, (normalize_markup(html), result)
win.show_all()
Gtk.main()
diff --git a/tryton/common/popup_menu.py b/tryton/common/popup_menu.py
index fbb87ccbf6..ee01f29587 100644
--- a/tryton/common/popup_menu.py
+++ b/tryton/common/popup_menu.py
@@ -19,21 +19,21 @@ def populate(menu, model, record, title='', field=None, context=None):
'''
if record is None:
return
- elif isinstance(record, (int, long)):
+ elif isinstance(record, int):
if record < 0:
return
elif record.id < 0:
return
def load(record):
- if isinstance(record, (int, long)):
+ if isinstance(record, int):
screen = Screen(model, context=context)
screen.load([record])
record = screen.current_record
return record
def id_(record):
- if not isinstance(record, (int, long)):
+ if not isinstance(record, int):
return record.id
return record
diff --git a/tryton/common/selection.py b/tryton/common/selection.py
index 02032195f3..037d6d7073 100644
--- a/tryton/common/selection.py
+++ b/tryton/common/selection.py
@@ -90,10 +90,12 @@ class SelectionMixin(object):
def filter_selection(self, domain, record, field):
if not domain:
return
- test = lambda value: eval_domain(domain, {
- self.field_name: value[0],
- })
- self.selection = filter(test, self.selection)
+
+ def test(value):
+ return eval_domain(domain, {
+ self.field_name: value[0],
+ })
+ self.selection = list(filter(test, self.selection))
def get_inactive_selection(self, value):
if 'relation' not in self.attrs:
@@ -125,7 +127,7 @@ def selection_shortcuts(entry):
def freeze_value(value):
if isinstance(value, dict):
return tuple(sorted((k, freeze_value(v))
- for k, v in value.iteritems()))
+ for k, v in value.items()))
elif isinstance(value, (list, set)):
return tuple(freeze_value(v) for v in value)
else:
diff --git a/tryton/common/timedelta.py b/tryton/common/timedelta.py
index b98b268b8c..9aa9d94757 100644
--- a/tryton/common/timedelta.py
+++ b/tryton/common/timedelta.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 __future__ import division
+
import datetime
import gettext
import locale
@@ -46,7 +46,7 @@ def format(value, converter=None):
if value < 0:
sign = '-'
value = abs(value)
- converter = sorted(converter.items(), key=operator.itemgetter(1),
+ converter = sorted(list(converter.items()), key=operator.itemgetter(1),
reverse=True)
values = []
for k, v in converter:
@@ -77,7 +77,7 @@ def parse(text, converter=None):
if not converter:
converter = DEFAULT_CONVERTER
- for separator in _get_separators().values():
+ for separator in list(_get_separators().values()):
text = text.replace(separator, separator + ' ')
seconds = 0
@@ -90,7 +90,7 @@ def parse(text, converter=None):
except ValueError:
pass
else:
- for key, separator in _get_separators().items():
+ for key, separator in list(_get_separators().items()):
if part.endswith(separator):
part = part[:-len(separator)]
try:
diff --git a/tryton/config.py b/tryton/config.py
index c5f8389c0d..b1612d5472 100644
--- a/tryton/config.py
+++ b/tryton/config.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.
"Options"
-import ConfigParser
+import configparser
import optparse
import os
import gettext
@@ -19,14 +19,14 @@ _ = gettext.gettext
def get_config_dir():
if os.name == 'nt':
appdata = os.environ['APPDATA']
- if not isinstance(appdata, unicode):
- appdata = unicode(appdata, sys.getfilesystemencoding())
+ if not isinstance(appdata, str):
+ appdata = str(appdata, sys.getfilesystemencoding())
return os.path.join(appdata, '.config', 'tryton',
__version__.rsplit('.', 1)[0])
return os.path.join(os.environ['HOME'], '.config', 'tryton',
__version__.rsplit('.', 1)[0])
if not os.path.isdir(get_config_dir()):
- os.makedirs(get_config_dir(), 0700)
+ os.makedirs(get_config_dir(), 0o700)
class ConfigManager(object):
@@ -119,15 +119,15 @@ class ConfigManager(object):
def save(self):
try:
- configparser = ConfigParser.ConfigParser()
- for entry in self.config.keys():
+ parser = configparser.ConfigParser()
+ for entry in list(self.config.keys()):
if not len(entry.split('.')) == 2:
continue
section, name = entry.split('.')
- if not configparser.has_section(section):
- configparser.add_section(section)
- configparser.set(section, name, self.config[entry])
- configparser.write(open(self.rcfile, 'wb'))
+ if not parser.has_section(section):
+ parser.add_section(section)
+ parser.set(section, name, str(self.config[entry]))
+ parser.write(open(self.rcfile, 'w'))
except IOError:
logging.getLogger(__name__).warn(
_('Unable to write config file %s.')
@@ -136,10 +136,10 @@ class ConfigManager(object):
return True
def load(self):
- configparser = ConfigParser.ConfigParser()
- configparser.read([self.rcfile])
- for section in configparser.sections():
- for (name, value) in configparser.items(section):
+ parser = configparser.ConfigParser()
+ parser.read([self.rcfile])
+ for section in parser.sections():
+ for (name, value) in parser.items(section):
if value.lower() == 'true':
value = True
elif value.lower() == 'false':
@@ -164,8 +164,8 @@ CONFIG = ConfigManager()
CURRENT_DIR = os.path.dirname(__file__)
if hasattr(sys, 'frozen'):
CURRENT_DIR = os.path.dirname(sys.executable)
-if not isinstance(CURRENT_DIR, unicode):
- CURRENT_DIR = unicode(CURRENT_DIR, sys.getfilesystemencoding())
+if not isinstance(CURRENT_DIR, str):
+ CURRENT_DIR = str(CURRENT_DIR, sys.getfilesystemencoding())
PIXMAPS_DIR = os.path.join(CURRENT_DIR, 'data', 'pixmaps', 'tryton')
if not os.path.isdir(PIXMAPS_DIR):
@@ -175,4 +175,4 @@ if not os.path.isdir(PIXMAPS_DIR):
'tryton', 'data/pixmaps/tryton')
TRYTON_ICON = gtk.gdk.pixbuf_new_from_file(
- os.path.join(PIXMAPS_DIR, 'tryton-icon.png').encode('utf-8'))
+ os.path.join(PIXMAPS_DIR, 'tryton-icon.png'))
diff --git a/tryton/exceptions.py b/tryton/exceptions.py
index eb50b4eeaf..3adcce7f1b 100644
--- a/tryton/exceptions.py
+++ b/tryton/exceptions.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 jsonrpc import Fault
+from .jsonrpc import Fault
TrytonServerError = Fault
diff --git a/tryton/fingerprints.py b/tryton/fingerprints.py
index 04c7c40361..2913572c70 100644
--- a/tryton/fingerprints.py
+++ b/tryton/fingerprints.py
@@ -28,10 +28,10 @@ class Fingerprints(dict):
def save(self):
with open(KNOWN_HOSTS_PATH, 'w') as known_hosts:
known_hosts.writelines('%s %s' % (host, sha1)
- + os.linesep for host, sha1 in self.iteritems())
+ + os.linesep for host, sha1 in self.items())
def __setitem__(self, key, value):
- assert isinstance(key, basestring)
+ assert isinstance(key, str)
if value:
assert len(value) == 59 # len of formated sha1
else:
diff --git a/tryton/gui/__init__.py b/tryton/gui/__init__.py
index ed2a492011..0dad8d0854 100644
--- a/tryton/gui/__init__.py
+++ b/tryton/gui/__init__.py
@@ -1,3 +1,3 @@
# 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 main import *
+from .main import *
diff --git a/tryton/gui/main.py b/tryton/gui/main.py
index f9a589dc00..70ffb5aa22 100644
--- a/tryton/gui/main.py
+++ b/tryton/gui/main.py
@@ -4,15 +4,14 @@
import os
import sys
import gettext
-from urlparse import urlparse, parse_qsl
-import urllib
-import gobject
-import gtk
+from urllib.parse import urlparse, parse_qsl, unquote
import json
import webbrowser
import threading
import logging
+import gobject
+import gtk
from gi.repository import GLib
import tryton.rpc as rpc
@@ -313,7 +312,7 @@ class Main(object):
if callback:
callback()
return False
- if search_text != widget.get_text().decode('utf-8'):
+ if search_text != widget.get_text():
return end()
gmodel = global_search_completion.get_model()
if not search_text or not gmodel:
@@ -328,7 +327,7 @@ class Main(object):
result = result()
except RPCException:
result = []
- if search_text != widget.get_text().decode('utf-8'):
+ if search_text != widget.get_text():
if callback:
callback()
return False
@@ -357,7 +356,7 @@ class Main(object):
return False
def changed(widget):
- search_text = widget.get_text().decode('utf-8')
+ search_text = widget.get_text()
gobject.timeout_add(300, update, widget, search_text)
def activate(widget):
@@ -367,7 +366,7 @@ class Main(object):
common.message(_('No result found.'))
else:
widget.emit('changed')
- search_text = widget.get_text().decode('utf-8')
+ search_text = widget.get_text()
update(widget, search_text, message)
self.global_search_entry.connect('changed', changed)
@@ -834,20 +833,22 @@ class Main(object):
language = CONFIG['client.lang']
try:
host, port, database, username = DBLogin().run()
- except TrytonError, exception:
+ except TrytonError as exception:
if exception.faultCode == 'QueryCanceled':
return
raise
- func = lambda parameters: rpc.login(
- host, port, database, username, parameters, language)
+
+ def login(parameters):
+ return rpc.login(
+ host, port, database, username, parameters, language)
self.set_title() # Adds username/profile while password is asked
try:
- common.Login(func)
- except TrytonError, exception:
+ common.Login(login)
+ except TrytonError as exception:
if exception.faultCode == 'QueryCanceled':
return
raise
- except TrytonServerError, exception:
+ except TrytonServerError as exception:
if exception.faultCode.startswith('404'):
return self.sig_login()
raise
@@ -1223,8 +1224,8 @@ class Main(object):
urlp = urlparse('http' + url[6:])
hostname = common.get_hostname(urlp.netloc)
port = common.get_port(urlp.netloc)
- database, path = map(urllib.unquote,
- (urlp.path[1:].split('/', 1) + [''])[:2])
+ database, path = list(map(unquote,
+ (urlp.path[1:].split('/', 1) + [''])[:2]))
if (not path or
hostname != rpc._HOST or
int(port) != rpc._PORT or
diff --git a/tryton/gui/window/__init__.py b/tryton/gui/window/__init__.py
index 359c96fee4..c0947c1755 100644
--- a/tryton/gui/window/__init__.py
+++ b/tryton/gui/window/__init__.py
@@ -1,5 +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 window import *
-from email import *
-from limit import *
+from .window import *
+from .email import *
+from .limit import *
diff --git a/tryton/gui/window/about.py b/tryton/gui/window/about.py
index 17f4995892..daf2016fdc 100644
--- a/tryton/gui/window/about.py
+++ b/tryton/gui/window/about.py
@@ -42,7 +42,7 @@ class About(object):
self.win.set_authors(AUTHORS)
self.win.set_translator_credits(_('translator-credits'))
self.win.set_logo(GdkPixbuf.Pixbuf.new_from_file(
- os.path.join(PIXMAPS_DIR, 'tryton.png').encode('utf-8')))
+ os.path.join(PIXMAPS_DIR, 'tryton.png')))
self.win.run()
parent.present()
self.win.destroy()
diff --git a/tryton/gui/window/attachment.py b/tryton/gui/window/attachment.py
index 0bd7c18edc..b570bd9cb2 100644
--- a/tryton/gui/window/attachment.py
+++ b/tryton/gui/window/attachment.py
@@ -2,8 +2,8 @@
# this repository contains the full copyright notices and license terms.
"Attachment"
import os
-import urllib
-import urlparse
+import urllib.request
+import urllib.parse
import sys
import gettext
@@ -42,9 +42,9 @@ class Attachment(WinForm):
data_field = self.screen.group.fields['data']
name_field = self.screen.group.fields[data_field.attrs['filename']]
new_record = self.screen.new()
- file_name = os.path.basename(urlparse.urlparse(uri).path)
+ file_name = os.path.basename(urllib.parse.urlparse(uri).path)
name_field.set_client(new_record, file_name)
- uri = urllib.unquote(uri)
+ uri = urllib.parse.unquote(uri)
uri = uri.decode('utf-8').encode(sys.getfilesystemencoding())
- data_field.set_client(new_record, urllib.urlopen(uri).read())
+ data_field.set_client(new_record, urllib.request.urlopen(uri).read())
self.screen.display()
diff --git a/tryton/gui/window/board.py b/tryton/gui/window/board.py
index dcad939d9a..0b6083b326 100644
--- a/tryton/gui/window/board.py
+++ b/tryton/gui/window/board.py
@@ -7,7 +7,7 @@ from tryton.gui import Main
from tryton.gui.window.view_board import ViewBoard
from tryton.common import RPCExecute, RPCException
-from tabcontent import TabContent
+from .tabcontent import TabContent
_ = gettext.gettext
@@ -60,6 +60,9 @@ class Board(SignalEvent, TabContent):
and self.board.context == value.board.context
and self.name == value.name)
+ def __hash__(self):
+ return id(self)
+
def sig_win_close(self, widget):
Main.get_main().sig_win_close(widget)
diff --git a/tryton/gui/window/dblogin.py b/tryton/gui/window/dblogin.py
index 44418c10d9..7f0dfeb14d 100644
--- a/tryton/gui/window/dblogin.py
+++ b/tryton/gui/window/dblogin.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 ConfigParser
+import configparser
import gtk
import gobject
import os
@@ -29,7 +29,7 @@ class DBListEditor(object):
# GTK Stuffs
self.parent = parent
- self.dialog = gtk.Dialog(title=_(u'Profile Editor'), parent=parent,
+ self.dialog = gtk.Dialog(title=_('Profile Editor'), parent=parent,
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
self.ok_button = self.dialog.add_button(gtk.STOCK_OK,
gtk.RESPONSE_ACCEPT)
@@ -44,19 +44,19 @@ class DBListEditor(object):
self.cell.connect('editing-started', self.edit_started)
self.profile_tree = gtk.TreeView()
self.profile_tree.set_model(profile_store)
- self.profile_tree.insert_column_with_attributes(-1, _(u'Profile'),
+ self.profile_tree.insert_column_with_attributes(-1, _('Profile'),
self.cell, text=0)
self.profile_tree.connect('cursor-changed', self.profile_selected)
scroll = gtk.ScrolledWindow()
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scroll.add(self.profile_tree)
- self.add_button = gtk.Button(_(u'_Add'), use_underline=True)
+ self.add_button = gtk.Button(_('_Add'), use_underline=True)
self.add_button.connect('clicked', self.profile_create)
add_image = gtk.Image()
add_image.set_from_stock('gtk-add', gtk.ICON_SIZE_BUTTON)
self.add_button.set_image(add_image)
self.add_button.set_always_show_image(True)
- self.remove_button = gtk.Button(_(u'_Remove'), use_underline=True)
+ self.remove_button = gtk.Button(_('_Remove'), use_underline=True)
self.remove_button.connect('clicked', self.profile_delete)
remove_image = gtk.Image()
remove_image.set_from_stock('gtk-remove', gtk.ICON_SIZE_BUTTON)
@@ -70,7 +70,7 @@ class DBListEditor(object):
table = gtk.Table(4, 2, homogeneous=False)
table.set_row_spacings(3)
table.set_col_spacings(3)
- host = gtk.Label(set_underline(_(u'Host:')))
+ host = gtk.Label(set_underline(_('Host:')))
host.set_use_underline(True)
host.set_alignment(1, 0.5)
host.set_padding(3, 3)
@@ -81,7 +81,7 @@ class DBListEditor(object):
host.set_mnemonic_widget(self.host_entry)
table.attach(host, 0, 1, 1, 2, yoptions=False, xoptions=gtk.FILL)
table.attach(self.host_entry, 1, 2, 1, 2, yoptions=False)
- database = gtk.Label(set_underline(_(u'Database:')))
+ database = gtk.Label(set_underline(_('Database:')))
database.set_use_underline(True)
database.set_alignment(1, 0.5)
database.set_padding(3, 3)
@@ -101,7 +101,7 @@ class DBListEditor(object):
self.database_combo.set_model(dbstore)
self.database_combo.connect('changed', self.dbcombo_changed)
self.database_progressbar = gtk.ProgressBar()
- self.database_progressbar.set_text(_(u'Fetching databases list'))
+ self.database_progressbar.set_text(_('Fetching databases list'))
image = gtk.Image()
image.set_from_stock('tryton-new', gtk.ICON_SIZE_BUTTON)
db_box = gtk.VBox(homogeneous=True)
@@ -117,7 +117,7 @@ class DBListEditor(object):
db_box.set_size_request(width, height)
table.attach(database, 0, 1, 2, 3, yoptions=False, xoptions=gtk.FILL)
table.attach(db_box, 1, 2, 2, 3, yoptions=False)
- username = gtk.Label(set_underline(_(u'Username:')))
+ username = gtk.Label(set_underline(_('Username:')))
username.set_use_underline(True)
username.set_alignment(1, 0.5)
username.set_padding(3, 3)
@@ -214,7 +214,7 @@ class DBListEditor(object):
try:
entry_value = self.profiles.get(self.current_profile['name'],
field)
- except ConfigParser.NoOptionError:
+ except configparser.NoOptionError:
entry_value = ''
entry.set_text(entry_value)
if field == 'database':
@@ -304,9 +304,9 @@ class DBListEditor(object):
label = None
if self.test_server_version(host, port) is False:
- label = _(u'Incompatible version of the server')
+ label = _('Incompatible version of the server')
elif dbs is None:
- label = _(u'Could not connect to the server')
+ label = _('Could not connect to the server')
if label:
self.database_label.set_label('%s' % label)
self.database_label.show()
@@ -433,7 +433,7 @@ class DBLogin(object):
self.combo_profile.add_attribute(cell, 'sensitive', 1)
self.combo_profile.set_model(self.profile_store)
self.combo_profile.connect('changed', self.profile_changed)
- self.profile_label = gtk.Label(set_underline(_(u'Profile:')))
+ self.profile_label = gtk.Label(set_underline(_('Profile:')))
self.profile_label.set_use_underline(True)
self.profile_label.set_justify(gtk.JUSTIFY_RIGHT)
self.profile_label.set_alignment(1, 0.5)
@@ -492,7 +492,7 @@ class DBLogin(object):
# Profile informations
self.profile_cfg = os.path.join(get_config_dir(), 'profiles.cfg')
- self.profiles = ConfigParser.SafeConfigParser()
+ self.profiles = configparser.SafeConfigParser()
if not os.path.exists(self.profile_cfg):
short_version = '.'.join(__version__.split('.', 2)[:2])
name = 'demo%s.tryton.org' % short_version
@@ -533,7 +533,7 @@ class DBLogin(object):
profile = self.profile_store[position][0]
try:
username = self.profiles.get(profile, 'username')
- except ConfigParser.NoOptionError:
+ except configparser.NoOptionError:
username = ''
host = self.profiles.get(profile, 'host')
self.entry_host.set_text('%s' % host)
@@ -624,10 +624,10 @@ class DBLogin(object):
if not test:
if test is False:
common.warning('',
- _(u'Incompatible version of the server'))
+ _('Incompatible version of the server'))
else:
common.warning('',
- _(u'Could not connect to the server'))
+ _('Could not connect to the server'))
continue
database = self.entry_database.get_text()
login = self.entry_login.get_text()
diff --git a/tryton/gui/window/form.py b/tryton/gui/window/form.py
index 29d8ca15f1..eb213d7247 100644
--- a/tryton/gui/window/form.py
+++ b/tryton/gui/window/form.py
@@ -20,7 +20,7 @@ from tryton.common import RPCExecute, RPCException
from tryton.common.datetime_strftime import datetime_strftime
from tryton import plugins
-from tabcontent import TabContent
+from .tabcontent import TabContent
_ = gettext.gettext
@@ -57,7 +57,7 @@ class Form(SignalEvent, TabContent):
self.screen.signal_connect(self, 'unread-note', self._unread_note)
if res_id not in (None, False):
- if isinstance(res_id, (int, long)):
+ if isinstance(res_id, int):
res_id = [res_id]
self.screen.load(res_id)
else:
@@ -94,6 +94,9 @@ class Form(SignalEvent, TabContent):
and self.screen.limit == value.screen.limit
and self.screen.search_value == value.screen.search_value)
+ def __hash__(self):
+ return id(self)
+
def destroy(self):
super(Form, self).destroy()
self.screen.signal_unconnect(self)
diff --git a/tryton/gui/window/limit.py b/tryton/gui/window/limit.py
index 29302724ad..cc31ffb21c 100644
--- a/tryton/gui/window/limit.py
+++ b/tryton/gui/window/limit.py
@@ -30,7 +30,7 @@ class Limit(object):
label = gtk.Label(_('Limit:'))
hbox.pack_start(label, expand=True, fill=True)
adjustment = gtk.Adjustment(value=CONFIG['client.limit'],
- lower=1, upper=sys.maxint, step_incr=10, page_incr=100)
+ lower=1, upper=sys.maxsize, step_incr=10, page_incr=100)
self.spin_limit = gtk.SpinButton()
self.spin_limit.configure(adjustment, climb_rate=1, digits=0)
self.spin_limit.set_numeric(False)
diff --git a/tryton/gui/window/preference.py b/tryton/gui/window/preference.py
index 462f54e197..e7bcf84780 100644
--- a/tryton/gui/window/preference.py
+++ b/tryton/gui/window/preference.py
@@ -87,12 +87,14 @@ class Preference(NoModal):
if self.screen.current_record.validate():
vals = copy.copy(self.screen.get())
context = rpc.CONTEXT.copy()
- func = lambda parameters: rpc.execute(
- 'model', 'res.user', 'set_preferences', vals, parameters,
- context)
+
+ def login(parameters):
+ return rpc.execute(
+ 'model', 'res.user', 'set_preferences',
+ vals, parameters, context)
try:
- Login(func)
- except TrytonError, exception:
+ Login(login)
+ except TrytonError as exception:
if exception.faultCode == 'QueryCanceled':
return
raise
diff --git a/tryton/gui/window/view_board/__init__.py b/tryton/gui/window/view_board/__init__.py
index a4789b0a51..e33419253d 100644
--- a/tryton/gui/window/view_board/__init__.py
+++ b/tryton/gui/window/view_board/__init__.py
@@ -1,3 +1,3 @@
# 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 view_board import *
+from .view_board import *
diff --git a/tryton/gui/window/view_form/__init__.py b/tryton/gui/window/view_form/__init__.py
index fdb85fd5a4..016355b5a9 100644
--- a/tryton/gui/window/view_form/__init__.py
+++ b/tryton/gui/window/view_form/__init__.py
@@ -1,3 +1,3 @@
# 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 screen
+from . import screen
diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py
index e57553cd31..89a253cdf9 100644
--- a/tryton/gui/window/view_form/model/field.py
+++ b/tryton/gui/window/view_form/model/field.py
@@ -287,7 +287,7 @@ class TimeDeltaField(Field):
return group.context.get(self.attrs.get('converter'))
def set_client(self, record, value, force_change=False):
- if isinstance(value, basestring):
+ if isinstance(value, str):
value = common.timedelta.parse(value, self.converter(record.group))
super(TimeDeltaField, self).set_client(
record, value, force_change=force_change)
@@ -320,10 +320,14 @@ class FloatField(Field):
return self._default
def set_client(self, record, value, force_change=False, factor=1):
- if isinstance(value, basestring):
+ if isinstance(value, str):
value = self.convert(value)
if value is not None:
- value /= factor
+ # Keep the same type
+ if isinstance(value, int):
+ value //= factor
+ else:
+ value /= factor
super(FloatField, self).set_client(record, value,
force_change=force_change)
@@ -332,7 +336,7 @@ class FloatField(Field):
if value is not None:
digits = self.digits(record, factor=factor)
if digits:
- p = digits[1]
+ p = int(digits[1])
else:
d = value * factor
if not isinstance(d, Decimal):
@@ -423,7 +427,7 @@ class M2OField(Field):
def set(self, record, value):
rec_name = record.value.get(self.name + '.rec_name') or ''
- if not rec_name and value >= 0:
+ if not rec_name and value is not None and value >= 0:
try:
result, = RPCExecute('model', self.attrs['relation'], 'read',
[value], ['rec_name'])
@@ -496,7 +500,7 @@ class O2MField(Field):
def _set_default_value(self, record, fields=None):
if record.value.get(self.name) is not None:
return
- from group import Group
+ from .group import Group
parent_name = self.attrs.get('relation_field', '')
fields = fields or {}
context = record.expr_eval(self.attrs.get('context', {}))
@@ -593,7 +597,7 @@ class O2MField(Field):
group = record.value[self.name]
if value is None:
value = []
- if not value or isinstance(value[0], (int, long)):
+ if not value or isinstance(value[0], int):
mode = 'list ids'
else:
mode = 'list values'
@@ -640,7 +644,7 @@ class O2MField(Field):
fields = record.group.fields
if fields:
fields = dict((fname, field.attrs)
- for fname, field in fields.iteritems())
+ for fname, field in fields.items())
record.value[self.name] = None
self._set_default_value(record, fields=fields)
@@ -655,7 +659,7 @@ class O2MField(Field):
if value is None:
value = []
# domain inversion could try to set id as value
- if isinstance(value, (int, long)):
+ if isinstance(value, int):
value = [value]
previous_ids = self.get_eval(record)
@@ -801,7 +805,7 @@ class ReferenceField(Field):
def set_client(self, record, value, force_change=False):
if value:
- if isinstance(value, basestring):
+ if isinstance(value, str):
value = value.split(',')
ref_model, ref_id = value
if isinstance(ref_id, (tuple, list)):
@@ -825,7 +829,7 @@ class ReferenceField(Field):
if not value:
record.value[self.name] = self._default
return
- if isinstance(value, basestring):
+ if isinstance(value, str):
ref_model, ref_id = value.split(',')
if not ref_id:
ref_id = None
@@ -888,14 +892,13 @@ class _FileCache(object):
class BinaryField(Field):
_default = None
- cast = bytearray if bytes == str else bytes
def get(self, record):
result = record.value.get(self.name, self._default)
if isinstance(result, _FileCache):
try:
with open(result.path, 'rb') as fp:
- result = self.cast(fp.read())
+ result = fp.read()
except IOError:
result = self.get_data(record)
return result
@@ -905,8 +908,11 @@ class BinaryField(Field):
def set_client(self, record, value, force_change=False):
_, filename = tempfile.mkstemp(prefix='tryton_')
+ data = value or b''
+ if isinstance(data, str):
+ data = data.encode('utf-8')
with open(filename, 'wb') as fp:
- fp.write(value or '')
+ fp.write(data)
self.set(record, _FileCache(filename))
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
@@ -918,24 +924,27 @@ class BinaryField(Field):
result = record.value.get(self.name) or 0
if isinstance(result, _FileCache):
result = os.stat(result.path).st_size
- elif isinstance(result, (basestring, bytes, bytearray)):
+ elif isinstance(result, (str, bytes)):
result = len(result)
return result
def get_data(self, record):
if not isinstance(record.value.get(self.name),
- (basestring, bytes, bytearray, _FileCache)):
+ (str, bytes, _FileCache)):
if record.id < 0:
- return ''
+ return b''
context = record.get_context()
try:
values, = RPCExecute('model', record.model_name, 'read',
[record.id], [self.name], context=context)
except RPCException:
- return ''
+ return b''
_, filename = tempfile.mkstemp(prefix='tryton_')
+ data = values[self.name] or b''
+ if isinstance(data, str):
+ data = data.encode('utf-8')
with open(filename, 'wb') as fp:
- fp.write(values[self.name] or '')
+ fp.write(data)
self.set(record, _FileCache(filename))
return self.get(record)
diff --git a/tryton/gui/window/view_form/model/group.py b/tryton/gui/window/view_form/model/group.py
index 93a1f053a1..d3bc06f401 100644
--- a/tryton/gui/window/view_form/model/group.py
+++ b/tryton/gui/window/view_form/model/group.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.
-from record import Record
-from field import Field, M2OField, ReferenceField
+from .record import Record
+from .field import Field, M2OField, ReferenceField
from tryton import rpc
from tryton.signal_event import SignalEvent
from tryton.common.domain_inversion import is_leaf
@@ -154,7 +154,7 @@ class Group(SignalEvent, list):
return '' % (self.model_name, id(self))
def load_fields(self, fields):
- for name, attr in fields.iteritems():
+ for name, attr in fields.items():
field = Field.get_field(attr['type'])
attr['name'] = name
self.fields[name] = field(attr)
@@ -184,7 +184,7 @@ class Group(SignalEvent, list):
return root
def written(self, ids):
- if isinstance(ids, (int, long)):
+ if isinstance(ids, int):
ids = [ids]
ids = [x for x in self.on_write_ids(ids) or [] if x not in ids]
if not ids:
@@ -400,7 +400,7 @@ class Group(SignalEvent, list):
return None
return self[self.current_idx]
- def next(self):
+ def __next__(self):
if len(self) and self.current_idx is not None:
self.current_idx = (self.current_idx + 1) % len(self)
elif len(self):
@@ -411,7 +411,7 @@ class Group(SignalEvent, list):
def add_fields(self, fields):
to_add = {}
- for name, attr in fields.iteritems():
+ for name, attr in fields.items():
if name not in self.fields:
to_add[name] = attr
else:
@@ -429,7 +429,7 @@ class Group(SignalEvent, list):
if len(new) and len(to_add):
try:
values = RPCExecute('model', self.model_name, 'default_get',
- to_add.keys(), context=self.context)
+ list(to_add.keys()), context=self.context)
except RPCException:
return False
for record in new:
diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py
index e885b7b907..6942405485 100644
--- a/tryton/gui/window/view_form/model/record.py
+++ b/tryton/gui/window/view_form/model/record.py
@@ -3,7 +3,7 @@
from tryton.signal_event import SignalEvent
import tryton.common as common
from tryton.pyson import PYSONDecoder
-import field as fields
+from . import field as fields
from tryton.common import RPCExecute, RPCException
from tryton.config import CONFIG
@@ -45,12 +45,12 @@ class Record(SignalEvent):
if name == '*':
loading = 'eager'
views = set()
- for field in self.group.fields.itervalues():
+ for field in self.group.fields.values():
if field.attrs.get('loading', 'eager') == 'lazy':
loading = 'lazy'
views |= field.views
# Set a valid name for next loaded check
- for fname, field in self.group.fields.iteritems():
+ for fname, field in self.group.fields.items():
if field.attrs.get('loading', 'eager') == loading:
name = fname
break
@@ -60,10 +60,10 @@ class Record(SignalEvent):
if loading == 'eager':
fields = ((fname, field)
- for fname, field in self.group.fields.iteritems()
+ for fname, field in self.group.fields.items()
if field.attrs.get('loading', 'eager') == 'eager')
else:
- fields = self.group.fields.iteritems()
+ fields = self.group.fields.items()
fnames = [fname for fname, field in fields
if fname not in self._loaded
@@ -113,20 +113,20 @@ class Record(SignalEvent):
ctx = record_context.copy()
ctx.update(dict(('%s.%s' % (self.model_name, fname), 'size')
- for fname, field in self.group.fields.iteritems()
+ for fname, field in self.group.fields.items()
if field.attrs['type'] == 'binary' and fname in fnames))
exception = None
try:
values = RPCExecute('model', self.model_name, 'read',
- id2record.keys(), fnames, context=ctx)
- except RPCException, exception:
+ list(id2record.keys()), fnames, context=ctx)
+ except RPCException as exception:
values = [{'id': x} for x in id2record]
default_values = dict((f, None) for f in fnames)
for value in values:
value.update(default_values)
self.exception = True
id2value = dict((value['id'], value) for value in values)
- for id, record in id2record.iteritems():
+ for id, record in id2record.items():
if not record.exception:
record.exception = bool(exception)
value = id2value.get(id)
@@ -253,7 +253,7 @@ class Record(SignalEvent):
def get(self):
value = {}
- for name, field in self.group.fields.iteritems():
+ for name, field in self.group.fields.items():
if (field.attrs.get('readonly')
and not isinstance(field, fields.O2MField)):
continue
@@ -267,7 +267,7 @@ class Record(SignalEvent):
def get_eval(self):
value = {}
- for name, field in self.group.fields.iteritems():
+ for name, field in self.group.fields.items():
if name not in self._loaded and self.id >= 0:
continue
value[name] = field.get_eval(self)
@@ -276,7 +276,7 @@ class Record(SignalEvent):
def get_on_change_value(self, skip=None):
value = {}
- for name, field in self.group.fields.iteritems():
+ for name, field in self.group.fields.items():
if skip and name in skip:
continue
if (self.id >= 0
@@ -295,7 +295,7 @@ class Record(SignalEvent):
def get_timestamp(self):
result = {self.model_name + ',' + str(self.id): self._timestamp}
- for name, field in self.group.fields.iteritems():
+ for name, field in self.group.fields.items():
if name in self._loaded:
result.update(field.get_timestamp(self))
return result
@@ -376,7 +376,7 @@ class Record(SignalEvent):
context.setdefault('default_rec_name', rec_name)
try:
vals = RPCExecute('model', self.model_name, 'default_get',
- self.group.fields.keys(), context=context)
+ list(self.group.fields.keys()), context=context)
except RPCException:
return
if (self.parent
@@ -404,7 +404,7 @@ class Record(SignalEvent):
elif fields is None:
self._check_load()
res = True
- for field_name, field in self.group.fields.iteritems():
+ for field_name, field in self.group.fields.items():
if fields is not None and field_name not in fields:
continue
if field.attrs.get('readonly'):
@@ -417,7 +417,7 @@ class Record(SignalEvent):
def _get_invalid_fields(self):
fields = {}
- for fname, field in self.group.fields.iteritems():
+ for fname, field in self.group.fields.items():
invalid = field.get_state_attrs(self).get('invalid')
if invalid:
fields[fname] = invalid
@@ -430,7 +430,7 @@ class Record(SignalEvent):
def set_default(self, val, signal=True, validate=True):
fieldnames = []
- for fieldname, value in val.items():
+ for fieldname, value in list(val.items()):
if fieldname not in self.group.fields:
continue
if fieldname == self.group.exclude_field:
@@ -455,7 +455,7 @@ class Record(SignalEvent):
def set(self, val, signal=True, validate=True):
later = {}
fieldnames = []
- for fieldname, value in val.iteritems():
+ for fieldname, value in val.items():
if fieldname == '_timestamp':
# Always keep the older timestamp
if not self._timestamp:
@@ -478,7 +478,7 @@ class Record(SignalEvent):
self.group.fields[fieldname].set(self, value)
self._loaded.add(fieldname)
fieldnames.append(fieldname)
- for fieldname, value in later.iteritems():
+ for fieldname, value in later.items():
self.group.fields[fieldname].set(self, value)
self._loaded.add(fieldname)
if validate:
@@ -487,7 +487,7 @@ class Record(SignalEvent):
self.signal('record-changed')
def set_on_change(self, values):
- for fieldname, value in values.items():
+ for fieldname, value in list(values.items()):
if fieldname not in self.group.fields:
continue
if isinstance(self.group.fields[fieldname], (fields.M2OField,
@@ -520,7 +520,7 @@ class Record(SignalEvent):
self.signal('record-changed')
def expr_eval(self, expr):
- if not isinstance(expr, basestring):
+ if not isinstance(expr, str):
return expr
ctx = self.get_eval()
ctx['context'] = self.get_context()
@@ -605,7 +605,7 @@ class Record(SignalEvent):
self.group.fields[fieldname].set_on_change(self, result)
def autocomplete_with(self, field_name):
- for fieldname, fieldinfo in self.group.fields.iteritems():
+ for fieldname, fieldinfo in self.group.fields.items():
autocomplete = fieldinfo.attrs.get('autocomplete', [])
if field_name not in autocomplete:
continue
@@ -625,7 +625,7 @@ class Record(SignalEvent):
def set_field_context(self):
from .group import Group
- for name, field in self.group.fields.iteritems():
+ for name, field in self.group.fields.items():
value = self.value.get(name)
if not isinstance(value, Group):
continue
@@ -677,7 +677,7 @@ class Record(SignalEvent):
return clicks
def destroy(self):
- for v in self.value.itervalues():
+ for v in self.value.values():
if hasattr(v, 'destroy'):
v.destroy()
super(Record, self).destroy()
diff --git a/tryton/gui/window/view_form/screen/__init__.py b/tryton/gui/window/view_form/screen/__init__.py
index a33693ab54..70b9392183 100644
--- a/tryton/gui/window/view_form/screen/__init__.py
+++ b/tryton/gui/window/view_form/screen/__init__.py
@@ -1,3 +1,3 @@
# 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 screen import *
+from .screen import *
diff --git a/tryton/gui/window/view_form/screen/screen.py b/tryton/gui/window/view_form/screen/screen.py
index 58ad9d0e40..dc311e857d 100644
--- a/tryton/gui/window/view_form/screen/screen.py
+++ b/tryton/gui/window/view_form/screen/screen.py
@@ -8,11 +8,11 @@ import datetime
import calendar
import json
import collections
-import urllib
-import urlparse
+import urllib.parse
import xml.dom.minidom
import gettext
import logging
+from operator import itemgetter
import gtk
@@ -170,7 +170,7 @@ class Screen(SignalEvent):
view_tree = self.fields_view_tree[view_id]
fields = copy.deepcopy(view_tree['fields'])
- for name, props in fields.iteritems():
+ for name, props in fields.items():
if props['type'] not in ('selection', 'reference'):
continue
if isinstance(props['selection'], (tuple, list)):
@@ -202,7 +202,7 @@ class Screen(SignalEvent):
):
if name not in fields:
fields[name] = {
- 'string': string.decode('utf-8'),
+ 'string': string,
'name': name,
'type': type_,
}
@@ -224,7 +224,7 @@ class Screen(SignalEvent):
props['selection'])
except RPCException:
selection = []
- selection.sort(lambda x, y: cmp(x[1], y[1]))
+ selection.sort(key=itemgetter(1))
return selection
def search_prev(self, search_string):
@@ -355,7 +355,7 @@ class Screen(SignalEvent):
fields_views = {}
if self.group is not None:
self.group.signal_unconnect(self)
- for name, field in self.group.fields.iteritems():
+ for name, field in self.group.fields.items():
fields[name] = field.attrs
fields_views[name] = field.views
self.tree_states_done.clear()
@@ -375,7 +375,7 @@ class Screen(SignalEvent):
self._record_modified)
self.__group.signal_connect(self, 'group-changed', self._group_changed)
self.__group.add_fields(fields)
- for name, views in fields_views.iteritems():
+ for name, views in fields_views.items():
self.__group.fields[name].views.update(views)
self.__group.exclude_field = self.exclude_field
@@ -827,7 +827,7 @@ class Screen(SignalEvent):
timestamp = self.parent._timestamp if self.parent else None
for view in self.views:
if view.view_type == 'form':
- for widgets in view.widgets.itervalues():
+ for widgets in view.widgets.values():
for widget in widgets:
if hasattr(widget, 'screen'):
widget.screen.save_tree_state(store)
@@ -1036,7 +1036,7 @@ class Screen(SignalEvent):
record = self.current_record
domain_string = _('"%s" is not valid according to its domain')
domain_parser = DomainParser(
- {n: f.attrs for n, f in record.group.fields.iteritems()})
+ {n: f.attrs for n, f in record.group.fields.items()})
fields = []
for field, invalid in sorted(record.invalid_fields.items()):
string = record.group.fields[field].attrs['string']
@@ -1134,7 +1134,7 @@ class Screen(SignalEvent):
except RPCException:
action = None
self.reload(ids, written=True)
- if isinstance(action, basestring):
+ if isinstance(action, str):
self.client_action(action)
elif action:
Action.execute(action, {
@@ -1208,7 +1208,7 @@ class Screen(SignalEvent):
if view_ids:
query_string.append(('views', json.dumps(
view_ids, separators=(',', ':'))))
- query_string = urllib.urlencode(query_string)
- return urlparse.urlunparse(('tryton',
+ query_string = urllib.parse.urlencode(query_string)
+ return urllib.parse.urlunparse(('tryton',
'%s:%s' % (rpc._HOST, rpc._PORT),
'/'.join(path), query_string, '', ''))
diff --git a/tryton/gui/window/view_form/view/calendar_.py b/tryton/gui/window/view_form/view/calendar_.py
index de9bd606a9..e8e39f131d 100644
--- a/tryton/gui/window/view_form/view/calendar_.py
+++ b/tryton/gui/window/view_form/view/calendar_.py
@@ -11,7 +11,7 @@ from . import View
try:
from .calendar_gtk.calendar_ import Calendar_
from .calendar_gtk.toolbar import Toolbar
-except ImportError, e:
+except ImportError as e:
Calendar_ = None
Toolbar = None
diff --git a/tryton/gui/window/view_form/view/calendar_gtk/calendar_.py b/tryton/gui/window/view_form/view/calendar_gtk/calendar_.py
index f1d8212163..471513e380 100644
--- a/tryton/gui/window/view_form/view/calendar_gtk/calendar_.py
+++ b/tryton/gui/window/view_form/view/calendar_gtk/calendar_.py
@@ -3,7 +3,7 @@
import calendar
import datetime
import goocalendar
-from dates_period import DatesPeriod
+from .dates_period import DatesPeriod
class Calendar_(goocalendar.Calendar):
diff --git a/tryton/gui/window/view_form/view/form.py b/tryton/gui/window/view_form/view/form.py
index f4a35707e8..4569d46e1d 100644
--- a/tryton/gui/window/view_form/view/form.py
+++ b/tryton/gui/window/view_form/view/form.py
@@ -466,13 +466,13 @@ class ViewForm(View):
return cls.WIDGETS[name]
def get_fields(self):
- return self.widgets.keys()
+ return list(self.widgets.keys())
def __getitem__(self, name):
return self.widgets[name][0]
def destroy(self):
- for widget_name in self.widgets.keys():
+ for widget_name in list(self.widgets.keys()):
for widget in self.widgets[widget_name]:
widget.destroy()
self.widget.destroy()
@@ -480,7 +480,7 @@ class ViewForm(View):
def set_value(self, focused_widget=False):
record = self.screen.current_record
if record:
- for name, widgets in self.widgets.iteritems():
+ for name, widgets in self.widgets.items():
if name in record.group.fields:
field = record.group.fields[name]
for widget in widgets:
@@ -498,7 +498,7 @@ class ViewForm(View):
@property
def modified(self):
- return any(w.modified for widgets in self.widgets.itervalues()
+ return any(w.modified for widgets in self.widgets.values()
for w in widgets)
def get_buttons(self):
@@ -507,7 +507,7 @@ class ViewForm(View):
def reset(self):
record = self.screen.current_record
if record:
- for name, widgets in self.widgets.iteritems():
+ for name, widgets in self.widgets.items():
field = record.group.fields.get(name)
if field and 'invalid' in field.get_state_attrs(record):
for widget in widgets:
@@ -523,13 +523,13 @@ class ViewForm(View):
(name,
field.attrs.get('loading', 'eager') == 'eager',
len(field.views))
- for name, field in record.group.fields.iteritems()
+ for name, field in record.group.fields.items()
if self.view_id in field.views)
fields = sorted(fields, key=operator.itemgetter(1, 2))
for field, _, _ in fields:
record[field].get(record)
focused_widget = find_focused_child(self.widget)
- for name, widgets in self.widgets.iteritems():
+ for name, widgets in self.widgets.items():
field = None
if record:
field = record.group.fields.get(name)
diff --git a/tryton/gui/window/view_form/view/form_gtk/dictionary.py b/tryton/gui/window/view_form/view/form_gtk/dictionary.py
index b841d1e7cf..520e6c9e5b 100644
--- a/tryton/gui/window/view_form/view/form_gtk/dictionary.py
+++ b/tryton/gui/window/view_form/view/form_gtk/dictionary.py
@@ -126,7 +126,7 @@ class DictSelectionEntry(DictEntry):
text = child.get_text()
value = None
if text:
- for txt, val in self._selection.items():
+ for txt, val in list(self._selection.items()):
if not val:
continue
if txt[:len(text)].lower() == text.lower():
@@ -390,7 +390,7 @@ class DictWidget(Widget):
def _sig_add(self, *args):
context = self.field.get_context(self.record)
- value = self.wid_text.get_text().decode('utf-8')
+ value = self.wid_text.get_text()
domain = self.field.domain_get(self.record)
def callback(result):
@@ -439,20 +439,20 @@ class DictWidget(Widget):
def get_value(self):
return dict((key, widget.get_value())
- for key, widget in self.fields.items())
+ for key, widget in list(self.fields.items()))
@property
def modified(self):
if self.record and self.field:
value = self.field.get_client(self.record)
return any(widget.modified(value)
- for widget in self.fields.itervalues())
+ for widget in self.fields.values())
return False
def _readonly_set(self, readonly):
self._readonly = readonly
self._set_button_sensitive()
- for widget in self.fields.values():
+ for widget in list(self.fields.values()):
widget.set_readonly(readonly)
self.wid_text.set_sensitive(not readonly)
self.wid_text.set_editable(not readonly)
@@ -461,7 +461,7 @@ class DictWidget(Widget):
self.but_add.set_sensitive(bool(
not self._readonly
and self.attrs.get('create', True)))
- for button in self.buttons.itervalues():
+ for button in self.buttons.values():
button.set_sensitive(bool(
not self._readonly
and self.attrs.get('delete', True)))
@@ -507,7 +507,7 @@ class DictWidget(Widget):
context = self.field.get_context(self.record)
domain = self.field.domain_get(self.record)
batchlen = min(10, CONFIG['client.limit'])
- for i in xrange(0, len(keys), batchlen):
+ for i in range(0, len(keys), batchlen):
sub_keys = keys[i:i + batchlen]
try:
key_ids = RPCExecute('model', self.schema_model, 'search',
@@ -534,15 +534,15 @@ class DictWidget(Widget):
record_id = record.id if record else None
if record_id != self._record_id:
- for key in self.fields.keys():
+ for key in list(self.fields.keys()):
self._sig_remove(None, key, modified=False)
self._record_id = record_id
value = field.get_client(record) if field else {}
- new_key_names = set(value.iterkeys()) - set(self.keys)
+ new_key_names = set(value.keys()) - set(self.keys)
if new_key_names:
self.add_keys(list(new_key_names))
- for key, val in sorted(value.iteritems()):
+ for key, val in sorted(value.items()):
if key not in self.keys:
continue
if key not in self.fields:
diff --git a/tryton/gui/window/view_form/view/form_gtk/image.py b/tryton/gui/window/view_form/view/form_gtk/image.py
index 93e61fba4c..67dfb76028 100644
--- a/tryton/gui/window/view_form/view/form_gtk/image.py
+++ b/tryton/gui/window/view_form/view/form_gtk/image.py
@@ -2,7 +2,7 @@
# this repository contains the full copyright notices and license terms.
import gtk
import gettext
-import urllib
+import urllib.request
from tryton.common import resize_pixbuf, data2pixbuf, BIG_IMAGE_SIZE
from .widget import Widget
@@ -76,12 +76,14 @@ class Image(BinaryMixin, Widget):
if info == 0:
uri = selection.get_text().split('\n')[0]
if uri:
- self.field.set_client(self.record, urllib.urlopen(uri).read())
+ self.field.set_client(
+ self.record, urllib.request.urlopen(uri).read())
self.update_img()
elif info == 1:
uri = selection.data.split('\r\n')[0]
if uri:
- self.field.set_client(self.record, urllib.urlopen(uri).read())
+ self.field.set_client(
+ self.record, urllib.request.urlopen(uri).read())
self.update_img()
elif info == 2:
data = selection.get_pixbuf()
@@ -93,7 +95,7 @@ class Image(BinaryMixin, Widget):
value = None
if self.field:
value = self.field.get_client(self.record)
- if isinstance(value, (int, long)):
+ if isinstance(value, int):
if value > BIG_IMAGE_SIZE:
value = False
else:
diff --git a/tryton/gui/window/view_form/view/form_gtk/many2many.py b/tryton/gui/window/view_form/view/form_gtk/many2many.py
index 63f290d689..eee926b888 100644
--- a/tryton/gui/window/view_form/view/form_gtk/many2many.py
+++ b/tryton/gui/window/view_form/view/form_gtk/many2many.py
@@ -151,7 +151,7 @@ class Many2Many(Widget):
domain = [domain, add_remove]
context = self.field.get_search_context(self.record)
order = self.field.get_search_order(self.record)
- value = self.wid_text.get_text().decode('utf-8')
+ value = self.wid_text.get_text()
self.focus_out = False
diff --git a/tryton/gui/window/view_form/view/form_gtk/many2one.py b/tryton/gui/window/view_form/view/form_gtk/many2one.py
index 845f4cd5f1..5ab8c668be 100644
--- a/tryton/gui/window/view_form/view/form_gtk/many2one.py
+++ b/tryton/gui/window/view_form/view/form_gtk/many2one.py
@@ -121,7 +121,7 @@ class Many2One(Widget):
domain = self.field.domain_get(self.record)
context = self.field.get_search_context(self.record)
order = self.field.get_search_order(self.record)
- text = self.wid_text.get_text().decode('utf-8')
+ text = self.wid_text.get_text()
def callback(result):
if result:
@@ -212,7 +212,7 @@ class Many2One(Widget):
domain = self.field.domain_get(self.record)
context = self.field.get_search_context(self.record)
order = self.field.get_search_order(self.record)
- text = self.wid_text.get_text().decode('utf-8')
+ text = self.wid_text.get_text()
def callback(result):
if result:
diff --git a/tryton/gui/window/view_form/view/form_gtk/one2many.py b/tryton/gui/window/view_form/view/form_gtk/one2many.py
index b913abe493..863b25a818 100644
--- a/tryton/gui/window/view_form/view/form_gtk/one2many.py
+++ b/tryton/gui/window/view_form/view/form_gtk/one2many.py
@@ -413,8 +413,8 @@ class One2Many(Widget):
self.screen.group.remove(first, remove=True)
return
- fields = product.keys()
- for values in itertools.product(*product.values()):
+ fields = list(product.keys())
+ for values in itertools.product(*list(product.values())):
if first:
record = first
first = None
@@ -478,7 +478,7 @@ class One2Many(Widget):
domain = [domain, self.record.expr_eval(self.attrs.get('add_remove'))]
removed_ids = self.field.get_removed_ids(self.record)
domain = ['OR', domain, ('id', 'in', removed_ids)]
- text = self.wid_text.get_text().decode('utf-8')
+ text = self.wid_text.get_text()
self.focus_out = False
diff --git a/tryton/gui/window/view_form/view/form_gtk/richtextbox.py b/tryton/gui/window/view_form/view/form_gtk/richtextbox.py
index e74e5a12ee..b54a3de088 100644
--- a/tryton/gui/window/view_form/view/form_gtk/richtextbox.py
+++ b/tryton/gui/window/view_form/view/form_gtk/richtextbox.py
@@ -259,7 +259,7 @@ class RichTextBox(TextBox):
start.backward_chars(length)
end = iter_.copy()
# Apply tags activated from toolbar
- for name, widget in self.tag_widgets[textview].iteritems():
+ for name, widget in self.tag_widgets[textview].items():
self._apply_tool(text_buffer, name, widget, start, end)
def _apply_tool(self, text_buffer, name, tool, start, end):
diff --git a/tryton/gui/window/view_form/view/form_gtk/state_widget.py b/tryton/gui/window/view_form/view/form_gtk/state_widget.py
index 6913f97bea..6c8b7cc570 100644
--- a/tryton/gui/window/view_form/view/form_gtk/state_widget.py
+++ b/tryton/gui/window/view_form/view/form_gtk/state_widget.py
@@ -93,7 +93,7 @@ class Notebook(StateMixin, gtk.Notebook):
else:
state_changes = {}
if state_changes.get('readonly', self.attrs.get('readonly')):
- for widgets in self.widgets.itervalues():
+ for widgets in self.widgets.values():
for widget in widgets:
widget._readonly_set(True)
diff --git a/tryton/gui/window/view_form/view/form_gtk/textbox.py b/tryton/gui/window/view_form/view/form_gtk/textbox.py
index 8a2503ff6e..c36824a8dd 100644
--- a/tryton/gui/window/view_form/view/form_gtk/textbox.py
+++ b/tryton/gui/window/view_form/view/form_gtk/textbox.py
@@ -111,7 +111,7 @@ class TextBox(Widget, TranslateMixin):
buf = textview.get_buffer()
iter_start = buf.get_start_iter()
iter_end = buf.get_end_iter()
- return buf.get_text(iter_start, iter_end, False).decode('utf-8')
+ return buf.get_text(iter_start, iter_end, False)
def display(self, record, field):
super(TextBox, self).display(record, field)
@@ -152,7 +152,6 @@ class TextBufferLimitSize(gtk.TextBuffer):
def do_insert_text(self, iter, text, length):
free_chars = self.max_length - self.get_char_count()
- # Slice operation needs an unicode string to work as expected
- text = text.decode('utf-8')[0:free_chars].encode('utf-8')
+ text = text[0:free_chars]
length = len(text)
return gtk.TextBuffer.do_insert_text(self, iter, text, length)
diff --git a/tryton/gui/window/view_form/view/form_gtk/widget.py b/tryton/gui/window/view_form/view/form_gtk/widget.py
index bbb1712220..ccb78d7f2e 100644
--- a/tryton/gui/window/view_form/view/form_gtk/widget.py
+++ b/tryton/gui/window/view_form/view/form_gtk/widget.py
@@ -220,7 +220,7 @@ class TranslateDialog(NoModal):
def response(self, win, response):
if response == gtk.RESPONSE_OK:
- for code, widget in self.widgets.iteritems():
+ for code, widget in self.widgets.items():
widget, editing, fuzzy = widget
if not editing.get_active():
continue
diff --git a/tryton/gui/window/view_form/view/graph.py b/tryton/gui/window/view_form/view/graph.py
index 8912c4e500..d37c5f20db 100644
--- a/tryton/gui/window/view_form/view/graph.py
+++ b/tryton/gui/window/view_form/view/graph.py
@@ -111,7 +111,7 @@ class ViewGraph(View):
hbox.pack_start(gtk.Label(_('Width:')), False, True)
spinwidth = gtk.SpinButton()
- spinwidth.configure(gtk.Adjustment(400.0, 0.0, sys.maxint, 1.0, 10.0),
+ spinwidth.configure(gtk.Adjustment(400.0, 0.0, sys.maxsize, 1.0, 10.0),
climb_rate=1, digits=0)
spinwidth.set_numeric(True)
spinwidth.set_activates_default(True)
@@ -119,7 +119,8 @@ class ViewGraph(View):
hbox.pack_start(gtk.Label(_('Height:')), False, True)
spinheight = gtk.SpinButton()
- spinheight.configure(gtk.Adjustment(200.0, 0.0, sys.maxint, 1.0, 10.0),
+ spinheight.configure(gtk.Adjustment(
+ 200.0, 0.0, sys.maxsize, 1.0, 10.0),
climb_rate=1, digits=0)
spinheight.set_numeric(True)
spinheight.set_activates_default(True)
diff --git a/tryton/gui/window/view_form/view/graph_gtk/bar.py b/tryton/gui/window/view_form/view/graph_gtk/bar.py
index 04e990f69f..164d516e66 100644
--- a/tryton/gui/window/view_form/view/graph_gtk/bar.py
+++ b/tryton/gui/window/view_form/view/graph_gtk/bar.py
@@ -8,7 +8,7 @@ import datetime
import cairo
-from graph import Graph
+from .graph import Graph
import tryton.common as common
import tryton.rpc as rpc
@@ -142,7 +142,7 @@ class VerticalBar(Bar):
self.bars = []
i = 0
- keys = self.datas.keys()
+ keys = list(self.datas.keys())
keys.sort()
for xfield in keys:
j = 0
@@ -198,7 +198,7 @@ class HorizontalBar(Bar):
self.bars = []
i = 0
- keys = self.datas.keys()
+ keys = list(self.datas.keys())
keys.sort()
for xfield in keys:
j = 0
diff --git a/tryton/gui/window/view_form/view/graph_gtk/graph.py b/tryton/gui/window/view_form/view/graph_gtk/graph.py
index c192a744cc..d29b24ad1e 100644
--- a/tryton/gui/window/view_form/view/graph_gtk/graph.py
+++ b/tryton/gui/window/view_form/view/graph_gtk/graph.py
@@ -127,7 +127,7 @@ class Graph(gtk.DrawingArea):
self.drawGraph(cx, width, height)
self.drawAxis(cx, width, height)
self.drawLegend(cx, width, height)
- surface.write_to_png(filename.encode('utf-8'))
+ surface.write_to_png(filename)
self.queue_draw()
@@ -176,7 +176,7 @@ class Graph(gtk.DrawingArea):
base = 1
else:
base = 10 ** int(math.log(self.yrange, 10))
- for i in xrange(int(self.yrange / base) + 1):
+ for i in range(int(self.yrange / base) + 1):
val = int(self.minyval / base) * base + i * base
h = (val - self.minyval) * self.yscale
label = locale.format('%.2f', val, True)
@@ -186,7 +186,7 @@ class Graph(gtk.DrawingArea):
def XLabels(self):
xlabels = []
i = 0.0
- keys = self.datas.keys()
+ keys = list(self.datas.keys())
keys.sort()
for key in keys:
if self.xrange == 0:
@@ -439,7 +439,7 @@ class Graph(gtk.DrawingArea):
else:
self.xscale = 1.0 / self.xrange
- if not self.datas.values():
+ if not list(self.datas.values()):
self.maxyval = 0.0
self.minyval = 0.0
else:
diff --git a/tryton/gui/window/view_form/view/graph_gtk/line.py b/tryton/gui/window/view_form/view/graph_gtk/line.py
index dc3842f6ad..36deceee8b 100644
--- a/tryton/gui/window/view_form/view/graph_gtk/line.py
+++ b/tryton/gui/window/view_form/view/graph_gtk/line.py
@@ -8,7 +8,7 @@ import datetime
import cairo
-from graph import Graph
+from .graph import Graph
import tryton.common as common
import tryton.rpc as rpc
@@ -23,7 +23,7 @@ class Line(Graph):
self.points = []
i = 0
- keys = self.datas.keys()
+ keys = list(self.datas.keys())
keys.sort()
for xfield in keys:
j = 0
diff --git a/tryton/gui/window/view_form/view/graph_gtk/pie.py b/tryton/gui/window/view_form/view/graph_gtk/pie.py
index aa7469a6e2..ccca4d563f 100644
--- a/tryton/gui/window/view_form/view/graph_gtk/pie.py
+++ b/tryton/gui/window/view_form/view/graph_gtk/pie.py
@@ -8,7 +8,7 @@ import datetime
import cairo
-from graph import Graph, Area
+from .graph import Graph, Area
import tryton.common as common
import tryton.rpc as rpc
@@ -16,7 +16,7 @@ import tryton.rpc as rpc
class Pie(Graph):
def _getDatasKeys(self):
- return self.datas.keys()
+ return list(self.datas.keys())
def drawLegend(self, cr, width, height):
pass
@@ -71,7 +71,7 @@ class Pie(Graph):
def updateGraph(self):
self.sum = 0.0
- for xkey in self.datas.iterkeys():
+ for xkey in self.datas.keys():
key = self.yfields[0].get('key', self.yfields[0]['name'])
if self.datas[xkey][key] > 0:
self.sum += self.datas[xkey][key]
@@ -79,7 +79,7 @@ class Pie(Graph):
fraction = angle = 0.0
self.slices = []
- for xkey in self.datas.iterkeys():
+ for xkey in self.datas.keys():
key = self.yfields[0].get('key', self.yfields[0]['name'])
value = self.datas[xkey][key]
if value > 0:
diff --git a/tryton/gui/window/view_form/view/list.py b/tryton/gui/window/view_form/view/list.py
index dc947c646f..562cf73f7e 100644
--- a/tryton/gui/window/view_form/view/list.py
+++ b/tryton/gui/window/view_form/view/list.py
@@ -158,8 +158,7 @@ class AdaptModelGroup(gtk.GenericTreeModel):
pos += 1
except KeyError:
continue
- self.group.sort(lambda x, y:
- cmp(new_order[ids2pos[x.id]], new_order[ids2pos[y.id]]))
+ self.group.sort(key=lambda x: new_order[ids2pos[x.id]])
prev = None
for record in self.group:
if prev:
@@ -561,7 +560,7 @@ class ViewTree(View):
column.arrow.set(gtk.ARROW_NONE, gtk.SHADOW_NONE)
model = self.treeview.get_model()
unsaved_records = [x for x in model.group if x.id < 0]
- search_string = self.screen.screen_container.get_text() or u''
+ search_string = self.screen.screen_container.get_text() or ''
if (self.screen.search_count == len(model)
or unsaved_records
or self.screen.parent):
@@ -969,7 +968,7 @@ class ViewTree(View):
if last_col and last_col.name in fields:
del fields[last_col.name]
- if fields and any(fields.itervalues()):
+ if fields and any(fields.values()):
model_name = self.screen.model_name
try:
RPCExecute('model', 'ir.ui.view_tree_width', 'set_width',
diff --git a/tryton/gui/window/view_form/view/list_gtk/editabletree.py b/tryton/gui/window/view_form/view/list_gtk/editabletree.py
index 76a93d135d..1e1af35c22 100644
--- a/tryton/gui/window/view_form/view/list_gtk/editabletree.py
+++ b/tryton/gui/window/view_form/view/list_gtk/editabletree.py
@@ -142,7 +142,7 @@ class EditableTreeView(TreeView):
if event.keyval == gtk.keysyms.Right:
if isinstance(entry, gtk.Entry):
if entry.get_position() >= \
- len(entry.get_text().decode('utf-8')) \
+ len(entry.get_text()) \
and not entry.get_selection_bounds():
leaving = True
else:
@@ -180,7 +180,7 @@ class EditableTreeView(TreeView):
gobject.idle_add(self.set_cursor, path,
self.prev_column(path, column), True)
elif keyval in self.leaving_record_events:
- fields = self.view.widgets.keys()
+ fields = list(self.view.widgets.keys())
if not record.validate(fields):
invalid_fields = record.invalid_fields
col = None
diff --git a/tryton/gui/window/view_form/view/list_gtk/widget.py b/tryton/gui/window/view_form/view/list_gtk/widget.py
index 6cc14dc9f1..33370fda1a 100644
--- a/tryton/gui/window/view_form/view/list_gtk/widget.py
+++ b/tryton/gui/window/view_form/view/list_gtk/widget.py
@@ -480,9 +480,12 @@ class Binary(GenericText):
file_path = os.path.join(dtemp, filename)
with open(file_path, 'wb') as fp:
if hasattr(field, 'get_data'):
- fp.write(field.get_data(record))
+ data = field.get_data(record)
else:
- fp.write(field.get(record))
+ data = field.get(record)
+ if isinstance(data, str):
+ data = data.encode('utf-8')
+ fp.write(data)
root, type_ = os.path.splitext(filename)
if type_:
type_ = type_[1:]
@@ -527,7 +530,7 @@ class Image(GenericText):
record = store.get_value(iter_, 0)
field = record[self.field_name]
value = field.get_client(record)
- if isinstance(value, (int, long)):
+ if isinstance(value, int):
if value > common.BIG_IMAGE_SIZE:
value = None
else:
@@ -665,7 +668,7 @@ class M2O(GenericText):
context=context, domain=domain,
order=order, view_ids=self.attrs.get('view_ids', '').split(','),
new=create_access, title=self.attrs.get('string'))
- win.screen.search_filter(quote(text.decode('utf-8')))
+ win.screen.search_filter(quote(text))
return win
def set_completion(self, entry, path):
@@ -819,7 +822,8 @@ class Selection(GenericText, SelectionMixin, PopdownMixin):
record = store.get_value(store.get_iter(path), 0)
field = record[self.attrs['name']]
- set_value = lambda *a: self.set_value(editable, record, field)
+ def set_value(*a):
+ return self.set_value(editable, record, field)
editable.get_child().connect('activate', set_value)
editable.get_child().connect('focus-out-event', set_value)
editable.connect('changed', set_value)
diff --git a/tryton/gui/window/view_form/view/screen_container.py b/tryton/gui/window/view_form/view/screen_container.py
index 9e62994428..3752e04aea 100644
--- a/tryton/gui/window/view_form/view/screen_container.py
+++ b/tryton/gui/window/view_form/view/screen_container.py
@@ -344,7 +344,7 @@ class ScreenContainer(object):
model.append([r.strip()])
def get_text(self):
- return self.search_entry.get_text().decode('utf-8')
+ return self.search_entry.get_text()
def set_text(self, value):
self.search_entry.set_text(value)
@@ -370,7 +370,7 @@ class ScreenContainer(object):
bool(current_text))
for id_, name, domain in self.bookmarks():
text = self.screen.domain_parser.string(domain)
- domain = self.screen.domain_parser.parse(text.decode('utf-8'))
+ domain = self.screen.domain_parser.parse(text)
if (text == current_text
or domain == current_domain):
if icon_stock != 'tryton-star':
@@ -549,7 +549,7 @@ class ScreenContainer(object):
self.search_window.handler_unblock_by_func(window_hide)
vbox = gtk.VBox()
- fields = [f for f in self.screen.domain_parser.fields.itervalues()
+ fields = [f for f in self.screen.domain_parser.fields.values()
if f.get('searchable', True)]
self.search_table = gtk.Table(rows=len(fields), columns=2)
self.search_table.set_homogeneous(False)
@@ -617,8 +617,8 @@ class ScreenContainer(object):
self.search_window.add(vbox)
vbox.show_all()
- new_size = map(sum, zip(self.search_table.size_request(),
- scrolled.size_request()))
+ new_size = list(map(sum, list(zip(self.search_table.size_request(),
+ scrolled.size_request()))))
self.search_window.set_default_size(*new_size)
parent = widget.get_toplevel()
diff --git a/tryton/gui/window/win_export.py b/tryton/gui/window/win_export.py
index 777adab9d8..398fae9cbb 100644
--- a/tryton/gui/window/win_export.py
+++ b/tryton/gui/window/win_export.py
@@ -3,7 +3,6 @@
import csv
import os
import tempfile
-import types
import gtk
import gobject
@@ -94,8 +93,10 @@ class WinExport(WinCSV):
def model_populate(self, fields, parent_node=None, prefix_field='',
prefix_name=''):
- key = lambda (n, f): f.get('string') or n
- for name, field in sorted(fields.items(), key=key, reverse=True):
+
+ def key(n_f):
+ return n_f[1].get('string') or n_f[0]
+ for name, field in sorted(list(fields.items()), key=key, reverse=True):
string_ = field['string'] or name
@@ -314,7 +315,7 @@ class WinExport(WinCSV):
try:
writer = csv.writer(
- open(fname, 'wb+'),
+ open(fname, 'w+', encoding=encoding),
quotechar=self.get_quotechar(),
delimiter=self.get_delimiter())
if self.add_field_names.get_active():
@@ -322,9 +323,6 @@ class WinExport(WinCSV):
for line in data:
row = []
for val in line:
- if isinstance(type(val), types.StringType):
- val = val.replace('\n', ' ').replace('\t', ' ')
- val = val.encode(encoding)
row.append(val)
writer.writerow(row)
if popup:
@@ -333,7 +331,7 @@ class WinExport(WinCSV):
else:
common.message(_('%d records saved.') % len(data))
return True
- except IOError, exception:
+ except IOError as exception:
common.warning(_("Operation failed.\nError message:\n%s")
% exception, _('Error'))
return False
diff --git a/tryton/gui/window/win_form.py b/tryton/gui/window/win_form.py
index f1f21cf8d6..1b0b08a364 100644
--- a/tryton/gui/window/win_form.py
+++ b/tryton/gui/window/win_form.py
@@ -316,7 +316,7 @@ class WinForm(NoModal, InfoBar):
from tryton.gui.window.win_search import WinSearch
domain = self.domain[:]
model_name = self.screen.model_name
- value = self.wid_text.get_text().decode('utf-8')
+ value = self.wid_text.get_text()
def callback(result):
if result:
@@ -390,7 +390,7 @@ class WinForm(NoModal, InfoBar):
validate = False
elif validate and self.screen.current_view.view_type == 'form':
view = self.screen.current_view
- for widgets in view.widgets.itervalues():
+ for widgets in view.widgets.values():
for widget in widgets:
if (hasattr(widget, 'screen')
and widget.screen.pre_validate):
diff --git a/tryton/gui/window/win_import.py b/tryton/gui/window/win_import.py
index 11828eea04..57ddf92740 100644
--- a/tryton/gui/window/win_import.py
+++ b/tryton/gui/window/win_import.py
@@ -56,9 +56,9 @@ class WinImport(WinCSV):
def model_populate(self, fields, parent_node=None, prefix_field='',
prefix_name=''):
- fields_order = fields.keys()
- fields_order.sort(lambda x, y: -cmp(fields[x].get('string', ''),
- fields[y].get('string', '')))
+ fields_order = list(fields.keys())
+ fields_order.sort(
+ key=lambda x: fields[x].get('string', ''), reverse=True)
for field in fields_order:
if not fields[field].get('readonly', False):
self.fields_data[prefix_field + field] = fields[field]
@@ -101,21 +101,20 @@ class WinImport(WinCSV):
common.message(_('You must select an import file first.'))
return True
+ encoding = self.get_encoding()
self.csv_skip.set_value(1)
try:
data = csv.reader(
- open(fname, 'rb'),
+ open(fname, 'r', encoding=encoding),
quotechar=self.get_quotechar(),
delimiter=self.get_delimiter())
except IOError:
common.warning(_('Error opening CSV file'), _('Error'))
return True
self.sig_unsel_all()
- encoding = self.get_encoding()
word = ''
for line in data:
for word in line:
- word = word.decode(encoding)
if word not in self.fields_invert and word not in self.fields:
iter = self.model1.get_iter_first()
prefix = ''
@@ -181,14 +180,14 @@ class WinImport(WinCSV):
skip = self.csv_skip.get_value_as_int()
encoding = self.get_encoding()
reader = csv.reader(
- open(fname, 'rb'),
+ open(fname, 'r', encoding=encoding),
quotechar=self.get_quotechar(),
delimiter=self.get_delimiter())
data = []
for i, line in enumerate(reader):
if i < skip or not line:
continue
- data.append([x.decode(encoding).encode('utf-8') for x in line])
+ data.append([x for x in line])
try:
count = RPCExecute(
'model', self.model, 'import_data', fields, data,
diff --git a/tryton/gui/window/window.py b/tryton/gui/window/window.py
index a7ea86c248..3095277d13 100644
--- a/tryton/gui/window/window.py
+++ b/tryton/gui/window/window.py
@@ -22,10 +22,10 @@ class Window(object):
def create(model, **attributes):
from tryton.gui import Main
if model:
- from form import Form
+ from .form import Form
win = Form(model, **attributes)
else:
- from board import Board
+ from .board import Board
win = Board(model, **attributes)
win.icon = attributes.get('icon')
Main.get_main().win_add(win, hide_current=Window.hide_current,
@@ -35,7 +35,7 @@ class Window(object):
def create_wizard(action, data, direct_print=False, email_print=False,
email=None, name='', context=None, icon=None, window=False):
from tryton.gui import Main
- from wizard import WizardForm, WizardDialog
+ from .wizard import WizardForm, WizardDialog
if window:
win = WizardForm(name=name)
win.icon = icon
diff --git a/tryton/gui/window/wizard.py b/tryton/gui/window/wizard.py
index 78b6799c16..bca960ca72 100644
--- a/tryton/gui/window/wizard.py
+++ b/tryton/gui/window/wizard.py
@@ -93,7 +93,7 @@ class Wizard(InfoBar):
def callback(result):
try:
result = result()
- except RPCException, rpc_exception:
+ except RPCException as rpc_exception:
if (not isinstance(rpc_exception.exception,
TrytonServerError)
or not self.screen):
@@ -180,7 +180,7 @@ class Wizard(InfoBar):
self.update_buttons(record)
def update_buttons(self, record):
- for button in self.states.itervalues():
+ for button in self.states.values():
button.state_set(record)
def update(self, view, defaults, buttons):
diff --git a/tryton/ipc.py b/tryton/ipc.py
index 17847f606f..45150c6c69 100644
--- a/tryton/ipc.py
+++ b/tryton/ipc.py
@@ -58,7 +58,7 @@ class FileServer(IPCServer):
def setup(self):
config = open(self.config, 'w')
- print >> config, self.tmpdir
+ print(self.tmpdir, file=config)
def clean(self):
try:
@@ -101,7 +101,7 @@ class FIFOServer(IPCServer):
def setup(self):
self.filename = os.path.join(self.tmpdir, 'Socket')
- os.mkfifo(self.filename, 0600)
+ os.mkfifo(self.filename, 0o600)
if os.path.lexists(self.config):
os.remove(self.config)
os.symlink(self.filename, self.config)
@@ -160,7 +160,7 @@ class FileClient(IPCClient):
tmpdir = open(self.filename, 'r').readline().strip()
_, tmpfile = tempfile.mkstemp(dir=tmpdir, text=True)
with open(tmpfile, 'w') as tmpfile:
- print >> tmpfile, message
+ print(message, file=tmpfile)
return True
@@ -173,9 +173,10 @@ class FIFOClient(IPCClient):
if not os.path.lexists(self.filename):
return False
fifo = open(self.filename, 'w')
- print >> fifo, message
+ print(message, file=fifo)
return True
+
if hasattr(os, 'mkfifo'):
Server = FIFOServer
Client = FIFOClient
diff --git a/tryton/jsonrpc.py b/tryton/jsonrpc.py
index b7ce78545c..87d1cb380c 100644
--- a/tryton/jsonrpc.py
+++ b/tryton/jsonrpc.py
@@ -1,21 +1,21 @@
# 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
+import xmlrpc.client
import json
import ssl
-import httplib
+import http.client
from decimal import Decimal
import datetime
import socket
import gzip
-import StringIO
+import io
import hashlib
import base64
import threading
import errno
from functools import partial
from contextlib import contextmanager
-import string
+from functools import reduce
__all__ = ["ResponseError", "Fault", "ProtocolError", "Transport",
"ServerProxy", "ServerPool"]
@@ -23,11 +23,11 @@ CONNECT_TIMEOUT = 5
DEFAULT_TIMEOUT = None
-class ResponseError(xmlrpclib.ResponseError):
+class ResponseError(xmlrpc.client.ResponseError):
pass
-class Fault(xmlrpclib.Fault):
+class Fault(xmlrpc.client.Fault):
def __init__(self, faultCode, faultString='', **extra):
super(Fault, self).__init__(faultCode, faultString, **extra)
@@ -40,7 +40,7 @@ class Fault(xmlrpclib.Fault):
)
-class ProtocolError(xmlrpclib.ProtocolError):
+class ProtocolError(xmlrpc.client.ProtocolError):
pass
@@ -57,8 +57,7 @@ def object_hook(dct):
elif dct['__class__'] == 'timedelta':
return datetime.timedelta(seconds=dct['seconds'])
elif dct['__class__'] == 'bytes':
- cast = bytearray if bytes == str else bytes
- return cast(base64.decodestring(dct['base64']))
+ return base64.decodestring(dct['base64'].encode('utf-8'))
elif dct['__class__'] == 'Decimal':
return Decimal(dct['decimal'])
return dct
@@ -94,9 +93,9 @@ class JSONEncoder(json.JSONEncoder):
return {'__class__': 'timedelta',
'seconds': obj.total_seconds(),
}
- elif isinstance(obj, (bytes, bytearray)):
+ elif isinstance(obj, bytes):
return {'__class__': 'bytes',
- 'base64': base64.encodestring(obj),
+ 'base64': base64.encodestring(obj).decode('utf-8'),
}
elif isinstance(obj, Decimal):
return {'__class__': 'Decimal',
@@ -122,19 +121,19 @@ class JSONUnmarshaller(object):
self.data = []
def feed(self, data):
- self.data.append(data)
+ self.data.append(data.decode('utf-8'))
def close(self):
return json.loads(''.join(self.data), object_hook=object_hook)
-class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
+class Transport(xmlrpc.client.SafeTransport):
accept_gzip_encoding = True
encode_threshold = 1400 # common MTU
def __init__(self, fingerprints=None, ca_certs=None, session=None):
- xmlrpclib.Transport.__init__(self)
+ xmlrpc.client.Transport.__init__(self)
self._connection = (None, None)
self.__fingerprints = fingerprints
self.__ca_certs = ca_certs
@@ -146,13 +145,14 @@ class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
return parser, target
def get_host_info(self, host):
- host, extra_headers, x509 = xmlrpclib.Transport.get_host_info(
+ host, extra_headers, x509 = xmlrpc.client.Transport.get_host_info(
self, host)
if extra_headers is None:
extra_headers = []
if self.session:
- auth = base64.encodestring(self.session)
- auth = string.join(string.split(auth), "") # get rid of whitespace
+ auth = base64.encodestring(
+ self.session.encode('utf-8')).decode('ascii')
+ auth = ''.join(auth.split()) # get rid of whitespace
extra_headers.append(
('Authorization', 'Session ' + auth),
)
@@ -165,7 +165,7 @@ class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
self.encode_threshold < len(request_body) and
gzip):
connection.putheader("Content-Encoding", "gzip")
- buffer = StringIO.StringIO()
+ buffer = io.BytesIO()
output = gzip.GzipFile(mode='wb', fileobj=buffer)
output.write(request_body)
output.close()
@@ -183,7 +183,7 @@ class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
ssl_ctx = ssl.create_default_context(cafile=self.__ca_certs)
- class HTTPSConnection(httplib.HTTPSConnection):
+ class HTTPSConnection(http.client.HTTPSConnection):
def connect(self):
sock = socket.create_connection((self.host, self.port),
@@ -195,7 +195,7 @@ class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
sock, server_hostname=self.host)
def http_connection():
- self._connection = host, httplib.HTTPConnection(host,
+ self._connection = host, http.client.HTTPConnection(host,
timeout=CONNECT_TIMEOUT)
self._connection[1].connect()
sock = self._connection[1].sock
@@ -242,7 +242,7 @@ class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
return self._connection[1]
-class ServerProxy(xmlrpclib.ServerProxy):
+class ServerProxy(xmlrpc.client.ServerProxy):
__id = 0
def __init__(self, host, port, database='', verbose=0,
@@ -262,7 +262,7 @@ class ServerProxy(xmlrpclib.ServerProxy):
'id': id_,
'method': methodname,
'params': params,
- }, cls=JSONEncoder, separators=(',', ':'))
+ }, cls=JSONEncoder, separators=(',', ':')).encode('utf-8')
try:
try:
@@ -272,7 +272,7 @@ class ServerProxy(xmlrpclib.ServerProxy):
request,
verbose=self.__verbose
)
- except (socket.error, httplib.HTTPException), v:
+ except (socket.error, http.client.HTTPException) as v:
if (isinstance(v, socket.error)
and v.args[0] == errno.EPIPE):
raise
@@ -284,9 +284,9 @@ class ServerProxy(xmlrpclib.ServerProxy):
request,
verbose=self.__verbose
)
- except xmlrpclib.ProtocolError, e:
+ except xmlrpc.client.ProtocolError as e:
raise Fault(str(e.errcode), e.errmsg)
- except:
+ except Exception:
self.__transport.close()
raise
@@ -303,7 +303,7 @@ class ServerProxy(xmlrpclib.ServerProxy):
@property
def ssl(self):
return isinstance(self.__transport.make_connection(self.__host),
- httplib.HTTPSConnection)
+ http.client.HTTPSConnection)
class ServerPool(object):
@@ -337,14 +337,14 @@ class ServerPool(object):
def close(self):
with self._lock:
- for conn in self._pool + self._used.values():
+ for conn in self._pool + list(self._used.values()):
conn.close()
self._pool = []
self._used.clear()
@property
def ssl(self):
- for conn in self._pool + self._used.values():
+ for conn in self._pool + list(self._used.values()):
return conn.ssl
return False
diff --git a/tryton/plugins/__init__.py b/tryton/plugins/__init__.py
index b15fab3623..8ba24dd64d 100644
--- a/tryton/plugins/__init__.py
+++ b/tryton/plugins/__init__.py
@@ -19,7 +19,7 @@ def register():
os.path.join(get_config_dir(), 'plugins'),
os.path.join(CURRENT_DIR, 'plugins'),
]
- paths = filter(os.path.isdir, paths)
+ paths = list(filter(os.path.isdir, paths))
imported = set()
for path in paths:
diff --git a/tryton/pyson.py b/tryton/pyson.py
index 9a451097be..199e839110 100644
--- a/tryton/pyson.py
+++ b/tryton/pyson.py
@@ -9,7 +9,7 @@ from functools import reduce, wraps
def reduced_type(types):
types = types.copy()
- for k, r in [(long, int), (str, basestring), (unicode, basestring)]:
+ for k, r in [(int, int), (str, str), (str, str)]:
if k in types:
types.remove(k)
types.add(r)
@@ -313,10 +313,10 @@ 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, 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])
@@ -346,7 +346,7 @@ class Greater(PYSON):
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
@@ -438,9 +438,9 @@ class Get(PYSON):
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() == set([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
@@ -474,20 +474,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(set([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])), \
'obj must be a dict or a list'
if obj.types() == set([dict]):
- assert isinstance(key, basestring), 'key must be a string'
+ 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
@@ -523,10 +523,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(set([int, 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
@@ -590,7 +590,7 @@ class DateTime(Date):
assert i.types() == set([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
@@ -651,10 +651,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(set([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
diff --git a/tryton/rpc.py b/tryton/rpc.py
index d899a73431..8f6560b10d 100644
--- a/tryton/rpc.py
+++ b/tryton/rpc.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 socket
import ssl
@@ -41,7 +41,7 @@ def db_list(host, port):
result = connection.common.db.list()
logging.getLogger(__name__).debug(repr(result))
return result
- except Fault, exception:
+ except Fault as exception:
if exception.faultCode == 'AccessDenied':
logging.getLogger(__name__).debug('AccessDenied')
return []
@@ -58,7 +58,7 @@ def server_version(host, port):
result = connection.common.server.version()
logging.getLogger(__name__).debug(repr(result))
return result
- except (Fault, socket.error, ssl.SSLError, ssl.CertificateError), e:
+ except (Fault, socket.error, ssl.SSLError, ssl.CertificateError) as e:
logging.getLogger(__name__).error(e)
return None
@@ -96,7 +96,7 @@ def logout():
logging.getLogger(__name__).info('common.db.logout()')
with CONNECTION() as conn:
conn.common.db.logout()
- except (Fault, socket.error, httplib.CannotSendRequest):
+ except (Fault, socket.error, http.client.CannotSendRequest):
pass
CONNECTION.close()
CONNECTION = None
@@ -136,7 +136,7 @@ def _execute(blocking, *args):
logging.getLogger(__name__).info('%s%s' % (name, args))
with CONNECTION() as conn:
result = getattr(conn, name)(*args)
- except (httplib.CannotSendRequest, socket.error), exception:
+ except (http.client.CannotSendRequest, socket.error) as exception:
raise TrytonServerUnavailable(*exception.args)
if not CONFIG['dev']:
if key and method == 'fields_view_get':
diff --git a/tryton/signal_event.py b/tryton/signal_event.py
index 6cb6107021..43ebf2abb8 100644
--- a/tryton/signal_event.py
+++ b/tryton/signal_event.py
@@ -25,7 +25,7 @@ class SignalEvent(object):
def signal_unconnect(self, key, signal=None):
if signal is None:
- signal = self.__connects.keys()
+ signal = list(self.__connects.keys())
else:
signal = [signal]
for sig in signal:
diff --git a/tryton/translate.py b/tryton/translate.py
index 651641697a..eea44f2f3d 100644
--- a/tryton/translate.py
+++ b/tryton/translate.py
@@ -190,7 +190,7 @@ def setlang(lang=None, locale_dict=None):
if locale_dict:
conv = locale.localeconv()
- for field in locale_dict.keys():
+ for field in list(locale_dict.keys()):
if field == 'date':
continue
conv[field] = locale_dict[field]