From 5866b1ece8c54a2f8a892d9a442bf6090409db3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 6 May 2019 14:45:34 +0200 Subject: [PATCH 001/110] Create branch 5.2 --HG-- branch : 5.2 From cd05edd1abe8ac35d46580f2b3adbd6ceb7ee10a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 6 May 2019 14:45:35 +0200 Subject: [PATCH 002/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index 394cf7807..6ffca3110 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.0" +__version__ = "5.2.1" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From d691e6215dcc7233f3ce27d1ae7fc1361b7143c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 9 May 2019 09:33:17 +0200 Subject: [PATCH 003/110] Remove Python 3.4 classifier (grafted from 755eddf83411796a5a2c9de92b4da2f244e923b0) --HG-- branch : 5.2 --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 3cb851043..cc2d60b56 100755 --- a/setup.py +++ b/setup.py @@ -121,7 +121,6 @@ def run(self): 'Natural Language :: Turkish', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', From 03b6af14959236b364aaf895333af12ea6ba2719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 22 May 2019 18:29:04 +0200 Subject: [PATCH 004/110] Always update ir.model.data if the filesystem value is different We must compare against the filesystem value instead of the old value because it is the actual target. issue8353 review261471002 (grafted from 71187a2c37b6a377d5464db9218e58cf920e91b1) --HG-- branch : 5.2 --- trytond/convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/convert.py b/trytond/convert.py index b6df78c64..eb135eaa9 100644 --- a/trytond/convert.py +++ b/trytond/convert.py @@ -764,7 +764,7 @@ def write_records(self, module, model, fs_values = old_values.copy() fs_values.update(new_values) - if values != old_values: + if values != fs_values: self.grouped_model_data.extend(([self.ModelData(mdata_id)], { 'fs_id': fs_id, 'model': model, From 7d872704c741927166b5eb77d2efc38383afb31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 10 Jun 2019 18:17:31 +0200 Subject: [PATCH 005/110] Prepare release 5.2.1 --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index d10472738..b0050cf44 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.1 - 2019-06-10 +* Bug fixes (see mercurial logs for details) + Version 5.2.0 - 2019-05-06 * Bug fixes (see mercurial logs for details) * Add sort and translate options to Reference field From c3ffa9ba5e90f23677d5866b771b82e9dddc959a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 10 Jun 2019 18:17:31 +0200 Subject: [PATCH 006/110] Added tag 5.2.1 for changeset aee8cd9fecdf --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index f3916c4df..e47faa083 100644 --- a/.hgtags +++ b/.hgtags @@ -20,3 +20,4 @@ e7a6d0e8002237f624ecc8a45a6a472f7baadd08 4.6.0 321b0104a732419c11aa4926785502e0615b89c9 4.8.0 e8cadc044d59edff8a2ca73932c723d507517ec8 5.0.0 ef34e4e92d45ab2b2b2956bbb6aa8b3ca259eb13 5.2.0 +aee8cd9fecdfbf082030abd07cb2f98ed4bb839a 5.2.1 From 786a537717162f9d8af6c2a94a4adca5b4a75314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 10 Jun 2019 18:17:50 +0200 Subject: [PATCH 007/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index 6ffca3110..8b93fa7d0 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.1" +__version__ = "5.2.2" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From b34bf00f5ec7c5c805238d28df65820699ced6a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 12 Jun 2019 19:15:00 +0200 Subject: [PATCH 008/110] Skip tests relying on assertion when python is optimized issue8381 review271471002 (grafted from 136794626cf54bb41a048d555e58df7cdd4efde1) --HG-- branch : 5.2 --- trytond/tests/test_pyson.py | 139 ++++++++++++++++++++++-------------- trytond/tests/test_tools.py | 3 + 2 files changed, 87 insertions(+), 55 deletions(-) diff --git a/trytond/tests/test_pyson.py b/trytond/tests/test_pyson.py index 9b04febb6..c3c4e128a 100644 --- a/trytond/tests/test_pyson.py +++ b/trytond/tests/test_pyson.py @@ -4,6 +4,8 @@ import unittest import datetime +import sys + from decimal import Decimal from trytond import pyson @@ -109,8 +111,9 @@ def test_And(self): 's': [True, False], }) - self.assertRaises(AssertionError, pyson.And, True) - self.assertRaises(AssertionError, pyson.And) + if not sys.flags.optimize: + self.assertRaises(AssertionError, pyson.And, True) + self.assertRaises(AssertionError, pyson.And) self.assertEqual(pyson.And(True, False).types(), set([bool])) @@ -151,8 +154,9 @@ def test_Or(self): 's': [True, False], }) - self.assertRaises(AssertionError, pyson.Or, True) - self.assertRaises(AssertionError, pyson.Or) + if not sys.flags.optimize: + self.assertRaises(AssertionError, pyson.Or, True) + self.assertRaises(AssertionError, pyson.Or) self.assertEqual(pyson.Or(True, False).types(), set([bool])) @@ -194,7 +198,8 @@ def test_Equal(self): 's2': 'test', }) - self.assertRaises(AssertionError, pyson.Equal, 'test', True) + if not sys.flags.optimize: + self.assertRaises(AssertionError, pyson.Equal, 'test', True) self.assertEqual(pyson.Equal('test', 'test').types(), set([bool])) @@ -216,8 +221,9 @@ def test_Greater(self): 'e': False, }) - self.assertRaises(AssertionError, pyson.Greater, 'test', 0) - self.assertRaises(AssertionError, pyson.Greater, 1, 'test') + if not sys.flags.optimize: + self.assertRaises(AssertionError, pyson.Greater, 'test', 0) + self.assertRaises(AssertionError, pyson.Greater, 1, 'test') self.assertEqual(pyson.Greater(1, 0).types(), set([bool])) @@ -256,8 +262,9 @@ def test_Less(self): 'e': False, }) - self.assertRaises(AssertionError, pyson.Less, 'test', 1) - self.assertRaises(AssertionError, pyson.Less, 0, 'test') + if not sys.flags.optimize: + self.assertRaises(AssertionError, pyson.Less, 'test', 1) + self.assertRaises(AssertionError, pyson.Less, 0, 'test') self.assertEqual(pyson.Less(0, 1).types(), set([bool])) @@ -296,7 +303,8 @@ def test_If(self): 'e': 'bar', }) - self.assertRaises(AssertionError, pyson.If, True, 'foo', False) + if not sys.flags.optimize: + self.assertRaises(AssertionError, pyson.If, True, 'foo', False) self.assertEqual(pyson.If(True, 'foo', 'bar').types(), set([str])) @@ -320,8 +328,10 @@ def test_Get(self): 'd': 'default', }) - self.assertRaises(AssertionError, pyson.Get, 'test', 'foo', 'default') - self.assertRaises(AssertionError, pyson.Get, {}, 1, 'default') + if not sys.flags.optimize: + self.assertRaises( + AssertionError, pyson.Get, 'test', 'foo', 'default') + self.assertRaises(AssertionError, pyson.Get, {}, 1, 'default') self.assertEqual(pyson.Get({}, 'foo', 'default').types(), set([str])) @@ -350,8 +360,9 @@ def test_In(self): 'v': {'foo': 'bar'}, }) - self.assertRaises(AssertionError, pyson.In, object(), {}) - self.assertRaises(AssertionError, pyson.In, 'test', 'foo') + if not sys.flags.optimize: + self.assertRaises(AssertionError, pyson.In, object(), {}) + self.assertRaises(AssertionError, pyson.In, 'test', 'foo') self.assertEqual(pyson.In('foo', {}).types(), set([bool])) @@ -400,18 +411,19 @@ def test_Date(self): 'dd': -7 }) - self.assertRaises(AssertionError, pyson.Date, 'test', 1, 12, -1, 12, - -7) - self.assertRaises(AssertionError, pyson.Date, 2010, 'test', 12, -1, 12, - -7) - self.assertRaises(AssertionError, pyson.Date, 2010, 1, 'test', -1, 12, - -7) - self.assertRaises(AssertionError, pyson.Date, 2010, 1, 12, 'test', 12, - -7) - self.assertRaises(AssertionError, pyson.Date, 2010, 1, 12, -1, 'test', - -7) - self.assertRaises(AssertionError, pyson.Date, 2010, 1, 12, -1, 12, - 'test') + if not sys.flags.optimize: + self.assertRaises( + AssertionError, pyson.Date, 'test', 1, 12, -1, 12, -7) + self.assertRaises( + AssertionError, pyson.Date, 2010, 'test', 12, -1, 12, -7) + self.assertRaises( + AssertionError, pyson.Date, 2010, 1, 'test', -1, 12, -7) + self.assertRaises( + AssertionError, pyson.Date, 2010, 1, 12, 'test', 12, -7) + self.assertRaises( + AssertionError, pyson.Date, 2010, 1, 12, -1, 'test', -7) + self.assertRaises( + AssertionError, pyson.Date, 2010, 1, 12, -1, 12, 'test') self.assertEqual(pyson.Date(2010, 1, 12, -1, 12, -7).types(), set([datetime.date])) @@ -464,34 +476,49 @@ def test_DateTime(self): 'dms': 1, }) - self.assertRaises(AssertionError, pyson.DateTime, 'test', 1, 12, 10, - 30, 20, 0, -1, 12, -7, 2, 15, 30, 1) - self.assertRaises(AssertionError, pyson.DateTime, 2010, 'test', 12, 10, - 30, 20, 0, -1, 12, -7, 2, 15, 30, 1) - self.assertRaises(AssertionError, pyson.DateTime, 2010, 1, 'test', 10, - 30, 20, 0, -1, 12, -7, 2, 15, 30, 1) - self.assertRaises(AssertionError, pyson.DateTime, 2010, 1, 12, 'test', - 30, 20, 0, -1, 12, -7, 2, 15, 30, 1) - self.assertRaises(AssertionError, pyson.DateTime, 2010, 1, 12, 10, - 'test', 20, 0, -1, 12, -7, 2, 15, 30, 1) - self.assertRaises(AssertionError, pyson.DateTime, 2010, 1, 12, 10, 30, - 'test', 0, -1, 12, -7, 2, 15, 30, 1) - self.assertRaises(AssertionError, pyson.DateTime, 2010, 1, 12, 10, 30, - 20, 'test', -1, 12, -7, 2, 15, 30, 1) - self.assertRaises(AssertionError, pyson.DateTime, 2010, 1, 12, 10, 30, - 20, 0, 'test', 12, -7, 2, 15, 30, 1) - self.assertRaises(AssertionError, pyson.DateTime, 2010, 1, 12, 10, 30, - 20, 0, -1, 'test', -7, 2, 15, 30, 1) - self.assertRaises(AssertionError, pyson.DateTime, 2010, 1, 12, 10, 30, - 20, 0, -1, 12, 'test', 2, 15, 30, 1) - self.assertRaises(AssertionError, pyson.DateTime, 2010, 1, 12, 10, 30, - 20, 0, -1, 12, -7, 'test', 15, 30, 1) - self.assertRaises(AssertionError, pyson.DateTime, 2010, 1, 12, 10, 30, - 20, 0, -1, 12, -7, 2, 'test', 30, 1) - self.assertRaises(AssertionError, pyson.DateTime, 2010, 1, 12, 10, 30, - 20, 0, -1, 12, -7, 2, 15, 'test', 1) - self.assertRaises(AssertionError, pyson.DateTime, 2010, 1, 12, 10, 30, - 20, 0, -1, 12, -7, 2, 15, 30, 'test') + if not sys.flags.optimize: + self.assertRaises( + AssertionError, pyson.DateTime, + 'test', 1, 12, 10, 30, 20, 0, -1, 12, -7, 2, 15, 30, 1) + self.assertRaises( + AssertionError, pyson.DateTime, + 2010, 'test', 12, 10, 30, 20, 0, -1, 12, -7, 2, 15, 30, 1) + self.assertRaises( + AssertionError, pyson.DateTime, + 2010, 1, 'test', 10, 30, 20, 0, -1, 12, -7, 2, 15, 30, 1) + self.assertRaises( + AssertionError, pyson.DateTime, + 2010, 1, 12, 'test', 30, 20, 0, -1, 12, -7, 2, 15, 30, 1) + self.assertRaises( + AssertionError, pyson.DateTime, + 2010, 1, 12, 10, 'test', 20, 0, -1, 12, -7, 2, 15, 30, 1) + self.assertRaises( + AssertionError, pyson.DateTime, + 2010, 1, 12, 10, 30, 'test', 0, -1, 12, -7, 2, 15, 30, 1) + self.assertRaises( + AssertionError, pyson.DateTime, + 2010, 1, 12, 10, 30, 20, 'test', -1, 12, -7, 2, 15, 30, 1) + self.assertRaises( + AssertionError, pyson.DateTime, + 2010, 1, 12, 10, 30, 20, 0, 'test', 12, -7, 2, 15, 30, 1) + self.assertRaises( + AssertionError, pyson.DateTime, + 2010, 1, 12, 10, 30, 20, 0, -1, 'test', -7, 2, 15, 30, 1) + self.assertRaises( + AssertionError, pyson.DateTime, + 2010, 1, 12, 10, 30, 20, 0, -1, 12, 'test', 2, 15, 30, 1) + self.assertRaises( + AssertionError, pyson.DateTime, + 2010, 1, 12, 10, 30, 20, 0, -1, 12, -7, 'test', 15, 30, 1) + self.assertRaises( + AssertionError, pyson.DateTime, + 2010, 1, 12, 10, 30, 20, 0, -1, 12, -7, 2, 'test', 30, 1) + self.assertRaises( + AssertionError, pyson.DateTime, + 2010, 1, 12, 10, 30, 20, 0, -1, 12, -7, 2, 15, 'test', 1) + self.assertRaises( + AssertionError, pyson.DateTime, + 2010, 1, 12, 10, 30, 20, 0, -1, 12, -7, 2, 15, 30, 'test') self.assertEqual(pyson.DateTime(2010, 1, 12, 10, 30, 20, 0, -1, 12, -7, 2, 15, 30, 1).types(), set([datetime.datetime])) @@ -552,7 +579,8 @@ def test_Len(self): 'v': [1, 2, 3], }) - self.assertRaises(AssertionError, pyson.Len, object()) + if not sys.flags.optimize: + self.assertRaises(AssertionError, pyson.Len, object()) self.assertEqual(pyson.Len([1, 2, 3]).types(), set([int])) @@ -581,6 +609,7 @@ def test_TimeDelta_types(self): self.assertEqual( pyson.TimeDelta(seconds=10).types(), {datetime.timedelta}) + @unittest.skipIf(sys.flags.optimize, "assert removed by optimization") def test_TimeDelta_invalid_type(self): "Test pyson.TimeDelta invalid type" with self.assertRaises(AssertionError): diff --git a/trytond/tests/test_tools.py b/trytond/tests/test_tools.py index 7f1734af8..600b9a477 100644 --- a/trytond/tests/test_tools.py +++ b/trytond/tests/test_tools.py @@ -4,6 +4,8 @@ import unittest import doctest +import sys + import sql import sql.operators @@ -51,6 +53,7 @@ def test_reduce_ids_complex_small_continue(self): (((self.table.id >= 1) & (self.table.id <= 12)) | (self.table.id.in_([15, 18, 19, 21])))) + @unittest.skipIf(sys.flags.optimize, "assert removed by optimization") def test_reduce_ids_float(self): 'Test reduce_ids with integer as float' self.assertEqual(reduce_ids(self.table.id, From 5e205817dde6779251d800999cc143b27d91a7f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 1 Jul 2019 21:23:35 +0200 Subject: [PATCH 009/110] Prepare release 5.2.2 --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index b0050cf44..362d4976f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.2 - 2019-07-01 +* Bug fixes (see mercurial logs for details) + Version 5.2.1 - 2019-06-10 * Bug fixes (see mercurial logs for details) From c937bf6646ddf274f4fbd37a33422121620fd6c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 1 Jul 2019 21:23:36 +0200 Subject: [PATCH 010/110] Added tag 5.2.2 for changeset 06b6af6a1a71 --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index e47faa083..a1fd261f3 100644 --- a/.hgtags +++ b/.hgtags @@ -21,3 +21,4 @@ e7a6d0e8002237f624ecc8a45a6a472f7baadd08 4.6.0 e8cadc044d59edff8a2ca73932c723d507517ec8 5.0.0 ef34e4e92d45ab2b2b2956bbb6aa8b3ca259eb13 5.2.0 aee8cd9fecdfbf082030abd07cb2f98ed4bb839a 5.2.1 +06b6af6a1a7182b83884cc2a3000a12966d1e14c 5.2.2 From 85541a8f74d083b565cb476c3d09d9f074b8664f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 1 Jul 2019 21:23:56 +0200 Subject: [PATCH 011/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index 8b93fa7d0..ea011d6be 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.2" +__version__ = "5.2.3" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From f00a2e5610a1ca525ac9a60945f1e0d52fe86217 Mon Sep 17 00:00:00 2001 From: David Harper Date: Fri, 28 Jun 2019 22:11:59 +0200 Subject: [PATCH 012/110] Correct name of bus class config option issue8461 review265631003 (grafted from 8f02ce4fd4b6a901a78980d3bbfa15a4a31629bf) --HG-- branch : 5.2 --- trytond/bus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/bus.py b/trytond/bus.py index 9a1461cee..d46ba2888 100644 --- a/trytond/bus.py +++ b/trytond/bus.py @@ -213,7 +213,7 @@ def publish(cls, channel, message): cursor.execute('NOTIFY "%s", %%s' % cls._channel, (payload,)) -if config.get('bus', 'queue'): +if config.get('bus', 'class'): Bus = resolve(config.get('bus', 'class')) else: Bus = LongPollingBus From c983dad2ba873410104f3212b921565687208f11 Mon Sep 17 00:00:00 2001 From: David Harper Date: Fri, 28 Jun 2019 22:14:25 +0200 Subject: [PATCH 013/110] Correct underline on passlib in config docs issue8460 review273551002 (grafted from ade59478241007e6c6893b9c5e23238ef04b5e98) --HG-- branch : 5.2 --- doc/topics/configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/topics/configuration.rst b/doc/topics/configuration.rst index 15bb23faf..31d4bca5a 100644 --- a/doc/topics/configuration.rst +++ b/doc/topics/configuration.rst @@ -360,7 +360,7 @@ The time in seconds until the reset password expires. Default: `86400` (24h) passlib -------- +~~~~~~~ The path to the `INI file to load as CryptContext `_. From dc170db22341b8fece8326f820027c1687fd58e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 17 Jul 2019 21:16:20 +0200 Subject: [PATCH 014/110] Prepare release 5.2.3 --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 362d4976f..bedf98326 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.3 - 2019-07-17 +* Bug fixes (see mercurial logs for details) + Version 5.2.2 - 2019-07-01 * Bug fixes (see mercurial logs for details) From 5d5cd9ff487cefd8792dc19fbe73a883fc6430ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 17 Jul 2019 21:16:20 +0200 Subject: [PATCH 015/110] Added tag 5.2.3 for changeset d06499aa4be1 --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index a1fd261f3..238413401 100644 --- a/.hgtags +++ b/.hgtags @@ -22,3 +22,4 @@ e8cadc044d59edff8a2ca73932c723d507517ec8 5.0.0 ef34e4e92d45ab2b2b2956bbb6aa8b3ca259eb13 5.2.0 aee8cd9fecdfbf082030abd07cb2f98ed4bb839a 5.2.1 06b6af6a1a7182b83884cc2a3000a12966d1e14c 5.2.2 +d06499aa4be143227c8f1aed0188106f2ee51c0c 5.2.3 From a17ba3aa44211ab2db6f576e56a28bfd81d7b5fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 17 Jul 2019 21:16:40 +0200 Subject: [PATCH 016/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index ea011d6be..eda4bf938 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.3" +__version__ = "5.2.4" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From fda09659ed6f5fcbc63faf3c975d3880a0065c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 8 Jul 2019 21:24:33 +0200 Subject: [PATCH 017/110] Use separate connection to query cache table The table is a hot spot for contention so we must minimize the duration lock are kept on it. As at the start of a transaction, the cache may be sync and so it locks all the row in the table, the transaction used for this query should not be the main one that lasts a long time. Idem for the commit. issue8447 review283591002 (grafted from 634f8347fb75237db22474e41ae1425c7af9208a) --HG-- branch : 5.2 --- trytond/cache.py | 89 ++++++++++++++++++++++--------------- trytond/modules/__init__.py | 3 ++ trytond/tests/test_cache.py | 13 ++++++ 3 files changed, 68 insertions(+), 37 deletions(-) diff --git a/trytond/cache.py b/trytond/cache.py index df428cb99..208c27e1d 100644 --- a/trytond/cache.py +++ b/trytond/cache.py @@ -165,8 +165,9 @@ def _clear(self, dbname, timestamp=None): @classmethod def sync(cls, transaction): - dbname = transaction.database.name - if not _clear_timeout and transaction.database.has_channel(): + database = transaction.database + dbname = database.name + if not _clear_timeout and database.has_channel(): with cls._listener_lock: if dbname not in cls._listener: cls._listener[dbname] = listener = threading.Thread( @@ -175,12 +176,17 @@ def sync(cls, transaction): return if (datetime.now() - cls._clean_last).total_seconds() < _clear_timeout: return - with transaction.connection.cursor() as cursor: - table = Table(cls._table) - cursor.execute(*table.select(_cast(table.timestamp), table.name)) - timestamps = {} - for timestamp, name in cursor.fetchall(): - timestamps[name] = timestamp + connection = database.get_connection(readonly=True, autocommit=True) + try: + with connection.cursor() as cursor: + table = Table(cls._table) + cursor.execute(*table.select( + _cast(table.timestamp), table.name)) + timestamps = {} + for timestamp, name in cursor.fetchall(): + timestamps[name] = timestamp + finally: + database.put_connection(connection) for name, timestamp in timestamps.items(): try: inst = cls._instances[name] @@ -197,39 +203,48 @@ def commit(cls, transaction): reset = cls._reset.setdefault(transaction, set()) if not reset: return - dbname = transaction.database.name - with transaction.connection.cursor() as cursor: - if not _clear_timeout and transaction.database.has_channel(): + database = transaction.database + dbname = database.name + if not _clear_timeout and transaction.database.has_channel(): + with transaction.connection.cursor() as cursor: cursor.execute( 'NOTIFY "%s", %%s' % cls._channel, (json.dumps(list(reset), separators=(',', ':')),)) - else: - for name in reset: - cursor.execute(*table.select(table.name, - where=table.name == name, - limit=1)) - if cursor.fetchone(): - # It would be better to insert only - cursor.execute(*table.update([table.timestamp], - [CurrentTimestamp()], + else: + connection = database.get_connection( + readonly=False, autocommit=True) + try: + with connection.cursor() as cursor: + for name in reset: + cursor.execute(*table.select(table.name, table.id, + table.timestamp, + where=table.name == name, + limit=1)) + if cursor.fetchone(): + # It would be better to insert only + cursor.execute(*table.update([table.timestamp], + [CurrentTimestamp()], + where=table.name == name)) + else: + cursor.execute(*table.insert( + [table.timestamp, table.name], + [[CurrentTimestamp(), name]])) + + cursor.execute(*table.select( + Max(table.timestamp), + where=table.name == name)) + timestamp, = cursor.fetchone() + + cursor.execute(*table.select( + _cast(Max(table.timestamp)), where=table.name == name)) - else: - cursor.execute(*table.insert( - [table.timestamp, table.name], - [[CurrentTimestamp(), name]])) - - cursor.execute(*table.select( - Max(table.timestamp), - where=table.name == name)) - timestamp, = cursor.fetchone() - - cursor.execute(*table.select( - _cast(Max(table.timestamp)), - where=table.name == name)) - timestamp, = cursor.fetchone() - - inst = cls._instances[name] - inst._clear(dbname, timestamp) + timestamp, = cursor.fetchone() + + inst = cls._instances[name] + inst._clear(dbname, timestamp) + connection.commit() + finally: + database.put_connection(connection) reset.clear() @classmethod diff --git a/trytond/modules/__init__.py b/trytond/modules/__init__.py index e39a0eeda..11185137d 100644 --- a/trytond/modules/__init__.py +++ b/trytond/modules/__init__.py @@ -14,6 +14,7 @@ from sql.functions import CurrentTimestamp import trytond.tools as tools +from trytond.cache import Cache from trytond.config import config from trytond.exceptions import MissingDependenciesException from trytond.transaction import Transaction @@ -266,6 +267,8 @@ def load_module_graph(graph, pool, update=None, lang=None): ])) module2state[module] = 'activated' + # Avoid clearing cache to prevent dead lock on ir.cache table + Cache.rollback(transaction) transaction.commit() if not update: diff --git a/trytond/tests/test_cache.py b/trytond/tests/test_cache.py index de7cb532e..0ec712a11 100644 --- a/trytond/tests/test_cache.py +++ b/trytond/tests/test_cache.py @@ -104,6 +104,19 @@ def test_memory_cache_transactions(self): self.wait_cache_sync() self.assertEqual(cache.get('foo'), None) + def test_memory_cache_nested_transactions(self): + "Test MemoryCache with nested transactions" + # Create entry in the cache table to trigger 2 updates + with Transaction().start(DB_NAME, USER): + cache.clear() + # Ensure sync is performed on start + time.sleep(cache_mod._clear_timeout) + + with Transaction().start(DB_NAME, USER) as transaction1: + cache.clear() + with transaction1.new_transaction(): + cache.clear() + def test_memory_cache_sync(self): "Test MemoryCache synchronisation" with Transaction().start(DB_NAME, USER): From a2c0a1139ec107bde9626e362006ebfa24e1bd5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 8 Jul 2019 21:30:43 +0200 Subject: [PATCH 018/110] Always return 'id' key in the read result The 'id' should not be removed from the result if a field use it as an extra (e.g. for context). issue8470 review263841002 (grafted from 03f313fcebbabfe8b0e468f406460abcd4d47fda) --HG-- branch : 5.2 --- trytond/model/modelsql.py | 1 + 1 file changed, 1 insertion(+) diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index 89e156353..3bea329f5 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -704,6 +704,7 @@ def read(cls, ids, fields_names): extra_fields.add(field.datetime_field) if field.context: extra_fields.update(fields.get_eval_fields(field.context)) + extra_fields.discard('id') all_fields = ( set(fields_names) | set(fields_related.keys()) | extra_fields) From bfbd18a45e96abdc262d5db4811e646ced6226d4 Mon Sep 17 00:00:00 2001 From: David Harper Date: Mon, 8 Jul 2019 21:49:05 +0200 Subject: [PATCH 019/110] Only encode report_content_html if it is not None issue8476 review249621002 (grafted from 54b15f36c1a7ac8b1737ac549446e791b05c349a) --HG-- branch : 5.2 --- trytond/ir/action.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/trytond/ir/action.py b/trytond/ir/action.py index 9c2e5d272..c69b02ac8 100644 --- a/trytond/ir/action.py +++ b/trytond/ir/action.py @@ -636,7 +636,8 @@ def get_report_content_html(cls, reports, name): @classmethod def set_report_content_html(cls, reports, name, value): - value = value.encode('utf-8') + if value is not None: + value = value.encode('utf-8') cls.set_report_content(reports, name[:-5], value) @fields.depends('name', 'template_extension') From ba5b2c263313cf39f5f0309dfb03ba4d7f675ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 13 Jul 2019 00:06:22 +0200 Subject: [PATCH 020/110] Ensure count of grouped_slice is at least 1 issue8474 review257701002 (grafted from e4a93fc515600b48b4723acc4a3dc0dc01657aec) --HG-- branch : 5.2 --- trytond/tools/misc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/trytond/tools/misc.py b/trytond/tools/misc.py index e46e55c3a..ee2763c9d 100644 --- a/trytond/tools/misc.py +++ b/trytond/tools/misc.py @@ -225,6 +225,7 @@ def grouped_slice(records, count=None): from trytond.transaction import Transaction if count is None: count = Transaction().database.IN_MAX + count = max(1, count) for i in range(0, len(records), count): yield islice(records, i, i + count) From c66667f30474384bd531b0feb7e9c13b518c4ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 13 Jul 2019 00:08:30 +0200 Subject: [PATCH 021/110] Use non grouped domain when grouped record > IN_MAX The break when grouped records are greater than IN_MAX, should break all the loops in order to prevent use of the grouped domain. issue8474 review257701002 (grafted from 4f54d7c7dc50c5089cd4a4f27495dc41bdf2c36c) --HG-- branch : 5.2 --- trytond/model/modelstorage.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py index 1d5da76b9..a3aff4520 100644 --- a/trytond/model/modelstorage.py +++ b/trytond/model/modelstorage.py @@ -1071,7 +1071,8 @@ def validate_domain(field): in_max = Transaction().database.IN_MAX count = in_max // 10 new_domains = {} - for sub_domains in grouped_slice(list(domains.keys()), count): + for sub_domains in grouped_slice( + list(domains.keys()), count): grouped_domain = ['OR'] grouped_records = [] for d in sub_domains: @@ -1082,7 +1083,11 @@ def validate_domain(field): break grouped_domain.append( [('id', 'in', [r.id for r in relations]), d]) - new_domains[freeze(grouped_domain)] = grouped_records + else: + new_domains[freeze(grouped_domain)] = \ + grouped_records + continue + break else: domains = new_domains else: From 7c465fe1742c10ec6eba4ac0566d9cd4e0dab572 Mon Sep 17 00:00:00 2001 From: Sebastien Marie Date: Wed, 17 Jul 2019 00:55:17 +0200 Subject: [PATCH 022/110] Skip the Function field without setter in Data.sync() issue8478 review251711002 (grafted from 30b54939766454ee7a461a5ddcbe04256edbecae) --HG-- branch : 5.2 --- trytond/ir/model.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/trytond/ir/model.py b/trytond/ir/model.py index 89d5fe729..167bac1bc 100644 --- a/trytond/ir/model.py +++ b/trytond/ir/model.py @@ -1239,6 +1239,17 @@ def load_values(cls, values): @classmethod @ModelView.button def sync(cls, records): + def settable(Model, fieldname): + try: + field = Model._fields[fieldname] + except KeyError: + return False + + if isinstance(field, fields.Function) and not field.setter: + return False + + return True + with Transaction().set_user(0): pool = Pool() to_write = [] @@ -1254,7 +1265,7 @@ def sync(cls, records): # if they come from version < 3.2 if values != fs_values: values = {f: v for f, v in fs_values.items() - if f in Model._fields} + if settable(Model, f)} record = Model(data.db_id) models_to_write[Model].extend(([record], values)) to_write.extend([[data], { From c87199cd935c340d6c2893f746aad3bf3d23f804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 17 Jul 2019 21:15:37 +0200 Subject: [PATCH 023/110] Remove duplicate button attributes and move warning at the end (grafted from f4393bd5664c3341bd9c4cb82af78a23cd43f641) --HG-- branch : 5.2 --- doc/topics/views/index.rst | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/doc/topics/views/index.rst b/doc/topics/views/index.rst index c3a5d1b7f..6477a070e 100644 --- a/doc/topics/views/index.rst +++ b/doc/topics/views/index.rst @@ -345,16 +345,6 @@ Display a button. The function may return an `ir.action` id or one of those client side action keywords: - * ``string``: The string that will be displayed inside the button. - - * ``confirm``: A string that will be shown in order to request - confirmation when clicking the button. - - * ``help``: see in common-attributes-help_. - -The button should be registered on ``ir.model.button`` where the default value -of the ``string``, ``confirm`` and ``help`` attributes can be can be defined. - .. _topics-views-client-actions: * ``new``: to create a new record @@ -384,6 +374,13 @@ of the ``string``, ``confirm`` and ``help`` attributes can be can be defined. toolbar. The valid values are the keywords starting with `form_` from :ref:`Actions ` without the `form_` part. + +.. warning:: + The button should be registered on ``ir.model.button`` where the default + value of the ``string``, ``confirm`` and ``help`` attributes can be can be + defined. + + notebook ^^^^^^^^ From 8a9495955e5a4f1892849c909307f262963691b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 1 Aug 2019 22:56:15 +0200 Subject: [PATCH 024/110] Prepare release 5.2.4 --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index bedf98326..20f6fb4da 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.4 - 2019-08-01 +* Bug fixes (see mercurial logs for details) + Version 5.2.3 - 2019-07-17 * Bug fixes (see mercurial logs for details) From 0f9210f24a1cf6fad4e936607c64e340b1775a68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 1 Aug 2019 22:56:15 +0200 Subject: [PATCH 025/110] Added tag 5.2.4 for changeset e49a560dd265 --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 238413401..e7302fcc6 100644 --- a/.hgtags +++ b/.hgtags @@ -23,3 +23,4 @@ ef34e4e92d45ab2b2b2956bbb6aa8b3ca259eb13 5.2.0 aee8cd9fecdfbf082030abd07cb2f98ed4bb839a 5.2.1 06b6af6a1a7182b83884cc2a3000a12966d1e14c 5.2.2 d06499aa4be143227c8f1aed0188106f2ee51c0c 5.2.3 +e49a560dd265f6d018abf3974b227c6a841a8d12 5.2.4 From e5956d2a586e16dd787e82e694ad27cfe7c7897a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 1 Aug 2019 22:56:35 +0200 Subject: [PATCH 026/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index eda4bf938..32b556c5a 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.4" +__version__ = "5.2.5" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From a4bb47555969a115bb537b073ebd66c5f2663d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 28 Jul 2019 16:05:45 +0200 Subject: [PATCH 027/110] Convert cache_name by TableHandler when used as database name The name of the cache can be longer than the maximum length of database name. (grafted from 6aa537cd2ea7cc1bce7d806bcd569c4a70445ec4) --HG-- branch : 5.2 --- trytond/tests/test_tryton.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/trytond/tests/test_tryton.py b/trytond/tests/test_tryton.py index a7cb12fe6..00579edb3 100644 --- a/trytond/tests/test_tryton.py +++ b/trytond/tests/test_tryton.py @@ -163,6 +163,7 @@ def _pg_restore(cache_file): return not subprocess.call(cmd, env=env) except OSError: cache_name, _ = os.path.splitext(os.path.basename(cache_file)) + cache_name = backend.get('TableHandler').convert_name(cache_name) with Transaction().start( None, 0, close=True, autocommit=True) as transaction: transaction.database.drop(transaction.connection, DB_NAME) @@ -180,6 +181,7 @@ def _pg_dump(cache_file): return not subprocess.call(cmd, env=env) except OSError: cache_name, _ = os.path.splitext(os.path.basename(cache_file)) + cache_name = backend.get('TableHandler').convert_name(cache_name) # Ensure any connection is left open backend.get('Database')(DB_NAME).close() with Transaction().start( From 5adc8c80abb48f81206f3ca63237260d3ff34950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 17 Aug 2019 11:51:17 +0200 Subject: [PATCH 028/110] Prepare release 5.2.5 --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 20f6fb4da..40d0ca16e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.5 - 2019-08-17 +* Bug fixes (see mercurial logs for details) + Version 5.2.4 - 2019-08-01 * Bug fixes (see mercurial logs for details) From d39b8389b1dcffe6e0fa6e72b2297e8c641424c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 17 Aug 2019 11:51:17 +0200 Subject: [PATCH 029/110] Added tag 5.2.5 for changeset 99b0686c8702 --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index e7302fcc6..d2e28a966 100644 --- a/.hgtags +++ b/.hgtags @@ -24,3 +24,4 @@ aee8cd9fecdfbf082030abd07cb2f98ed4bb839a 5.2.1 06b6af6a1a7182b83884cc2a3000a12966d1e14c 5.2.2 d06499aa4be143227c8f1aed0188106f2ee51c0c 5.2.3 e49a560dd265f6d018abf3974b227c6a841a8d12 5.2.4 +99b0686c8702d2b41898c54bcdbfc36af8253a2b 5.2.5 From 407de4c30300ab4f7cfeeedb887982170595e65c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 17 Aug 2019 11:51:35 +0200 Subject: [PATCH 030/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index 32b556c5a..140126c29 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.5" +__version__ = "5.2.6" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From d3c950eb2e47ba9dbe0ebb99b1804b5fdc698aa7 Mon Sep 17 00:00:00 2001 From: Sergi Almacellas Abellana Date: Fri, 23 Aug 2019 18:31:05 +0200 Subject: [PATCH 031/110] Return name as report_content_name when template extensions is empty issue8576 review287941002 (grafted from 50324f62bc75a8208b1dbd8c9d60b685c7d84150) --HG-- branch : 5.2 --- trytond/ir/action.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/trytond/ir/action.py b/trytond/ir/action.py index c69b02ac8..48e1fcdc9 100644 --- a/trytond/ir/action.py +++ b/trytond/ir/action.py @@ -642,9 +642,8 @@ def set_report_content_html(cls, reports, name, value): @fields.depends('name', 'template_extension') def on_change_with_report_content_name(self, name=None): - if not self.name: - return - return ''.join([self.name, os.extsep, self.template_extension]) + return ''.join( + filter(None, [self.name, os.extsep, self.template_extension])) @classmethod def get_pyson(cls, reports, name): From 1764b5c1ee53583735ef2e716a3b06271b32706c Mon Sep 17 00:00:00 2001 From: "Luciano Rossi (lukio)" Date: Fri, 30 Aug 2019 09:08:32 +0200 Subject: [PATCH 032/110] Add missing Date header on email sent issue8596 review272091002 (grafted from fc47f7e21925305bbb2d6783e74949ee83186469) --HG-- branch : 5.2 --- trytond/sendmail.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/trytond/sendmail.py b/trytond/sendmail.py index c290a074a..1557a9cac 100644 --- a/trytond/sendmail.py +++ b/trytond/sendmail.py @@ -3,6 +3,7 @@ import logging import smtplib from email.message import Message +from email.utils import formatdate from urllib.parse import parse_qs, unquote_plus from .config import config, parse_uri @@ -31,6 +32,8 @@ def sendmail(from_addr, to_addrs, msg, server=None): quit = True else: quit = False + if 'Date' not in msg: + msg['Date'] = formatdate() try: senderrs = server.sendmail(from_addr, to_addrs, msg.as_string()) except smtplib.SMTPException: From 1939634a9722b8330fc75c15619ce977e3017eab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 30 Aug 2019 11:32:32 +0200 Subject: [PATCH 033/110] Use MagicMock for message when testing sendmail Since issue8596, sendmail test message contains Date header so message must implement the magic method. (grafted from 464864aedc0460dd6717d6197eb736bfa2433169) --HG-- branch : 5.2 --- trytond/tests/test_sendmail.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/trytond/tests/test_sendmail.py b/trytond/tests/test_sendmail.py index 14bd364b1..9e887a72e 100644 --- a/trytond/tests/test_sendmail.py +++ b/trytond/tests/test_sendmail.py @@ -3,7 +3,7 @@ import smtplib import unittest from email.message import Message -from unittest.mock import Mock, patch, call +from unittest.mock import Mock, MagicMock, patch, call from trytond.sendmail import ( sendmail_transactional, sendmail, SMTPDataManager, get_smtp_server) @@ -21,7 +21,7 @@ def setUpClass(cls): @with_transaction() def test_sendmail_transactional(self): 'Test sendmail_transactional' - message = Mock() + message = MagicMock() datamanager = Mock() sendmail_transactional( 'tryton@example.com', 'foo@example.com', message, @@ -32,7 +32,7 @@ def test_sendmail_transactional(self): def test_sendmail(self): 'Test sendmail' - message = Mock() + message = MagicMock() server = Mock() sendmail( 'tryton@example.com', 'foo@example.com', message, server=server) @@ -89,8 +89,8 @@ def test_SMTPDataManager(self, get_smtp_server): # multiple join must return the same self.assertEqual(transaction.join(SMTPDataManager()), datamanager) - msg1 = Mock(Message) - msg2 = Mock(Message) + msg1 = MagicMock(Message) + msg2 = MagicMock(Message) datamanager.put('foo@example.com', 'bar@example.com', msg1) datamanager.put('bar@example.com', 'foo@example.com', msg2) @@ -105,7 +105,8 @@ def test_SMTPDataManager(self, get_smtp_server): server.reset_mock() - datamanager.put('foo@example.com', 'bar@example.com', Mock(Message)) + datamanager.put( + 'foo@example.com', 'bar@example.com', MagicMock(Message)) transaction.rollback() server.sendmail.assert_not_called() From f8e2c583c00aaeb3b191b74a468789ee353050a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 15 Sep 2019 14:08:29 +0200 Subject: [PATCH 034/110] Prepare release 5.2.6 --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 40d0ca16e..b44a9f66a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.6 - 2019-09-15 +* Bug fixes (see mercurial logs for details) + Version 5.2.5 - 2019-08-17 * Bug fixes (see mercurial logs for details) From a89359991e31f29a4f2c182ae9aae166b994c031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 15 Sep 2019 14:08:30 +0200 Subject: [PATCH 035/110] Added tag 5.2.6 for changeset 4363488df01b --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index d2e28a966..e62822224 100644 --- a/.hgtags +++ b/.hgtags @@ -25,3 +25,4 @@ aee8cd9fecdfbf082030abd07cb2f98ed4bb839a 5.2.1 d06499aa4be143227c8f1aed0188106f2ee51c0c 5.2.3 e49a560dd265f6d018abf3974b227c6a841a8d12 5.2.4 99b0686c8702d2b41898c54bcdbfc36af8253a2b 5.2.5 +4363488df01bb84b337267279b6a5e0e3b61d0ab 5.2.6 From 19f19049633633aa336c3e9183cf8cb81343a32e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 15 Sep 2019 14:08:52 +0200 Subject: [PATCH 036/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index 140126c29..d28167d7d 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.6" +__version__ = "5.2.7" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From 65f5bf1d197b3c0375d844d901330ab4b9b1fa4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 7 Sep 2019 23:34:20 +0200 Subject: [PATCH 037/110] Use stored value for One2Many changed without initial value If the client does not send the value for the One2Many (it happens when it is not modified), we must assume that it has the stored value unchanged. Otherwise the current records are considered as to be added but with only their changed values. issue8605 review265901016 (grafted from 6ec348fc27717b71e50f5c43337786ad7bbdf596) --HG-- branch : 5.2 --- trytond/model/modelview.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/model/modelview.py b/trytond/model/modelview.py index 4c521c79e..ec0e5ec79 100644 --- a/trytond/model/modelview.py +++ b/trytond/model/modelview.py @@ -775,7 +775,7 @@ def _changed_values(self): value = value.id elif field._type == 'one2many': targets = value - init_targets = list(init_values.get(fname, [])) + init_targets = list(init_values.get(fname, targets)) value = collections.defaultdict(list) value['remove'] = [t.id for t in init_targets if t.id] for i, target in enumerate(targets): From 3fce4f31d935cc8579217473b4c815c0e97fef9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 8 Sep 2019 22:11:31 +0200 Subject: [PATCH 038/110] Do not call _changed_values on non ModelView instance When the target is not a ModelView, we can not compute the changed values. (grafted from d592efdea38250a7fa9c2c7de274ce43208a51c6) --HG-- branch : 5.2 --- trytond/model/modelview.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/trytond/model/modelview.py b/trytond/model/modelview.py index ec0e5ec79..006a5a8d7 100644 --- a/trytond/model/modelview.py +++ b/trytond/model/modelview.py @@ -781,10 +781,11 @@ def _changed_values(self): for i, target in enumerate(targets): if target.id in value['remove']: value['remove'].remove(target.id) - target_changed = target._changed_values - if target_changed: - target_changed['id'] = target.id - value['update'].append(target_changed) + if isinstance(target, ModelView): + target_changed = target._changed_values + if target_changed: + target_changed['id'] = target.id + value['update'].append(target_changed) else: if isinstance(target, ModelView): # Ensure initial values are returned because target From 09229a45c69edb4764bc3933f83e12d2d25443fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 11 Sep 2019 23:49:34 +0200 Subject: [PATCH 039/110] Use new related API for _changed_values The rev 596528aa0139 introduced a new API for related values. The rec_name set by ModelView._changed_values must follow this new API to be used as cache by the clients. issue8637 review280081004 (grafted from 7f83468753593c7c6a9184a57af2aa84c6a8220c) --HG-- branch : 5.2 --- trytond/model/modelview.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/trytond/model/modelview.py b/trytond/model/modelview.py index 006a5a8d7..6b51f302d 100644 --- a/trytond/model/modelview.py +++ b/trytond/model/modelview.py @@ -765,7 +765,9 @@ def _changed_values(self): if field._type in ('many2one', 'one2one', 'reference'): if value: if isinstance(value, ModelStorage): - changed['%s.rec_name' % fname] = value.rec_name + changed['%s.' % fname] = { + 'rec_name': value.rec_name, + } if value.id is None: # Don't consider temporary instance as a change continue From 0bf6b35e0eedabc02f8240ebf07d9be146fcd064 Mon Sep 17 00:00:00 2001 From: Sebastien Marie Date: Tue, 17 Sep 2019 23:12:51 +0200 Subject: [PATCH 040/110] Import zero value as non null value issue8658 review282121004 (grafted from a0dfafa0c039cec82942302179826b6552bc1e61) --HG-- branch : 5.2 --- trytond/model/modelstorage.py | 21 ++++++++++++++--- trytond/tests/test_importdata.py | 39 ++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py index a3aff4520..3c3815870 100644 --- a/trytond/model/modelstorage.py +++ b/trytond/model/modelstorage.py @@ -857,11 +857,26 @@ def process_lines(data, prefix, fields_def, position=0, klass=cls): else: res = bool(int(value)) elif field_type == 'integer': - res = int(value) if value else None + if isinstance(value, int): + res = value + elif value: + res = int(value) + else: + res = None elif field_type == 'float': - res = float(value) if value else None + if isinstance(value, float): + res = value + elif value: + res = float(value) + else: + res = None elif field_type == 'numeric': - res = Decimal(value) if value else None + if isinstance(value, Decimal): + res = value + elif value: + res = Decimal(value) + else: + res = None elif field_type == 'date': if isinstance(value, datetime.date): res = value diff --git a/trytond/tests/test_importdata.py b/trytond/tests/test_importdata.py index bff8307f8..f05f25355 100644 --- a/trytond/tests/test_importdata.py +++ b/trytond/tests/test_importdata.py @@ -55,6 +55,9 @@ def test_integer(self): self.assertEqual(Integer.import_data(['integer'], [['1']]), 1) + self.assertEqual(Integer.import_data(['integer'], + [[0]]), 1) + self.assertEqual(Integer.import_data(['integer'], [[1]]), 1) @@ -79,6 +82,9 @@ def test_integer(self): self.assertEqual(Integer.import_data(['integer'], [['0']]), 1) + self.assertEqual(Integer.import_data(['integer'], + [[None]]), 1) + @with_transaction() def test_integer_required(self): 'Test required integer' @@ -111,6 +117,13 @@ def test_integer_required(self): self.assertEqual(IntegerRequired.import_data(['integer'], [['0']]), 1) + self.assertEqual(IntegerRequired.import_data(['integer'], + [[0]]), 1) + + with self.assertRaises(RequiredValidationError): + IntegerRequired.import_data(['integer'], [[None]]) + transaction.rollback() + @with_transaction() def test_float(self): 'Test float' @@ -120,6 +133,9 @@ def test_float(self): self.assertEqual(Float.import_data(['float'], [['1.1']]), 1) + self.assertEqual(Float.import_data(['float'], + [[0.0]]), 1) + self.assertEqual(Float.import_data(['float'], [[1.1]]), 1) @@ -144,6 +160,9 @@ def test_float(self): self.assertEqual(Float.import_data(['float'], [['0.0']]), 1) + self.assertEqual(Float.import_data(['float'], + [[None]]), 1) + @with_transaction() def test_float_required(self): 'Test required float' @@ -176,6 +195,13 @@ def test_float_required(self): self.assertEqual(FloatRequired.import_data(['float'], [['0.0']]), 1) + self.assertEqual(FloatRequired.import_data(['float'], + [[0.0]]), 1) + + with self.assertRaises(RequiredValidationError): + FloatRequired.import_data(['float'], [[None]]) + transaction.rollback() + @with_transaction() def test_numeric(self): 'Test numeric' @@ -185,6 +211,9 @@ def test_numeric(self): self.assertEqual(Numeric.import_data(['numeric'], [['1.1']]), 1) + self.assertEqual(Numeric.import_data(['numeric'], + [[Decimal('0.0')]]), 1) + self.assertEqual(Numeric.import_data(['numeric'], [[Decimal('1.1')]]), 1) @@ -209,6 +238,9 @@ def test_numeric(self): self.assertEqual(Numeric.import_data(['numeric'], [['0.0']]), 1) + self.assertEqual(Numeric.import_data(['numeric'], + [[None]]), 1) + @with_transaction() def test_numeric_required(self): 'Test required numeric' @@ -241,6 +273,13 @@ def test_numeric_required(self): self.assertEqual(NumericRequired.import_data(['numeric'], [['0.0']]), 1) + self.assertEqual(NumericRequired.import_data(['numeric'], + [[Decimal('0.0')]]), 1) + + with self.assertRaises(RequiredValidationError): + NumericRequired.import_data(['numeric'], [[None]]) + transaction.rollback() + @with_transaction() def test_char(self): 'Test char' From d30738a9927cb0771aa724a0990f6e25253b398f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 6 Oct 2019 13:45:36 +0200 Subject: [PATCH 041/110] Prepare release 5.2.7 --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index b44a9f66a..fa14fc975 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.7 - 2019-10-06 +* Bug fixes (see mercurial logs for details) + Version 5.2.6 - 2019-09-15 * Bug fixes (see mercurial logs for details) From dc506fa5c2876707bc59509d046a4b48ed14c21f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 6 Oct 2019 13:45:37 +0200 Subject: [PATCH 042/110] Added tag 5.2.7 for changeset a533a499c493 --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index e62822224..8cd31a2eb 100644 --- a/.hgtags +++ b/.hgtags @@ -26,3 +26,4 @@ d06499aa4be143227c8f1aed0188106f2ee51c0c 5.2.3 e49a560dd265f6d018abf3974b227c6a841a8d12 5.2.4 99b0686c8702d2b41898c54bcdbfc36af8253a2b 5.2.5 4363488df01bb84b337267279b6a5e0e3b61d0ab 5.2.6 +a533a499c493e206bf5fb8546038ba3e62af130b 5.2.7 From 05caf746ba4c18713e01ea400694c6d9dfca9df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 6 Oct 2019 13:45:55 +0200 Subject: [PATCH 043/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index d28167d7d..f09ed1af2 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.7" +__version__ = "5.2.8" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From 3cd34f023bd79427f3b731946a434c33faf57ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 8 Oct 2019 18:10:23 +0200 Subject: [PATCH 044/110] Correctly test field access to set readonly attribute Since r 1094d993d89c, field variable is the field instance and no more the field name. issue8692 review276071002 (grafted from ecc41bf57f909f07e27461b176566af2b70fccc7) --HG-- branch : 5.2 --- trytond/model/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/model/model.py b/trytond/model/model.py index 508098b66..7db028aed 100644 --- a/trytond/model/model.py +++ b/trytond/model/model.py @@ -181,7 +181,7 @@ def fields_get(cls, fields_names=None): if fields_names and fname not in fields_names: continue definition[fname] = field.definition(cls, language) - if not accesses.get(field, {}).get('write', True): + if not accesses.get(fname, {}).get('write', True): definition[fname]['readonly'] = True states = decoder.decode(definition[fname]['states']) states.pop('readonly', None) From 017a4464910aa73b77beec566d82c6eac75e635f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 16 Oct 2019 21:31:25 +0200 Subject: [PATCH 045/110] Always use search method on translations This ensure to have always the same order when selecting the record out of many duplicates (which can happen as there is no unique constraint since r 426276806f60). issue8705 review260271002 (grafted from bfc90c50b591ffc61b7eaa0e00182ce80edbe559) --HG-- branch : 5.2 --- trytond/ir/translation.py | 80 +++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 46 deletions(-) diff --git a/trytond/ir/translation.py b/trytond/ir/translation.py index dcc738a6f..907f994ce 100644 --- a/trytond/ir/translation.py +++ b/trytond/ir/translation.py @@ -11,7 +11,6 @@ from sql import Column, Null, Literal from sql.functions import Substring, Position from sql.conditionals import Case -from sql.operators import Or, And from sql.aggregate import Max from genshi.filters.i18n import extract as genshi_extract @@ -23,7 +22,7 @@ from ..model import ModelView, ModelSQL, fields from ..wizard import Wizard, StateView, StateTransition, StateAction, \ Button -from ..tools import file_open, reduce_ids, grouped_slice, cursor_dict +from ..tools import file_open, grouped_slice, cursor_dict from ..pyson import PYSONEncoder, Eval from ..transaction import Transaction from ..pool import Pool @@ -343,27 +342,20 @@ def get_ids(cls, name, ttype, lang, ids): translations.update( cls.get_ids(name, ttype, parent_lang, to_fetch)) - transaction = Transaction() - cursor = transaction.connection.cursor() - table = cls.__table__() - fuzzy_sql = table.fuzzy == False if Transaction().context.get('fuzzy_translation', False): - fuzzy_sql = None - in_max = transaction.database.IN_MAX // 7 - for sub_to_fetch in grouped_slice(to_fetch, in_max): - red_sql = reduce_ids(table.res_id, sub_to_fetch) - where = And(((table.lang == lang), - (table.type == ttype), - (table.name == name), - (table.value != ''), - (table.value != Null), - red_sql, - )) - if fuzzy_sql: - where &= fuzzy_sql - cursor.execute(*table.select(table.res_id, table.value, - where=where)) - translations.update(cursor) + fuzzy_clause = [] + else: + fuzzy_clause = [('fuzzy', '=', False)] + for sub_to_fetch in grouped_slice(to_fetch): + for translation in cls.search([ + ('lang', '=', lang), + ('type', '=', ttype), + ('name', '=', name), + ('value', '!=', ''), + ('value', '!=', None), + ('res_id', 'in', list(sub_to_fetch)), + ] + fuzzy_clause): + translations[translation.res_id] = translation.value # Don't store fuzzy translation in cache if not Transaction().context.get('fuzzy_translation', False): for res_id in to_fetch: @@ -523,10 +515,8 @@ def get_sources(cls, args): res = {} parent_args = [] parent_langs = [] - clause = [] + clauses = [] transaction = Transaction() - cursor = transaction.connection.cursor() - table = cls.__table__() if len(args) > transaction.database.IN_MAX: for sub_args in grouped_slice(args): res.update(cls.get_sources(list(sub_args))) @@ -549,17 +539,18 @@ def get_sources(cls, args): parent_args.append((name, ttype, parent_lang, source)) parent_langs.append(lang) res[(name, ttype, lang, source)] = None - where = And(((table.lang == lang), - (table.type == ttype), - (table.name == name), - (table.value != ''), - (table.value != Null), - (table.fuzzy == False), - (table.res_id == -1), - )) + clause = [ + ('lang', '=', lang), + ('type', '=', ttype), + ('name', '=', name), + ('value', '!=', ''), + ('value', '!=', None), + ('fuzzy', '=', False), + ('res_id', '=', -1), + ] if source is not None: - where &= table.src == source - clause.append(where) + clause.append(('src', '=', source)) + clauses.append(clause) # Get parent transactions if parent_args: @@ -569,17 +560,14 @@ def get_sources(cls, args): res[(name, ttype, lang, source)] = parent_src[ (name, ttype, parent_lang, source)] - if clause: - in_max = transaction.database.IN_MAX // 7 - for sub_clause in grouped_slice(clause, in_max): - cursor.execute(*table.select( - table.lang, table.type, table.name, table.src, - table.value, - where=Or(list(sub_clause)))) - for lang, ttype, name, source, value in cursor.fetchall(): - if (name, ttype, lang, source) not in args: - source = None - res[(name, ttype, lang, source)] = value + in_max = transaction.database.IN_MAX // 7 + for sub_clause in grouped_slice(clauses, in_max): + for translation in cls.search(['OR'] + list(sub_clause)): + key = (translation.name, translation.type, + translation.name, translation.name, translation.src) + if key not in args: + key = key[:-1] + (None,) + res[key] = translation.value for key in to_cache: cls._translation_cache.set(key, res[key]) return res From a5fa95fb60d8510d5d494028801a1bf20078e72e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 25 Oct 2019 23:05:38 +0200 Subject: [PATCH 046/110] Use proper key to compare with get_sources arguments The wrong key values was introduced in rev bfc90c50b591. issue8744 review254491002 (grafted from 3723276aa94f14d20f43f39a4e767d98e282512e) --HG-- branch : 5.2 --- trytond/ir/translation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/ir/translation.py b/trytond/ir/translation.py index 907f994ce..021ae3c9c 100644 --- a/trytond/ir/translation.py +++ b/trytond/ir/translation.py @@ -564,7 +564,7 @@ def get_sources(cls, args): for sub_clause in grouped_slice(clauses, in_max): for translation in cls.search(['OR'] + list(sub_clause)): key = (translation.name, translation.type, - translation.name, translation.name, translation.src) + translation.lang, translation.src) if key not in args: key = key[:-1] + (None,) res[key] = translation.value From 03851522ef96494a63b56a6c0f7a4740be7cb2f4 Mon Sep 17 00:00:00 2001 From: Sergi Almacellas Abellana Date: Tue, 22 Oct 2019 10:07:59 +0200 Subject: [PATCH 047/110] Add missing skiptest attribute on xml validation issue8735 review274091002 (grafted from dc3a2f95414f3a2057024a92aeed988afd30df76) --HG-- branch : 5.2 --- trytond/tryton.rnc | 1 + trytond/tryton.rng | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/trytond/tryton.rnc b/trytond/tryton.rnc index fe8769800..636ddf6c8 100644 --- a/trytond/tryton.rnc +++ b/trytond/tryton.rnc @@ -8,6 +8,7 @@ attlist.data &= attlist.data &= [ a:defaultValue = "0" ] attribute grouped { "0" | "1" }? attlist.data &= attribute depends { text } +attlist.data &= attribute skiptest { "0" | "1" }? record = element record { attlist.record, field* } attlist.record &= attribute model { text } attlist.record &= attribute id { text } diff --git a/trytond/tryton.rng b/trytond/tryton.rng index ea4c9c590..62a579e06 100644 --- a/trytond/tryton.rng +++ b/trytond/tryton.rng @@ -53,6 +53,17 @@ + + + + skiptest + + 0 + 1 + + + + record From 626836ffbb699c6d1613b5dc5adfe09b51860ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 8 Nov 2019 19:15:30 +0100 Subject: [PATCH 048/110] Prepare release 5.2.8 --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index fa14fc975..fd7c4d0e6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.8 - 2019-11-08 +* Bug fixes (see mercurial logs for details) + Version 5.2.7 - 2019-10-06 * Bug fixes (see mercurial logs for details) From 990b61b10ca871565f25e4d301aac8b6d4ee171c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 8 Nov 2019 19:15:30 +0100 Subject: [PATCH 049/110] Added tag 5.2.8 for changeset af82c05398fe --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 8cd31a2eb..599c6f69b 100644 --- a/.hgtags +++ b/.hgtags @@ -27,3 +27,4 @@ e49a560dd265f6d018abf3974b227c6a841a8d12 5.2.4 99b0686c8702d2b41898c54bcdbfc36af8253a2b 5.2.5 4363488df01bb84b337267279b6a5e0e3b61d0ab 5.2.6 a533a499c493e206bf5fb8546038ba3e62af130b 5.2.7 +af82c05398fea9ffc5f3713a7cd9e2418168fbba 5.2.8 From f66620c98b9bf022ac573a27b976d386c73b8f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 8 Nov 2019 19:15:49 +0100 Subject: [PATCH 050/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index f09ed1af2..c2a7acf04 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.8" +__version__ = "5.2.9" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From 1065211ce40ae2c35a5d8e4d7617ddc5a4d1b7f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 30 Oct 2019 14:09:20 +0100 Subject: [PATCH 051/110] Replace __unicode__ by __str__ The unicode method is no supported by Python3. There is only __str__. issue8769 review262341002 (grafted from 7cf1e86564732f686879d85bef64fd950fdc2a69) --HG-- branch : 5.2 --- trytond/exceptions.py | 8 ++++---- trytond/model/model.py | 3 --- trytond/report/report.py | 2 -- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/trytond/exceptions.py b/trytond/exceptions.py index d66fa217c..5e9dfa72c 100644 --- a/trytond/exceptions.py +++ b/trytond/exceptions.py @@ -14,7 +14,7 @@ def __init__(self, message, description=''): self.description = description self.code = 1 - def __unicode__(self): + def __str__(self): return '%s - %s' % (self.message, self.description) @@ -29,7 +29,7 @@ def __init__(self, name, message, description=''): self.description = description self.code = 2 - def __unicode__(self): + def __str__(self): return '%s - %s' % (self.message, self.description) @@ -55,7 +55,7 @@ def __init__(self, message): self.message = message self.code = 4 - def __unicode__(self): + def __str__(self): return self.message @@ -68,5 +68,5 @@ class MissingDependenciesException(TrytonException): def __init__(self, missings): self.missings = missings - def __unicode__(self): + def __str__(self): return 'Missing dependencies: %s' % ' '.join(self.missings) diff --git a/trytond/model/model.py b/trytond/model/model.py index 7db028aed..7b865b135 100644 --- a/trytond/model/model.py +++ b/trytond/model/model.py @@ -265,9 +265,6 @@ def __int__(self): def __str__(self): return '%s,%s' % (self.__name__, self.id) - def __unicode__(self): - return '%s,%s' % (self.__name__, self.id) - def __repr__(self): if self.id is None or self.id < 0: return "Pool().get('%s')(**%s)" % (self.__name__, diff --git a/trytond/report/report.py b/trytond/report/report.py index 11504a695..89aafc30b 100644 --- a/trytond/report/report.py +++ b/trytond/report/report.py @@ -229,8 +229,6 @@ def __int__(self): def __str__(self): return '%s,%s' % (Model.__name__, self.id) - def __unicode__(self): - return '%s,%s' % (Model.__name__, self.id) return [TranslateModel(id) for id in ids] @classmethod From b66cb770399e02d4165692d49154884bb77c5e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 1 Nov 2019 23:04:50 +0100 Subject: [PATCH 052/110] Cast timestamp epoch into FLOAT to avoid rounding On PostgreSQL-12, the epoch FLOAT is now 6 digits but rounded to 5 digits when cast into NUMERIC. issue8779 review264501002 (grafted from 5a45753983a39d6e14a21bd7126a87cd257641c8) --HG-- branch : 5.2 --- trytond/model/modelsql.py | 2 +- trytond/tests/test_modelsql.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index 3bea329f5..4e231e2a5 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -538,7 +538,7 @@ def __check_timestamp(cls, ids): '%s,%s' % (cls.__name__, id_)) except KeyError: continue - sql_type = fields.Numeric('timestamp').sql_type().base + sql_type = fields.Float('timestamp').sql_type().base where.append((table.id == id_) & (Extract('EPOCH', Coalesce(table.write_date, table.create_date) diff --git a/trytond/tests/test_modelsql.py b/trytond/tests/test_modelsql.py index 3a4c8a3ea..4c52c8f06 100644 --- a/trytond/tests/test_modelsql.py +++ b/trytond/tests/test_modelsql.py @@ -282,6 +282,7 @@ def test_check_timestamp(self): # timestamp precision of sqlite is the second time.sleep(1) + transaction.timestamp[str(record)] = timestamp ModelsqlTimestamp.write([record], {}) transaction.commit() From eab35a0b9c73b0f2607fdacbe2e77c2fb0dc02a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 3 Nov 2019 17:49:03 +0100 Subject: [PATCH 053/110] Compare timestamps as Char for inequality Databases are too random about rounding when casting epoch into numerical value. So it is better to check against inequality of the string. issue8779 review254571002 (grafted from cb8c5a6f3a2536e7202048ab301196fa813960a8) --HG-- branch : 5.2 --- trytond/model/modelsql.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index 4e231e2a5..345798a19 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -538,11 +538,11 @@ def __check_timestamp(cls, ids): '%s,%s' % (cls.__name__, id_)) except KeyError: continue - sql_type = fields.Float('timestamp').sql_type().base + sql_type = fields.Char('timestamp').sql_type().base where.append((table.id == id_) & (Extract('EPOCH', Coalesce(table.write_date, table.create_date) - ).cast(sql_type) > timestamp)) + ).cast(sql_type) != timestamp)) if where: cursor.execute(*table.select(table.id, where=where, limit=1)) if cursor.fetchone(): From c10ce00fb0e8dcda22f34d8e7fa4d40252f6cb43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 5 Nov 2019 10:33:25 +0100 Subject: [PATCH 054/110] Do not check timestamp when it is None issue8779 review268321002 (grafted from 3e6a252d097b011a21818c0449cc856f5b278446) --HG-- branch : 5.2 --- trytond/model/modelsql.py | 2 ++ trytond/tests/test_modelsql.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index 345798a19..c0f6ca9be 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -538,6 +538,8 @@ def __check_timestamp(cls, ids): '%s,%s' % (cls.__name__, id_)) except KeyError: continue + if timestamp is None: + continue sql_type = fields.Char('timestamp').sql_type().base where.append((table.id == id_) & (Extract('EPOCH', diff --git a/trytond/tests/test_modelsql.py b/trytond/tests/test_modelsql.py index 4c52c8f06..c259139ad 100644 --- a/trytond/tests/test_modelsql.py +++ b/trytond/tests/test_modelsql.py @@ -294,6 +294,10 @@ def test_check_timestamp(self): self.assertRaises(ConcurrencyException, ModelsqlTimestamp.delete, [record]) + transaction.timestamp[str(record)] = None + ModelsqlTimestamp.write([record], {}) + transaction.commit() + transaction.timestamp.pop(str(record), None) ModelsqlTimestamp.write([record], {}) transaction.commit() From 69d3919c9d314a4c75ba878288724f1fea0151fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 27 Jun 2019 21:45:37 +0200 Subject: [PATCH 055/110] Store database pool per process The connection pool can not be shared between multiple processes. issue8281 review285251002 (grafted from f9982286317ae2eb420dfb190d8a2e2a94b31773) --HG-- branch : 5.2 --- trytond/backend/postgresql/database.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/trytond/backend/postgresql/database.py b/trytond/backend/postgresql/database.py index 69dc5f084..ef553112c 100644 --- a/trytond/backend/postgresql/database.py +++ b/trytond/backend/postgresql/database.py @@ -1,5 +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 collections import defaultdict import time import logging import os @@ -108,7 +109,7 @@ class JSONContains(BinaryOperator): class Database(DatabaseInterface): _lock = RLock() - _databases = {} + _databases = defaultdict(dict) _connpool = None _list_cache = {} _list_cache_timestamp = {} @@ -130,13 +131,14 @@ class Database(DatabaseInterface): def __new__(cls, name='template1'): with cls._lock: now = datetime.now() - for database in list(cls._databases.values()): + databases = cls._databases[os.getpid()] + for database in list(databases.values()): if ((now - database._last_use).total_seconds() > _timeout and database.name != name and not database._connpool._used): database.close() - if name in cls._databases: - inst = cls._databases[name] + if name in databases: + inst = databases[name] else: if name == 'template1': minconn = 0 @@ -147,7 +149,7 @@ def __new__(cls, name='template1'): inst._connpool = ThreadedConnectionPool( minconn, _maxconn, **cls._connection_params(name), cursor_factory=LoggingCursor) - cls._databases[name] = inst + databases[name] = inst inst._last_use = datetime.now() return inst @@ -200,7 +202,7 @@ def close(self): with self._lock: logger.info('disconnect from "%s"', self.name) self._connpool.closeall() - self._databases.pop(self.name) + self._databases[os.getpid()].pop(self.name) @classmethod def create(cls, connection, database_name, template='template0'): From 3f206ba14080db89f969cb1367648509e33c2b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 2 Dec 2019 20:37:01 +0100 Subject: [PATCH 056/110] Prepare release 5.2.9 [skip ci] --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index fd7c4d0e6..b49a74d3d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.9 - 2019-12-02 +* Bug fixes (see mercurial logs for details) + Version 5.2.8 - 2019-11-08 * Bug fixes (see mercurial logs for details) From 3933a5d953046bf3fc94a7a8a6a84345f597ac6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 2 Dec 2019 20:37:01 +0100 Subject: [PATCH 057/110] Add tag 5.2.9 [skip ci] --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 599c6f69b..5e21bdb88 100644 --- a/.hgtags +++ b/.hgtags @@ -28,3 +28,4 @@ e49a560dd265f6d018abf3974b227c6a841a8d12 5.2.4 4363488df01bb84b337267279b6a5e0e3b61d0ab 5.2.6 a533a499c493e206bf5fb8546038ba3e62af130b 5.2.7 af82c05398fea9ffc5f3713a7cd9e2418168fbba 5.2.8 +5fb0c06625b777dbc55a615cc8d11ed1ae63c2af 5.2.9 From 8d8b4ad2c6c02a03c144a72feb1bdace8b0b22cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 2 Dec 2019 20:37:21 +0100 Subject: [PATCH 058/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index c2a7acf04..a4f4ba6de 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.9" +__version__ = "5.2.10" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From f3319cac88a7ae7cdd3d1de4b42e384c566866cb Mon Sep 17 00:00:00 2001 From: Sergi Almacellas Abellana Date: Fri, 29 Nov 2019 15:16:11 +0100 Subject: [PATCH 059/110] Adapt file mode depending on data type in soffice conversion issue8848 review278211002 (grafted from 8afd9b41c1e8c602993cef460132ee8cdbc227cd) --HG-- branch : 5.2 --- trytond/report/report.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/trytond/report/report.py b/trytond/report/report.py index 89aafc30b..8ce87d509 100644 --- a/trytond/report/report.py +++ b/trytond/report/report.py @@ -306,7 +306,8 @@ def convert(cls, report, data, timeout=5 * 60): path = os.path.join( dtemp, report.report_name + os.extsep + input_format) oext = FORMAT2EXT.get(output_format, output_format) - with open(path, 'wb+') as fp: + mode = 'w+' if isinstance(data, str) else 'wb+' + with open(path, mode) as fp: fp.write(data) try: cmd = ['soffice', From c1000a5443f98f9e4146d9e2af94099b58468331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 16 Dec 2019 23:47:29 +0100 Subject: [PATCH 060/110] Prepare release 5.2.10 [skip ci] --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index b49a74d3d..3bbbe37d9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.10 - 2019-12-16 +* Bug fixes (see mercurial logs for details) + Version 5.2.9 - 2019-12-02 * Bug fixes (see mercurial logs for details) From 0aa4f25865bd7f7f57616cb92450616d7ae20071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 16 Dec 2019 23:47:30 +0100 Subject: [PATCH 061/110] Add tag 5.2.10 [skip ci] --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 5e21bdb88..7bac96b23 100644 --- a/.hgtags +++ b/.hgtags @@ -29,3 +29,4 @@ e49a560dd265f6d018abf3974b227c6a841a8d12 5.2.4 a533a499c493e206bf5fb8546038ba3e62af130b 5.2.7 af82c05398fea9ffc5f3713a7cd9e2418168fbba 5.2.8 5fb0c06625b777dbc55a615cc8d11ed1ae63c2af 5.2.9 +ab630bf13ef2514b0c2b24f4343619a6064a5f41 5.2.10 From f02542c9abbbd1390b9f050b5cf36fc4a79a2aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 16 Dec 2019 23:47:48 +0100 Subject: [PATCH 062/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index a4f4ba6de..54770fa2d 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.10" +__version__ = "5.2.11" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From a462c471dfd41923cbe7f6c03cffd85e9fdd92fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 11 Dec 2019 18:36:49 +0100 Subject: [PATCH 063/110] Update default value of expand attribute to match client behavior issue8889 (grafted from a6afcbe3df477cef70c0f1f06e562c1377e652cf) --HG-- branch : 5.2 --- trytond/ir/ui/form.rnc | 4 ++-- trytond/ir/ui/form.rng | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/trytond/ir/ui/form.rnc b/trytond/ir/ui/form.rnc index eda2a299f..362ad501a 100644 --- a/trytond/ir/ui/form.rnc +++ b/trytond/ir/ui/form.rnc @@ -113,7 +113,7 @@ attlist.image &= [ a:defaultValue = "0" ] attribute yexpand { "0" | "1" }? attlist.image &= [ a:defaultValue = "0" ] attribute yfill { "0" | "1" }? attlist.image &= - [ a:defaultValue = "0" ] attribute xexpand { "0" | "1" }? + [ a:defaultValue = "1" ] attribute xexpand { "0" | "1" }? attlist.image &= [ a:defaultValue = "48" ] attribute size {text }? attlist.image &= [ a:defaultValue = "0" ] attribute xfill { "0" | "1" }? @@ -132,7 +132,7 @@ attlist.separator &= attlist.separator &= [ a:defaultValue = "0" ] attribute yfill { "0" | "1" }? attlist.separator &= - [ a:defaultValue = "0" ] attribute xexpand { "0" | "1" }? + [ a:defaultValue = "1" ] attribute xexpand { "0" | "1" }? attlist.separator &= [ a:defaultValue = "0" ] attribute xfill { "0" | "1" }? attlist.separator &= attribute help { text }? diff --git a/trytond/ir/ui/form.rng b/trytond/ir/ui/form.rng index bd83b0f2d..9a770af35 100644 --- a/trytond/ir/ui/form.rng +++ b/trytond/ir/ui/form.rng @@ -520,7 +520,7 @@ - + xexpand 0 @@ -639,7 +639,7 @@ - + xexpand 0 From 05793d7d9bb2c1dcda3191b32de89de345fdc665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 9 Jan 2020 22:07:30 +0100 Subject: [PATCH 064/110] Prepare release 5.2.11 [skip ci] --HG-- branch : 5.2 --- CHANGELOG | 3 +++ COPYRIGHT | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3bbbe37d9..af03cbb49 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.11 - 2020-01-09 +* Bug fixes (see mercurial logs for details) + Version 5.2.10 - 2019-12-16 * Bug fixes (see mercurial logs for details) diff --git a/COPYRIGHT b/COPYRIGHT index 9f4714320..6ccacd177 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,7 +1,7 @@ Copyright (C) 2004-2008 Tiny SPRL. -Copyright (C) 2007-2019 Cédric Krier. +Copyright (C) 2007-2020 Cédric Krier. Copyright (C) 2007-2013 Bertrand Chenal. -Copyright (C) 2008-2019 B2CK SPRL. +Copyright (C) 2008-2020 B2CK SPRL. Copyright (C) 2011 Openlabs Technologies & Consulting (P) Ltd. Copyright (C) 2011-2019 Nicolas Évrard. From 4893b05ff99ccaec8a5fbe8d86ac33954e1f51a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 9 Jan 2020 22:07:31 +0100 Subject: [PATCH 065/110] Add tag 5.2.11 [skip ci] --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 7bac96b23..b927fef2b 100644 --- a/.hgtags +++ b/.hgtags @@ -30,3 +30,4 @@ a533a499c493e206bf5fb8546038ba3e62af130b 5.2.7 af82c05398fea9ffc5f3713a7cd9e2418168fbba 5.2.8 5fb0c06625b777dbc55a615cc8d11ed1ae63c2af 5.2.9 ab630bf13ef2514b0c2b24f4343619a6064a5f41 5.2.10 +2577bf7becc73304de840426946460250ad3e4ef 5.2.11 From c5385a067c7aa0637f7a1cb0561183e87a34e537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 9 Jan 2020 22:08:02 +0100 Subject: [PATCH 066/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index 54770fa2d..b8bc416e5 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.11" +__version__ = "5.2.12" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From 908f6c3357a2acf0c230042a0330372d5af5b410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 13 Jan 2020 23:58:14 +0100 Subject: [PATCH 067/110] Apply Exclude where clause when searching other records When we simulate the Exclude constraints, we must apply the where clause also to existing records. issue8954 review284651002 (grafted from d03e797cd90862ab3be45aebd0175654aeecc7de) --HG-- branch : 5.2 --- trytond/model/modelsql.py | 2 ++ trytond/tests/modelsql.py | 9 ++++++++- trytond/tests/test_modelsql.py | 15 ++++++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index c0f6ca9be..e0c9f4312 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -1590,6 +1590,8 @@ def validate(cls, records): clause &= Literal(False) clause &= operator(column, value) where |= clause + if isinstance(sql, Exclude) and sql.where: + where &= sql.where cursor.execute( *table.select(table.id, where=where, limit=1)) if cursor.fetchone(): diff --git a/trytond/tests/modelsql.py b/trytond/tests/modelsql.py index 34c855bf2..0ab9346f4 100644 --- a/trytond/tests/modelsql.py +++ b/trytond/tests/modelsql.py @@ -1,5 +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 sql import Literal from sql.operators import Equal from trytond.model import ModelSQL, fields, Check, Unique, Exclude @@ -111,13 +112,19 @@ class ModelExclude(ModelSQL): "ModelSQL with exclude constraint" __name__ = 'test.modelsql.exclude' value = fields.Integer("Value") + condition = fields.Boolean("Condition") + + @classmethod + def default_condition(cls): + return True @classmethod def __setup__(cls): super(ModelExclude, cls).__setup__() t = cls.__table__() cls._sql_constraints = [ - ('exclude', Exclude(t, (t.value, Equal), where=t.value > 0), + ('exclude', Exclude(t, (t.value, Equal), + where=t.condition == Literal(True)), "Value must be unique."), ] diff --git a/trytond/tests/test_modelsql.py b/trytond/tests/test_modelsql.py index c259139ad..5cad32db9 100644 --- a/trytond/tests/test_modelsql.py +++ b/trytond/tests/test_modelsql.py @@ -467,7 +467,20 @@ def test_constraint_exclude_exclusion(self): pool = Pool() Model = pool.get('test.modelsql.exclude') - records = Model.create([{'value': -1}, {'value': -1}]) + records = Model.create([{'value': 1, 'condition': False}] * 2) + + self.assertEqual(len(records), 2) + + @with_transaction() + def test_constraint_exclude_exclusion_mixed(self): + "Test exclude constraint exclusion mixed" + pool = Pool() + Model = pool.get('test.modelsql.exclude') + + records = Model.create([ + {'value': 1, 'condition': False}, + {'value': 1, 'condition': True}, + ]) self.assertEqual(len(records), 2) From d15b733b7610a67441f4f2ec5eb8a73071837210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 2 Feb 2020 16:55:25 +0100 Subject: [PATCH 068/110] Prepare release 5.2.12 [skip ci] --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index af03cbb49..8cd99cb2e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.12 - 2020-02-02 +* Bug fixes (see mercurial logs for details) + Version 5.2.11 - 2020-01-09 * Bug fixes (see mercurial logs for details) From a8071e9a251885eb01b2eabb1c6127672dd46c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 2 Feb 2020 16:55:25 +0100 Subject: [PATCH 069/110] Add tag 5.2.12 [skip ci] --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index b927fef2b..4cd6b36bc 100644 --- a/.hgtags +++ b/.hgtags @@ -31,3 +31,4 @@ af82c05398fea9ffc5f3713a7cd9e2418168fbba 5.2.8 5fb0c06625b777dbc55a615cc8d11ed1ae63c2af 5.2.9 ab630bf13ef2514b0c2b24f4343619a6064a5f41 5.2.10 2577bf7becc73304de840426946460250ad3e4ef 5.2.11 +d9d5240b1e8966f80f74f15933cb3fa641cba5c1 5.2.12 From 6a88a15238026b777daa25cb3bd73be45461044d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 2 Feb 2020 16:55:45 +0100 Subject: [PATCH 070/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index b8bc416e5..af2586e5f 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.12" +__version__ = "5.2.13" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From 7be96b604a7c3afdbd897c26d1e529e5ba6bfbd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 10 Feb 2020 23:13:26 +0100 Subject: [PATCH 071/110] Test memory cache with committed transaction as current The tests did not correspond to a proper use case because the transaction committed was not the current transaction. So this could lead to wrong cache being reset and wrong cache being tested. issue8502 review289171002 (grafted from bcf8d6f2fff18d01a1b8ae614206780a43181a32) --HG-- branch : 5.2 --- trytond/tests/test_cache.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/trytond/tests/test_cache.py b/trytond/tests/test_cache.py index 0ec712a11..1673e6d32 100644 --- a/trytond/tests/test_cache.py +++ b/trytond/tests/test_cache.py @@ -93,16 +93,15 @@ def test_memory_cache_transactions(self): cache.set('foo', 'baz') self.assertEqual(cache.get('foo'), 'baz') - Transaction().set_current_transaction(transaction1) - self.addCleanup(transaction1.stop) - self.assertEqual(cache.get('foo'), 'bar') + with Transaction().set_current_transaction(transaction1): + self.assertEqual(cache.get('foo'), 'bar') transaction2.commit() for n in range(10): - if cache.get('foo') is None: + if cache.get('foo') == 'baz': break self.wait_cache_sync() - self.assertEqual(cache.get('foo'), None) + self.assertEqual(cache.get('foo'), 'baz') def test_memory_cache_nested_transactions(self): "Test MemoryCache with nested transactions" From b9463c6b703d02f6ffd1a3423d18041f81ab8052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 12 Feb 2020 00:15:15 +0100 Subject: [PATCH 072/110] Read context fields if they are also in depends This implements the real intention of changeset a892e7a649a5. issue9036 review280791002 (grafted from b2a0b6051e8618f84165343c9a56eda8ed12ff83) --HG-- branch : 5.2 --- trytond/model/modelstorage.py | 2 +- trytond/tests/field_context.py | 4 +++- trytond/tests/test_field_context.py | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py index 3c3815870..9a7d4026d 100644 --- a/trytond/model/modelstorage.py +++ b/trytond/model/modelstorage.py @@ -1456,7 +1456,7 @@ def overrided(item): if field.context: eval_fields = fields.get_eval_fields(field.context) for context_field_name in eval_fields: - if context_field_name in field.depends: + if context_field_name not in field.depends: continue context_field = self._fields.get(context_field_name) if context_field not in ffields: diff --git a/trytond/tests/field_context.py b/trytond/tests/field_context.py index e575b7dc7..61d2ec98a 100644 --- a/trytond/tests/field_context.py +++ b/trytond/tests/field_context.py @@ -17,7 +17,9 @@ class FieldContextParent(ModelSQL): child = fields.Many2One('test.field_context.child', 'Child', context={ 'name': Eval('name'), - }) + 'rec_name': Eval('rec_name'), + }, + depends=['name']) class FieldContextChild(ModelSQL): diff --git a/trytond/tests/test_field_context.py b/trytond/tests/test_field_context.py index c60c377af..6ea9dc23c 100644 --- a/trytond/tests/test_field_context.py +++ b/trytond/tests/test_field_context.py @@ -24,10 +24,12 @@ def test_context(self): parent = Parent(name='foo', child=child) parent.save() self.assertEqual(parent.child._context['name'], 'foo') + self.assertEqual(parent.child._context['rec_name'], '') parent.name = 'bar' parent.save() self.assertEqual(parent.child._context['name'], 'bar') + self.assertEqual(parent.child._context['rec_name'], '') def suite(): From 032b3c1032f33c5ae65cb3b08c17f19816f4ada6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 15 Feb 2020 01:43:11 +0100 Subject: [PATCH 073/110] Disable cache on default_get of ModelSingleton The default_get is used by the clients as a read method to fill the form on opening. issue9045 review282931003 (grafted from 339f2332f9cc3ce585b4dd4dc3f3767692dc2506) --HG-- branch : 5.2 --- trytond/model/modelsingleton.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/trytond/model/modelsingleton.py b/trytond/model/modelsingleton.py index 752ff9820..404da4b51 100644 --- a/trytond/model/modelsingleton.py +++ b/trytond/model/modelsingleton.py @@ -9,6 +9,12 @@ class ModelSingleton(ModelStorage): Define a singleton model in Tryton. """ + @classmethod + def __setup__(cls): + super().__setup__() + # Cache disable because it is used as a read by the client + cls.__rpc__['default_get'].cache = None + @classmethod def get_singleton(cls): ''' From c3eb425a0ac6040a468ccc751d82731ba63f2dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 1 Mar 2020 21:03:32 +0100 Subject: [PATCH 074/110] Set auth-method to trust for drone build issue9063 --HG-- branch : 5.2 --- .drone.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.drone.yml b/.drone.yml index c45281213..8b3b37ff8 100644 --- a/.drone.yml +++ b/.drone.yml @@ -19,6 +19,8 @@ pipeline: services: postgresql: image: postgres + environment: + - POSTGRES_HOST_AUTH_METHOD=trust when: matrix: DATABASE: postgresql From d7e87d96f0b70261cccfd18bf3840c5651da949a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 9 Mar 2020 18:24:23 +0100 Subject: [PATCH 075/110] Enable check_access context when checking wizard access issue9108 review261041002 (grafted from 6ed96b847340dbb7ab8606170a112a0b840f4728) --HG-- branch : 5.2 --- CHANGELOG | 2 ++ trytond/wizard/wizard.py | 25 +++++++++++++------------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8cd99cb2e..d4c1f1a53 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Enable check_access context when checking wizard access (issue9108) + Version 5.2.12 - 2020-02-02 * Bug fixes (see mercurial logs for details) diff --git a/trytond/wizard/wizard.py b/trytond/wizard/wizard.py index 8275938e5..e9002a136 100644 --- a/trytond/wizard/wizard.py +++ b/trytond/wizard/wizard.py @@ -224,18 +224,19 @@ def check_access(cls): if Transaction().user == 0: return - model = context.get('active_model') - if model: - ModelAccess.check(model, 'read') - groups = set(User.get_groups()) - wizard_groups = ActionWizard.get_groups(cls.__name__, - action_id=context.get('action_id')) - if wizard_groups: - if not groups & wizard_groups: - raise UserError('Calling wizard %s is not allowed!' - % cls.__name__) - elif model: - ModelAccess.check(model, 'write') + with Transaction().set_context(_check_access=True): + model = context.get('active_model') + if model: + ModelAccess.check(model, 'read') + groups = set(User.get_groups()) + wizard_groups = ActionWizard.get_groups(cls.__name__, + action_id=context.get('action_id')) + if wizard_groups: + if not groups & wizard_groups: + raise UserError('Calling wizard %s is not allowed!' + % cls.__name__) + elif model: + ModelAccess.check(model, 'write') @classmethod def create(cls): From 8c85ed20aff38a7817dcbdcaa1176d61f76bc214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 9 Mar 2020 18:29:34 +0100 Subject: [PATCH 076/110] Prepare release 5.2.13 [skip ci] --HG-- branch : 5.2 --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index d4c1f1a53..dc2940cee 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +Version 5.2.13 - 2020-03-09 +* Bug fixes (see mercurial logs for details) * Enable check_access context when checking wizard access (issue9108) Version 5.2.12 - 2020-02-02 From 5a0cbb528bd2f1000b0927c391a607aed693ca24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 9 Mar 2020 18:29:34 +0100 Subject: [PATCH 077/110] Add tag 5.2.13 [skip ci] --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 4cd6b36bc..9192ab5a1 100644 --- a/.hgtags +++ b/.hgtags @@ -32,3 +32,4 @@ af82c05398fea9ffc5f3713a7cd9e2418168fbba 5.2.8 ab630bf13ef2514b0c2b24f4343619a6064a5f41 5.2.10 2577bf7becc73304de840426946460250ad3e4ef 5.2.11 d9d5240b1e8966f80f74f15933cb3fa641cba5c1 5.2.12 +8fc2d6f98d7b2b9a0f674e23a7ee486970a30902 5.2.13 From deda8921c313a647d0a0f37ed77db0073675a675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 9 Mar 2020 18:29:53 +0100 Subject: [PATCH 078/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index af2586e5f..4b7ee9ebf 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.13" +__version__ = "5.2.14" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From 11b7df441bb267fde6722a949dd8052b81e318ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 17 Mar 2020 23:21:20 +0100 Subject: [PATCH 079/110] Convert interval into seconds The correct way to convert interval into seconds is to use "EXTRACT(EPOCH FROM ...)". The "SECOND" gives only the value of the second field. issue9126 review297181005 (grafted from 7ab2d9708d71280e3bd6885f0d1916b8bcec092e) --HG-- branch : 5.2 --- trytond/ir/queue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/ir/queue.py b/trytond/ir/queue.py index 2f42a1a96..1c63a5801 100644 --- a/trytond/ir/queue.py +++ b/trytond/ir/queue.py @@ -103,7 +103,7 @@ def pull(cls, database, connection, name=None): candidates.expected_at.nulls_first], limit=1)) next_timeout = With('seconds', query=candidates.select( - Min(Extract('second', + Min(Extract('EPOCH', candidates.scheduled_at - CurrentTimestamp()) ), where=candidates.scheduled_at >= CurrentTimestamp())) From 6ad288410c69520f064a7e74e5019b029ac305ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 26 Mar 2020 00:12:43 +0100 Subject: [PATCH 080/110] Skip check access on ir.ui.menu for wizard User launching a wizard from menu should not need write access on the menu. issue9173 review301161002 (grafted from c9b0bdd34b75bd5f6504d202ea0dacefe571130b) --HG-- branch : 5.2 --- trytond/wizard/wizard.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trytond/wizard/wizard.py b/trytond/wizard/wizard.py index e9002a136..41e39c94b 100644 --- a/trytond/wizard/wizard.py +++ b/trytond/wizard/wizard.py @@ -226,7 +226,7 @@ def check_access(cls): with Transaction().set_context(_check_access=True): model = context.get('active_model') - if model: + if model and model != 'ir.ui.menu': ModelAccess.check(model, 'read') groups = set(User.get_groups()) wizard_groups = ActionWizard.get_groups(cls.__name__, @@ -235,7 +235,7 @@ def check_access(cls): if not groups & wizard_groups: raise UserError('Calling wizard %s is not allowed!' % cls.__name__) - elif model: + elif model and model != 'ir.ui.menu': ModelAccess.check(model, 'write') @classmethod From ef7e11b48e303cf70ae371dfc3b01384b600d3d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 4 Apr 2020 15:34:49 +0200 Subject: [PATCH 081/110] Prepare release 5.2.14 [skip ci] --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index dc2940cee..27c69a2bd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.14 - 2020-04-04 +* Bug fixes (see mercurial logs for details) + Version 5.2.13 - 2020-03-09 * Bug fixes (see mercurial logs for details) * Enable check_access context when checking wizard access (issue9108) From 084aef0f71868af81cab2b646135658dd1f84883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 4 Apr 2020 15:34:50 +0200 Subject: [PATCH 082/110] Add tag 5.2.14 [skip ci] --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 9192ab5a1..6cbd2aaa2 100644 --- a/.hgtags +++ b/.hgtags @@ -33,3 +33,4 @@ ab630bf13ef2514b0c2b24f4343619a6064a5f41 5.2.10 2577bf7becc73304de840426946460250ad3e4ef 5.2.11 d9d5240b1e8966f80f74f15933cb3fa641cba5c1 5.2.12 8fc2d6f98d7b2b9a0f674e23a7ee486970a30902 5.2.13 +732633a7fe34a4a20c6408c0f33181505a05aeeb 5.2.14 From 4a6dec5a5cf8c26ae6111d93d279b5bef81c7791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 4 Apr 2020 15:35:29 +0200 Subject: [PATCH 083/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index 4b7ee9ebf..473c09c5f 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.14" +__version__ = "5.2.15" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From a452335a6cc4bd462bc53b05a297c5502b136e1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 10 Apr 2020 18:25:03 +0200 Subject: [PATCH 084/110] Do not use only cache if context depends on other fields We must read all the required fields to set a correct context on the field. issue9187 review297391002 (grafted from a4b513b75f9ca23a19ee7eb7b12d3cb9621b36cb) --HG-- branch : 5.2 --- trytond/model/modelstorage.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py index 9a7d4026d..d7fc46adf 100644 --- a/trytond/model/modelstorage.py +++ b/trytond/model/modelstorage.py @@ -1445,11 +1445,13 @@ def overrided(item): ifields = islice(ifields, 0, threshold) ffields.update(ifields) + require_context_field = False # add datetime_field for field in list(ffields.values()): if hasattr(field, 'datetime_field') and field.datetime_field: datetime_field = self._fields[field.datetime_field] ffields[field.datetime_field] = datetime_field + require_context_field = True # add depends of field with context for field in list(ffields.values()): @@ -1459,12 +1461,14 @@ def overrided(item): if context_field_name not in field.depends: continue context_field = self._fields.get(context_field_name) + require_context_field = True if context_field not in ffields: ffields[context_field_name] = context_field def filter_(id_): - return (name not in self._cache.get(id_, {}) - and name not in self._local_cache.get(id_, {})) + return (id_ == self.id # Ensure the value is read + or (name not in self._cache.get(id_, {}) + and name not in self._local_cache.get(id_, {}))) def unique(ids): s = set() @@ -1532,7 +1536,8 @@ def instantiate(field, value, data): self._transaction.set_user(self._user), \ self._transaction.reset_context(), \ self._transaction.set_context(self._context): - if self.id in self._cache and name in self._cache[self.id]: + if (self.id in self._cache and name in self._cache[self.id] + and not require_context_field): # Use values from cache ids = islice(chain(islice(self._ids, index, None), islice(self._ids, 0, max(index - 1, 0))), From 826fb6c5b170ea58c792d35723e6a0084439293e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 10 Apr 2020 18:26:35 +0200 Subject: [PATCH 085/110] Properly test if report type in get_email We must test if it is a subclass and not an instance of Report. issue9196 review307221002 (grafted from 0f87869670a10d69c75c58b089f5be8ed3bbf211) --HG-- branch : 5.2 --- trytond/report/report.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/trytond/report/report.py b/trytond/report/report.py index 8ce87d509..d42e2a45a 100644 --- a/trytond/report/report.py +++ b/trytond/report/report.py @@ -2,6 +2,7 @@ # this repository contains the full copyright notices and license terms. import datetime import os +import inspect import logging import subprocess import tempfile @@ -363,7 +364,7 @@ def get_email(report, record, languages): pool = Pool() ActionReport = pool.get('ir.action.report') report_id = None - if isinstance(report, Report): + if inspect.isclass(report) and issubclass(report, Report): Report_ = report else: if isinstance(report, ActionReport): From 3c95410d6aef6815d8f0d8a3dad3ddcce580b697 Mon Sep 17 00:00:00 2001 From: Udo Spallek Date: Sun, 19 Apr 2020 09:59:48 +0200 Subject: [PATCH 086/110] Clear report path when copying reports issue9174 review299191002 (grafted from b1fb6c624ae9355ec8f46f903c2fe1c093362d2e) --HG-- branch : 5.2 --- trytond/ir/action.py | 1 + 1 file changed, 1 insertion(+) diff --git a/trytond/ir/action.py b/trytond/ir/action.py index 48e1fcdc9..a0c86cc46 100644 --- a/trytond/ir/action.py +++ b/trytond/ir/action.py @@ -668,6 +668,7 @@ def copy(cls, reports, default=None): for report in reports: if report.report: default['report_content'] = None + default['report'] = None default['report_name'] = report.report_name new_reports.extend(super(ActionReport, cls).copy([report], default=default)) From a4a7c2aa101d2bbd855044744a49f48e7a890865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 19 Apr 2020 16:37:51 +0200 Subject: [PATCH 087/110] Limit size of cache to reset per notify call issue8781 review309371003 (grafted from 4e240b35cbc8afa91197a6a3edb666df2a6c012b) --HG-- branch : 5.2 --- trytond/cache.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/trytond/cache.py b/trytond/cache.py index 208c27e1d..583e49c0f 100644 --- a/trytond/cache.py +++ b/trytond/cache.py @@ -16,7 +16,7 @@ from trytond import backend from trytond.config import config from trytond.transaction import Transaction -from trytond.tools import resolve +from trytond.tools import resolve, grouped_slice __all__ = ['BaseCache', 'Cache', 'LRUDict'] _clear_timeout = config.getint('cache', 'clean_timeout', default=5 * 60) @@ -207,9 +207,12 @@ def commit(cls, transaction): dbname = database.name if not _clear_timeout and transaction.database.has_channel(): with transaction.connection.cursor() as cursor: - cursor.execute( - 'NOTIFY "%s", %%s' % cls._channel, - (json.dumps(list(reset), separators=(',', ':')),)) + # The count computed as + # 8000 (max notify size) / 64 (max name data len) + for sub_reset in grouped_slice(reset, 125): + cursor.execute( + 'NOTIFY "%s", %%s' % cls._channel, + (json.dumps(list(sub_reset), separators=(',', ':')),)) else: connection = database.get_connection( readonly=False, autocommit=True) From ba64fb7af7fc1e7f1f5dbed6514cb6d594738a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=C3=89vrard?= Date: Tue, 21 Apr 2020 14:06:10 +0200 Subject: [PATCH 088/110] Do not write metadata on ir.note when writing on only the unread field issue9245 review305371002 (grafted from b95af4a8509edc75b731dffb97e36c8ed62dd382) --HG-- branch : 5.2 --- trytond/ir/note.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/trytond/ir/note.py b/trytond/ir/note.py index 8f988f195..16157b2eb 100644 --- a/trytond/ir/note.py +++ b/trytond/ir/note.py @@ -100,11 +100,13 @@ def set_unread(cls, notes, name, value): @classmethod def write(cls, notes, values, *args): # Avoid changing write meta data if only unread is set - if args or values.keys() != ['unread']: + if args or set(values.keys()) != {'unread'}: super(Note, cls).write(notes, values, *args) else: # Check access write and clean cache - ModelStorage.write(notes, values) + # Use __func__ to directly access ModelStorage's write method and + # pass it the right class + ModelStorage.write.__func__(cls, notes, values) cls.set_unread(notes, 'unread', values['unread']) From 66e7078a358c227520e101ba8af62061aeaaa5c4 Mon Sep 17 00:00:00 2001 From: Sergi Almacellas Abellana Date: Sat, 25 Apr 2020 08:50:14 +0200 Subject: [PATCH 089/110] Check model read access only on wizard with table_query models issue9251 review297631002 (grafted from ce5ad8f394402a99bc7d96a594317e849257d34f) --HG-- branch : 5.2 --- trytond/wizard/wizard.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/trytond/wizard/wizard.py b/trytond/wizard/wizard.py index 41e39c94b..72a9d9c5a 100644 --- a/trytond/wizard/wizard.py +++ b/trytond/wizard/wizard.py @@ -12,6 +12,7 @@ from trytond.transaction import Transaction from trytond.url import URLMixin from trytond.protocols.jsonrpc import JSONDecoder, JSONEncoder +from trytond.model import ModelSQL from trytond.model.fields import states_validate from trytond.pyson import PYSONEncoder from trytond.rpc import RPC @@ -236,7 +237,10 @@ def check_access(cls): raise UserError('Calling wizard %s is not allowed!' % cls.__name__) elif model and model != 'ir.ui.menu': - ModelAccess.check(model, 'write') + Model = pool.get(model) + if (not callable(getattr(Model, 'table_query', None)) + or Model.write.__func__ != ModelSQL.write.__func__): + ModelAccess.check(model, 'write') @classmethod def create(cls): From 2fb363c1661bed5887108fac5e5e37723f0262e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 1 May 2020 15:14:34 +0200 Subject: [PATCH 090/110] Prepare release 5.2.15 [skip ci] --HG-- branch : 5.2 --- CHANGELOG | 3 +++ COPYRIGHT | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 27c69a2bd..7abe92226 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.15 - 2020-05-01 +* Bug fixes (see mercurial logs for details) + Version 5.2.14 - 2020-04-04 * Bug fixes (see mercurial logs for details) diff --git a/COPYRIGHT b/COPYRIGHT index 6ccacd177..439a6eca2 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -3,7 +3,7 @@ Copyright (C) 2007-2020 Cédric Krier. Copyright (C) 2007-2013 Bertrand Chenal. Copyright (C) 2008-2020 B2CK SPRL. Copyright (C) 2011 Openlabs Technologies & Consulting (P) Ltd. -Copyright (C) 2011-2019 Nicolas Évrard. +Copyright (C) 2011-2020 Nicolas Évrard. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From c7abaedb0975f50667e3d1994f7653cf3d7e1575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 1 May 2020 15:14:35 +0200 Subject: [PATCH 091/110] Add tag 5.2.15 [skip ci] --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 6cbd2aaa2..a385e7725 100644 --- a/.hgtags +++ b/.hgtags @@ -34,3 +34,4 @@ ab630bf13ef2514b0c2b24f4343619a6064a5f41 5.2.10 d9d5240b1e8966f80f74f15933cb3fa641cba5c1 5.2.12 8fc2d6f98d7b2b9a0f674e23a7ee486970a30902 5.2.13 732633a7fe34a4a20c6408c0f33181505a05aeeb 5.2.14 +d647abbefe43bc434319efacbff63a0954e1a3d9 5.2.15 From ded305a866f8d3834c0ea88ea181c3a8d85ce9a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 1 May 2020 15:14:50 +0200 Subject: [PATCH 092/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index 473c09c5f..db7ddbf01 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.15" +__version__ = "5.2.16" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From 78264ca7227fb4be1b9ee10fc1c16e43485683a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 28 Apr 2020 00:28:06 +0200 Subject: [PATCH 093/110] Reschedule task failing for operational errors We should always try to execute them later as operational errors should be temporary. issue9252 review319301002 (grafted from 5d3eafccb4d148bd0df1389a405e4ff36c08121b) --HG-- branch : 5.2 --- trytond/worker.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/trytond/worker.py b/trytond/worker.py index bbf2bca63..3001d83df 100644 --- a/trytond/worker.py +++ b/trytond/worker.py @@ -1,6 +1,8 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. +import datetime as dt import logging +import random import select import signal import time @@ -114,5 +116,16 @@ def run_task(pool, task_id): continue raise logger.info('task "%d" done', task_id) + except backend.DatabaseOperationalError: + try: + with Transaction().start(pool.database_name, 0) as transaction: + task = Queue(task_id) + scheduled_at = dt.datetime.now() + scheduled_at += dt.timedelta( + seconds=random.randint(0, 2 ** retry)) + Queue.push(task.name, task.data, scheduled_at=scheduled_at) + except Exception: + logger.critical( + 'rescheduling task "%d" failed', task_id, exc_info=True) except Exception: logger.critical('task "%d" failed', task_id, exc_info=True) From b1ea91894ee9b396c146122c1f0fb87bc31f152f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 15 May 2020 21:42:01 +0200 Subject: [PATCH 094/110] Prepare release 5.2.16 [skip ci] --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 7abe92226..4ebdef9f6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.16 - 2020-05-15 +* Bug fixes (see mercurial logs for details) + Version 5.2.15 - 2020-05-01 * Bug fixes (see mercurial logs for details) From 0270daedcf0e47719ab08cc56e83b991250831a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 15 May 2020 21:42:01 +0200 Subject: [PATCH 095/110] Add tag 5.2.16 [skip ci] --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index a385e7725..b9c802f70 100644 --- a/.hgtags +++ b/.hgtags @@ -35,3 +35,4 @@ d9d5240b1e8966f80f74f15933cb3fa641cba5c1 5.2.12 8fc2d6f98d7b2b9a0f674e23a7ee486970a30902 5.2.13 732633a7fe34a4a20c6408c0f33181505a05aeeb 5.2.14 d647abbefe43bc434319efacbff63a0954e1a3d9 5.2.15 +8e77e3156c32026b61b2ebe535f4521402b69305 5.2.16 From ec4514986018cc87c478f2d3ac927fab6757d8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 15 May 2020 21:42:17 +0200 Subject: [PATCH 096/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index db7ddbf01..3ed49d51d 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.16" +__version__ = "5.2.17" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From 4b4e8bc5155517024867bde2f773b5cb58c91102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 16 May 2020 22:05:27 +0200 Subject: [PATCH 097/110] Correctly import DatabaseOperationalError Introduced by the backport of issue9252 issue9343 (grafted from 295580216dea4275124e95156580c8342f9ada51) --HG-- branch : 5.2 --- trytond/worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/worker.py b/trytond/worker.py index 3001d83df..fd494cff4 100644 --- a/trytond/worker.py +++ b/trytond/worker.py @@ -116,7 +116,7 @@ def run_task(pool, task_id): continue raise logger.info('task "%d" done', task_id) - except backend.DatabaseOperationalError: + except DatabaseOperationalError: try: with Transaction().start(pool.database_name, 0) as transaction: task = Queue(task_id) From 6d4e21a57941e6f0e4461ed3e6d39dc7e06f7d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 18 May 2020 22:47:27 +0200 Subject: [PATCH 098/110] Prepare release 5.2.17 [skip ci] --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 4ebdef9f6..3bc7c7fe2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.17 - 2020-05-18 +* Bug fixes (see mercurial logs for details) + Version 5.2.16 - 2020-05-15 * Bug fixes (see mercurial logs for details) From 4b843af6814ba99d1182e07db4476c42ee2e3eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 18 May 2020 22:47:27 +0200 Subject: [PATCH 099/110] Add tag 5.2.17 [skip ci] --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index b9c802f70..51dd90e65 100644 --- a/.hgtags +++ b/.hgtags @@ -36,3 +36,4 @@ d9d5240b1e8966f80f74f15933cb3fa641cba5c1 5.2.12 732633a7fe34a4a20c6408c0f33181505a05aeeb 5.2.14 d647abbefe43bc434319efacbff63a0954e1a3d9 5.2.15 8e77e3156c32026b61b2ebe535f4521402b69305 5.2.16 +ce808db9a1f59e4621c1b7eef8f297924b59ada5 5.2.17 From c77b7d3bef7da8e2e725ceedd6be68f12f18929a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 18 May 2020 22:47:42 +0200 Subject: [PATCH 100/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index 3ed49d51d..5a3f6c809 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.17" +__version__ = "5.2.18" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From a2f1e9d2ff8a705a86f2b482ba63eace349ee460 Mon Sep 17 00:00:00 2001 From: Sergi Almacellas Abellana Date: Fri, 22 May 2020 11:27:27 +0200 Subject: [PATCH 101/110] Ensure User application key is different on bunch creation issue9306 review321301002 (grafted from 8021e386894bd1e907d5a855c1ba2cf3af3a3bae) --HG-- branch : 5.2 --- trytond/res/user.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/trytond/res/user.py b/trytond/res/user.py index c481464d6..70d355407 100644 --- a/trytond/res/user.py +++ b/trytond/res/user.py @@ -901,6 +901,8 @@ def __setup__(cls): 'depends': ['state'], }, }) + # Do not cache default_key as it depends on time + cls.__rpc__['default_get'].cache = None @classmethod def default_key(cls): @@ -945,6 +947,11 @@ def check(cls, key, application): def create(cls, vlist): pool = Pool() User = pool.get('res.user') + vlist = [v.copy() for v in vlist] + for values in vlist: + # Ensure we get a different key for each record + # default methods are called only once + values.setdefault('key', cls.default_key()) applications = super(UserApplication, cls).create(vlist) User._get_preferences_cache.clear() return applications From c2cbc98a6dcf8709d3a0cb047a47f9ec79e5b630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 3 Jun 2020 22:13:26 +0200 Subject: [PATCH 102/110] Prepare release 5.2.18 [skip ci] --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 3bc7c7fe2..af2c8367b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.18 - 2020-06-03 +* Bug fixes (see mercurial logs for details) + Version 5.2.17 - 2020-05-18 * Bug fixes (see mercurial logs for details) From 8fb24e5626c877523ad0caf75b607523336d4f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 3 Jun 2020 22:13:26 +0200 Subject: [PATCH 103/110] Add tag 5.2.18 [skip ci] --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 51dd90e65..d595aaaea 100644 --- a/.hgtags +++ b/.hgtags @@ -37,3 +37,4 @@ d9d5240b1e8966f80f74f15933cb3fa641cba5c1 5.2.12 d647abbefe43bc434319efacbff63a0954e1a3d9 5.2.15 8e77e3156c32026b61b2ebe535f4521402b69305 5.2.16 ce808db9a1f59e4621c1b7eef8f297924b59ada5 5.2.17 +3bfd0307262590bcae72c4e6778544761ae74149 5.2.18 From dcbce5cf74bfb74316cfa126ef48826cf8009a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 3 Jun 2020 22:13:43 +0200 Subject: [PATCH 104/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index 5a3f6c809..413aa8f5a 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.18" +__version__ = "5.2.19" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From 955ae1e92d623b98c16a6560e1070332d5ada76a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 17 Jun 2020 00:25:57 +0200 Subject: [PATCH 105/110] Wait cache sync after clearing cache in test The test relies on the cache being cleared before setting a value from the old transaction. So we must wait for the synchronisation to happen when channel is used. issue9413 review327461011 (grafted from e14beb9c906cdebd2b28a1115cddfcad8e52d6c3) --HG-- branch : 5.2 --- trytond/tests/test_cache.py | 1 + 1 file changed, 1 insertion(+) diff --git a/trytond/tests/test_cache.py b/trytond/tests/test_cache.py index 1673e6d32..539112586 100644 --- a/trytond/tests/test_cache.py +++ b/trytond/tests/test_cache.py @@ -136,6 +136,7 @@ def test_memory_cache_old_transaction(self): self.addCleanup(transaction2.stop) cache.clear() transaction2.commit() + self.wait_cache_sync() # Set value from old transaction Transaction().set_current_transaction(transaction1) From f0145434e0ff9e5dd66889f57529502dec275615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 1 Jul 2020 22:30:46 +0200 Subject: [PATCH 106/110] Prepare release 5.2.19 [skip ci] --HG-- branch : 5.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index af2c8367b..9369859b4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 5.2.19 - 2020-07-01 +* Bug fixes (see mercurial logs for details) + Version 5.2.18 - 2020-06-03 * Bug fixes (see mercurial logs for details) From 8228464c463ac6d7c051a7c7c887bcef0cce7f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 1 Jul 2020 22:30:46 +0200 Subject: [PATCH 107/110] Add tag 5.2.19 [skip ci] --HG-- branch : 5.2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index d595aaaea..992b1c6fd 100644 --- a/.hgtags +++ b/.hgtags @@ -38,3 +38,4 @@ d647abbefe43bc434319efacbff63a0954e1a3d9 5.2.15 8e77e3156c32026b61b2ebe535f4521402b69305 5.2.16 ce808db9a1f59e4621c1b7eef8f297924b59ada5 5.2.17 3bfd0307262590bcae72c4e6778544761ae74149 5.2.18 +aff310d8f73f6db33d60e6d2e865cb4dc1983522 5.2.19 From 083df1dcaee06c6cfa6bba66906204eaa57c3986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 1 Jul 2020 22:31:02 +0200 Subject: [PATCH 108/110] Increase version number --HG-- branch : 5.2 --- trytond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/__init__.py b/trytond/__init__.py index 413aa8f5a..f7e049ec8 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,7 +5,7 @@ import warnings from email import charset -__version__ = "5.2.19" +__version__ = "5.2.20" os.environ['TZ'] = 'UTC' if hasattr(time, 'tzset'): From 14f43de271e30c6e2cbea8fdb8fac3c499e321b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 27 Jun 2020 00:52:49 +0200 Subject: [PATCH 109/110] Do not set slots on ShowViewStart The model is generic and must accept that Wizard.execute set any field on it. issue9425 review301821003 (grafted from c60abb47491a7bcd54ad943762b3f67f00c43b0f) --HG-- branch : 5.2 --- trytond/ir/ui/view.py | 1 + 1 file changed, 1 insertion(+) diff --git a/trytond/ir/ui/view.py b/trytond/ir/ui/view.py index 01056559a..081a4fbf7 100644 --- a/trytond/ir/ui/view.py +++ b/trytond/ir/ui/view.py @@ -211,6 +211,7 @@ def write(cls, views, values, *args): class ShowViewStart(ModelView): 'Show view' __name__ = 'ir.ui.view.show.start' + __no_slots__ = True class ShowView(Wizard): From 5d8239e90cdcdcb3eebd7f1ab1e4ffa34fc9e649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 27 Jun 2020 00:54:03 +0200 Subject: [PATCH 110/110] Make reschedule delay linear An exponential factor growths too fast and so reschedule too much in the future. issue9428 review315631002 (grafted from bb6b1390c85250456d1801e192c3ed6ddb8e3a1d) --HG-- branch : 5.2 --- trytond/worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/worker.py b/trytond/worker.py index fd494cff4..569aa7b91 100644 --- a/trytond/worker.py +++ b/trytond/worker.py @@ -122,7 +122,7 @@ def run_task(pool, task_id): task = Queue(task_id) scheduled_at = dt.datetime.now() scheduled_at += dt.timedelta( - seconds=random.randint(0, 2 ** retry)) + seconds=random.randint(0, 2 * retry)) Queue.push(task.name, task.data, scheduled_at=scheduled_at) except Exception: logger.critical(