tryton 7.0.21__py3-none-any.whl → 7.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tryton might be problematic. Click here for more details.

Files changed (94) hide show
  1. tryton/__init__.py +1 -1
  2. tryton/cache.py +34 -0
  3. tryton/common/common.py +125 -69
  4. tryton/common/completion.py +2 -2
  5. tryton/common/domain_inversion.py +1 -2
  6. tryton/common/domain_parser.py +7 -17
  7. tryton/common/selection.py +6 -3
  8. tryton/common/tempfile.py +34 -0
  9. tryton/config.py +3 -2
  10. tryton/data/locale/bg/LC_MESSAGES/tryton.mo +0 -0
  11. tryton/data/locale/bg/LC_MESSAGES/tryton.po +28 -3
  12. tryton/data/locale/ca/LC_MESSAGES/tryton.mo +0 -0
  13. tryton/data/locale/ca/LC_MESSAGES/tryton.po +33 -5
  14. tryton/data/locale/cs/LC_MESSAGES/tryton.mo +0 -0
  15. tryton/data/locale/cs/LC_MESSAGES/tryton.po +28 -3
  16. tryton/data/locale/de/LC_MESSAGES/tryton.mo +0 -0
  17. tryton/data/locale/de/LC_MESSAGES/tryton.po +32 -4
  18. tryton/data/locale/es/LC_MESSAGES/tryton.mo +0 -0
  19. tryton/data/locale/es/LC_MESSAGES/tryton.po +32 -4
  20. tryton/data/locale/es_419/LC_MESSAGES/tryton.mo +0 -0
  21. tryton/data/locale/es_419/LC_MESSAGES/tryton.po +29 -4
  22. tryton/data/locale/et/LC_MESSAGES/tryton.mo +0 -0
  23. tryton/data/locale/et/LC_MESSAGES/tryton.po +32 -5
  24. tryton/data/locale/fa/LC_MESSAGES/tryton.mo +0 -0
  25. tryton/data/locale/fa/LC_MESSAGES/tryton.po +32 -6
  26. tryton/data/locale/fi/LC_MESSAGES/tryton.mo +0 -0
  27. tryton/data/locale/fi/LC_MESSAGES/tryton.po +28 -3
  28. tryton/data/locale/fr/LC_MESSAGES/tryton.mo +0 -0
  29. tryton/data/locale/fr/LC_MESSAGES/tryton.po +32 -4
  30. tryton/data/locale/hu/LC_MESSAGES/tryton.mo +0 -0
  31. tryton/data/locale/hu/LC_MESSAGES/tryton.po +32 -5
  32. tryton/data/locale/id/LC_MESSAGES/tryton.mo +0 -0
  33. tryton/data/locale/id/LC_MESSAGES/tryton.po +30 -3
  34. tryton/data/locale/it/LC_MESSAGES/tryton.mo +0 -0
  35. tryton/data/locale/it/LC_MESSAGES/tryton.po +31 -5
  36. tryton/data/locale/ja_JP/LC_MESSAGES/tryton.mo +0 -0
  37. tryton/data/locale/lo/LC_MESSAGES/tryton.mo +0 -0
  38. tryton/data/locale/lo/LC_MESSAGES/tryton.po +31 -5
  39. tryton/data/locale/lt/LC_MESSAGES/tryton.mo +0 -0
  40. tryton/data/locale/lt/LC_MESSAGES/tryton.po +32 -5
  41. tryton/data/locale/nl/LC_MESSAGES/tryton.mo +0 -0
  42. tryton/data/locale/nl/LC_MESSAGES/tryton.po +32 -4
  43. tryton/data/locale/pl/LC_MESSAGES/tryton.mo +0 -0
  44. tryton/data/locale/pl/LC_MESSAGES/tryton.po +32 -5
  45. tryton/data/locale/pt/LC_MESSAGES/tryton.mo +0 -0
  46. tryton/data/locale/pt/LC_MESSAGES/tryton.po +31 -5
  47. tryton/data/locale/ro/LC_MESSAGES/tryton.mo +0 -0
  48. tryton/data/locale/ro/LC_MESSAGES/tryton.po +43 -16
  49. tryton/data/locale/ru/LC_MESSAGES/tryton.mo +0 -0
  50. tryton/data/locale/ru/LC_MESSAGES/tryton.po +29 -5
  51. tryton/data/locale/sl/LC_MESSAGES/tryton.mo +0 -0
  52. tryton/data/locale/sl/LC_MESSAGES/tryton.po +32 -4
  53. tryton/data/locale/tr/LC_MESSAGES/tryton.mo +0 -0
  54. tryton/data/locale/tr/LC_MESSAGES/tryton.po +28 -3
  55. tryton/data/locale/uk/LC_MESSAGES/tryton.mo +0 -0
  56. tryton/data/locale/uk/LC_MESSAGES/tryton.po +32 -5
  57. tryton/data/locale/zh_CN/LC_MESSAGES/tryton.mo +0 -0
  58. tryton/data/locale/zh_CN/LC_MESSAGES/tryton.po +32 -4
  59. tryton/device_cookie.py +1 -1
  60. tryton/gui/main.py +3 -2
  61. tryton/gui/window/about.py +1 -1
  62. tryton/gui/window/dblogin.py +2 -2
  63. tryton/gui/window/email_.py +1 -1
  64. tryton/gui/window/form.py +4 -3
  65. tryton/gui/window/log.py +24 -2
  66. tryton/gui/window/view_form/model/field.py +56 -62
  67. tryton/gui/window/view_form/model/group.py +3 -1
  68. tryton/gui/window/view_form/model/record.py +55 -16
  69. tryton/gui/window/view_form/screen/screen.py +22 -22
  70. tryton/gui/window/view_form/view/calendar_gtk/calendar_.py +7 -12
  71. tryton/gui/window/view_form/view/form.py +4 -14
  72. tryton/gui/window/view_form/view/form_gtk/binary.py +3 -3
  73. tryton/gui/window/view_form/view/form_gtk/dictionary.py +33 -27
  74. tryton/gui/window/view_form/view/form_gtk/document.py +10 -9
  75. tryton/gui/window/view_form/view/form_gtk/many2many.py +17 -7
  76. tryton/gui/window/view_form/view/form_gtk/many2one.py +21 -13
  77. tryton/gui/window/view_form/view/form_gtk/one2many.py +25 -6
  78. tryton/gui/window/view_form/view/form_gtk/state_widget.py +6 -2
  79. tryton/gui/window/view_form/view/list.py +47 -56
  80. tryton/gui/window/view_form/view/list_gtk/widget.py +36 -23
  81. tryton/gui/window/view_form/view/screen_container.py +5 -3
  82. tryton/gui/window/win_export.py +1 -2
  83. tryton/gui/window/win_form.py +6 -8
  84. tryton/gui/window/wizard.py +11 -10
  85. tryton/jsonrpc.py +41 -27
  86. tryton/pyson.py +54 -4
  87. tryton/rpc.py +18 -0
  88. tryton/tests/test_common_domain_parser.py +0 -8
  89. {tryton-7.0.21.data → tryton-7.2.0.data}/scripts/tryton +1 -2
  90. {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/METADATA +6 -20
  91. {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/RECORD +94 -92
  92. {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/WHEEL +1 -1
  93. {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/LICENSE +0 -0
  94. {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/top_level.txt +0 -0
@@ -150,16 +150,17 @@ class Wizard(InfoBar):
150
150
 
151
151
  def end(self, callback=None):
152
152
  def end_callback(action):
153
- self.destroy(action=action())
154
- if callback:
155
- callback()
156
- try:
157
- RPCExecute('wizard', self.action, 'delete', self.session_id,
158
- process_exception=False, callback=end_callback)
159
- except Exception:
160
- logger.warn(
161
- "Unable to delete session %s of wizard %s",
162
- self.session_id, self.action, exc_info=True)
153
+ try:
154
+ self.destroy(action=action())
155
+ if callback:
156
+ callback()
157
+ except RPCException:
158
+ logger.warn(
159
+ "Unable to delete session %s of wizard %s",
160
+ self.session_id, self.action, exc_info=True)
161
+ RPCExecute(
162
+ 'wizard', self.action, 'delete', self.session_id,
163
+ process_exception=False, callback=end_callback)
163
164
 
164
165
  def clean(self):
165
166
  for widget in self.widget.get_children():
tryton/jsonrpc.py CHANGED
@@ -3,6 +3,7 @@
3
3
  import base64
4
4
  import datetime
5
5
  import errno
6
+ import gettext
6
7
  import hashlib
7
8
  import http.client
8
9
  import json
@@ -11,17 +12,20 @@ import socket
11
12
  import ssl
12
13
  import threading
13
14
  import xmlrpc.client
14
- from collections import defaultdict
15
15
  from contextlib import contextmanager
16
16
  from decimal import Decimal
17
17
  from functools import partial, reduce
18
18
  from urllib.parse import quote, urljoin
19
19
 
20
+ from .cache import CacheDict
21
+ from .config import CONFIG
22
+
20
23
  __all__ = ["ResponseError", "Fault", "ProtocolError", "Transport",
21
24
  "ServerProxy", "ServerPool"]
22
25
  CONNECT_TIMEOUT = 5
23
26
  DEFAULT_TIMEOUT = None
24
27
  logger = logging.getLogger(__name__)
28
+ _ = gettext.gettext
25
29
 
26
30
 
27
31
  def deepcopy(obj):
@@ -193,34 +197,38 @@ class Transport(xmlrpc.client.SafeTransport):
193
197
  ssl_ctx = ssl.create_default_context(cafile=self.__ca_certs)
194
198
 
195
199
  def http_connection():
196
- self._connection = host, http.client.HTTPConnection(chost,
197
- timeout=CONNECT_TIMEOUT)
198
- self._connection[1].connect()
199
- sock = self._connection[1].sock
200
- sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
201
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
200
+ connection = http.client.HTTPConnection(
201
+ chost, timeout=CONNECT_TIMEOUT)
202
+ self._connection = host, connection
203
+ connection.connect()
204
+ sock = connection.sock
205
+ if sock:
206
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
207
+ return connection
202
208
 
203
209
  def https_connection(allow_http=False):
204
- self._connection = host, http.client.HTTPSConnection(chost,
205
- timeout=CONNECT_TIMEOUT, context=ssl_ctx)
210
+ connection = http.client.HTTPSConnection(
211
+ chost, timeout=CONNECT_TIMEOUT, context=ssl_ctx)
212
+ self._connection = host, connection
206
213
  try:
207
- self._connection[1].connect()
208
- sock = self._connection[1].sock
209
- sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
210
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
211
- try:
212
- peercert = sock.getpeercert(True)
213
- except socket.error:
214
- peercert = None
214
+ connection.connect()
215
+ sock = connection.sock
216
+ if sock:
217
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
218
+ try:
219
+ peercert = sock.getpeercert(True)
220
+ except socket.error:
221
+ peercert = None
215
222
 
216
223
  def format_hash(value):
217
224
  return reduce(lambda x, y: x + y[1].upper()
218
225
  + ((y[0] % 2 and y[0] + 1 < len(value)) and ':' or ''),
219
226
  enumerate(value), '')
220
- return format_hash(hashlib.sha1(peercert).hexdigest())
227
+ return connection, format_hash(
228
+ hashlib.sha1(peercert).hexdigest())
221
229
  except (socket.error, ssl.SSLError, ssl.CertificateError):
222
230
  if allow_http:
223
- http_connection()
231
+ return http_connection(), None
224
232
  else:
225
233
  raise
226
234
 
@@ -228,17 +236,19 @@ class Transport(xmlrpc.client.SafeTransport):
228
236
  if (self.__fingerprints is not None
229
237
  and self.__fingerprints.exists(chost)):
230
238
  if self.__fingerprints.get(chost):
231
- fingerprint = https_connection()
239
+ connection, fingerprint = https_connection()
232
240
  else:
233
- http_connection()
241
+ connection = http_connection()
234
242
  else:
235
- fingerprint = https_connection(allow_http=True)
243
+ connection, fingerprint = https_connection(allow_http=True)
236
244
 
237
245
  if self.__fingerprints is not None:
238
246
  self.__fingerprints.set(chost, fingerprint)
239
- self._connection[1].timeout = DEFAULT_TIMEOUT
240
- self._connection[1].sock.settimeout(DEFAULT_TIMEOUT)
241
- return self._connection[1]
247
+ connection.timeout = DEFAULT_TIMEOUT
248
+ sock = connection.sock
249
+ if sock:
250
+ sock.settimeout(DEFAULT_TIMEOUT)
251
+ return connection
242
252
 
243
253
  @property
244
254
  def encode_threshold(self):
@@ -302,7 +312,8 @@ class ServerProxy(xmlrpc.client.ServerProxy):
302
312
  self.__transport.close()
303
313
  raise
304
314
  if response['id'] != id_:
305
- raise ResponseError('Invalid response id (%s) excpected %s' %
315
+ raise ResponseError(
316
+ _("Invalid response id (%s) expected %s") %
306
317
  (response['id'], id_))
307
318
  if response.get('error'):
308
319
  raise Fault(*response['error'])
@@ -396,7 +407,10 @@ class ServerPool(object):
396
407
  class _Cache:
397
408
 
398
409
  def __init__(self):
399
- self.store = defaultdict(dict)
410
+ cache_size = CONFIG['rpc.cache_size']
411
+ self.store = CacheDict(
412
+ cache_len=cache_size,
413
+ default_factory=lambda: CacheDict(cache_len=cache_size))
400
414
 
401
415
  def cached(self, prefix):
402
416
  return prefix in self.store
tryton/pyson.py CHANGED
@@ -9,6 +9,8 @@ from dateutil.relativedelta import relativedelta
9
9
 
10
10
 
11
11
  class PYSON(object):
12
+ _operator = None
13
+ _binary_operator = None
12
14
 
13
15
  def pyson(self):
14
16
  raise NotImplementedError
@@ -81,8 +83,17 @@ class PYSON(object):
81
83
  return In(k, self)
82
84
 
83
85
  def __repr__(self):
84
- klass = self.__class__.__name__
85
- return '%s(%s)' % (klass, ', '.join(map(repr, self.__repr_params__)))
86
+ params = self.__repr_params__
87
+ if self._operator and isinstance(params[0], PYSON):
88
+ return '%s.%s(%s)' % (
89
+ repr(params[0]), self._operator,
90
+ ', '.join(map(repr, params[1:])))
91
+ elif self._binary_operator and isinstance(params[0], PYSON):
92
+ return '(%s %s %s)' % (
93
+ repr(params[0]), self._binary_operator, repr(params[1]))
94
+ else:
95
+ klass = self.__class__.__name__
96
+ return '%s(%s)' % (klass, ', '.join(map(repr, params)))
86
97
 
87
98
  @property
88
99
  def __repr_params__(self):
@@ -137,7 +148,10 @@ class Eval(PYSON):
137
148
 
138
149
  @property
139
150
  def __repr_params__(self):
140
- return self._value, self._default
151
+ params = (self._value,)
152
+ if self._default != '':
153
+ params += (self._default,)
154
+ return params
141
155
 
142
156
  def pyson(self):
143
157
  return {
@@ -183,6 +197,17 @@ class Not(PYSON):
183
197
  v = bool(v)
184
198
  self._value = v
185
199
 
200
+ def __repr__(self):
201
+ if (isinstance(self._value, Equal)
202
+ and isinstance(self._value._statement1, PYSON)):
203
+ return '(%s != %s)' % (
204
+ repr(self._value._statement1),
205
+ repr(self._value._statement2))
206
+ elif isinstance(self._value, PYSON):
207
+ return '~%s' % repr(self._value)
208
+ else:
209
+ return super().__repr__()
210
+
186
211
  @property
187
212
  def __repr_params__(self):
188
213
  return (self._value,)
@@ -226,6 +251,8 @@ class Bool(PYSON):
226
251
 
227
252
 
228
253
  class And(PYSON):
254
+ _pyson_class = 'And'
255
+ _binary_operator = '&'
229
256
 
230
257
  def __init__(self, *statements, **kwargs):
231
258
  super(And, self).__init__()
@@ -258,6 +285,7 @@ class And(PYSON):
258
285
 
259
286
 
260
287
  class Or(And):
288
+ _binary_operator = '|'
261
289
 
262
290
  def pyson(self):
263
291
  res = super(Or, self).pyson()
@@ -270,6 +298,7 @@ class Or(And):
270
298
 
271
299
 
272
300
  class Equal(PYSON):
301
+ _binary_operator = '=='
273
302
 
274
303
  def __init__(self, s1, s2):
275
304
  statement1, statement2 = s1, s2
@@ -332,6 +361,10 @@ class Greater(PYSON):
332
361
  self._statement2 = statement2
333
362
  self._equal = equal
334
363
 
364
+ @property
365
+ def _binary_operator(self):
366
+ return '<=' if self._equal else '<'
367
+
335
368
  @property
336
369
  def __repr_params__(self):
337
370
  return (self._statement1, self._statement2, self._equal)
@@ -378,6 +411,10 @@ class Greater(PYSON):
378
411
 
379
412
  class Less(Greater):
380
413
 
414
+ @property
415
+ def _binary_operator(self):
416
+ return '>=' if self._equal else '>'
417
+
381
418
  def pyson(self):
382
419
  res = super(Less, self).pyson()
383
420
  res['__class__'] = 'Less'
@@ -440,6 +477,7 @@ class If(PYSON):
440
477
 
441
478
 
442
479
  class Get(PYSON):
480
+ _operator = 'get'
443
481
 
444
482
  def __init__(self, v, k, d=''):
445
483
  obj, key, default = v, k, d
@@ -458,7 +496,10 @@ class Get(PYSON):
458
496
 
459
497
  @property
460
498
  def __repr_params__(self):
461
- return (self._obj, self._key, self._default)
499
+ params = (self._obj, self._key)
500
+ if self._default != '':
501
+ params += (self._default,)
502
+ return params
462
503
 
463
504
  def pyson(self):
464
505
  return {
@@ -480,6 +521,7 @@ class Get(PYSON):
480
521
 
481
522
 
482
523
  class In(PYSON):
524
+ _operator = 'in_'
483
525
 
484
526
  def __init__(self, k, v):
485
527
  key, obj = k, v
@@ -508,6 +550,14 @@ class In(PYSON):
508
550
  self._key = key
509
551
  self._obj = obj
510
552
 
553
+ def __repr__(self):
554
+ params = self.__repr_params__
555
+ if isinstance(params[1], PYSON):
556
+ return '%s.contains(%s)' % (
557
+ repr(params[1]), ', '.join(map(repr, params[:1] + params[2:])))
558
+ else:
559
+ return super().__repr__()
560
+
511
561
  @property
512
562
  def __repr_params__(self):
513
563
  return (self._key, self._obj)
tryton/rpc.py CHANGED
@@ -145,6 +145,24 @@ def logout():
145
145
  _USER = None
146
146
 
147
147
 
148
+ def reset_password():
149
+ from tryton import common
150
+ host = CONFIG['login.host']
151
+ hostname = common.get_hostname(host)
152
+ port = common.get_port(host)
153
+ database = CONFIG['login.db']
154
+ username = CONFIG['login.login']
155
+ language = CONFIG['client.lang']
156
+ if not all([host, database, username]):
157
+ return
158
+ try:
159
+ connection = ServerProxy(hostname, port, database)
160
+ logger.info('common.db.reset_password(%s, %s)', (username, language))
161
+ connection.common.db.reset_password(username, language)
162
+ except Fault as exception:
163
+ logger.debug(exception.faultCode)
164
+
165
+
148
166
  def execute(*args):
149
167
  global CONNECTION, _USER
150
168
  if CONNECTION is None:
@@ -592,8 +592,6 @@ class DomainParserTestCase(TestCase):
592
592
  })
593
593
  valid = ('name', '=', 'Doe')
594
594
  invalid = ('surname', '=', 'John')
595
- self.assertTrue(dom.stringable([]))
596
- self.assertTrue(dom.stringable([[]]))
597
595
  self.assertTrue(dom.stringable([valid]))
598
596
  self.assertFalse(dom.stringable([invalid]))
599
597
  self.assertTrue(dom.stringable(['AND', valid]))
@@ -681,15 +679,9 @@ class DomainParserTestCase(TestCase):
681
679
  dom.string([('name', 'not ilike', '%Doe%')]), 'Name: !Doe')
682
680
  self.assertEqual(
683
681
  dom.string([('name', 'in', ['John', 'Jane'])]), 'Name: John;Jane')
684
- self.assertEqual(
685
- dom.string([('name', 'in', ['John', ''])]), 'Name: John;""')
686
- self.assertEqual(
687
- dom.string([('name', 'in', ['John'])]), 'Name: =John')
688
682
  self.assertEqual(
689
683
  dom.string([('name', 'not in', ['John', 'Jane'])]),
690
684
  'Name: !John;Jane')
691
- self.assertEqual(
692
- dom.string([('name', 'not in', ['John'])]), 'Name: !=John')
693
685
  self.assertEqual(
694
686
  dom.string([
695
687
  ('name', 'ilike', '%Doe%'),
@@ -15,8 +15,7 @@ if hasattr(sys, 'frozen'):
15
15
  share = os.path.join(prefix, 'share')
16
16
  os.environ['GTK_EXE_PREFIX'] = prefix
17
17
  os.environ['GTK_DATA_PREFIX'] = prefix
18
- os.environ['EV_BACKENDS_DIR'] = os.path.join(
19
- prefix, 'lib', 'evince', '4', 'backends')
18
+ os.environ['EV_BACKENDS_DIR'] = prefix
20
19
  os.environ['XDG_DATA_DIRS'] = share
21
20
  os.environ['GDK_PIXBUF_MODULE_FILE'] = os.path.join(
22
21
  share, 'gtk-3.0', 'gdk-pixbuf.loaders')
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.1
2
2
  Name: tryton
3
- Version: 7.0.21
3
+ Version: 7.2.0
4
4
  Summary: Tryton desktop client
5
5
  Home-page: http://www.tryton.org/
6
- Download-URL: http://downloads.tryton.org/7.0/
6
+ Download-URL: http://downloads.tryton.org/7.2/
7
7
  Author: Tryton
8
8
  Author-email: foundation@tryton.org
9
9
  License: GPL-3
@@ -52,25 +52,11 @@ Requires-Python: >=3.8
52
52
  License-File: LICENSE
53
53
  Requires-Dist: pycairo
54
54
  Requires-Dist: python-dateutil
55
- Requires-Dist: PyGObject>=3.19
55
+ Requires-Dist: PyGObject >=3.19
56
56
  Provides-Extra: calendar
57
- Requires-Dist: GooCalendar>=0.7; extra == "calendar"
57
+ Requires-Dist: GooCalendar >=0.7 ; extra == 'calendar'
58
58
  Provides-Extra: sound
59
- Requires-Dist: playsound; extra == "sound"
60
- Dynamic: author
61
- Dynamic: author-email
62
- Dynamic: classifier
63
- Dynamic: description
64
- Dynamic: download-url
65
- Dynamic: home-page
66
- Dynamic: keywords
67
- Dynamic: license
68
- Dynamic: platform
69
- Dynamic: project-url
70
- Dynamic: provides-extra
71
- Dynamic: requires-dist
72
- Dynamic: requires-python
73
- Dynamic: summary
59
+ Requires-Dist: playsound ; extra == 'sound'
74
60
 
75
61
  tryton
76
62
  ======