From 69bc232aad76c83bb2b20553e0abc2ed7c971bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 26 Jun 2018 19:11:51 +0200 Subject: [PATCH] Remove support for Python 2.7 issue7497 review53261002 --- .drone.yml | 6 -- CHANGELOG | 1 + INSTALL | 2 +- invoice.py | 6 +- party.py | 6 +- product.py | 6 +- purchase.py | 13 ++-- setup.py | 21 ++---- stock.py | 12 ++-- tests/scenario_purchase.rst | 86 +++++++++++------------ tests/scenario_purchase_empty.rst | 10 +-- tests/scenario_purchase_modify_header.rst | 2 +- tox.ini | 6 +- 13 files changed, 76 insertions(+), 101 deletions(-) diff --git a/.drone.yml b/.drone.yml index e0bc2f7026..5da6083eb7 100644 --- a/.drone.yml +++ b/.drone.yml @@ -25,12 +25,6 @@ services: matrix: include: - - IMAGE: python:2.7 - TOXENV: py27 - DATABASE: sqlite - - IMAGE: python:2.7 - TOXENV: py27 - DATABASE: postgresql - IMAGE: python:3.4 TOXENV: py34 DATABASE: sqlite diff --git a/CHANGELOG b/CHANGELOG index 162a96a3d5..53aa79f7a9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Remove support for Python 2.7 * Invoice purchased product instead of received product * Prevent delete lines if purchase is not draft or canceled * Use today as fallback for purchase date report diff --git a/INSTALL b/INSTALL index c131afe924..1f3c0b8fa5 100644 --- a/INSTALL +++ b/INSTALL @@ -4,7 +4,7 @@ Installing trytond_purchase Prerequisites ------------- - * Python 2.7 or later (http://www.python.org/) + * Python 3.4 or later (http://www.python.org/) * trytond (http://www.tryton.org/) * python-sql 0.4 or later (http://code.google.com/p/python-sql/) * trytond_company (http://www.tryton.org/) diff --git a/invoice.py b/invoice.py index 3d43d35c4e..a95d0d9f59 100644 --- a/invoice.py +++ b/invoice.py @@ -26,8 +26,7 @@ def process_purchase(func): return wrapper -class Invoice: - __metaclass__ = PoolMeta +class Invoice(metaclass=PoolMeta): __name__ = 'account.invoice' purchase_exception_state = fields.Function(fields.Selection([ ('', ''), @@ -110,8 +109,7 @@ class Invoice: super(Invoice, cls).cancel(invoices) -class InvoiceLine: - __metaclass__ = PoolMeta +class InvoiceLine(metaclass=PoolMeta): __name__ = 'account.invoice.line' @classmethod diff --git a/party.py b/party.py index 8b91ea1c32..ebbf15df10 100644 --- a/party.py +++ b/party.py @@ -5,8 +5,7 @@ from trytond.pool import PoolMeta, Pool __all__ = ['PartyReplace', 'PartyErase'] -class PartyReplace: - __metaclass__ = PoolMeta +class PartyReplace(metaclass=PoolMeta): __name__ = 'party.replace' @classmethod @@ -17,8 +16,7 @@ class PartyReplace: ] -class PartyErase: - __metaclass__ = PoolMeta +class PartyErase(metaclass=PoolMeta): __name__ = 'party.erase' @classmethod diff --git a/product.py b/product.py index 3e8da479df..a68b69f7a9 100644 --- a/product.py +++ b/product.py @@ -17,8 +17,7 @@ from trytond.modules.product import price_digits __all__ = ['Template', 'Product', 'ProductSupplier', 'ProductSupplierPrice'] -class Template: - __metaclass__ = PoolMeta +class Template(metaclass=PoolMeta): __name__ = "product.template" purchasable = fields.Boolean('Purchasable', states={ 'readonly': ~Eval('active', True), @@ -85,8 +84,7 @@ class Template: super(Template, cls).write(*args) -class Product: - __metaclass__ = PoolMeta +class Product(metaclass=PoolMeta): __name__ = 'product.product' purchase_price_uom = fields.Function(fields.Numeric( diff --git a/purchase.py b/purchase.py index 893edd7b87..9d2f400282 100644 --- a/purchase.py +++ b/purchase.py @@ -65,7 +65,7 @@ def search_shipments_returns(model_name): return [('lines.moves.shipment' + nested,) + tuple(clause[1:3]) + (model_name,) + tuple(clause[3:])] else: - if isinstance(clause[2], basestring): + if isinstance(clause[2], str): target = 'rec_name' else: target = 'id' @@ -471,7 +471,7 @@ class Purchase(Workflow, ModelSQL, ModelView, TaxableMixin): for line in self.lines: self.untaxed_amount += getattr(line, 'amount', None) or 0 taxes = self._get_taxes() - self.tax_amount = sum(t['amount'] for t in taxes.itervalues()) + self.tax_amount = sum(t['amount'] for t in taxes.values()) if self.currency: self.untaxed_amount = self.currency.round(self.untaxed_amount) self.tax_amount = self.currency.round(self.tax_amount) @@ -498,7 +498,7 @@ class Purchase(Workflow, ModelSQL, ModelView, TaxableMixin): return taxable_lines def get_tax_amount(self): - taxes = self._get_taxes().itervalues() + taxes = iter(self._get_taxes().values()) return sum((tax['amount'] for tax in taxes), Decimal(0)) @classmethod @@ -538,7 +538,7 @@ class Purchase(Workflow, ModelSQL, ModelView, TaxableMixin): 'tax_amount': tax_amount, 'total_amount': total_amount, } - for key in result.keys(): + for key in list(result.keys()): if key not in names: del result[key] return result @@ -1342,10 +1342,7 @@ class PurchaseLine(sequence_ordered(), ModelSQL, ModelView): dates = filter( None, (m.effective_date or m.planned_date for m in self.moves if m.state != 'cancel')) - if dates: - return min(dates) - else: - return + return min(dates, default=None) delivery_date = None if self.delivery_date_edit: delivery_date = self.delivery_date_store diff --git a/setup.py b/setup.py index 0e35359b73..bd6da54edc 100644 --- a/setup.py +++ b/setup.py @@ -1,15 +1,12 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from setuptools import setup -import re -import os import io -try: - from configparser import ConfigParser -except ImportError: - from ConfigParser import ConfigParser +import os +import re +from configparser import ConfigParser +from setuptools import setup def read(fname): @@ -27,6 +24,7 @@ def get_require_version(name): major_version, minor_version + 1) return require + config = ConfigParser() config.readfp(open('tryton.cfg')) info = dict(config.items('tryton')) @@ -102,7 +100,6 @@ setup(name=name, 'Natural Language :: Slovenian', 'Natural Language :: Spanish', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', @@ -112,6 +109,7 @@ setup(name=name, 'Topic :: Office/Business :: Financial :: Accounting', ], license='GPL-3', + python_requires='>=3.4', install_requires=requires, dependency_links=dependency_links, zip_safe=False, @@ -121,10 +119,5 @@ setup(name=name, """, test_suite='tests', test_loader='trytond.test_loader:Loader', - use_2to3=True, - convert_2to3_doctests=[ - 'tests/scenario_purchase.rst', - 'tests/scenario_purchase_empty.rst', - ], tests_require=tests_require, ) diff --git a/stock.py b/stock.py index e6fe1a1a3f..dbb507e705 100644 --- a/stock.py +++ b/stock.py @@ -15,8 +15,7 @@ from trytond.modules.product import price_digits __all__ = ['ShipmentIn', 'ShipmentInReturn', 'Move', 'Location'] -class ShipmentIn: - __metaclass__ = PoolMeta +class ShipmentIn(metaclass=PoolMeta): __name__ = 'stock.shipment.in' @classmethod @@ -83,8 +82,7 @@ class ShipmentIn: return super(ShipmentIn, cls).draft(shipments) -class ShipmentInReturn: - __metaclass__ = PoolMeta +class ShipmentInReturn(metaclass=PoolMeta): __name__ = 'stock.shipment.in.return' @classmethod @@ -166,8 +164,7 @@ class ShipmentInReturn: return super(ShipmentInReturn, cls).draft(shipments) -class Move: - __metaclass__ = PoolMeta +class Move(metaclass=PoolMeta): __name__ = 'stock.move' purchase = fields.Function(fields.Many2One('purchase.purchase', 'Purchase', states={ @@ -355,8 +352,7 @@ class Move: Purchase.process(purchases) -class Location: - __metaclass__ = PoolMeta +class Location(metaclass=PoolMeta): __name__ = 'stock.location' supplier_return_location = fields.Many2One( diff --git a/tests/scenario_purchase.rst b/tests/scenario_purchase.rst index d280f2c20b..197a742f35 100644 --- a/tests/scenario_purchase.rst +++ b/tests/scenario_purchase.rst @@ -125,7 +125,7 @@ Create an Inventory:: >>> inventory_line.expected_quantity = 0.0 >>> inventory.click('confirm') >>> inventory.state - u'done' + 'done' Purchase 5 products:: @@ -157,11 +157,11 @@ Purchase 5 products:: >>> purchase.untaxed_amount, purchase.tax_amount, purchase.total_amount (Decimal('25.00'), Decimal('2.50'), Decimal('27.50')) >>> purchase.state - u'processing' + 'processing' >>> purchase.shipment_state - u'waiting' + 'waiting' >>> purchase.invoice_state - u'waiting' + 'waiting' >>> len(purchase.moves), len(purchase.shipment_returns), len(purchase.invoices) (2, 0, 1) >>> invoice, = purchase.invoices @@ -189,9 +189,9 @@ Post invoice and check no new invoices:: >>> invoice.click('post') >>> purchase.reload() >>> purchase.shipment_state - u'waiting' + 'waiting' >>> purchase.invoice_state - u'waiting' + 'waiting' >>> len(purchase.moves), len(purchase.shipment_returns), len(purchase.invoices) (2, 0, 1) @@ -217,11 +217,11 @@ Purchase 5 products with an invoice method 'on shipment':: >>> purchase.click('confirm') >>> purchase.click('process') >>> purchase.state - u'processing' + 'processing' >>> purchase.shipment_state - u'waiting' + 'waiting' >>> purchase.invoice_state - u'none' + 'none' >>> len(purchase.moves), len(purchase.shipment_returns), len(purchase.invoices) (2, 0, 0) @@ -250,17 +250,17 @@ Validate Shipments:: >>> shipment.click('done') >>> purchase.reload() >>> purchase.shipment_state - u'received' + 'received' >>> len(purchase.shipments), len(purchase.shipment_returns) (1, 0) Open supplier invoice:: >>> purchase.invoice_state - u'waiting' + 'waiting' >>> invoice, = purchase.invoices >>> invoice.type - u'in' + 'in' >>> invoice_line1, invoice_line2 = sorted(invoice.lines, ... key=lambda l: l.quantity or 0) >>> for line in invoice.lines: @@ -289,9 +289,9 @@ Create the report:: >>> purchase_report = Report('purchase.purchase') >>> ext, _, _, name = purchase_report.execute([purchase], {}) >>> ext - u'odt' + 'odt' >>> name - u'Purchase' + 'Purchase' Create a Return:: @@ -311,11 +311,11 @@ Create a Return:: >>> return_.click('confirm') >>> return_.click('process') >>> return_.state - u'processing' + 'processing' >>> return_.shipment_state - u'waiting' + 'waiting' >>> return_.invoice_state - u'none' + 'none' >>> (len(return_.shipments), len(return_.shipment_returns), ... len(return_.invoices)) (0, 1, 0) @@ -325,30 +325,30 @@ Check Return Shipments:: >>> ShipmentReturn = Model.get('stock.shipment.in.return') >>> ship_return, = return_.shipment_returns >>> ship_return.state - u'waiting' + 'waiting' >>> move_return, = ship_return.moves >>> move_return.product.rec_name - u'product' + 'product' >>> move_return.quantity 4.0 >>> ship_return.click('assign_try') True >>> ship_return.click('done') >>> ship_return.state - u'done' + 'done' >>> return_.reload() >>> return_.state - u'processing' + 'processing' >>> return_.shipment_state - u'received' + 'received' >>> return_.invoice_state - u'waiting' + 'waiting' Open supplier credit note:: >>> credit_note, = return_.invoices >>> credit_note.type - u'in' + 'in' >>> len(credit_note.lines) 1 >>> sum(l.quantity for l in credit_note.lines) @@ -378,11 +378,11 @@ Mixing return and purchase:: >>> mix.click('confirm') >>> mix.click('process') >>> mix.state - u'processing' + 'processing' >>> mix.shipment_state - u'waiting' + 'waiting' >>> mix.invoice_state - u'waiting' + 'waiting' >>> len(mix.moves), len(mix.shipment_returns), len(mix.invoices) (2, 1, 1) @@ -408,7 +408,7 @@ Checking Shipments:: >>> mix_return.click('done') >>> move_return, = mix_return.moves >>> move_return.product.rec_name - u'product' + 'product' >>> move_return.quantity 2.0 @@ -417,7 +417,7 @@ Checking the invoice:: >>> mix.reload() >>> mix_invoice, = mix.invoices >>> mix_invoice.type - u'in' + 'in' >>> len(mix_invoice.lines) 3 >>> sorted(l.quantity for l in mix_invoice.lines if l.quantity) @@ -447,11 +447,11 @@ Mixing stuff with an invoice method 'on shipment':: >>> mix.click('confirm') >>> mix.click('process') >>> mix.state - u'processing' + 'processing' >>> mix.shipment_state - u'waiting' + 'waiting' >>> mix.invoice_state - u'none' + 'none' >>> len(mix.moves), len(mix.shipment_returns), len(mix.invoices) (2, 1, 0) @@ -477,7 +477,7 @@ Checking Shipments:: >>> mix_return.click('done') >>> move_return, = mix_return.moves >>> move_return.product.rec_name - u'product' + 'product' >>> move_return.quantity 3.0 @@ -494,11 +494,11 @@ Purchase services:: >>> service_purchase.click('confirm') >>> service_purchase.click('process') >>> service_purchase.state - u'processing' + 'processing' >>> service_purchase.shipment_state - u'none' + 'none' >>> service_purchase.invoice_state - u'waiting' + 'waiting' >>> service_invoice, = service_purchase.invoices Pay the service invoice:: @@ -511,17 +511,17 @@ Pay the service invoice:: >>> pay.execute('choice') >>> service_invoice.reload() >>> service_invoice.state - u'paid' + 'paid' Check service purchase states:: >>> service_purchase.reload() >>> service_purchase.invoice_state - u'paid' + 'paid' >>> service_purchase.shipment_state - u'none' + 'none' >>> service_purchase.state - u'done' + 'done' Create a purchase to be invoiced on shipment partially and check correctly linked to invoices:: @@ -556,7 +556,7 @@ linked to invoices:: >>> stock_move.quantity 5.0 >>> stock_move.state - u'done' + 'done' Create a purchase to be invoiced on order, partially send it and check correctly linked to invoices:: @@ -592,11 +592,11 @@ correctly linked to invoices:: >>> draft_stock_move.quantity 2.0 >>> draft_stock_move.state - u'draft' + 'draft' >>> stock_move.quantity 8.0 >>> stock_move.state - u'done' + 'done' Deleting a line from a invoice should recreate it:: diff --git a/tests/scenario_purchase_empty.rst b/tests/scenario_purchase_empty.rst index ff1f4023e3..44345a2582 100644 --- a/tests/scenario_purchase_empty.rst +++ b/tests/scenario_purchase_empty.rst @@ -37,7 +37,7 @@ Create empty purchase:: >>> purchase.party = supplier >>> purchase.click('quote') >>> purchase.state - u'quotation' + 'quotation' >>> purchase.untaxed_amount Decimal('0') >>> purchase.tax_amount @@ -46,17 +46,17 @@ Create empty purchase:: Decimal('0') >>> purchase.click('confirm') >>> purchase.state - u'confirmed' + 'confirmed' >>> purchase.click('process') >>> purchase.state - u'done' + 'done' >>> purchase.shipment_state - u'none' + 'none' >>> len(purchase.moves) 0 >>> len(purchase.shipment_returns) 0 >>> purchase.invoice_state - u'none' + 'none' >>> len(purchase.invoices) 0 diff --git a/tests/scenario_purchase_modify_header.rst b/tests/scenario_purchase_modify_header.rst index 6b0ed3b4b1..ec3440048b 100644 --- a/tests/scenario_purchase_modify_header.rst +++ b/tests/scenario_purchase_modify_header.rst @@ -92,6 +92,6 @@ Change the party:: >>> modify_header.execute('modify') >>> purchase.party.name - u'Another Supplier' + 'Another Supplier' >>> purchase.untaxed_amount, purchase.tax_amount, purchase.total_amount (Decimal('15.00'), Decimal('0'), Decimal('15.00')) diff --git a/tox.ini b/tox.ini index 2082ab4f4d..adbcbf8431 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,11 @@ [tox] -envlist = {py27,py34,py35,py36}-{sqlite,postgresql},pypy-{sqlite,postgresql} +envlist = {py34,py35,py36}-{sqlite,postgresql},pypy3-{sqlite,postgresql} [testenv] commands = {envpython} setup.py test deps = - {py27,py34,py35,py36}-postgresql: psycopg2 >= 2.5 - pypy-postgresql: psycopg2cffi >= 2.5 + {py34,py35,py36}-postgresql: psycopg2 >= 2.5 + pypy3-postgresql: psycopg2cffi >= 2.5 sqlite: sqlitebck setenv = sqlite: TRYTOND_DATABASE_URI={env:SQLITE_URI:sqlite://}