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.
- {proteus-7.4.1 → proteus-7.6.0}/CHANGELOG +4 -2
- {proteus-7.4.1 → proteus-7.6.0}/COPYRIGHT +2 -2
- {proteus-7.4.1/proteus.egg-info → proteus-7.6.0}/PKG-INFO +23 -8
- {proteus-7.4.1 → proteus-7.6.0}/doc/conf.py +7 -0
- {proteus-7.4.1 → proteus-7.6.0}/proteus/__init__.py +59 -37
- {proteus-7.4.1 → proteus-7.6.0}/proteus/config.py +6 -6
- {proteus-7.4.1 → proteus-7.6.0}/proteus/pyson.py +18 -18
- proteus-7.6.0/proteus/tests/test_action.py +49 -0
- {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/test_model.py +4 -4
- {proteus-7.4.1 → proteus-7.6.0/proteus.egg-info}/PKG-INFO +23 -8
- {proteus-7.4.1 → proteus-7.6.0}/proteus.egg-info/SOURCES.txt +1 -0
- proteus-7.6.0/proteus.egg-info/requires.txt +9 -0
- {proteus-7.4.1 → proteus-7.6.0}/setup.py +2 -2
- {proteus-7.4.1 → proteus-7.6.0}/tox.ini +1 -1
- proteus-7.4.1/proteus.egg-info/requires.txt +0 -9
- {proteus-7.4.1 → proteus-7.6.0}/LICENSE +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/MANIFEST.in +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/README.rst +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/doc/index.rst +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/doc/releases.rst +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/doc/requirements-doc.txt +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/__init__.py +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/common.py +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/test_config.py +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/test_context.py +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/test_readme.py +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/test_report.py +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/proteus/tests/test_wizard.py +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/proteus.egg-info/dependency_links.txt +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/proteus.egg-info/top_level.txt +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/proteus.egg-info/zip-safe +0 -0
- {proteus-7.4.1 → proteus-7.6.0}/setup.cfg +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
|
|
2
|
-
Version 7.
|
|
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-
|
|
2
|
-
Copyright (C) 2010-
|
|
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
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: proteus
|
|
3
|
-
Version: 7.
|
|
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.
|
|
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.
|
|
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.
|
|
37
|
+
Requires-Dist: trytond<7.7,>=7.6; extra == "trytond"
|
|
38
38
|
Provides-Extra: test
|
|
39
|
-
Requires-Dist: trytond<7.
|
|
40
|
-
Requires-Dist: trytond_party<7.
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
226
|
+
super().__set__(instance, value)
|
|
227
227
|
|
|
228
228
|
|
|
229
229
|
class DateDescriptor(FieldDescriptor):
|
|
230
230
|
def __get__(self, instance, owner):
|
|
231
|
-
value = super(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
257
|
+
super().__set__(instance, value)
|
|
258
258
|
|
|
259
259
|
|
|
260
260
|
class DictDescriptor(FieldDescriptor):
|
|
261
261
|
def __get__(self, instance, owner):
|
|
262
|
-
value = super(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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.
|
|
340
|
-
self.assertEqual(trigger.
|
|
339
|
+
trigger.on_time_ = True
|
|
340
|
+
self.assertEqual(trigger.on_create_, False)
|
|
341
341
|
|
|
342
|
-
trigger.
|
|
343
|
-
self.assertEqual(trigger.
|
|
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
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: proteus
|
|
3
|
-
Version: 7.
|
|
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.
|
|
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.
|
|
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.
|
|
37
|
+
Requires-Dist: trytond<7.7,>=7.6; extra == "trytond"
|
|
38
38
|
Provides-Extra: test
|
|
39
|
-
Requires-Dist: trytond<7.
|
|
40
|
-
Requires-Dist: trytond_party<7.
|
|
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
|
|
@@ -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.
|
|
85
|
+
python_requires='>=3.9',
|
|
86
86
|
install_requires=[
|
|
87
87
|
'defusedxml',
|
|
88
88
|
"python-dateutil",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|