Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package proteus for openSUSE:Factory checked in at 2022-03-07 17:48:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/proteus (Old) and /work/SRC/openSUSE:Factory/.proteus.new.1958 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "proteus" Mon Mar 7 17:48:08 2022 rev:21 rq:959984 version:6.0.5 Changes: -------- --- /work/SRC/openSUSE:Factory/proteus/proteus.changes 2022-01-26 21:27:07.585933679 +0100 +++ /work/SRC/openSUSE:Factory/.proteus.new.1958/proteus.changes 2022-03-07 17:48:55.967085333 +0100 @@ -1,0 +2,10 @@ +Wed Mar 2 11:34:12 UTC 2022 - Axel Braun <axel.br...@gmx.de> + +- Version 6.0.5 - Bugfix Release + +------------------------------------------------------------------- +Sat Feb 12 17:17:24 UTC 2022 - Axel Braun <axel.br...@gmx.de> + +- Version bump to Tryton 6.0 series + +------------------------------------------------------------------- Old: ---- proteus-5.0.11.tar.gz proteus-5.0.11.tar.gz.asc New: ---- proteus-6.0.5.tar.gz proteus-6.0.5.tar.gz.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ proteus.spec ++++++ --- /var/tmp/diff_new_pack.z2ARCv/_old 2022-03-07 17:48:56.623085143 +0100 +++ /var/tmp/diff_new_pack.z2ARCv/_new 2022-03-07 17:48:56.627085142 +0100 @@ -1,8 +1,8 @@ # # spec file for package proteus # -# Copyright (c) 2022 SUSE LLC -# Copyright (c) 2019 Dr. Axel Braun +# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2014-2021 Dr. Axel Braun # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,9 +17,9 @@ # -%define majorver 5.0 +%define majorver 6.0 Name: proteus -Version: %{majorver}.11 +Version: %{majorver}.5 Release: 0 Summary: A library to access Tryton's modules like a client License: GPL-3.0-or-later @@ -30,7 +30,6 @@ Source2: https://keybase.io/cedrickrier/pgp_keys.asc?fingerprint=7C5A4360F6DF81ABA91FD54D6FF50AFE03489130#/%{name}.keyring # List of additional build dependencies BuildRequires: fdupes -BuildRequires: python-rpm-macros BuildRequires: python3-devel BuildRequires: python3-lxml BuildRequires: python3-psycopg2 @@ -38,7 +37,7 @@ BuildRequires: python3-setuptools Requires: python3-dateutil Requires: trytond -BuildRoot: %{_tmppath}/%{name}-%{version}-build + BuildArch: noarch %description @@ -48,15 +47,15 @@ %setup -q %build -python3 setup.py build +%python3_build %install -python3 setup.py install --prefix=%_prefix --root=%buildroot +%python3_install --prefix=%_prefix --root=%buildroot %fdupes -s %{buildroot} %files %defattr(-,root,root) -%doc README +%doc README.rst %license LICENSE %{python3_sitelib}/* ++++++ proteus-5.0.11.tar.gz -> proteus-6.0.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/.drone.yml new/proteus-6.0.5/.drone.yml --- old/proteus-5.0.11/.drone.yml 2021-06-06 09:19:28.000000000 +0200 +++ new/proteus-6.0.5/.drone.yml 2021-09-27 23:36:47.000000000 +0200 @@ -1,21 +1,40 @@ clone: hg: image: plugins/hg + environment: + - HG_SHARE_POOL=/root/.cache/hg + volumes: + - cache:/root/.cache pipeline: tox: image: ${IMAGE} + environment: + - CFLAGS=-O0 + - TOX_TESTENV_PASSENV=CFLAGS CI_BUILD_NUMBER CI_JOB_NUMBER CI_JOB_ID commands: + - echo "[extensions]" >> /root/.hgrc + - echo "hgext.share =" >> /root/.hgrc + - echo "[share]" >> /root/.hgrc + - echo "pool = /root/.cache/hg" >> /root/.hgrc - pip install tox pydot - tox -e "${TOXENV}" volumes: - cache:/root/.cache + check_dist: + image: ${IMAGE} + commands: + - pip install twine + - python setup.py sdist + - twine check dist/* matrix: include: - - IMAGE: python:3.5 - TOXENV: py35 - IMAGE: python:3.6 TOXENV: py36 - IMAGE: python:3.7 TOXENV: py37 + - IMAGE: python:3.8 + TOXENV: py38 + - IMAGE: python:3.9 + TOXENV: py39 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/.flake8 new/proteus-6.0.5/.flake8 --- old/proteus-5.0.11/.flake8 1970-01-01 01:00:00.000000000 +0100 +++ new/proteus-6.0.5/.flake8 2021-09-27 23:36:47.000000000 +0200 @@ -0,0 +1,2 @@ +[flake8] +ignore=E123,E124,E126,E128,E741,W503 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/.hgtags new/proteus-6.0.5/.hgtags --- old/proteus-5.0.11/.hgtags 2022-01-15 16:19:08.000000000 +0100 +++ new/proteus-6.0.5/.hgtags 2022-03-01 19:23:18.000000000 +0100 @@ -15,14 +15,13 @@ cd4731fb373698d480a625a286de776a0b9bdc6e 4.6.0 3867b51d50056fda52882150a017a9dce6811264 4.8.0 e86f9d540f29dfcd39e8f198b50940c8b91250d6 5.0.0 -0835c94a8c6d24a067ffdda19fc9e09b8ec7489b 5.0.1 -7189865cf26973984b5ec6b2056caa2de0c1d2ab 5.0.2 -7e1fcdd39ecf86ae7e825b86822d4840f91725b8 5.0.3 -21a1c19303aefb60f54fe27ca5189071d856213c 5.0.4 -4ef3c1a7214f00a7e96a606bc2396acfb94aa2a7 5.0.5 -a5aabc18347d6b635ae3a57a7ec2ae4faa24aacf 5.0.6 -12da615377eb694e54bfbd0c12aa36779bb33b27 5.0.7 -df21f1e8edd84079d0619ef84b2f663e5a1c87e4 5.0.8 -a49aa4240d49a817c4d98bdc146a31f23722016d 5.0.9 -de793cd5456707bf07e76af2e04aeeb6c733460a 5.0.10 -d6494835473801df14b3c358a05119607a94443b 5.0.11 +f4a0b0e282f39c013c44e5012e6b85e4b8a4ae40 5.2.0 +03daaf3994610fde107d98d8458b2731e03c5306 5.4.0 +a8061a1d268776766c44ceb97094d229dbe31962 5.6.0 +8bc88e82ff9edc33725d08bc0c6f2862f221a127 5.8.0 +ac15e7f1881fd78d30eac0d7c772638265830c77 6.0.0 +14cd94a3e75463a9e4ab4c562cce82470070731b 6.0.1 +b579da9fa80282eaf0657e6d2826147c2c6c2f3d 6.0.2 +dc3d781b552d24dcfb7b0ced4322b1ba17e6ca09 6.0.3 +8e6d0969604981e6d024b203424b1b3f0bcd4cb3 6.0.4 +c20749d242200840432ab0b87ce7c0072ee1be34 6.0.5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/CHANGELOG new/proteus-6.0.5/CHANGELOG --- old/proteus-5.0.11/CHANGELOG 2022-01-15 16:19:08.000000000 +0100 +++ new/proteus-6.0.5/CHANGELOG 2022-03-01 19:23:17.000000000 +0100 @@ -1,35 +1,42 @@ -Version 5.0.11 - 2022-01-15 +Version 6.0.5 - 2022-03-01 * Bug fixes (see mercurial logs for details) +* Use defusedxml to parse XML (11244) -Version 5.0.10 - 2021-10-01 +Version 6.0.4 - 2022-01-15 * Bug fixes (see mercurial logs for details) -Version 5.0.9 - 2021-07-01 +Version 6.0.3 - 2021-10-01 * Bug fixes (see mercurial logs for details) -Version 5.0.8 - 2020-11-11 +Version 6.0.2 - 2021-07-01 * Bug fixes (see mercurial logs for details) -Version 5.0.7 - 2019-12-02 +Version 6.0.1 - 2021-05-15 * Bug fixes (see mercurial logs for details) -Version 5.0.6 - 2019-11-08 +Version 6.0.0 - 2021-05-03 * Bug fixes (see mercurial logs for details) +* Add support for Python 3.9 +* Unify PYSON string format -Version 5.0.5 - 2019-10-23 +Version 5.8.0 - 2020-11-02 * Bug fixes (see mercurial logs for details) +* Remove support for Python 3.5 +* Support PYSON comparison of date and datetime -Version 5.0.4 - 2019-09-15 +Version 5.6.0 - 2020-05-04 * Bug fixes (see mercurial logs for details) +* Add support for Python 3.8 +* Use same __str__ as trytond instances -Version 5.0.3 - 2019-04-22 +Version 5.4.0 - 2019-11-04 * Bug fixes (see mercurial logs for details) +* Support dot notation on PYSON Eval +* Add __slots__ -Version 5.0.2 - 2019-02-19 -* Bug fixes (see mercurial logs for details) - -Version 5.0.1 - 2018-12-02 +Version 5.2.0 - 2019-05-06 * Bug fixes (see mercurial logs for details) +* Remove support for Python 3.4 Version 5.0.0 - 2018-10-01 * Bug fixes (see mercurial logs for details) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/INSTALL new/proteus-6.0.5/INSTALL --- old/proteus-5.0.11/INSTALL 2019-10-23 13:12:22.000000000 +0200 +++ new/proteus-6.0.5/INSTALL 1970-01-01 01:00:00.000000000 +0100 @@ -1,27 +0,0 @@ -Installing proteus -================== - -Prerequisites -------------- - - * Python 3.4 or later (http://www.python.org/) - * python-dateutil (http://labix.org/python-dateutil) - * Optional: trytond (http://www.tryton.org/) - -Installation ------------- - -Once you've downloaded and unpacked the proteus source release, enter the -directory where the archive was unpacked, and run: - - python setup.py install - -Note that you may need administrator/root privileges for this step, as -this command will by default attempt to install module to the Python -site-packages directory on your system. - -For advanced options, please refer to the easy_install and/or the distutils -documentation: - - http://setuptools.readthedocs.io/en/latest/easy_install.html - http://docs.python.org/inst/inst.html diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/MANIFEST.in new/proteus-6.0.5/MANIFEST.in --- old/proteus-5.0.11/MANIFEST.in 2019-10-23 13:12:22.000000000 +0200 +++ new/proteus-6.0.5/MANIFEST.in 2021-09-27 23:36:47.000000000 +0200 @@ -1,5 +1,5 @@ -include LICENSE -include COPYRIGHT -include README -include INSTALL include CHANGELOG +include COPYRIGHT +include LICENSE +include README.rst +include doc/* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/PKG-INFO new/proteus-6.0.5/PKG-INFO --- old/proteus-5.0.11/PKG-INFO 2022-01-15 16:19:10.074019700 +0100 +++ new/proteus-6.0.5/PKG-INFO 2022-03-01 19:23:19.383111000 +0100 @@ -1,22 +1,22 @@ Metadata-Version: 2.1 Name: proteus -Version: 5.0.11 +Version: 6.0.5 Summary: Library to access Tryton server as a client Home-page: http://www.tryton.org/ Author: Tryton -Author-email: issue_trac...@tryton.org +Author-email: b...@tryton.org License: LGPL-3 -Download-URL: http://downloads.tryton.org/5.0/ -Description: proteus - ======= +Download-URL: http://downloads.tryton.org/6.0/ +Project-URL: Bug Tracker, https://bugs.tryton.org/ +Project-URL: Documentation, https://docs.tryton.org/ +Project-URL: Forum, https://www.tryton.org/forum +Project-URL: Source Code, https://hg.tryton.org/proteus +Description: ======================= + Tryton Scripting Client + ======================= A library to access Tryton's models like a client. - Installing - ---------- - - See INSTALL - Example of usage ---------------- @@ -29,7 +29,7 @@ >>> config = config.set_trytond('sqlite:///:memory:') - Installing a module + Activating a module ~~~~~~~~~~~~~~~~~~~ Find the module, call the activate button and run the upgrade wizard. @@ -80,7 +80,7 @@ Addresses are store on party with a `One2Many` field. So the new address just needs to be appended to the list `addresses`. - >>> address = party.addresses.new(zip='42') + >>> address = party.addresses.new(postal_code='42') >>> party.save() >>> party.addresses #doctest: +ELLIPSIS [proteus.Model.get('party.address')(...)] @@ -121,9 +121,9 @@ Addresses are ordered by sequence which means they can be stored following a specific order. The `set_sequence` method stores the current order. - >>> address = party.addresses.new(zip='69') + >>> address = party.addresses.new(postal_code='69') >>> party.save() - >>> address = party.addresses.new(zip='23') + >>> address = party.addresses.new(postal_code='23') >>> party.save() Now changing the order. @@ -137,32 +137,6 @@ >>> party.addresses == reversed_addresses True - Support - ------- - - If you encounter any problems with Tryton, please don't hesitate to ask - questions on the Tryton bug tracker, mailing list, wiki or IRC channel: - - http://bugs.tryton.org/ - http://groups.tryton.org/ - http://wiki.tryton.org/ - irc://irc.freenode.net/tryton - - License - ------- - - See LICENSE - - Copyright - --------- - - See COPYRIGHT - - - For more information please visit the Tryton web site: - - http://www.tryton.org/ - Keywords: tryton library cli Platform: any Classifier: Development Status :: 5 - Production/Stable @@ -173,12 +147,13 @@ Classifier: Intended Audience :: Legal Industry Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Office/Business -Requires-Python: >=3.4 +Requires-Python: >=3.6 Provides-Extra: trytond diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/README new/proteus-6.0.5/README --- old/proteus-5.0.11/README 2019-10-23 13:12:22.000000000 +0200 +++ new/proteus-6.0.5/README 1970-01-01 01:00:00.000000000 +0100 @@ -1,155 +0,0 @@ -proteus -======= - -A library to access Tryton's models like a client. - -Installing ----------- - -See INSTALL - -Example of usage ----------------- - - >>> from proteus import config, Model, Wizard, Report - -Configuration -~~~~~~~~~~~~~ - -Configuration to connect to a sqlite memory database using trytond as module. - - >>> config = config.set_trytond('sqlite:///:memory:') - -Installing a module -~~~~~~~~~~~~~~~~~~~ - -Find the module, call the activate button and run the upgrade wizard. - - >>> Module = Model.get('ir.module') - >>> party_module, = Module.find([('name', '=', 'party')]) - >>> party_module.click('activate') - >>> Wizard('ir.module.activate_upgrade').execute('upgrade') - -Creating a party -~~~~~~~~~~~~~~~~ - -First instanciate a new Party: - - >>> Party = Model.get('party.party') - >>> party = Party() - >>> party.id < 0 - True - -Fill the fields: - - >>> party.name = 'ham' - -Save the instance into the server: - - >>> party.save() - >>> party.name - 'ham' - >>> party.id > 0 - True - -Setting the language of the party -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The language on party is a `Many2One` relation field. So it requires to get a -`Model` instance as value. - - >>> Lang = Model.get('ir.lang') - >>> en, = Lang.find([('code', '=', 'en')]) - >>> party.lang = en - >>> party.save() - >>> party.lang.code - 'en' - -Creating an address for the party -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Addresses are store on party with a `One2Many` field. So the new address just -needs to be appended to the list `addresses`. - - >>> address = party.addresses.new(zip='42') - >>> party.save() - >>> party.addresses #doctest: +ELLIPSIS - [proteus.Model.get('party.address')(...)] - -Adding category to the party -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Categories are linked to party with a `Many2Many` field. - -So first create a category - - >>> Category = Model.get('party.category') - >>> category = Category() - >>> category.name = 'spam' - >>> category.save() - -Append it to categories of the party - - >>> party.categories.append(category) - >>> party.save() - >>> party.categories #doctest: +ELLIPSIS - [proteus.Model.get('party.category')(...)] - -Print party label -~~~~~~~~~~~~~~~~~ - -There is a label report on `Party`. - - >>> label = Report('party.label') - -The report is executed with a list of records and some extra data. - - >>> type_, data, print_, name = label.execute([party], {}) - -Sorting addresses and register order -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Addresses are ordered by sequence which means they can be stored following a -specific order. The `set_sequence` method stores the current order. - - >>> address = party.addresses.new(zip='69') - >>> party.save() - >>> address = party.addresses.new(zip='23') - >>> party.save() - -Now changing the order. - - >>> reversed_addresses = list(reversed(party.addresses)) - >>> while party.addresses: - ... _ = party.addresses.pop() - >>> party.addresses.extend(reversed_addresses) - >>> party.addresses.set_sequence() - >>> party.save() - >>> party.addresses == reversed_addresses - True - -Support -------- - -If you encounter any problems with Tryton, please don't hesitate to ask -questions on the Tryton bug tracker, mailing list, wiki or IRC channel: - - http://bugs.tryton.org/ - http://groups.tryton.org/ - http://wiki.tryton.org/ - irc://irc.freenode.net/tryton - -License -------- - -See LICENSE - -Copyright ---------- - -See COPYRIGHT - - -For more information please visit the Tryton web site: - - http://www.tryton.org/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/README.rst new/proteus-6.0.5/README.rst --- old/proteus-5.0.11/README.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/proteus-6.0.5/README.rst 2021-09-27 23:36:47.000000000 +0200 @@ -0,0 +1,125 @@ +======================= +Tryton Scripting Client +======================= + +A library to access Tryton's models like a client. + +Example of usage +---------------- + + >>> from proteus import config, Model, Wizard, Report + +Configuration +~~~~~~~~~~~~~ + +Configuration to connect to a sqlite memory database using trytond as module. + + >>> config = config.set_trytond('sqlite:///:memory:') + +Activating a module +~~~~~~~~~~~~~~~~~~~ + +Find the module, call the activate button and run the upgrade wizard. + + >>> Module = Model.get('ir.module') + >>> party_module, = Module.find([('name', '=', 'party')]) + >>> party_module.click('activate') + >>> Wizard('ir.module.activate_upgrade').execute('upgrade') + +Creating a party +~~~~~~~~~~~~~~~~ + +First instanciate a new Party: + + >>> Party = Model.get('party.party') + >>> party = Party() + >>> party.id < 0 + True + +Fill the fields: + + >>> party.name = 'ham' + +Save the instance into the server: + + >>> party.save() + >>> party.name + 'ham' + >>> party.id > 0 + True + +Setting the language of the party +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The language on party is a `Many2One` relation field. So it requires to get a +`Model` instance as value. + + >>> Lang = Model.get('ir.lang') + >>> en, = Lang.find([('code', '=', 'en')]) + >>> party.lang = en + >>> party.save() + >>> party.lang.code + 'en' + +Creating an address for the party +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Addresses are store on party with a `One2Many` field. So the new address just +needs to be appended to the list `addresses`. + + >>> address = party.addresses.new(postal_code='42') + >>> party.save() + >>> party.addresses #doctest: +ELLIPSIS + [proteus.Model.get('party.address')(...)] + +Adding category to the party +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Categories are linked to party with a `Many2Many` field. + +So first create a category + + >>> Category = Model.get('party.category') + >>> category = Category() + >>> category.name = 'spam' + >>> category.save() + +Append it to categories of the party + + >>> party.categories.append(category) + >>> party.save() + >>> party.categories #doctest: +ELLIPSIS + [proteus.Model.get('party.category')(...)] + +Print party label +~~~~~~~~~~~~~~~~~ + +There is a label report on `Party`. + + >>> label = Report('party.label') + +The report is executed with a list of records and some extra data. + + >>> type_, data, print_, name = label.execute([party], {}) + +Sorting addresses and register order +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Addresses are ordered by sequence which means they can be stored following a +specific order. The `set_sequence` method stores the current order. + + >>> address = party.addresses.new(postal_code='69') + >>> party.save() + >>> address = party.addresses.new(postal_code='23') + >>> party.save() + +Now changing the order. + + >>> reversed_addresses = list(reversed(party.addresses)) + >>> while party.addresses: + ... _ = party.addresses.pop() + >>> party.addresses.extend(reversed_addresses) + >>> party.addresses.set_sequence() + >>> party.save() + >>> party.addresses == reversed_addresses + True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/doc/index.rst new/proteus-6.0.5/doc/index.rst --- old/proteus-5.0.11/doc/index.rst 2019-10-23 13:12:22.000000000 +0200 +++ new/proteus-6.0.5/doc/index.rst 2021-09-27 23:36:47.000000000 +0200 @@ -1 +1,125 @@ -.. include:: ../README +======================= +Tryton Scripting Client +======================= + +A library to access Tryton's models like a client. + +Example of usage +---------------- + + >>> from proteus import config, Model, Wizard, Report + +Configuration +~~~~~~~~~~~~~ + +Configuration to connect to a sqlite memory database using trytond as module. + + >>> config = config.set_trytond('sqlite:///:memory:') + +Activating a module +~~~~~~~~~~~~~~~~~~~ + +Find the module, call the activate button and run the upgrade wizard. + + >>> Module = Model.get('ir.module') + >>> party_module, = Module.find([('name', '=', 'party')]) + >>> party_module.click('activate') + >>> Wizard('ir.module.activate_upgrade').execute('upgrade') + +Creating a party +~~~~~~~~~~~~~~~~ + +First instanciate a new Party: + + >>> Party = Model.get('party.party') + >>> party = Party() + >>> party.id < 0 + True + +Fill the fields: + + >>> party.name = 'ham' + +Save the instance into the server: + + >>> party.save() + >>> party.name + 'ham' + >>> party.id > 0 + True + +Setting the language of the party +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The language on party is a `Many2One` relation field. So it requires to get a +`Model` instance as value. + + >>> Lang = Model.get('ir.lang') + >>> en, = Lang.find([('code', '=', 'en')]) + >>> party.lang = en + >>> party.save() + >>> party.lang.code + 'en' + +Creating an address for the party +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Addresses are store on party with a `One2Many` field. So the new address just +needs to be appended to the list `addresses`. + + >>> address = party.addresses.new(postal_code='42') + >>> party.save() + >>> party.addresses #doctest: +ELLIPSIS + [proteus.Model.get('party.address')(...)] + +Adding category to the party +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Categories are linked to party with a `Many2Many` field. + +So first create a category + + >>> Category = Model.get('party.category') + >>> category = Category() + >>> category.name = 'spam' + >>> category.save() + +Append it to categories of the party + + >>> party.categories.append(category) + >>> party.save() + >>> party.categories #doctest: +ELLIPSIS + [proteus.Model.get('party.category')(...)] + +Print party label +~~~~~~~~~~~~~~~~~ + +There is a label report on `Party`. + + >>> label = Report('party.label') + +The report is executed with a list of records and some extra data. + + >>> type_, data, print_, name = label.execute([party], {}) + +Sorting addresses and register order +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Addresses are ordered by sequence which means they can be stored following a +specific order. The `set_sequence` method stores the current order. + + >>> address = party.addresses.new(postal_code='69') + >>> party.save() + >>> address = party.addresses.new(postal_code='23') + >>> party.save() + +Now changing the order. + + >>> reversed_addresses = list(reversed(party.addresses)) + >>> while party.addresses: + ... _ = party.addresses.pop() + >>> party.addresses.extend(reversed_addresses) + >>> party.addresses.set_sequence() + >>> party.save() + >>> party.addresses == reversed_addresses + True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/proteus/__init__.py new/proteus-6.0.5/proteus/__init__.py --- old/proteus-5.0.11/proteus/__init__.py 2022-01-06 01:03:41.000000000 +0100 +++ new/proteus-6.0.5/proteus/__init__.py 2022-01-15 16:17:52.000000000 +0100 @@ -10,7 +10,7 @@ import proteus.config -__version__ = "5.0.11" +__version__ = "6.0.5" __all__ = ['Model', 'Wizard', 'Report'] _MODELS = threading.local() @@ -18,6 +18,8 @@ class _EvalEnvironment(dict): 'Dictionary for evaluation' + __slots__ = ('parent', 'eval_type') + def __init__(self, parent, eval_type='eval'): super(_EvalEnvironment, self).__init__() self.parent = parent @@ -55,7 +57,8 @@ def __str__(self): return str(self.parent) - __repr__ = __str__ + def __repr__(self): + return repr(self.parent) def __contains__(self, item): if item == 'id': @@ -86,6 +89,8 @@ >>> Example().method() 1 """ + __slots__ = ('func') + def __init__(self, func): self.func = func @@ -175,6 +180,22 @@ super(NumericDescriptor, self).__set__(instance, value) +class SelectionDescriptor(FieldDescriptor): + + def __set__(self, instance, value): + assert isinstance(value, str) or value is None + # TODO add selection validation + super().__set__(instance, value) + + +class MultiSelectionDescriptor(FieldDescriptor): + + def __set__(self, instance, value): + assert isinstance(value, (list, type(None))) + # TODO add selection validation + super().__set__(instance, value) + + class ReferenceDescriptor(FieldDescriptor): def __get__(self, instance, owner): value = super(ReferenceDescriptor, self).__get__(instance, owner) @@ -407,7 +428,8 @@ 'char': CharDescriptor, 'text': CharDescriptor, 'binary': BinaryDescriptor, - 'selection': CharDescriptor, # TODO implement its own descriptor + 'selection': SelectionDescriptor, + 'multiselection': MultiSelectionDescriptor, 'integer': IntegerDescriptor, 'biginteger': IntegerDescriptor, 'float': FloatDescriptor, @@ -451,6 +473,8 @@ class MetaModel(type): 'Meta class for Model' + __slots__ = () + def __new__(mcs, name, bases, dict): if self.model_name in getattr(_MODELS, models_key): return getattr(_MODELS, models_key)[self.model_name] @@ -485,6 +509,9 @@ class ModelList(list): 'List for Model' + __slots__ = ('model_name', 'parent', 'parent_field_name', 'parent_name', + 'domain', 'context', 'add_remove', 'search_order', 'search_context', + 'record_removed', 'record_deleted') def __init__(self, definition, sequence=None, parent=None, parent_field_name=''): @@ -567,11 +594,12 @@ raise NotImplementedError insert.__doc__ = list.insert.__doc__ - def pop(self, index=-1): + def pop(self, index=-1, _changed=True): self.record_removed.add(self[index]) self[index]._group = None res = super(ModelList, self).pop(index) - self._changed() + if _changed: + self._changed() return res pop.__doc__ = list.pop.__doc__ @@ -654,6 +682,7 @@ class Model(object): 'Model class for Tryton records' + __slots__ = ('__id', '_values', '_changed', '_group', '__context') __counter = -1 _proxy = None @@ -729,7 +758,7 @@ name = name.encode('utf-8') class Spam(Model, metaclass=MetaModelFactory(name, config=config)()): - pass + __slots__ = () return Spam @classmethod @@ -745,8 +774,7 @@ del models[name] def __str__(self): - return '<%s(%d)>' % (self.__class__.__name__, self.id) - __str__.__doc__ = object.__str__.__doc__ + return '%s,%d' % (self.__class__.__name__, self.id) def __repr__(self): if self._config == proteus.config.get_config(): @@ -754,12 +782,11 @@ self.id) return "proteus.Model.get('%s', %s)(%d)" % (self.__class__.__name__, repr(self._config), self.id) - __repr__.__doc__ = object.__repr__.__doc__ def __eq__(self, other): if isinstance(other, Model): - return ((self.__class__.__name__, self.id) == - (other.__class__.__name__, other.id)) + return ((self.__class__.__name__, self.id) + == (other.__class__.__name__, other.id)) return NotImplemented def __ne__(self, other): @@ -1026,36 +1053,44 @@ scope = scope[i] else: res[arg] = scope - res['id'] = self.id return res def _on_change_set(self, field, value): if (self._fields[field]['type'] in ('one2many', 'many2many') and not isinstance(value, (list, tuple))): - to_remove = [] + if value and value.get('delete'): + for record_id in value['delete']: + for record in getattr(self, field): + if record.id == record_id: + getattr(self, field).remove(record, _changed=False) if value and value.get('remove'): for record_id in value['remove']: - for record in getattr(self, field): + for i, record in enumerate(getattr(self, field)): if record.id == record_id: - to_remove.append(record) - for record in to_remove: - # remove without signal - getattr(self, field).remove(record, _changed=False) + getattr(self, field).pop(i, _changed=False) if value and (value.get('add') or value.get('update')): for index, vals in value.get('add', []): group = getattr(self, field) Relation = Model.get( self._fields[field]['relation'], self._config) config = Relation._config + id_ = vals.pop('id', None) with config.reset_context(), \ config.set_context(self._context): - record = Relation(_group=group, _default=False) - record._set_on_change(vals) - # append without signal - if index == -1: - list.append(group, record) + record = Relation(id=id_, _group=group, _default=False) + try: + idx = group.index(record) + except ValueError: + # append without signal + if index == -1: + list.append(group, record) + else: + list.insert(group, index, record) else: - list.insert(group, index, record) + record = group[idx] + group.record_removed.discard(record) + group.record_deleted.discard(record) + record._set_on_change(vals) for vals in value.get('update', []): if 'id' not in vals: continue @@ -1146,6 +1181,9 @@ class Wizard(object): 'Wizard class for Tryton wizards' + __slots__ = ('name', 'form', 'form_state', 'actions', '_config', + '_context', '_proxy', 'session_id', 'start_state', 'end_state', + 'states', 'state', 'models', 'action') def __init__(self, name, models=None, action=None, config=None, context=None): @@ -1224,6 +1262,7 @@ class Report(object): 'Report class for Tryton reports' + __slots__ = ('name', '_config', '_context', '_proxy') def __init__(self, name, config=None, context=None): super(Report, self).__init__() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/proteus/config.py new/proteus-6.0.5/proteus/config.py --- old/proteus-5.0.11/proteus/config.py 2021-09-27 23:49:25.000000000 +0200 +++ new/proteus-6.0.5/proteus/config.py 2022-03-01 19:23:15.000000000 +0100 @@ -11,8 +11,12 @@ import xmlrpc.client from decimal import Decimal +import defusedxml.xmlrpc + __all__ = ['set_trytond', 'set_xmlrpc', 'get_config'] +defusedxml.xmlrpc.monkey_patch() + def dump_decimal(self, value, write): value = {'__class__': 'Decimal', @@ -21,13 +25,6 @@ self.dump_struct(value, write) -def dump_bytes(self, value, write): - self.write = write - value = xmlrpc.client.Binary(value) - value.encode(self) - del self.write - - def dump_date(self, value, write): value = {'__class__': 'date', 'year': value.year, @@ -67,8 +64,6 @@ xmlrpc.client.Marshaller.dispatch[datetime.date] = dump_date xmlrpc.client.Marshaller.dispatch[datetime.time] = dump_time xmlrpc.client.Marshaller.dispatch[datetime.timedelta] = dump_timedelta -if bytes != str: - xmlrpc.client.Marshaller.dispatch[bytes] = dump_bytes xmlrpc.client.Marshaller.dispatch[int] = dump_long @@ -100,6 +95,7 @@ return self.decoders[dct['__class__']](dct) return dct + XMLRPCDecoder.register('date', lambda dct: datetime.date(dct['year'], dct['month'], dct['day'])) XMLRPCDecoder.register('time', @@ -121,6 +117,7 @@ self._stack[mark:] = [dct] self._value = 0 + xmlrpc.client.Unmarshaller.dispatch['struct'] = end_struct _CONFIG = threading.local() @@ -239,6 +236,11 @@ super(TrytondConfig, self).__init__() if not database: database = os.environ.get('TRYTOND_DATABASE_URI') + elif (os.environ.get('TRYTOND_DATABASE_URI') + and not urllib.parse.urlparse(database).scheme): + url = urllib.parse.urlparse(os.environ['TRYTOND_DATABASE_URI']) + os.environ['TRYTOND_DATABASE_URI'] = urllib.parse.urlunparse( + url._replace(path=database)) else: os.environ['TRYTOND_DATABASE_URI'] = database if not config_file: @@ -332,7 +334,7 @@ super(XmlrpcConfig, self).__init__() self.url = url self.server = xmlrpc.client.ServerProxy( - url, allow_none=1, use_datetime=1, **kwargs) + url, allow_none=True, use_builtin_types=True, **kwargs) # TODO add user self.user = None self._context = self.server.model.res.user.get_preferences(True, {}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/proteus/pyson.py new/proteus-6.0.5/proteus/pyson.py --- old/proteus-5.0.11/proteus/pyson.py 2020-10-30 19:56:47.000000000 +0100 +++ new/proteus-6.0.5/proteus/pyson.py 2021-09-27 23:36:47.000000000 +0200 @@ -153,6 +153,12 @@ @staticmethod def eval(dct, context): + if '.' in dct['v']: + base, name = dct['v'].split('.', 1) + return Eval.eval({ + 'v': name, + 'd': dct['d'], + }, context.get(base) or {}) return context.get(dct['v'], dct['d']) @@ -296,11 +302,15 @@ super(Greater, self).__init__() for i in (statement1, statement2): if isinstance(i, PYSON): - assert i.types().issubset({int, float, type(None)}), \ - 'statement must be an integer or a float' + assert i.types().issubset({ + int, float, type(None), + datetime.datetime, datetime.date}), \ + 'statement must be an integer, float, date or datetime' else: - assert isinstance(i, (int, float, type(None))), \ - 'statement must be an integer or a float' + assert isinstance(i, ( + int, float, type(None), + datetime.datetime, datetime.date)), \ + 'statement must be an integer, float, date or datetime' if isinstance(equal, PYSON): if equal.types() != {bool}: equal = Bool(equal) @@ -332,11 +342,19 @@ dct[i] = 0.0 if not isinstance(dct[i], (int, float)): dct = dct.copy() - dct[i] = float(dct[i]) + stmt = dct[i] + if isinstance(stmt, datetime.datetime): + stmt = stmt.timestamp() + elif isinstance(stmt, datetime.date): + time = datetime.time(0, 0) + stmt = datetime.datetime.combine(stmt, time).timestamp() + dct[i] = float(stmt) return dct @staticmethod def eval(dct, context): + if dct['s1'] is None or dct['s2'] is None: + return False dct = Greater._convert(dct) if dct['e']: return dct['s1'] >= dct['s2'] @@ -353,6 +371,8 @@ @staticmethod def eval(dct, context): + if dct['s1'] is None or dct['s2'] is None: + return False dct = Less._convert(dct) if dct['e']: return dct['s1'] <= dct['s2'] @@ -495,7 +515,7 @@ class Date(PYSON): def __init__(self, year=None, month=None, day=None, - delta_years=0, delta_months=0, delta_days=0, **kwargs): + delta_years=0, delta_months=0, delta_days=0, start=None, **kwargs): year = kwargs.get('y', year) month = kwargs.get('M', month) day = kwargs.get('d', day) @@ -516,11 +536,13 @@ self._delta_years = delta_years self._delta_months = delta_months self._delta_days = delta_days + self._start = start @property def __repr_params__(self): return (self._year, self._month, self._day, - self._delta_years, self._delta_months, self._delta_days) + self._delta_years, self._delta_months, self._delta_days, + self._start) def pyson(self): return { @@ -531,6 +553,7 @@ 'dy': self._delta_years, 'dM': self._delta_months, 'dd': self._delta_days, + 'start': self._start, } def types(self): @@ -538,7 +561,12 @@ @staticmethod def eval(dct, context): - return datetime.date.today() + relativedelta( + today = dct.get('start') + if isinstance(today, datetime.datetime): + today = today.date() + if not isinstance(today, datetime.date): + today = datetime.date.today() + return today + relativedelta( year=dct['y'], month=dct['M'], day=dct['d'], @@ -554,7 +582,7 @@ hour=None, minute=None, second=None, microsecond=None, delta_years=0, delta_months=0, delta_days=0, delta_hours=0, delta_minutes=0, delta_seconds=0, - delta_microseconds=0, **kwargs): + delta_microseconds=0, start=None, **kwargs): hour = kwargs.get('h', hour) minute = kwargs.get('m', minute) second = kwargs.get('s', second) @@ -565,7 +593,7 @@ delta_microseconds = kwargs.get('dms', delta_microseconds) super(DateTime, self).__init__(year=year, month=month, day=day, delta_years=delta_years, delta_months=delta_months, - delta_days=delta_days, **kwargs) + delta_days=delta_days, start=start, **kwargs) for i in (hour, minute, second, microsecond, delta_hours, delta_minutes, delta_seconds, delta_microseconds): if isinstance(i, PYSON): @@ -588,9 +616,10 @@ date_params = super(DateTime, self).__repr_params__ return (date_params[:3] + (self._hour, self._minute, self._second, self._microsecond) - + date_params[3:] + + date_params[3:-1] + (self._delta_hours, self._delta_minutes, self._delta_seconds, - self._delta_microseconds)) + self._delta_microseconds) + + date_params[-1:]) def pyson(self): res = super(DateTime, self).pyson() @@ -610,7 +639,13 @@ @staticmethod def eval(dct, context): - return datetime.datetime.utcnow() + relativedelta( + now = dct.get('start') + if (isinstance(now, datetime.date) + and not isinstance(now, datetime.datetime)): + now = datetime.datetime.combine(now, datetime.time()) + if not isinstance(now, datetime.datetime): + now = datetime.datetime.utcnow() + return now + relativedelta( year=dct['y'], month=dct['M'], day=dct['d'], @@ -712,4 +747,6 @@ 'DateTime': DateTime, 'TimeDelta': TimeDelta, 'Len': Len, + 'true': True, + 'false': False, } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/proteus/tests/__init__.py new/proteus-6.0.5/proteus/tests/__init__.py --- old/proteus-5.0.11/proteus/tests/__init__.py 2018-10-01 15:51:19.000000000 +0200 +++ new/proteus-6.0.5/proteus/tests/__init__.py 2021-09-27 23:36:47.000000000 +0200 @@ -10,10 +10,11 @@ os.environ.setdefault('TRYTOND_DATABASE_URI', 'sqlite:///:memory:') os.environ.setdefault('DB_NAME', ':memory:') -from trytond.tests.test_tryton import doctest_setup, doctest_teardown +from trytond.tests.test_tryton import ( # noqa: E402 + doctest_setup, doctest_teardown) here = os.path.dirname(__file__) -readme = os.path.normpath(os.path.join(here, '..', '..', 'README')) +readme = os.path.normpath(os.path.join(here, '..', '..', 'README.rst')) def test_suite(): @@ -46,6 +47,7 @@ runner = unittest.TextTestRunner() return runner.run(suite) + if __name__ == '__main__': sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname( os.path.abspath(__file__))))) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/proteus/tests/test_model.py new/proteus-6.0.5/proteus/tests/test_model.py --- old/proteus-5.0.11/proteus/tests/test_model.py 2019-11-15 22:44:58.000000000 +0100 +++ new/proteus-6.0.5/proteus/tests/test_model.py 2021-09-27 23:36:47.000000000 +0200 @@ -108,6 +108,14 @@ self.assertEqual(len(User(groups=[x.id for x in groups]).groups), len(groups)) + def test_assign_unknown_field(self): + "Test it is not allowed to assign unknown field" + User = Model.get('res.user') + user = User() + + with self.assertRaises(AttributeError): + user.unknown_field = 'foo' + def test_save(self): User = Model.get('res.user') test = User() @@ -291,7 +299,6 @@ Group = Model.get('res.group') test = Group() test.name = 'Test delete' - test.login = 'test delete' test.save() test.delete() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/proteus.egg-info/PKG-INFO new/proteus-6.0.5/proteus.egg-info/PKG-INFO --- old/proteus-5.0.11/proteus.egg-info/PKG-INFO 2022-01-15 16:19:09.000000000 +0100 +++ new/proteus-6.0.5/proteus.egg-info/PKG-INFO 2022-03-01 19:23:18.000000000 +0100 @@ -1,22 +1,22 @@ Metadata-Version: 2.1 Name: proteus -Version: 5.0.11 +Version: 6.0.5 Summary: Library to access Tryton server as a client Home-page: http://www.tryton.org/ Author: Tryton -Author-email: issue_trac...@tryton.org +Author-email: b...@tryton.org License: LGPL-3 -Download-URL: http://downloads.tryton.org/5.0/ -Description: proteus - ======= +Download-URL: http://downloads.tryton.org/6.0/ +Project-URL: Bug Tracker, https://bugs.tryton.org/ +Project-URL: Documentation, https://docs.tryton.org/ +Project-URL: Forum, https://www.tryton.org/forum +Project-URL: Source Code, https://hg.tryton.org/proteus +Description: ======================= + Tryton Scripting Client + ======================= A library to access Tryton's models like a client. - Installing - ---------- - - See INSTALL - Example of usage ---------------- @@ -29,7 +29,7 @@ >>> config = config.set_trytond('sqlite:///:memory:') - Installing a module + Activating a module ~~~~~~~~~~~~~~~~~~~ Find the module, call the activate button and run the upgrade wizard. @@ -80,7 +80,7 @@ Addresses are store on party with a `One2Many` field. So the new address just needs to be appended to the list `addresses`. - >>> address = party.addresses.new(zip='42') + >>> address = party.addresses.new(postal_code='42') >>> party.save() >>> party.addresses #doctest: +ELLIPSIS [proteus.Model.get('party.address')(...)] @@ -121,9 +121,9 @@ Addresses are ordered by sequence which means they can be stored following a specific order. The `set_sequence` method stores the current order. - >>> address = party.addresses.new(zip='69') + >>> address = party.addresses.new(postal_code='69') >>> party.save() - >>> address = party.addresses.new(zip='23') + >>> address = party.addresses.new(postal_code='23') >>> party.save() Now changing the order. @@ -137,32 +137,6 @@ >>> party.addresses == reversed_addresses True - Support - ------- - - If you encounter any problems with Tryton, please don't hesitate to ask - questions on the Tryton bug tracker, mailing list, wiki or IRC channel: - - http://bugs.tryton.org/ - http://groups.tryton.org/ - http://wiki.tryton.org/ - irc://irc.freenode.net/tryton - - License - ------- - - See LICENSE - - Copyright - --------- - - See COPYRIGHT - - - For more information please visit the Tryton web site: - - http://www.tryton.org/ - Keywords: tryton library cli Platform: any Classifier: Development Status :: 5 - Production/Stable @@ -173,12 +147,13 @@ Classifier: Intended Audience :: Legal Industry Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Office/Business -Requires-Python: >=3.4 +Requires-Python: >=3.6 Provides-Extra: trytond diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/proteus.egg-info/SOURCES.txt new/proteus-6.0.5/proteus.egg-info/SOURCES.txt --- old/proteus-5.0.11/proteus.egg-info/SOURCES.txt 2022-01-15 16:19:09.000000000 +0100 +++ new/proteus-6.0.5/proteus.egg-info/SOURCES.txt 2022-03-01 19:23:19.000000000 +0100 @@ -1,11 +1,11 @@ .drone.yml +.flake8 .hgtags CHANGELOG COPYRIGHT -INSTALL LICENSE MANIFEST.in -README +README.rst setup.py tox.ini doc/index.rst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/proteus.egg-info/requires.txt new/proteus-6.0.5/proteus.egg-info/requires.txt --- old/proteus-5.0.11/proteus.egg-info/requires.txt 2022-01-15 16:19:09.000000000 +0100 +++ new/proteus-6.0.5/proteus.egg-info/requires.txt 2022-03-01 19:23:18.000000000 +0100 @@ -1,4 +1,5 @@ +defusedxml python-dateutil [trytond] -trytond<5.1,>=5.0 +trytond<6.1,>=6.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/setup.py new/proteus-6.0.5/setup.py --- old/proteus-5.0.11/setup.py 2019-10-23 13:12:22.000000000 +0200 +++ new/proteus-6.0.5/setup.py 2022-03-01 19:23:15.000000000 +0100 @@ -40,19 +40,39 @@ version = '%s.%s.dev0' % (major_version, minor_version) download_url = 'hg+http://hg.tryton.org/%s#egg=%s-%s' % ( name, name, version) +local_version = [] +if os.environ.get('CI_JOB_ID'): + local_version.append(os.environ['CI_JOB_ID']) +else: + for build in ['CI_BUILD_NUMBER', 'CI_JOB_NUMBER']: + if os.environ.get(build): + local_version.append(os.environ[build]) + else: + local_version = [] + break +if local_version: + version += '+' + '.'.join(local_version) dependency_links = [] if minor_version % 2: - dependency_links.append('https://trydevpi.tryton.org/') + dependency_links.append( + 'https://trydevpi.tryton.org/?local_version=' + + '.'.join(local_version)) setup(name=name, version=version, description='Library to access Tryton server as a client', - long_description=read('README'), + long_description=read('README.rst'), author='Tryton', - author_email='issue_trac...@tryton.org', + author_email='b...@tryton.org', url='http://www.tryton.org/', download_url=download_url, + project_urls={ + "Bug Tracker": 'https://bugs.tryton.org/', + "Documentation": 'https://docs.tryton.org/', + "Forum": 'https://www.tryton.org/forum', + "Source Code": 'https://hg.tryton.org/proteus', + }, keywords='tryton library cli', packages=find_packages(), classifiers=[ @@ -65,18 +85,20 @@ 'License :: OSI Approved :: ' 'GNU Library or Lesser General Public License (LGPL)', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Office/Business', ], platforms='any', license='LGPL-3', - python_requires='>=3.4', + python_requires='>=3.6', install_requires=[ + 'defusedxml', "python-dateutil", ], extras_require={ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/proteus-5.0.11/tox.ini new/proteus-6.0.5/tox.ini --- old/proteus-5.0.11/tox.ini 2019-10-23 13:12:22.000000000 +0200 +++ new/proteus-6.0.5/tox.ini 2021-09-27 23:36:47.000000000 +0200 @@ -1,5 +1,5 @@ [tox] -envlist = py34,py35,py36,py37,pypy3 +envlist = py36,py37,py38,py39,pypy3 [testenv] commands = {envpython} setup.py test @@ -7,4 +7,4 @@ setenv = TRYTOND_DATABASE_URI={env:TRYTOND_DATBASE_URI:sqlite://} DB_NAME={env:DB_NAME::memory:} -install_command = pip install --pre --find-links https://trydevpi.tryton.org/ {opts} {packages} +install_command = pip install --pre --find-links https://trydevpi.tryton.org/?local_version={env:CI_JOB_ID:{env:CI_BUILD_NUMBER}.{env:CI_JOB_NUMBER}} {opts} {packages}