proteus 7.4.1__tar.gz → 7.6.0__tar.gz

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.
Files changed (32) hide show
  1. {proteus-7.4.1 → proteus-7.6.0}/CHANGELOG +4 -2
  2. {proteus-7.4.1 → proteus-7.6.0}/COPYRIGHT +2 -2
  3. {proteus-7.4.1/proteus.egg-info → proteus-7.6.0}/PKG-INFO +23 -8
  4. {proteus-7.4.1 → proteus-7.6.0}/doc/conf.py +7 -0
  5. {proteus-7.4.1 → proteus-7.6.0}/proteus/__init__.py +59 -37
  6. {proteus-7.4.1 → proteus-7.6.0}/proteus/config.py +6 -6
  7. {proteus-7.4.1 → proteus-7.6.0}/proteus/pyson.py +18 -18
  8. proteus-7.6.0/proteus/tests/test_action.py +49 -0
  9. {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/test_model.py +4 -4
  10. {proteus-7.4.1 → proteus-7.6.0/proteus.egg-info}/PKG-INFO +23 -8
  11. {proteus-7.4.1 → proteus-7.6.0}/proteus.egg-info/SOURCES.txt +1 -0
  12. proteus-7.6.0/proteus.egg-info/requires.txt +9 -0
  13. {proteus-7.4.1 → proteus-7.6.0}/setup.py +2 -2
  14. {proteus-7.4.1 → proteus-7.6.0}/tox.ini +1 -1
  15. proteus-7.4.1/proteus.egg-info/requires.txt +0 -9
  16. {proteus-7.4.1 → proteus-7.6.0}/LICENSE +0 -0
  17. {proteus-7.4.1 → proteus-7.6.0}/MANIFEST.in +0 -0
  18. {proteus-7.4.1 → proteus-7.6.0}/README.rst +0 -0
  19. {proteus-7.4.1 → proteus-7.6.0}/doc/index.rst +0 -0
  20. {proteus-7.4.1 → proteus-7.6.0}/doc/releases.rst +0 -0
  21. {proteus-7.4.1 → proteus-7.6.0}/doc/requirements-doc.txt +0 -0
  22. {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/__init__.py +0 -0
  23. {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/common.py +0 -0
  24. {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/test_config.py +0 -0
  25. {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/test_context.py +0 -0
  26. {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/test_readme.py +0 -0
  27. {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/test_report.py +0 -0
  28. {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/test_wizard.py +0 -0
  29. {proteus-7.4.1 → proteus-7.6.0}/proteus.egg-info/dependency_links.txt +0 -0
  30. {proteus-7.4.1 → proteus-7.6.0}/proteus.egg-info/top_level.txt +0 -0
  31. {proteus-7.4.1 → proteus-7.6.0}/proteus.egg-info/zip-safe +0 -0
  32. {proteus-7.4.1 → proteus-7.6.0}/setup.cfg +0 -0
@@ -1,8 +1,10 @@
1
1
 
2
- Version 7.4.1 - 2025-01-01
2
+ Version 7.6.0 - 2025-04-28
3
3
  --------------------------
4
4
  * Bug fixes (see mercurial logs for details)
5
-
5
+ * Remove support for Python 3.8
6
+ * Add support for Python 3.13
7
+ * Add launch_action helper function
6
8
 
7
9
  Version 7.4.0 - 2024-11-04
8
10
  --------------------------
@@ -1,5 +1,5 @@
1
- Copyright (C) 2010-2024 Cédric Krier.
2
- Copyright (C) 2010-2024 B2CK SPRL.
1
+ Copyright (C) 2010-2025 Cédric Krier.
2
+ Copyright (C) 2010-2025 B2CK SPRL.
3
3
 
4
4
  This program is free software: you can redistribute it and/or modify
5
5
  it under the terms of the GNU Lesser General Public License as published by
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: proteus
3
- Version: 7.4.1
3
+ Version: 7.6.0
4
4
  Summary: Library to access Tryton server as a client
5
5
  Home-page: http://www.tryton.org/
6
- Download-URL: http://downloads.tryton.org/7.4/
6
+ Download-URL: http://downloads.tryton.org/7.6/
7
7
  Author: Tryton
8
8
  Author-email: foundation@tryton.org
9
9
  License: LGPL-3
@@ -22,22 +22,37 @@ Classifier: Intended Audience :: Legal Industry
22
22
  Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
23
23
  Classifier: Operating System :: OS Independent
24
24
  Classifier: Programming Language :: Python :: 3
25
- Classifier: Programming Language :: Python :: 3.8
26
25
  Classifier: Programming Language :: Python :: 3.9
27
26
  Classifier: Programming Language :: Python :: 3.10
28
27
  Classifier: Programming Language :: Python :: 3.11
29
28
  Classifier: Programming Language :: Python :: 3.12
29
+ Classifier: Programming Language :: Python :: 3.13
30
30
  Classifier: Programming Language :: Python :: Implementation :: CPython
31
31
  Classifier: Topic :: Office/Business
32
- Requires-Python: >=3.8
32
+ Requires-Python: >=3.9
33
33
  License-File: LICENSE
34
34
  Requires-Dist: defusedxml
35
35
  Requires-Dist: python-dateutil
36
36
  Provides-Extra: trytond
37
- Requires-Dist: trytond<7.5,>=7.4; extra == "trytond"
37
+ Requires-Dist: trytond<7.7,>=7.6; extra == "trytond"
38
38
  Provides-Extra: test
39
- Requires-Dist: trytond<7.5,>=7.4; extra == "test"
40
- Requires-Dist: trytond_party<7.5,>=7.4; extra == "test"
39
+ Requires-Dist: trytond<7.7,>=7.6; extra == "test"
40
+ Requires-Dist: trytond_party<7.7,>=7.6; extra == "test"
41
+ Dynamic: author
42
+ Dynamic: author-email
43
+ Dynamic: classifier
44
+ Dynamic: description
45
+ Dynamic: download-url
46
+ Dynamic: home-page
47
+ Dynamic: keywords
48
+ Dynamic: license
49
+ Dynamic: license-file
50
+ Dynamic: platform
51
+ Dynamic: project-url
52
+ Dynamic: provides-extra
53
+ Dynamic: requires-dist
54
+ Dynamic: requires-python
55
+ Dynamic: summary
41
56
 
42
57
  =======================
43
58
  Tryton Scripting Client
@@ -47,6 +47,13 @@ info = get_info()
47
47
 
48
48
  html_theme = 'sphinx_book_theme'
49
49
  html_theme_options = {
50
+ 'logo': {
51
+ 'alt_text': "Tryton Documentation",
52
+ 'image_light': 'https://docs.tryton.org/logo-light.svg',
53
+ 'image_dark': 'https://docs.tryton.org/logo-dark.svg',
54
+ 'link': base_url,
55
+ },
56
+ 'home_page_in_toc': True,
50
57
  'repository_provider': 'gitlab',
51
58
  'repository_url': 'https://code.tryton.org/tryton',
52
59
  'repository_branch': info['branch'],
@@ -10,7 +10,7 @@ from decimal import Decimal
10
10
 
11
11
  import proteus.config
12
12
 
13
- __version__ = "7.4.1"
13
+ __version__ = "7.6.0"
14
14
  __all__ = ['Model', 'Wizard', 'Report']
15
15
 
16
16
  _MODELS = threading.local()
@@ -21,7 +21,7 @@ class _EvalEnvironment(dict):
21
21
  __slots__ = ('parent', 'eval_type')
22
22
 
23
23
  def __init__(self, parent, eval_type='eval'):
24
- super(_EvalEnvironment, self).__init__()
24
+ super().__init__()
25
25
  self.parent = parent
26
26
  assert eval_type in ('eval', 'on_change')
27
27
  self.eval_type = eval_type
@@ -49,7 +49,7 @@ class _EvalEnvironment(dict):
49
49
  return self.__getitem__(item)
50
50
  except KeyError:
51
51
  pass
52
- return super(_EvalEnvironment, self).get(item, default)
52
+ return super().get(item, default)
53
53
 
54
54
  def __bool__(self):
55
55
  return True
@@ -109,7 +109,7 @@ class FieldDescriptor(object):
109
109
  default = None
110
110
 
111
111
  def __init__(self, name, definition):
112
- super(FieldDescriptor, self).__init__()
112
+ super().__init__()
113
113
  self.name = name
114
114
  self.definition = definition
115
115
  self.__doc__ = definition['string']
@@ -137,7 +137,7 @@ class BooleanDescriptor(FieldDescriptor):
137
137
 
138
138
  def __set__(self, instance, value):
139
139
  assert isinstance(value, bool)
140
- super(BooleanDescriptor, self).__set__(instance, value)
140
+ super().__set__(instance, value)
141
141
 
142
142
 
143
143
  class CharDescriptor(FieldDescriptor):
@@ -152,7 +152,7 @@ class CharDescriptor(FieldDescriptor):
152
152
  value = value.rstrip()
153
153
  else:
154
154
  value = value.strip()
155
- super(CharDescriptor, self).__set__(instance, value or '')
155
+ super().__set__(instance, value or '')
156
156
 
157
157
 
158
158
  class BinaryDescriptor(FieldDescriptor):
@@ -160,14 +160,14 @@ class BinaryDescriptor(FieldDescriptor):
160
160
 
161
161
  def __set__(self, instance, value):
162
162
  assert isinstance(value, (bytes, bytearray)) or value is None
163
- super(BinaryDescriptor, self).__set__(instance, value)
163
+ super().__set__(instance, value)
164
164
 
165
165
 
166
166
  class IntegerDescriptor(FieldDescriptor):
167
167
 
168
168
  def __set__(self, instance, value):
169
169
  assert isinstance(value, (int, type(None)))
170
- super(IntegerDescriptor, self).__set__(instance, value)
170
+ super().__set__(instance, value)
171
171
 
172
172
 
173
173
  class FloatDescriptor(FieldDescriptor):
@@ -176,7 +176,7 @@ class FloatDescriptor(FieldDescriptor):
176
176
  assert isinstance(value, (int, float, Decimal, type(None)))
177
177
  if value is not None:
178
178
  value = float(value)
179
- super(FloatDescriptor, self).__set__(instance, value)
179
+ super().__set__(instance, value)
180
180
 
181
181
 
182
182
  class NumericDescriptor(FieldDescriptor):
@@ -184,7 +184,7 @@ class NumericDescriptor(FieldDescriptor):
184
184
  def __set__(self, instance, value):
185
185
  assert isinstance(value, (type(None), Decimal))
186
186
  # TODO add digits validation
187
- super(NumericDescriptor, self).__set__(instance, value)
187
+ super().__set__(instance, value)
188
188
 
189
189
 
190
190
  class SelectionDescriptor(FieldDescriptor):
@@ -205,7 +205,7 @@ class MultiSelectionDescriptor(FieldDescriptor):
205
205
 
206
206
  class ReferenceDescriptor(FieldDescriptor):
207
207
  def __get__(self, instance, owner):
208
- value = super(ReferenceDescriptor, self).__get__(instance, owner)
208
+ value = super().__get__(instance, owner)
209
209
  if isinstance(value, str):
210
210
  model_name, id = value.split(',', 1)
211
211
  if model_name:
@@ -223,12 +223,12 @@ class ReferenceDescriptor(FieldDescriptor):
223
223
  assert value.startswith(',')
224
224
  elif isinstance(value, Model):
225
225
  assert value._config == instance._config
226
- super(ReferenceDescriptor, self).__set__(instance, value)
226
+ super().__set__(instance, value)
227
227
 
228
228
 
229
229
  class DateDescriptor(FieldDescriptor):
230
230
  def __get__(self, instance, owner):
231
- value = super(DateDescriptor, self).__get__(instance, owner)
231
+ value = super().__get__(instance, owner)
232
232
  if isinstance(value, datetime.datetime):
233
233
  value = value.date()
234
234
  instance._values[self.name] = value
@@ -236,43 +236,43 @@ class DateDescriptor(FieldDescriptor):
236
236
 
237
237
  def __set__(self, instance, value):
238
238
  assert isinstance(value, datetime.date) or value is None
239
- super(DateDescriptor, self).__set__(instance, value)
239
+ super().__set__(instance, value)
240
240
 
241
241
 
242
242
  class DateTimeDescriptor(FieldDescriptor):
243
243
  def __set__(self, instance, value):
244
244
  assert isinstance(value, datetime.datetime) or value is None
245
- super(DateTimeDescriptor, self).__set__(instance, value)
245
+ super().__set__(instance, value)
246
246
 
247
247
 
248
248
  class TimeDescriptor(FieldDescriptor):
249
249
  def __set__(self, instance, value):
250
250
  assert isinstance(value, datetime.time) or value is None
251
- super(TimeDescriptor, self).__set__(instance, value)
251
+ super().__set__(instance, value)
252
252
 
253
253
 
254
254
  class TimeDeltaDescriptor(FieldDescriptor):
255
255
  def __set__(self, instance, value):
256
256
  assert isinstance(value, datetime.timedelta) or value is None
257
- super(TimeDeltaDescriptor, self).__set__(instance, value)
257
+ super().__set__(instance, value)
258
258
 
259
259
 
260
260
  class DictDescriptor(FieldDescriptor):
261
261
  def __get__(self, instance, owner):
262
- value = super(DictDescriptor, self).__get__(instance, owner)
262
+ value = super().__get__(instance, owner)
263
263
  if value:
264
264
  value = value.copy()
265
265
  return value
266
266
 
267
267
  def __set__(self, instance, value):
268
268
  assert isinstance(value, dict) or value is None
269
- super(DictDescriptor, self).__set__(instance, value)
269
+ super().__set__(instance, value)
270
270
 
271
271
 
272
272
  class Many2OneDescriptor(FieldDescriptor):
273
273
  def __get__(self, instance, owner):
274
274
  Relation = Model.get(self.definition['relation'], instance._config)
275
- value = super(Many2OneDescriptor, self).__get__(instance, owner)
275
+ value = super().__get__(instance, owner)
276
276
  if isinstance(value, int):
277
277
  config = Relation._config
278
278
  with config.reset_context(), config.set_context(instance._context):
@@ -285,7 +285,7 @@ class Many2OneDescriptor(FieldDescriptor):
285
285
  assert isinstance(value, (Model, type(None)))
286
286
  if value:
287
287
  assert value._config == instance._config
288
- super(Many2OneDescriptor, self).__set__(instance, value)
288
+ super().__set__(instance, value)
289
289
 
290
290
 
291
291
  class One2OneDescriptor(Many2OneDescriptor):
@@ -298,7 +298,7 @@ class One2ManyDescriptor(FieldDescriptor):
298
298
  def __get__(self, instance, owner):
299
299
  from .pyson import PYSONDecoder
300
300
  Relation = Model.get(self.definition['relation'], instance._config)
301
- value = super(One2ManyDescriptor, self).__get__(instance, owner)
301
+ value = super().__get__(instance, owner)
302
302
  if not isinstance(value, ModelList):
303
303
  ctx = instance._context.copy() if instance._context else {}
304
304
  if self.definition.get('context'):
@@ -321,7 +321,7 @@ class Many2ManyDescriptor(One2ManyDescriptor):
321
321
 
322
322
  class ValueDescriptor(object):
323
323
  def __init__(self, name, definition):
324
- super(ValueDescriptor, self).__init__()
324
+ super().__init__()
325
325
  self.name = name
326
326
  self.definition = definition
327
327
 
@@ -331,7 +331,7 @@ class ValueDescriptor(object):
331
331
 
332
332
  class ReferenceValueDescriptor(ValueDescriptor):
333
333
  def __get__(self, instance, owner):
334
- value = super(ReferenceValueDescriptor, self).__get__(instance, owner)
334
+ value = super().__get__(instance, owner)
335
335
  if isinstance(value, Model):
336
336
  value = '%s,%s' % (value.__class__.__name__, value.id)
337
337
  return value or None
@@ -339,7 +339,7 @@ class ReferenceValueDescriptor(ValueDescriptor):
339
339
 
340
340
  class Many2OneValueDescriptor(ValueDescriptor):
341
341
  def __get__(self, instance, owner):
342
- value = super(Many2OneValueDescriptor, self).__get__(instance, owner)
342
+ value = super().__get__(instance, owner)
343
343
  return value and value.id or None
344
344
 
345
345
 
@@ -385,7 +385,7 @@ class Many2ManyValueDescriptor(One2ManyValueDescriptor):
385
385
 
386
386
  class EvalDescriptor(object):
387
387
  def __init__(self, name, definition):
388
- super(EvalDescriptor, self).__init__()
388
+ super().__init__()
389
389
  self.name = name
390
390
  self.definition = definition
391
391
 
@@ -395,7 +395,7 @@ class EvalDescriptor(object):
395
395
 
396
396
  class ReferenceEvalDescriptor(EvalDescriptor):
397
397
  def __get__(self, instance, owner):
398
- value = super(ReferenceEvalDescriptor, self).__get__(instance, owner)
398
+ value = super().__get__(instance, owner)
399
399
  if isinstance(value, Model):
400
400
  value = '%s,%s' % (value.__class__.__name__, value.id)
401
401
  return value or None
@@ -403,7 +403,7 @@ class ReferenceEvalDescriptor(EvalDescriptor):
403
403
 
404
404
  class Many2OneEvalDescriptor(EvalDescriptor):
405
405
  def __get__(self, instance, owner):
406
- value = super(Many2OneEvalDescriptor, self).__get__(instance, owner)
406
+ value = super().__get__(instance, owner)
407
407
  if value:
408
408
  return value.id
409
409
  return None
@@ -469,7 +469,7 @@ class MetaModelFactory(object):
469
469
  }
470
470
 
471
471
  def __init__(self, model_name, config=None):
472
- super(MetaModelFactory, self).__init__()
472
+ super().__init__()
473
473
  self.model_name = model_name
474
474
  self.config = config or proteus.config.get_config()
475
475
 
@@ -537,7 +537,7 @@ class ModelList(list):
537
537
  self.search_context = definition.get('search_context', '{}')
538
538
  self.record_removed = set()
539
539
  self.record_deleted = set()
540
- result = super(ModelList, self).__init__(sequence)
540
+ result = super().__init__(sequence)
541
541
  assert len(self) == len(set(self))
542
542
  self.__check(self, on_change=False)
543
543
  return result
@@ -605,7 +605,7 @@ class ModelList(list):
605
605
  def pop(self, index=-1, _changed=True):
606
606
  self.record_removed.add(self[index])
607
607
  self[index]._group = None
608
- res = super(ModelList, self).pop(index)
608
+ res = super().pop(index)
609
609
  if _changed:
610
610
  self._changed()
611
611
  return res
@@ -615,7 +615,7 @@ class ModelList(list):
615
615
  if record.id >= 0:
616
616
  self.record_deleted.add(record)
617
617
  record._group = None
618
- res = super(ModelList, self).remove(record)
618
+ res = super().remove(record)
619
619
  if _changed:
620
620
  self._changed()
621
621
  return res
@@ -698,7 +698,7 @@ class Model(object):
698
698
  _fields = None
699
699
 
700
700
  def __init__(self, id=None, _default=True, _group=None, **kwargs):
701
- super(Model, self).__init__()
701
+ super().__init__()
702
702
  if id is not None:
703
703
  assert not kwargs
704
704
  self.__id = id if id is not None else Model.__counter
@@ -1208,7 +1208,7 @@ class Wizard(object):
1208
1208
  context=None):
1209
1209
  if models:
1210
1210
  assert len(set(type(x) for x in models)) == 1
1211
- super(Wizard, self).__init__()
1211
+ super().__init__()
1212
1212
  self.name = name
1213
1213
  self.form = None
1214
1214
  self.form_state = None
@@ -1255,7 +1255,7 @@ class Wizard(object):
1255
1255
  if 'view' in result:
1256
1256
  view = result['view']
1257
1257
  self.form = Model.get(
1258
- view['fields_view']['model'], self._config)()
1258
+ view['fields_view']['model'], self._config)(_default=False)
1259
1259
  if 'defaults' in view:
1260
1260
  self.form._default_set(view['defaults'])
1261
1261
  if 'values' in view:
@@ -1287,7 +1287,7 @@ class Report(object):
1287
1287
  __slots__ = ('name', '_config', '_context', '_proxy')
1288
1288
 
1289
1289
  def __init__(self, name, config=None, context=None):
1290
- super(Report, self).__init__()
1290
+ super().__init__()
1291
1291
  self.name = name
1292
1292
  self._config = config or proteus.config.get_config()
1293
1293
  self._context = self._config.context
@@ -1312,7 +1312,29 @@ class Report(object):
1312
1312
  return self._proxy.execute(ids, data, self._context)
1313
1313
 
1314
1314
 
1315
- def _convert_action(action, data=None, context=None, config=None):
1315
+ def launch_action(xml_id, records, context=None, config=None):
1316
+ if records:
1317
+ assert len({type(x) for x in records}) == 1
1318
+ if context is None:
1319
+ context = {}
1320
+
1321
+ if isinstance(xml_id, str):
1322
+ ModelData = Model.get('ir.model.data')
1323
+
1324
+ if not config:
1325
+ config = proteus.config.get_config()
1326
+ context = config.context
1327
+
1328
+ action_id = ModelData.get_id(*xml_id.split('.'), context)
1329
+ elif isinstance(xml_id, int):
1330
+ action_id = xml_id
1331
+
1332
+ Action = Model.get('ir.action')
1333
+ action = Action.get_action_value(action_id, context)
1334
+ return _convert_action(action, records, context=context, config=config)
1335
+
1336
+
1337
+ def _convert_action(action, data=None, *, context=None, config=None):
1316
1338
  if config is None:
1317
1339
  config = proteus.config.get_config()
1318
1340
  records = None
@@ -143,7 +143,7 @@ class Config(object):
143
143
  'Config interface'
144
144
 
145
145
  def __init__(self):
146
- super(Config, self).__init__()
146
+ super().__init__()
147
147
  self._context = {}
148
148
 
149
149
  @property
@@ -175,7 +175,7 @@ class Config(object):
175
175
  class _TrytondMethod(object):
176
176
 
177
177
  def __init__(self, name, model, config):
178
- super(_TrytondMethod, self).__init__()
178
+ super().__init__()
179
179
  self._name = name
180
180
  self._object = model
181
181
  self._config = config
@@ -238,7 +238,7 @@ class TrytondProxy(object):
238
238
  'Proxy for function call for trytond'
239
239
 
240
240
  def __init__(self, name, config, type='model'):
241
- super(TrytondProxy, self).__init__()
241
+ super().__init__()
242
242
  self._config = config
243
243
  self._object = config.pool.get(name, type=type)
244
244
  __init__.__doc__ = object.__init__.__doc__
@@ -252,7 +252,7 @@ class TrytondConfig(Config):
252
252
  'Configuration for trytond'
253
253
 
254
254
  def __init__(self, database=None, user='admin', config_file=None):
255
- super(TrytondConfig, self).__init__()
255
+ super().__init__()
256
256
  if not database:
257
257
  database = os.environ.get('TRYTOND_DATABASE_URI')
258
258
  elif (os.environ.get('TRYTOND_DATABASE_URI')
@@ -337,7 +337,7 @@ class XmlrpcProxy(object):
337
337
  'Proxy for function call for XML-RPC'
338
338
 
339
339
  def __init__(self, name, config, type='model'):
340
- super(XmlrpcProxy, self).__init__()
340
+ super().__init__()
341
341
  self._config = config
342
342
  self._object = getattr(config.server, '%s.%s' % (type, name))
343
343
  __init__.__doc__ = object.__init__.__doc__
@@ -351,7 +351,7 @@ class XmlrpcConfig(Config):
351
351
  'Configuration for XML-RPC'
352
352
 
353
353
  def __init__(self, url, **kwargs):
354
- super(XmlrpcConfig, self).__init__()
354
+ super().__init__()
355
355
  self.url = url
356
356
  self.server = xmlrpc.client.ServerProxy(
357
357
  url, allow_none=True, use_builtin_types=True, **kwargs)
@@ -105,7 +105,7 @@ class PYSONEncoder(json.JSONEncoder):
105
105
  return TimeDelta(obj.days, obj.seconds, obj.microseconds)
106
106
  elif isinstance(obj, Decimal):
107
107
  return float(obj)
108
- return super(PYSONEncoder, self).default(obj)
108
+ return super().default(obj)
109
109
 
110
110
 
111
111
  class PYSONDecoder(json.JSONDecoder):
@@ -113,7 +113,7 @@ class PYSONDecoder(json.JSONDecoder):
113
113
  def __init__(self, context=None, noeval=False):
114
114
  self.__context = context or {}
115
115
  self.noeval = noeval
116
- super(PYSONDecoder, self).__init__(object_hook=self._object_hook)
116
+ super().__init__(object_hook=self._object_hook)
117
117
 
118
118
  def _object_hook(self, dct):
119
119
  if '__class__' in dct:
@@ -131,7 +131,7 @@ class PYSONDecoder(json.JSONDecoder):
131
131
  class Eval(PYSON):
132
132
 
133
133
  def __init__(self, v, d=''):
134
- super(Eval, self).__init__()
134
+ super().__init__()
135
135
  self._value = v
136
136
  self._default = d
137
137
 
@@ -175,7 +175,7 @@ class Eval(PYSON):
175
175
  class Not(PYSON):
176
176
 
177
177
  def __init__(self, v):
178
- super(Not, self).__init__()
178
+ super().__init__()
179
179
  if isinstance(v, PYSON):
180
180
  if v.types() != {bool}:
181
181
  v = Bool(v)
@@ -204,7 +204,7 @@ class Not(PYSON):
204
204
  class Bool(PYSON):
205
205
 
206
206
  def __init__(self, v):
207
- super(Bool, self).__init__()
207
+ super().__init__()
208
208
  self._value = v
209
209
 
210
210
  @property
@@ -228,7 +228,7 @@ class Bool(PYSON):
228
228
  class And(PYSON):
229
229
 
230
230
  def __init__(self, *statements, **kwargs):
231
- super(And, self).__init__()
231
+ super().__init__()
232
232
  statements = list(statements) + kwargs.get('s', [])
233
233
  for i, statement in enumerate(list(statements)):
234
234
  if isinstance(statement, PYSON):
@@ -260,7 +260,7 @@ class And(PYSON):
260
260
  class Or(And):
261
261
 
262
262
  def pyson(self):
263
- res = super(Or, self).pyson()
263
+ res = super().pyson()
264
264
  res['__class__'] = 'Or'
265
265
  return res
266
266
 
@@ -273,7 +273,7 @@ class Equal(PYSON):
273
273
 
274
274
  def __init__(self, s1, s2):
275
275
  statement1, statement2 = s1, s2
276
- super(Equal, self).__init__()
276
+ super().__init__()
277
277
  if isinstance(statement1, PYSON):
278
278
  types1 = statement1.types()
279
279
  else:
@@ -309,7 +309,7 @@ class Greater(PYSON):
309
309
 
310
310
  def __init__(self, s1, s2, e=False):
311
311
  statement1, statement2, equal = s1, s2, e
312
- super(Greater, self).__init__()
312
+ super().__init__()
313
313
  for i in (statement1, statement2):
314
314
  if isinstance(i, PYSON):
315
315
  assert i.types().issubset({
@@ -379,7 +379,7 @@ class Greater(PYSON):
379
379
  class Less(Greater):
380
380
 
381
381
  def pyson(self):
382
- res = super(Less, self).pyson()
382
+ res = super().pyson()
383
383
  res['__class__'] = 'Less'
384
384
  return res
385
385
 
@@ -398,7 +398,7 @@ class If(PYSON):
398
398
 
399
399
  def __init__(self, c, t, e=None):
400
400
  condition, then_statement, else_statement = c, t, e
401
- super(If, self).__init__()
401
+ super().__init__()
402
402
  if isinstance(condition, PYSON):
403
403
  if condition.types() != {bool}:
404
404
  condition = Bool(condition)
@@ -443,7 +443,7 @@ class Get(PYSON):
443
443
 
444
444
  def __init__(self, v, k, d=''):
445
445
  obj, key, default = v, k, d
446
- super(Get, self).__init__()
446
+ super().__init__()
447
447
  if isinstance(obj, PYSON):
448
448
  assert obj.types() == {dict}, 'obj must be a dict'
449
449
  else:
@@ -483,7 +483,7 @@ class In(PYSON):
483
483
 
484
484
  def __init__(self, k, v):
485
485
  key, obj = k, v
486
- super(In, self).__init__()
486
+ super().__init__()
487
487
  if isinstance(key, PYSON):
488
488
  assert key.types().issubset({str, int}), \
489
489
  'key must be a string or an integer or a long'
@@ -540,7 +540,7 @@ class Date(PYSON):
540
540
  delta_years = kwargs.get('dy', delta_years)
541
541
  delta_months = kwargs.get('dM', delta_months)
542
542
  delta_days = kwargs.get('dd', delta_days)
543
- super(Date, self).__init__()
543
+ super().__init__()
544
544
  for i in (year, month, day, delta_years, delta_months, delta_days):
545
545
  if isinstance(i, PYSON):
546
546
  assert i.types().issubset({int, type(None)}), \
@@ -609,7 +609,7 @@ class DateTime(Date):
609
609
  delta_minutes = kwargs.get('dm', delta_minutes)
610
610
  delta_seconds = kwargs.get('ds', delta_seconds)
611
611
  delta_microseconds = kwargs.get('dms', delta_microseconds)
612
- super(DateTime, self).__init__(year=year, month=month, day=day,
612
+ super().__init__(year=year, month=month, day=day,
613
613
  delta_years=delta_years, delta_months=delta_months,
614
614
  delta_days=delta_days, start=start, **kwargs)
615
615
  for i in (hour, minute, second, microsecond,
@@ -631,7 +631,7 @@ class DateTime(Date):
631
631
 
632
632
  @property
633
633
  def __repr_params__(self):
634
- date_params = super(DateTime, self).__repr_params__
634
+ date_params = super().__repr_params__
635
635
  return (date_params[:3]
636
636
  + (self._hour, self._minute, self._second, self._microsecond)
637
637
  + date_params[3:-1]
@@ -640,7 +640,7 @@ class DateTime(Date):
640
640
  + date_params[-1:])
641
641
 
642
642
  def pyson(self):
643
- res = super(DateTime, self).pyson()
643
+ res = super().pyson()
644
644
  res['__class__'] = 'DateTime'
645
645
  res['h'] = self._hour
646
646
  res['m'] = self._minute
@@ -723,7 +723,7 @@ class TimeDelta(PYSON):
723
723
  class Len(PYSON):
724
724
 
725
725
  def __init__(self, v):
726
- super(Len, self).__init__()
726
+ super().__init__()
727
727
  if isinstance(v, PYSON):
728
728
  assert v.types().issubset({dict, list, str}), \
729
729
  'value must be a dict or a list or a string'
@@ -0,0 +1,49 @@
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
+ import unittest
5
+
6
+ try:
7
+ import pydot
8
+ except ImportError:
9
+ pydot = None
10
+
11
+ from proteus import Model, Wizard, launch_action
12
+
13
+ from .common import ProteusTestCase
14
+
15
+
16
+ class TestAction(ProteusTestCase):
17
+
18
+ def test_act_window(self):
19
+ IrModel = Model.get('ir.model')
20
+ actions = IrModel.find([
21
+ ('name', '=', 'ir.action'),
22
+ ])
23
+ model_access = launch_action(
24
+ 'ir.act_model_access_form_relate_model', actions)
25
+ self.assertEqual(
26
+ {m.__class__.__name__ for m in model_access},
27
+ {'ir.model.access'})
28
+
29
+ def test_wizard(self):
30
+ IrModel = Model.get('ir.model')
31
+ actions = IrModel.find([
32
+ ('name', '=', 'ir.action'),
33
+ ])
34
+ wizard = launch_action('ir.print_model_graph', actions)
35
+ self.assertIsInstance(wizard, Wizard)
36
+ self.assertEqual(wizard.name, 'ir.model.print_model_graph')
37
+
38
+ @unittest.skipIf(not pydot, 'requires pydot')
39
+ def test_report(self):
40
+ IrModel = Model.get('ir.model')
41
+ actions = IrModel.find([
42
+ ('name', '=', 'ir.action'),
43
+ ])
44
+ ftype, data, direct_print, report_name = launch_action(
45
+ 'ir.report_model_workflow_graph', actions)
46
+ self.assertIsInstance(ftype, str)
47
+ self.assertIsInstance(data, bytes)
48
+ self.assertIsInstance(direct_print, bool)
49
+ self.assertIsInstance(report_name, str)
@@ -336,11 +336,11 @@ class TestModel(ProteusTestCase):
336
336
 
337
337
  trigger = Trigger()
338
338
 
339
- trigger.on_time = True
340
- self.assertEqual(trigger.on_create, False)
339
+ trigger.on_time_ = True
340
+ self.assertEqual(trigger.on_create_, False)
341
341
 
342
- trigger.on_create = True
343
- self.assertEqual(trigger.on_time, False)
342
+ trigger.on_create_ = True
343
+ self.assertEqual(trigger.on_time_, False)
344
344
 
345
345
  def test_on_change_with(self):
346
346
  Attachment = Model.get('ir.attachment')
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: proteus
3
- Version: 7.4.1
3
+ Version: 7.6.0
4
4
  Summary: Library to access Tryton server as a client
5
5
  Home-page: http://www.tryton.org/
6
- Download-URL: http://downloads.tryton.org/7.4/
6
+ Download-URL: http://downloads.tryton.org/7.6/
7
7
  Author: Tryton
8
8
  Author-email: foundation@tryton.org
9
9
  License: LGPL-3
@@ -22,22 +22,37 @@ Classifier: Intended Audience :: Legal Industry
22
22
  Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
23
23
  Classifier: Operating System :: OS Independent
24
24
  Classifier: Programming Language :: Python :: 3
25
- Classifier: Programming Language :: Python :: 3.8
26
25
  Classifier: Programming Language :: Python :: 3.9
27
26
  Classifier: Programming Language :: Python :: 3.10
28
27
  Classifier: Programming Language :: Python :: 3.11
29
28
  Classifier: Programming Language :: Python :: 3.12
29
+ Classifier: Programming Language :: Python :: 3.13
30
30
  Classifier: Programming Language :: Python :: Implementation :: CPython
31
31
  Classifier: Topic :: Office/Business
32
- Requires-Python: >=3.8
32
+ Requires-Python: >=3.9
33
33
  License-File: LICENSE
34
34
  Requires-Dist: defusedxml
35
35
  Requires-Dist: python-dateutil
36
36
  Provides-Extra: trytond
37
- Requires-Dist: trytond<7.5,>=7.4; extra == "trytond"
37
+ Requires-Dist: trytond<7.7,>=7.6; extra == "trytond"
38
38
  Provides-Extra: test
39
- Requires-Dist: trytond<7.5,>=7.4; extra == "test"
40
- Requires-Dist: trytond_party<7.5,>=7.4; extra == "test"
39
+ Requires-Dist: trytond<7.7,>=7.6; extra == "test"
40
+ Requires-Dist: trytond_party<7.7,>=7.6; extra == "test"
41
+ Dynamic: author
42
+ Dynamic: author-email
43
+ Dynamic: classifier
44
+ Dynamic: description
45
+ Dynamic: download-url
46
+ Dynamic: home-page
47
+ Dynamic: keywords
48
+ Dynamic: license
49
+ Dynamic: license-file
50
+ Dynamic: platform
51
+ Dynamic: project-url
52
+ Dynamic: provides-extra
53
+ Dynamic: requires-dist
54
+ Dynamic: requires-python
55
+ Dynamic: summary
41
56
 
42
57
  =======================
43
58
  Tryton Scripting Client
@@ -20,6 +20,7 @@ proteus.egg-info/top_level.txt
20
20
  proteus.egg-info/zip-safe
21
21
  proteus/tests/__init__.py
22
22
  proteus/tests/common.py
23
+ proteus/tests/test_action.py
23
24
  proteus/tests/test_config.py
24
25
  proteus/tests/test_context.py
25
26
  proteus/tests/test_model.py
@@ -0,0 +1,9 @@
1
+ defusedxml
2
+ python-dateutil
3
+
4
+ [test]
5
+ trytond<7.7,>=7.6
6
+ trytond_party<7.7,>=7.6
7
+
8
+ [trytond]
9
+ trytond<7.7,>=7.6
@@ -72,17 +72,17 @@ setup(name=name,
72
72
  'GNU Library or Lesser General Public License (LGPL)',
73
73
  'Operating System :: OS Independent',
74
74
  'Programming Language :: Python :: 3',
75
- 'Programming Language :: Python :: 3.8',
76
75
  'Programming Language :: Python :: 3.9',
77
76
  'Programming Language :: Python :: 3.10',
78
77
  'Programming Language :: Python :: 3.11',
79
78
  'Programming Language :: Python :: 3.12',
79
+ 'Programming Language :: Python :: 3.13',
80
80
  'Programming Language :: Python :: Implementation :: CPython',
81
81
  'Topic :: Office/Business',
82
82
  ],
83
83
  platforms='any',
84
84
  license='LGPL-3',
85
- python_requires='>=3.8',
85
+ python_requires='>=3.9',
86
86
  install_requires=[
87
87
  'defusedxml',
88
88
  "python-dateutil",
@@ -1,5 +1,5 @@
1
1
  [tox]
2
- envlist = py38,py39,py310,py311,py312
2
+ envlist = py39,py310,py311,py312,py313
3
3
 
4
4
  [testenv]
5
5
  usedevelop = true
@@ -1,9 +0,0 @@
1
- defusedxml
2
- python-dateutil
3
-
4
- [test]
5
- trytond<7.5,>=7.4
6
- trytond_party<7.5,>=7.4
7
-
8
- [trytond]
9
- trytond<7.5,>=7.4
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes