tryton 6.6.8__py3-none-any.whl → 6.8.1__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 (92) hide show
  1. tryton/__init__.py +1 -1
  2. tryton/action/main.py +32 -43
  3. tryton/bus.py +2 -0
  4. tryton/client.py +3 -0
  5. tryton/common/button.py +3 -1
  6. tryton/common/common.py +55 -43
  7. tryton/common/datetime_.py +14 -2
  8. tryton/common/domain_inversion.py +10 -10
  9. tryton/common/domain_parser.py +5 -2
  10. tryton/common/popup_menu.py +7 -0
  11. tryton/common/selection.py +3 -1
  12. tryton/config.py +22 -5
  13. tryton/data/locale/bg/LC_MESSAGES/tryton.mo +0 -0
  14. tryton/data/locale/bg/LC_MESSAGES/tryton.po +45 -39
  15. tryton/data/locale/ca/LC_MESSAGES/tryton.mo +0 -0
  16. tryton/data/locale/ca/LC_MESSAGES/tryton.po +41 -35
  17. tryton/data/locale/cs/LC_MESSAGES/tryton.mo +0 -0
  18. tryton/data/locale/cs/LC_MESSAGES/tryton.po +46 -39
  19. tryton/data/locale/de/LC_MESSAGES/tryton.mo +0 -0
  20. tryton/data/locale/de/LC_MESSAGES/tryton.po +41 -35
  21. tryton/data/locale/es/LC_MESSAGES/tryton.mo +0 -0
  22. tryton/data/locale/es/LC_MESSAGES/tryton.po +41 -35
  23. tryton/data/locale/es_419/LC_MESSAGES/tryton.mo +0 -0
  24. tryton/data/locale/es_419/LC_MESSAGES/tryton.po +171 -167
  25. tryton/data/locale/et/LC_MESSAGES/tryton.mo +0 -0
  26. tryton/data/locale/et/LC_MESSAGES/tryton.po +47 -39
  27. tryton/data/locale/fa/LC_MESSAGES/tryton.mo +0 -0
  28. tryton/data/locale/fa/LC_MESSAGES/tryton.po +46 -38
  29. tryton/data/locale/fi/LC_MESSAGES/tryton.mo +0 -0
  30. tryton/data/locale/fi/LC_MESSAGES/tryton.po +38 -32
  31. tryton/data/locale/fr/LC_MESSAGES/tryton.mo +0 -0
  32. tryton/data/locale/fr/LC_MESSAGES/tryton.po +42 -36
  33. tryton/data/locale/hu/LC_MESSAGES/tryton.mo +0 -0
  34. tryton/data/locale/hu/LC_MESSAGES/tryton.po +44 -34
  35. tryton/data/locale/id/LC_MESSAGES/tryton.mo +0 -0
  36. tryton/data/locale/id/LC_MESSAGES/tryton.po +40 -34
  37. tryton/data/locale/it/LC_MESSAGES/tryton.mo +0 -0
  38. tryton/data/locale/it/LC_MESSAGES/tryton.po +44 -34
  39. tryton/data/locale/ja_JP/LC_MESSAGES/tryton.mo +0 -0
  40. tryton/data/locale/lo/LC_MESSAGES/tryton.mo +0 -0
  41. tryton/data/locale/lo/LC_MESSAGES/tryton.po +46 -38
  42. tryton/data/locale/lt/LC_MESSAGES/tryton.mo +0 -0
  43. tryton/data/locale/lt/LC_MESSAGES/tryton.po +47 -37
  44. tryton/data/locale/nl/LC_MESSAGES/tryton.mo +0 -0
  45. tryton/data/locale/nl/LC_MESSAGES/tryton.po +41 -35
  46. tryton/data/locale/pl/LC_MESSAGES/tryton.mo +0 -0
  47. tryton/data/locale/pl/LC_MESSAGES/tryton.po +45 -35
  48. tryton/data/locale/pt/LC_MESSAGES/tryton.mo +0 -0
  49. tryton/data/locale/pt/LC_MESSAGES/tryton.po +46 -38
  50. tryton/data/locale/ro/LC_MESSAGES/tryton.mo +0 -0
  51. tryton/data/locale/ro/LC_MESSAGES/tryton.po +50 -48
  52. tryton/data/locale/ru/LC_MESSAGES/tryton.mo +0 -0
  53. tryton/data/locale/ru/LC_MESSAGES/tryton.po +45 -39
  54. tryton/data/locale/sl/LC_MESSAGES/tryton.mo +0 -0
  55. tryton/data/locale/sl/LC_MESSAGES/tryton.po +47 -38
  56. tryton/data/locale/tr/LC_MESSAGES/tryton.mo +0 -0
  57. tryton/data/locale/tr/LC_MESSAGES/tryton.po +39 -33
  58. tryton/data/locale/uk/LC_MESSAGES/tryton.mo +0 -0
  59. tryton/data/locale/uk/LC_MESSAGES/tryton.po +43 -35
  60. tryton/data/locale/zh_CN/LC_MESSAGES/tryton.mo +0 -0
  61. tryton/data/locale/zh_CN/LC_MESSAGES/tryton.po +45 -35
  62. tryton/data/pixmaps/tryton/tryton-icon.svg +1 -0
  63. tryton/gui/main.py +54 -61
  64. tryton/gui/window/dblogin.py +27 -10
  65. tryton/gui/window/form.py +21 -53
  66. tryton/gui/window/infobar.py +9 -4
  67. tryton/gui/window/log.py +95 -0
  68. tryton/gui/window/view_board/action.py +0 -4
  69. tryton/gui/window/view_form/model/field.py +36 -14
  70. tryton/gui/window/view_form/model/record.py +22 -9
  71. tryton/gui/window/view_form/screen/screen.py +45 -76
  72. tryton/gui/window/view_form/view/calendar_.py +24 -11
  73. tryton/gui/window/view_form/view/calendar_gtk/toolbar.py +6 -5
  74. tryton/gui/window/view_form/view/form.py +14 -5
  75. tryton/gui/window/view_form/view/form_gtk/many2many.py +10 -1
  76. tryton/gui/window/view_form/view/form_gtk/many2one.py +1 -0
  77. tryton/gui/window/view_form/view/form_gtk/one2many.py +7 -7
  78. tryton/gui/window/view_form/view/form_gtk/textbox.py +0 -2
  79. tryton/gui/window/view_form/view/form_gtk/widget.py +8 -10
  80. tryton/gui/window/view_form/view/list_form.py +61 -5
  81. tryton/gui/window/view_form/view/list_gtk/editabletree.py +13 -3
  82. tryton/gui/window/view_form/view/list_gtk/widget.py +97 -27
  83. tryton/gui/window/win_form.py +6 -5
  84. tryton/rpc.py +13 -15
  85. tryton/tests/test_common.py +46 -0
  86. tryton/tests/test_common_domain_parser.py +24 -24
  87. {tryton-6.6.8.dist-info → tryton-6.8.1.dist-info}/METADATA +6 -6
  88. {tryton-6.6.8.dist-info → tryton-6.8.1.dist-info}/RECORD +92 -89
  89. {tryton-6.6.8.data → tryton-6.8.1.data}/scripts/tryton +0 -0
  90. {tryton-6.6.8.dist-info → tryton-6.8.1.dist-info}/LICENSE +0 -0
  91. {tryton-6.6.8.dist-info → tryton-6.8.1.dist-info}/WHEEL +0 -0
  92. {tryton-6.6.8.dist-info → tryton-6.8.1.dist-info}/top_level.txt +0 -0
@@ -2,6 +2,7 @@
2
2
  # this repository contains the full copyright notices and license terms.
3
3
  import datetime
4
4
  import gettext
5
+ import logging
5
6
  import os
6
7
  import webbrowser
7
8
  from functools import partial, wraps
@@ -31,6 +32,7 @@ from tryton.gui.window.win_form import WinForm
31
32
  from tryton.gui.window.win_search import WinSearch
32
33
 
33
34
  _ = gettext.gettext
35
+ logger = logging.getLogger(__name__)
34
36
 
35
37
  COLORS = {n: v for n, v in zip(
36
38
  ['muted', 'success', 'warning', 'danger'],
@@ -50,6 +52,23 @@ def send_keys(renderer, editable, position, treeview):
50
52
  editable.connect('changed', changed)
51
53
 
52
54
 
55
+ def catch_errors(error_value=_('#ERROR')):
56
+ def decorator(func):
57
+ @wraps(func)
58
+ def wrapper(self, record):
59
+ if record.exception:
60
+ return error_value
61
+ try:
62
+ return func(self, record)
63
+ except Exception:
64
+ logger.error(
65
+ f"Error calling {func.__name__} for {self} with {record}",
66
+ exc_info=True)
67
+ return error_value
68
+ return wrapper
69
+ return decorator
70
+
71
+
53
72
  def realized(func):
54
73
 
55
74
  @wraps(func)
@@ -143,7 +162,7 @@ class Cell(object):
143
162
  if not store:
144
163
  store = self.view.treeview.get_model()
145
164
  record = store.get_value(iter_, 0)
146
- field = record[self.attrs['name']]
165
+ field = record.group.fields[self.attrs['name']]
147
166
  return record, field
148
167
 
149
168
  def _set_visual(self, cell, record):
@@ -330,9 +349,9 @@ class GenericText(Cell):
330
349
  callback=None):
331
350
  raise NotImplementedError
332
351
 
352
+ @catch_errors()
333
353
  def get_textual_value(self, record):
334
- if not record:
335
- return ''
354
+ record.load(self.attrs['name'], process_exception=False)
336
355
  return record[self.attrs['name']].get_client(record)
337
356
 
338
357
  def value_from_text(self, record, text, callback=None):
@@ -396,9 +415,9 @@ class Int(GenericText):
396
415
  return [self.renderer_suffix]
397
416
  return []
398
417
 
418
+ @catch_errors()
399
419
  def get_textual_value(self, record):
400
- if not record:
401
- return ''
420
+ record.load(self.attrs['name'], process_exception=False)
402
421
  return record[self.attrs['name']].get_client(
403
422
  record, factor=self.factor, grouping=self.grouping)
404
423
 
@@ -464,9 +483,9 @@ class Date(GenericText):
464
483
  else:
465
484
  return '%x'
466
485
 
486
+ @catch_errors()
467
487
  def get_textual_value(self, record):
468
- if not record:
469
- return ''
488
+ record.load(self.attrs['name'], process_exception=False)
470
489
  value = record[self.attrs['name']].get_client(record)
471
490
  if value:
472
491
  return value.strftime(self.renderer.props.format)
@@ -487,9 +506,9 @@ class Time(Date):
487
506
  else:
488
507
  return '%X'
489
508
 
509
+ @catch_errors()
490
510
  def get_textual_value(self, record):
491
- if not record:
492
- return ''
511
+ record.load(self.attrs['name'], process_exception=False)
493
512
  value = record[self.attrs['name']].get_client(record)
494
513
  if value is not None:
495
514
  if isinstance(value, datetime.datetime):
@@ -549,7 +568,9 @@ class Binary(GenericText):
549
568
  def suffixes(self):
550
569
  return [self.renderer_save, self.renderer_select]
551
570
 
571
+ @catch_errors()
552
572
  def get_textual_value(self, record):
573
+ record.load(self.attrs['name'], process_exception=False)
553
574
  field = record[self.attrs['name']]
554
575
  if hasattr(field, 'get_size'):
555
576
  size = field.get_size(record)
@@ -604,6 +625,11 @@ class _BinaryIcon(Cell):
604
625
  def view(self):
605
626
  return self.binary.view
606
627
 
628
+ @catch_errors(False)
629
+ def _fetch_data(self, record):
630
+ record.fetch(self.attrs['name'], process_exception=False)
631
+ return True
632
+
607
633
 
608
634
  class _BinarySave(_BinaryIcon):
609
635
  icon_name = 'tryton-save'
@@ -631,6 +657,9 @@ class _BinarySave(_BinaryIcon):
631
657
  @CellCache.cache
632
658
  def setter(self, column, cell, store, iter_, user_data=None):
633
659
  record, field = self._get_record_field_from_iter(iter_, store)
660
+ if not self._fetch_data(record):
661
+ cell.set_property('visible', False)
662
+ return
634
663
  if hasattr(field, 'get_size'):
635
664
  size = field.get_size(record)
636
665
  else:
@@ -671,6 +700,9 @@ class _BinarySelect(_BinaryIcon):
671
700
  @CellCache.cache
672
701
  def setter(self, column, cell, store, iter_, user_data=None):
673
702
  record, field = self._get_record_field_from_iter(iter_, store)
703
+ if not self._fetch_data(record):
704
+ cell.set_property('visible', False)
705
+ return
674
706
  if hasattr(field, 'get_size'):
675
707
  size = field.get_size(record)
676
708
  else:
@@ -710,6 +742,9 @@ class _BinaryOpen(_BinarySave):
710
742
  def setter(self, column, cell, store, iter_, user_data=None):
711
743
  super().setter(column, cell, store, iter_)
712
744
  record, field = self._get_record_field_from_iter(iter_, store)
745
+ if not self._fetch_data(record):
746
+ cell.set_property('visible', False)
747
+ return
713
748
  filename_field = record.group.fields.get(self.attrs.get('filename'))
714
749
  filename = filename_field.get(record)
715
750
  if not filename:
@@ -731,22 +766,33 @@ class Image(GenericText):
731
766
  @CellCache.cache
732
767
  def setter(self, column, cell, store, iter_, user_data=None):
733
768
  record, field = self._get_record_field_from_iter(iter_, store)
769
+ value = self._get_data(record)
770
+ if value is None:
771
+ cell.set_property('pixbuf', None)
772
+ return
773
+ pixbuf = data2pixbuf(value)
774
+ if pixbuf:
775
+ pixbuf = common.resize_pixbuf(pixbuf, self.width, self.height)
776
+ cell.set_property('pixbuf', pixbuf)
777
+ self._set_visual(cell, record)
778
+
779
+ @catch_errors(None)
780
+ def _get_data(self, record):
781
+ record.load(self.attrs['name'], process_exception=False)
782
+ field = record[self.attrs['name']]
734
783
  value = field.get_client(record)
735
784
  if isinstance(value, int):
736
785
  if value > CONFIG['image.max_size']:
737
786
  value = None
738
787
  else:
739
788
  value = field.get_data(record)
740
- pixbuf = data2pixbuf(value)
741
- if pixbuf:
742
- pixbuf = common.resize_pixbuf(pixbuf, self.width, self.height)
743
- cell.set_property('pixbuf', pixbuf)
744
- self._set_visual(cell, record)
789
+ return value
745
790
 
791
+ @catch_errors()
746
792
  def get_textual_value(self, record):
747
- if not record:
748
- return ''
749
- return str(record[self.attrs['name']].get_size(record))
793
+ record.load(self.attrs['name'], process_exception=False)
794
+ field = record[self.attrs['name']]
795
+ return str(field.get_size(record))
750
796
 
751
797
 
752
798
  class M2O(GenericText):
@@ -974,9 +1020,11 @@ class O2O(M2O):
974
1020
  class O2M(GenericText):
975
1021
  align = 0.5
976
1022
 
1023
+ @catch_errors()
977
1024
  def get_textual_value(self, record):
978
- return '( ' + str(len(record[self.attrs['name']]
979
- .get_eval(record))) + ' )'
1025
+ record.load(self.attrs['name'], process_exception=False)
1026
+ field = record[self.attrs['name']]
1027
+ return '( ' + str(len(field.get_eval(record))) + ' )'
980
1028
 
981
1029
  def value_from_text(self, record, text, callback=None):
982
1030
  if callback:
@@ -1045,16 +1093,24 @@ class Selection(GenericText, SelectionMixin, PopdownMixin):
1045
1093
  if 'renderer' not in kwargs:
1046
1094
  kwargs['renderer'] = CellRendererCombo
1047
1095
  super(Selection, self).__init__(*args, **kwargs)
1048
- self.init_selection()
1049
- # Use a variable let Python holding reference when calling set_property
1050
- model = self.get_popdown_model(self.selection)[0]
1051
- self.renderer.set_property('model', model)
1052
- self.renderer.set_property('text-column', 0)
1096
+ if self.view and self.view.editable:
1097
+ self.init_selection()
1098
+ # Use a variable let Python holding reference when calling
1099
+ # set_property
1100
+ model = self.get_popdown_model(self.selection)[0]
1101
+ self.renderer.set_property('model', model)
1102
+ self.renderer.set_property('text-column', 0)
1053
1103
 
1054
1104
  def get_value(self, record, field):
1055
1105
  return field.get(record)
1056
1106
 
1107
+ @catch_errors()
1057
1108
  def get_textual_value(self, record):
1109
+ related = self.attrs['name'] + ':string'
1110
+ if not self.view.editable and related in record.value:
1111
+ return record.value[related]
1112
+
1113
+ record.load(self.attrs['name'], process_exception=False)
1058
1114
  field = record[self.attrs['name']]
1059
1115
  self.update_selection(record, field)
1060
1116
  value = self.get_value(record, field)
@@ -1119,7 +1175,8 @@ class MultiSelection(GenericText, SelectionMixin):
1119
1175
 
1120
1176
  def __init__(self, *args, **kwargs):
1121
1177
  super().__init__(*args, **kwargs)
1122
- self.init_selection()
1178
+ if self.view and self.view.editable:
1179
+ self.init_selection()
1123
1180
 
1124
1181
  @realized
1125
1182
  @CellCache.cache
@@ -1131,7 +1188,13 @@ class MultiSelection(GenericText, SelectionMixin):
1131
1188
  if callback:
1132
1189
  callback()
1133
1190
 
1191
+ @catch_errors()
1134
1192
  def get_textual_value(self, record):
1193
+ related = self.attrs['name'] + ':string'
1194
+ if not self.view.editable and related in record.value:
1195
+ return ";".join(record.value[related])
1196
+
1197
+ record.load(self.attrs['name'], process_exception=False)
1135
1198
  field = record[self.attrs['name']]
1136
1199
  self.update_selection(record, field)
1137
1200
  selection = dict(self.selection)
@@ -1180,6 +1243,7 @@ class Reference(M2O):
1180
1243
  _, value = value.split(',')
1181
1244
  return int(value)
1182
1245
 
1246
+ @catch_errors()
1183
1247
  def get_textual_value(self, record):
1184
1248
  value = super().get_textual_value(record)
1185
1249
  if value:
@@ -1221,8 +1285,11 @@ class Dict(GenericText):
1221
1285
  super().setter(column, cell, store, iter_, user_data=None)
1222
1286
  cell.props.editable = False
1223
1287
 
1288
+ @catch_errors()
1224
1289
  def get_textual_value(self, record):
1225
- return '(%s)' % len(record[self.attrs['name']].get_client(record))
1290
+ record.load(self.attrs['name'], process_exception=False)
1291
+ field = record[self.attrs['name']]
1292
+ return '(%s)' % len(field.get_client(record))
1226
1293
 
1227
1294
 
1228
1295
  class ProgressBar(Cell):
@@ -1264,8 +1331,11 @@ class ProgressBar(Cell):
1264
1331
  callback=None):
1265
1332
  raise NotImplementedError
1266
1333
 
1334
+ @catch_errors()
1267
1335
  def get_textual_value(self, record):
1268
- return record[self.attrs['name']].get_client(record, factor=100) or ''
1336
+ record.load(self.attrs['name'], process_exception=False)
1337
+ field = record[self.attrs['name']]
1338
+ return field.get_client(record, factor=100) or ''
1269
1339
 
1270
1340
  def value_from_text(self, record, text, callback=None):
1271
1341
  field = record[self.attrs['name']]
@@ -21,7 +21,7 @@ _ = gettext.gettext
21
21
  class WinForm(NoModal, InfoBar):
22
22
  "Form window"
23
23
 
24
- def __init__(self, screen, callback, view_type='form',
24
+ def __init__(self, screen, callback=None, view_type='form',
25
25
  new=False, many=0, domain=None, context=None,
26
26
  save_current=False, title='', rec_name=None):
27
27
  tooltips = common.Tooltips()
@@ -345,7 +345,7 @@ class WinForm(NoModal, InfoBar):
345
345
  win.show()
346
346
 
347
347
  def record_message(self, position, size, *args):
348
- self.activate_save()
348
+ self.set_buttons_sensitive()
349
349
  if self.view_type != 'tree':
350
350
  return
351
351
  name = '_'
@@ -380,10 +380,10 @@ class WinForm(NoModal, InfoBar):
380
380
  self.label.set_text(line)
381
381
 
382
382
  def record_modified(self, *args):
383
- self.activate_save()
383
+ self.set_buttons_sensitive()
384
384
  self.info_bar_refresh()
385
385
 
386
- def activate_save(self):
386
+ def set_buttons_sensitive(self):
387
387
  modified = self.screen.modified()
388
388
  # Keep sensible as change could have been trigger by a Many2One edition
389
389
  sensitive = modified or self.but_ok.props.sensitive
@@ -448,7 +448,8 @@ class WinForm(NoModal, InfoBar):
448
448
  result = False
449
449
  else:
450
450
  result = response_id not in cancel_responses
451
- self.callback(result)
451
+ if self.callback:
452
+ self.callback(result)
452
453
  self.destroy()
453
454
 
454
455
  def new(self):
tryton/rpc.py CHANGED
@@ -45,12 +45,12 @@ context_reset()
45
45
  def db_list(host, port):
46
46
  try:
47
47
  connection = ServerProxy(host, port)
48
- logging.getLogger(__name__).info('common.db.list()')
48
+ logger.info('common.db.list()')
49
49
  result = connection.common.db.list()
50
- logging.getLogger(__name__).debug(repr(result))
50
+ logger.debug('%r', result)
51
51
  return result
52
52
  except Fault as exception:
53
- logging.getLogger(__name__).debug(exception.faultCode)
53
+ logger.debug(exception.faultCode)
54
54
  if exception.faultCode == str(HTTPStatus.FORBIDDEN.value):
55
55
  return []
56
56
  else:
@@ -60,13 +60,12 @@ def db_list(host, port):
60
60
  def server_version(host, port):
61
61
  try:
62
62
  connection = ServerProxy(host, port)
63
- logging.getLogger(__name__).info(
64
- 'common.server.version(None, None)')
63
+ logger.info('common.server.version(None, None)')
65
64
  result = connection.common.server.version()
66
- logging.getLogger(__name__).debug(repr(result))
65
+ logger.debug('%r', result)
67
66
  return result
68
67
  except Exception as e:
69
- logging.getLogger(__name__).error(e)
68
+ logger.exception(e)
70
69
  return None
71
70
 
72
71
 
@@ -75,10 +74,10 @@ def authentication_services(host, port):
75
74
  connection = ServerProxy(host, port)
76
75
  logger.info('common.authentication.services()')
77
76
  services = connection.common.authentication.services()
78
- logger.debug(repr(services))
77
+ logger.debug('%r', services)
79
78
  return connection.url, services
80
79
  except Exception as e:
81
- logger.error(e)
80
+ logger.exception(e)
82
81
  return '', []
83
82
 
84
83
 
@@ -119,10 +118,9 @@ def login(parameters):
119
118
  language = CONFIG['client.lang']
120
119
  parameters['device_cookie'] = device_cookie.get()
121
120
  connection = ServerProxy(hostname, port, database)
122
- logging.getLogger(__name__).info('common.db.login(%s, %s, %s)'
123
- % (username, 'x' * 10, language))
121
+ logger.info('common.db.login(%s, %s, %s)', username, 'x' * 10, language)
124
122
  result = connection.common.db.login(username, parameters, language)
125
- logging.getLogger(__name__).debug(repr(result))
123
+ logger.debug('%r', result)
126
124
  _USER = result[0]
127
125
  session = ':'.join(map(str, [username] + result))
128
126
  if CONNECTION is not None:
@@ -137,7 +135,7 @@ def logout():
137
135
  global CONNECTION, _USER
138
136
  if CONNECTION is not None:
139
137
  try:
140
- logging.getLogger(__name__).info('common.db.logout()')
138
+ logger.info('common.db.logout()')
141
139
  with CONNECTION() as conn:
142
140
  conn.common.db.logout()
143
141
  except (Fault, socket.error, http.client.CannotSendRequest):
@@ -154,12 +152,12 @@ def execute(*args):
154
152
  try:
155
153
  name = '.'.join(args[:3])
156
154
  args = args[3:]
157
- logging.getLogger(__name__).info('%s%s' % (name, args))
155
+ logger.info('%s%r', name, args)
158
156
  with CONNECTION() as conn:
159
157
  result = getattr(conn, name)(*args)
160
158
  except (http.client.CannotSendRequest, socket.error) as exception:
161
159
  raise TrytonServerUnavailable(*exception.args)
162
- logging.getLogger(__name__).debug(repr(result))
160
+ logger.debug('%r', result)
163
161
  return result
164
162
 
165
163
 
@@ -0,0 +1,46 @@
1
+ # This file is part of Tryton. The COPYRIGHT file at the top level of
2
+ # this repository contains the full copyright notices and license terms.
3
+
4
+ from unittest import TestCase
5
+
6
+ from tryton.common import humanize
7
+
8
+
9
+ class Humanize(TestCase):
10
+ "Test humanize"
11
+
12
+ def test_humanize(self):
13
+ "Test humanize"
14
+ for value, text in [
15
+ (0, '0'),
16
+ (1, '1'),
17
+ (5, '5'),
18
+ (10, '10'),
19
+ (50, '50'),
20
+ (100, '100'),
21
+ (1000, '1000'),
22
+ (1001, '1k'),
23
+ (1500, '1.5k'),
24
+ (1000000, '1000k'),
25
+ (1000001, '1M'),
26
+ (1010000, '1.01M'),
27
+ (10**33, '1000Q'),
28
+ (0.1, '0.1'),
29
+ (0.5, '0.5'),
30
+ (0.01, '0.01'),
31
+ (0.05, '0.05'),
32
+ (0.001, '1m'),
33
+ (0.0001, '0.1m'),
34
+ (0.000001, '1µ'),
35
+ (0.0000015, '1.5µ'),
36
+ (0.00000105, '1.05µ'),
37
+ (0.000001001, '1µ'),
38
+ (10**-33, '0.001q'),
39
+ ]:
40
+ with self.subTest(value=value):
41
+ self.assertEqual(humanize(value), text)
42
+ if value:
43
+ value *= -1
44
+ text = '-' + text
45
+ with self.subTest(value=value):
46
+ self.assertEqual(humanize(value), text)
@@ -529,31 +529,31 @@ class DomainParserTestCase(TestCase):
529
529
  double_null_ = ('e', None, None)
530
530
  for value, result in (
531
531
  (['a'], ['a']),
532
- (['a', 'or', 'b'], [['OR', 'a', 'b']]),
533
- (['a', 'or', 'b', 'or', 'c'], [['OR', ['OR', 'a', 'b'], 'c']]),
534
- (['a', 'b', 'or', 'c'], ['a', ['OR', 'b', 'c']]),
535
- (['a', 'or', 'b', 'c'], [['OR', 'a', 'b'], 'c']),
532
+ (['a', '|', 'b'], [['OR', 'a', 'b']]),
533
+ (['a', '|', 'b', '|', 'c'], [['OR', ['OR', 'a', 'b'], 'c']]),
534
+ (['a', 'b', '|', 'c'], ['a', ['OR', 'b', 'c']]),
535
+ (['a', '|', 'b', 'c'], [['OR', 'a', 'b'], 'c']),
536
536
  (['a', iter(['b', 'c'])], ['a', ['b', 'c']]),
537
537
  (['a', iter(['b', 'c']), 'd'], ['a', ['b', 'c'], 'd']),
538
- (['a', 'or', iter(['b', 'c'])], [['OR', 'a', ['b', 'c']]]),
539
- (['a', 'or', iter(['b', 'c']), 'd'],
538
+ (['a', '|', iter(['b', 'c'])], [['OR', 'a', ['b', 'c']]]),
539
+ (['a', '|', iter(['b', 'c']), 'd'],
540
540
  [['OR', 'a', ['b', 'c']], 'd']),
541
- (['a', iter(['b', 'c']), 'or', 'd'],
541
+ (['a', iter(['b', 'c']), '|', 'd'],
542
542
  ['a', ['OR', ['b', 'c'], 'd']]),
543
- (['a', 'or', iter(['b', 'or', 'c'])],
543
+ (['a', '|', iter(['b', '|', 'c'])],
544
544
  [['OR', 'a', [['OR', 'b', 'c']]]]),
545
- (['or'], []),
546
- (['or', 'a'], ['a']),
547
- (['a', iter(['or', 'b'])], ['a', ['b']]),
548
- (['a', 'or', 'or', 'b'], [['OR', 'a', 'b']]),
549
- (['or', 'or', 'a'], ['a']),
550
- (['or', 'or', 'a', 'b'], ['a', 'b']),
551
- (['or', 'or', 'a', 'or', 'b'], [['OR', 'a', 'b']]),
552
- (['a', iter(['b', 'or', 'c'])], ['a', [['OR', 'b', 'c']]]),
553
- ([a, iter([b, ('or',), c])], [a, [['OR', b, c]]]),
554
- (['a', iter(['b', 'or'])], ['a', [['OR', 'b']]]),
545
+ (['|'], []),
546
+ (['|', 'a'], ['a']),
547
+ (['a', iter(['|', 'b'])], ['a', ['b']]),
548
+ (['a', '|', '|', 'b'], [['OR', 'a', 'b']]),
549
+ (['|', '|', 'a'], ['a']),
550
+ (['|', '|', 'a', 'b'], ['a', 'b']),
551
+ (['|', '|', 'a', '|', 'b'], [['OR', 'a', 'b']]),
552
+ (['a', iter(['b', '|', 'c'])], ['a', [['OR', 'b', 'c']]]),
553
+ ([a, iter([b, ('|',), c])], [a, [['OR', b, c]]]),
554
+ (['a', iter(['b', '|'])], ['a', [['OR', 'b']]]),
555
555
  ([null_], [null_]),
556
- ([null_, 'or', double_null_], [['OR', null_, double_null_]]),
556
+ ([null_, '|', double_null_], [['OR', null_, double_null_]]),
557
557
  ):
558
558
  self.assertEqual(
559
559
  rlist(operatorize(iter(value))), result,
@@ -696,14 +696,14 @@ class DomainParserTestCase(TestCase):
696
696
  dom.string(['OR',
697
697
  ('name', 'ilike', '%Doe%'),
698
698
  ('name', 'ilike', '%Jane%')]),
699
- 'Name: Doe or Name: Jane')
699
+ 'Name: Doe | Name: Jane')
700
700
  self.assertEqual(
701
701
  dom.string([
702
702
  ('name', 'ilike', '%Doe%'),
703
703
  ['OR',
704
704
  ('name', 'ilike', '%John%'),
705
705
  ('name', 'ilike', '%Jane%')]]),
706
- 'Name: Doe (Name: John or Name: Jane)')
706
+ 'Name: Doe (Name: John | Name: Jane)')
707
707
  self.assertEqual(dom.string([]), '')
708
708
  self.assertEqual(
709
709
  dom.string([('surname', 'ilike', '%Doe%')]), '"(Sur)Name": Doe')
@@ -1121,10 +1121,10 @@ class DomainParserTestCase(TestCase):
1121
1121
  self.assertEqual(list(dom.completion('Name: !=foo')), [])
1122
1122
  self.assertEqual(list(dom.completion('')), ['Name: '])
1123
1123
  self.assertEqual(list(dom.completion(' ')), ['', 'Name: '])
1124
- self.assertEqual(list(dom.completion('Name: foo or')), ['Name: foo'])
1124
+ self.assertEqual(list(dom.completion('Name: foo |')), ['Name: foo'])
1125
1125
  self.assertEqual(
1126
- list(dom.completion('Name: foo (Name: foo or N')),
1127
- ['Name: foo (Name: foo or Name: '])
1126
+ list(dom.completion('Name: foo (Name: foo | N')),
1127
+ ['Name: foo (Name: foo | Name: '])
1128
1128
 
1129
1129
  def test_completion_many2one(self):
1130
1130
  "Test completion many2one"
@@ -1,16 +1,16 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tryton
3
- Version: 6.6.8
3
+ Version: 6.8.1
4
4
  Summary: Tryton desktop client
5
5
  Home-page: http://www.tryton.org/
6
- Download-URL: http://downloads.tryton.org/6.6/
6
+ Download-URL: http://downloads.tryton.org/6.8/
7
7
  Author: Tryton
8
- Author-email: bugs@tryton.org
8
+ Author-email: foundation@tryton.org
9
9
  License: GPL-3
10
10
  Project-URL: Bug Tracker, https://bugs.tryton.org/
11
11
  Project-URL: Documentation, https://docs.tryton.org/
12
12
  Project-URL: Forum, https://www.tryton.org/forum
13
- Project-URL: Source Code, https://hg.tryton.org/tryton
13
+ Project-URL: Source Code, https://code.tryton.org/tryton
14
14
  Keywords: business application ERP
15
15
  Platform: any
16
16
  Classifier: Development Status :: 5 - Production/Stable
@@ -42,12 +42,12 @@ Classifier: Natural Language :: Japanese
42
42
  Classifier: Natural Language :: Ukrainian
43
43
  Classifier: Operating System :: OS Independent
44
44
  Classifier: Programming Language :: Python :: 3
45
- Classifier: Programming Language :: Python :: 3.7
46
45
  Classifier: Programming Language :: Python :: 3.8
47
46
  Classifier: Programming Language :: Python :: 3.9
48
47
  Classifier: Programming Language :: Python :: 3.10
48
+ Classifier: Programming Language :: Python :: 3.11
49
49
  Classifier: Topic :: Office/Business
50
- Requires-Python: >=3.7
50
+ Requires-Python: >=3.8
51
51
  License-File: LICENSE
52
52
  Requires-Dist: pycairo
53
53
  Requires-Dist: python-dateutil