From 5cfde31ed21e2ac2dc1e59ba57b596c8a2603320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 16 Mar 2015 19:28:34 +0100 Subject: [PATCH] Use bytes and bytearray for Binary issue4637 review11091002 --- CHANGELOG | 1 + doc/ref/models/fields.rst | 2 +- trytond/backend/sqlite/database.py | 2 +- trytond/ir/action.py | 7 ++++--- trytond/ir/attachment.py | 2 +- trytond/ir/model.py | 2 +- trytond/ir/translation.py | 3 +-- trytond/model/fields/binary.py | 11 ++++++----- trytond/protocols/dispatcher.py | 5 ++++- trytond/protocols/jsonrpc.py | 19 ++++++++++++------- trytond/protocols/xmlrpc.py | 9 +++++---- trytond/report/report.py | 16 ++++++++-------- trytond/tests/test.py | 2 +- trytond/tests/test_fields.py | 23 +++++++++++++---------- trytond/tests/test_protocols.py | 14 ++++++++------ 15 files changed, 67 insertions(+), 51 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a7566c6824..642ac37894 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Use bytes and bytearray for Binary * Add button_change * Add support for PyPy * Add support for psycopg2cffi diff --git a/doc/ref/models/fields.rst b/doc/ref/models/fields.rst index 9e70222895..4dde9ef44e 100644 --- a/doc/ref/models/fields.rst +++ b/doc/ref/models/fields.rst @@ -383,7 +383,7 @@ Binary .. class:: Binary(string[, \**options]) -A binary field. It will be represented in Python by a ``str`` instance. +A binary field. It will be represented in Python by a ``bytes`` instance. :class:`Binary` has one extra optional argument: diff --git a/trytond/backend/sqlite/database.py b/trytond/backend/sqlite/database.py index c4e5e4cfe0..6c5ef48eb7 100644 --- a/trytond/backend/sqlite/database.py +++ b/trytond/backend/sqlite/database.py @@ -421,7 +421,7 @@ class Cursor(CursorInterface): sqlite.register_converter('NUMERIC', lambda val: Decimal(val)) if sys.version_info[0] == 2: - sqlite.register_adapter(Decimal, lambda val: buffer(str(val))) + sqlite.register_adapter(Decimal, lambda val: bytearray(str(val))) else: sqlite.register_adapter(Decimal, lambda val: bytes(str(val))) diff --git a/trytond/ir/action.py b/trytond/ir/action.py index 9cde704ddf..2482c6b987 100644 --- a/trytond/ir/action.py +++ b/trytond/ir/action.py @@ -533,7 +533,8 @@ class ActionReport(ActionMixin, ModelSQL, ModelView): limit=limit, offset=offset)) for report_id, report in cursor.fetchall(): if report: - report = buffer(base64.decodestring(str(report))) + report = fields.Binary.cast( + base64.decodestring(bytes(report))) cursor.execute(*action_report.update( [action_report.report_content_custom], [report], @@ -592,7 +593,7 @@ class ActionReport(ActionMixin, ModelSQL, ModelView): @classmethod def get_report_content(cls, reports, name): contents = {} - converter = buffer + converter = fields.Binary.cast default = None format_ = Transaction().context.pop('%s.%s' % (cls.__name__, name), '') @@ -624,7 +625,7 @@ class ActionReport(ActionMixin, ModelSQL, ModelView): @classmethod def get_style_content(cls, reports, name): contents = {} - converter = buffer + converter = fields.Binary.cast default = None format_ = Transaction().context.pop('%s.%s' % (cls.__name__, name), '') diff --git a/trytond/ir/attachment.py b/trytond/ir/attachment.py index 1f6ce37aa7..51f6097a77 100644 --- a/trytond/ir/attachment.py +++ b/trytond/ir/attachment.py @@ -130,7 +130,7 @@ class Attachment(ModelSQL, ModelView): else: try: with open(filename, 'rb') as file_p: - value = buffer(file_p.read()) + value = fields.Binary.cast(file_p.read()) except IOError: pass return value diff --git a/trytond/ir/model.py b/trytond/ir/model.py index 8348ee8687..5caa834daa 100644 --- a/trytond/ir/model.py +++ b/trytond/ir/model.py @@ -1009,7 +1009,7 @@ class ModelGraph(Report): graph.set('ratio', 'auto') cls.fill_graph(models, graph, level=data['level'], filter=filter) data = graph.create(prog='dot', format='png') - return ('png', buffer(data), False, action_report.name) + return ('png', fields.Binary.cast(data), False, action_report.name) @classmethod def fill_graph(cls, models, graph, level=1, filter=None): diff --git a/trytond/ir/translation.py b/trytond/ir/translation.py index 5f8c84ff56..37b9f856e1 100644 --- a/trytond/ir/translation.py +++ b/trytond/ir/translation.py @@ -1616,9 +1616,8 @@ class TranslationExport(Wizard): def transition_export(self): pool = Pool() Translation = pool.get('ir.translation') - file_data = Translation.translation_export( + self.result.file = Translation.translation_export( self.start.language.code, self.start.module.name) - self.result.file = buffer(file_data) if file_data else None return 'result' def default_result(self, fields): diff --git a/trytond/model/fields/binary.py b/trytond/model/fields/binary.py index 30dd441670..98d925dfc8 100644 --- a/trytond/model/fields/binary.py +++ b/trytond/model/fields/binary.py @@ -9,9 +9,10 @@ from ... import backend class Binary(Field): ''' - Define a binary field (``str``). + Define a binary field (``bytes``). ''' _type = 'binary' + cast = bytearray if bytes == str else bytes def __init__(self, string='', help='', required=False, readonly=False, domain=None, states=None, select=False, on_change=None, @@ -28,10 +29,10 @@ class Binary(Field): select=select, on_change=on_change, on_change_with=on_change_with, depends=depends, context=context, loading=loading) - @staticmethod - def get(ids, model, name, values=None): + @classmethod + def get(cls, ids, model, name, values=None): ''' - Convert the binary value into ``str`` + Convert the binary value into ``bytes`` :param ids: a list of ids :param model: a string with the name of the model @@ -42,7 +43,7 @@ class Binary(Field): if values is None: values = {} res = {} - converter = buffer + converter = cls.cast default = None format_ = Transaction().context.pop('%s.%s' % (model.__name__, name), '') diff --git a/trytond/protocols/dispatcher.py b/trytond/protocols/dispatcher.py index cb0d84efc0..663718574c 100644 --- a/trytond/protocols/dispatcher.py +++ b/trytond/protocols/dispatcher.py @@ -279,7 +279,10 @@ def dump(database_name, password): data = Database.dump(database_name) logger.info('DUMP DB: %s', database_name) - return buffer(data) + if bytes == str: + return bytearray(data) + else: + return bytes(data) def restore(database_name, password, data, update=False): diff --git a/trytond/protocols/jsonrpc.py b/trytond/protocols/jsonrpc.py index 39dd50bd75..df9f46e697 100644 --- a/trytond/protocols/jsonrpc.py +++ b/trytond/protocols/jsonrpc.py @@ -57,8 +57,12 @@ JSONDecoder.register('time', dct['microsecond'])) JSONDecoder.register('timedelta', lambda dct: datetime.timedelta(seconds=dct['seconds'])) -JSONDecoder.register('buffer', lambda dct: - buffer(base64.decodestring(dct['base64']))) + + +def _bytes_decoder(dct): + cast = bytearray if bytes == str else bytes + return cast(base64.decodestring(dct['base64'])) +JSONDecoder.register('bytes', _bytes_decoder) JSONDecoder.register('Decimal', lambda dct: Decimal(dct['decimal'])) @@ -112,11 +116,12 @@ JSONEncoder.register(datetime.timedelta, '__class__': 'timedelta', 'seconds': o.total_seconds(), }) -JSONEncoder.register(buffer, - lambda o: { - '__class__': 'buffer', - 'base64': base64.encodestring(o), - }) +_bytes_encoder = lambda o: { + '__class__': 'bytes', + 'base64': base64.encodestring(o), + } +JSONEncoder.register(bytes, _bytes_encoder) +JSONEncoder.register(bytearray, _bytes_encoder) JSONEncoder.register(Decimal, lambda o: { '__class__': 'Decimal', diff --git a/trytond/protocols/xmlrpc.py b/trytond/protocols/xmlrpc.py index 4836501366..2ce35cdac1 100644 --- a/trytond/protocols/xmlrpc.py +++ b/trytond/protocols/xmlrpc.py @@ -10,7 +10,6 @@ import SimpleXMLRPCServer import SocketServer import xmlrpclib import socket -import sys import base64 import datetime from types import DictType @@ -29,7 +28,7 @@ def dump_decimal(self, value, write): self.dump_struct(value, write) -def dump_buffer(self, value, write): +def dump_bytes(self, value, write): self.write = write value = xmlrpclib.Binary(value) value.encode(self) @@ -67,7 +66,8 @@ xmlrpclib.Marshaller.dispatch[type(None)] = \ xmlrpclib.Marshaller.dispatch[datetime.date] = dump_date xmlrpclib.Marshaller.dispatch[datetime.time] = dump_time xmlrpclib.Marshaller.dispatch[datetime.timedelta] = dump_timedelta -xmlrpclib.Marshaller.dispatch[buffer] = dump_buffer +xmlrpclib.Marshaller.dispatch[bytes] = dump_bytes +xmlrpclib.Marshaller.dispatch[bytearray] = dump_bytes def dump_struct(self, value, write, escape=xmlrpclib.escape): @@ -132,7 +132,8 @@ xmlrpclib.Unmarshaller.dispatch["dateTime.iso8601"] = _end_dateTime def _end_base64(self, data): value = xmlrpclib.Binary() value.decode(data) - self.append(buffer(value.data)) + cast = bytearray if bytes == str else bytes + self.append(cast(value.data)) self._value = 0 xmlrpclib.Unmarshaller.dispatch['base64'] = _end_base64 diff --git a/trytond/report/report.py b/trytond/report/report.py index 870eda6d7e..9dc99e93b0 100644 --- a/trytond/report/report.py +++ b/trytond/report/report.py @@ -147,8 +147,8 @@ class Report(URLMixin, PoolBase): report_context = cls.get_context(records, data) oext, content = cls.convert(action_report, cls.render(action_report, report_context)) - return (oext, buffer(content), action_report.direct_print, - action_report.name) + content = bytearray(content) if bytes == str else bytes(content) + return (oext, content, action_report.direct_print, action_report.name) @classmethod def _get_records(cls, ids, model, data): @@ -196,11 +196,11 @@ class Report(URLMixin, PoolBase): @classmethod def _prepare_template_file(cls, report): - # Convert to str as buffer from DB is not supported by StringIO - report_content = (str(report.report_content) if report.report_content - else False) - style_content = (str(report.style_content) if report.style_content - else False) + # Convert to str as value from DB is not supported by StringIO + report_content = (bytes(report.report_content) if report.report_content + else None) + style_content = (bytes(report.style_content) if report.style_content + else None) if not report_content: raise Exception('Error', 'Missing report file!') @@ -269,7 +269,7 @@ class Report(URLMixin, PoolBase): outzip.writestr(file, picture) if manifest: - outzip.writestr(MANIFEST, str(manifest)) + outzip.writestr(MANIFEST, bytes(manifest)) content_z.close() content_io.close() diff --git a/trytond/tests/test.py b/trytond/tests/test.py index 3eb21cc4c4..8b4b201428 100644 --- a/trytond/tests/test.py +++ b/trytond/tests/test.py @@ -697,7 +697,7 @@ class BinaryDefault(ModelSQL): @staticmethod def default_binary(): - return buffer('default') + return b'default' class BinaryRequired(ModelSQL): diff --git a/trytond/tests/test_fields.py b/trytond/tests/test_fields.py index 8ff8ca9920..bda99baecd 100644 --- a/trytond/tests/test_fields.py +++ b/trytond/tests/test_fields.py @@ -16,6 +16,7 @@ from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \ install_module from trytond.transaction import Transaction from trytond.exceptions import UserError +from trytond.model import fields class FieldsTestCase(unittest.TestCase): @@ -3190,32 +3191,34 @@ class FieldsTestCase(unittest.TestCase): with Transaction().start(DB_NAME, USER, context=CONTEXT) as transaction: bin1, = self.binary.create([{ - 'binary': buffer('foo'), + 'binary': fields.Binary.cast(b'foo'), }]) - self.assert_(bin1.binary == buffer('foo')) + self.assert_(bin1.binary == fields.Binary.cast(b'foo')) - self.binary.write([bin1], {'binary': buffer('bar')}) - self.assert_(bin1.binary == buffer('bar')) + self.binary.write([bin1], {'binary': fields.Binary.cast(b'bar')}) + self.assert_(bin1.binary == fields.Binary.cast(b'bar')) with transaction.set_context({'test.binary.binary': 'size'}): bin1_size = self.binary(bin1.id) - self.assert_(bin1_size.binary == len('bar')) - self.assert_(bin1_size.binary != buffer('bar')) + self.assert_(bin1_size.binary == len(b'bar')) + self.assert_(bin1_size.binary != fields.Binary.cast(b'bar')) bin2, = self.binary.create([{}]) self.assert_(bin2.binary is None) bin3, = self.binary_default.create([{}]) - self.assert_(bin3.binary == buffer('default')) + self.assert_(bin3.binary == fields.Binary.cast(b'default')) self.assertRaises(UserError, self.binary_required.create, [{}]) transaction.cursor.rollback() - bin4, = self.binary_required.create([{'binary': buffer('baz')}]) - self.assert_(bin4.binary == buffer('baz')) + bin4, = self.binary_required.create([{ + 'binary': fields.Binary.cast(b'baz'), + }]) + self.assert_(bin4.binary == fields.Binary.cast(b'baz')) self.assertRaises(UserError, self.binary_required.create, - [{'binary': buffer('')}]) + [{'binary': fields.Binary.cast(b'')}]) transaction.cursor.rollback() diff --git a/trytond/tests/test_protocols.py b/trytond/tests/test_protocols.py index 532798db16..28ae6660a4 100644 --- a/trytond/tests/test_protocols.py +++ b/trytond/tests/test_protocols.py @@ -30,9 +30,10 @@ class JSONTestCase(unittest.TestCase): 'Test time' self.dumps_loads(datetime.datetime.now().time()) - def test_buffer(self): - 'Test buffer' - self.dumps_loads(buffer('foo')) + def test_bytes(self): + 'Test bytes' + self.dumps_loads(bytes(b'foo')) + self.dumps_loads(bytearray(b'foo')) def test_decimal(self): 'Test Decimal' @@ -52,9 +53,10 @@ class XMLTestCase(unittest.TestCase): 'Test Decimal' self.dumps_loads(Decimal('3.141592653589793')) - def test_buffer(self): - 'Test buffer' - self.dumps_loads(buffer('foo')) + def test_bytes(self): + 'Test bytes' + self.dumps_loads(bytes(b'foo')) + self.dumps_loads(bytearray(b'foo')) def test_date(self): 'Test date'