From a43da122fbf2bbd25fbdba43b5b432988c0b1129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 26 Jun 2018 19:11:42 +0200 Subject: [PATCH] Remove support for Python 2.7 issue7497 review53261002 --- .drone.yml | 6 ----- CHANGELOG | 1 + INSTALL | 2 +- account.py | 22 +++++++------------ invoice.py | 22 +++++++++---------- party.py | 12 ++++------ setup.py | 22 ++++++------------- tests/scenario_invoice.rst | 14 ++++++------ tests/scenario_invoice_alternate_currency.rst | 6 ++--- tests/scenario_invoice_supplier.rst | 16 +++++++------- tests/test_account_invoice.py | 8 +++---- tox.ini | 6 ++--- 12 files changed, 57 insertions(+), 80 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 127dabb353..57fda354e6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Remove support for Python 2.7 * Use today as fallback for invoice date report Version 4.8.0 - 2018-04-23 diff --git a/INSTALL b/INSTALL index 547bcb3e26..6cd052632f 100644 --- a/INSTALL +++ b/INSTALL @@ -4,7 +4,7 @@ Installing trytond_account_invoice Prerequisites ------------- - * Python 2.7 or later (http://www.python.org/) + * Python 3.4 or later (http://www.python.org/) * python-dateutil (http://labix.org/python-dateutil) * python-sql (http://code.google.com/p/python-sql/) * trytond (http://www.tryton.org/) diff --git a/account.py b/account.py index b81099ec38..d200f688bc 100644 --- a/account.py +++ b/account.py @@ -18,8 +18,7 @@ __all__ = ['FiscalYear', 'RenewFiscalYear'] -class FiscalYear: - __metaclass__ = PoolMeta +class FiscalYear(metaclass=PoolMeta): __name__ = 'account.fiscalyear' invoice_sequences = fields.One2Many( 'account.fiscalyear.invoice_sequence', 'fiscalyear', @@ -72,8 +71,7 @@ class FiscalYear: return [{}] -class Period: - __metaclass__ = PoolMeta +class Period(metaclass=PoolMeta): __name__ = 'account.period' @classmethod @@ -188,8 +186,7 @@ class InvoiceSequence(sequence_ordered(), ModelSQL, ModelView, MatchMixin): return Transaction().context.get('company') -class Move: - __metaclass__ = PoolMeta +class Move(metaclass=PoolMeta): __name__ = 'account.move' @classmethod @@ -197,8 +194,7 @@ class Move: return super(Move, cls)._get_origin() + ['account.invoice'] -class MoveLine: - __metaclass__ = PoolMeta +class MoveLine(metaclass=PoolMeta): __name__ = 'account.move.line' invoice_payment = fields.Function(fields.Many2One( @@ -231,7 +227,7 @@ class MoveLine: pool = Pool() InvoicePaymentLine = pool.get('account.invoice-account.move.line') - ids = map(int, lines) + ids = list(map(int, lines)) result = dict.fromkeys(ids, None) for sub_ids in grouped_slice(ids): payment_lines = InvoicePaymentLine.search([ @@ -253,8 +249,7 @@ class MoveLine: return [('invoice_payments',) + tuple(domain[1:])] -class Reconciliation: - __metaclass__ = PoolMeta +class Reconciliation(metaclass=PoolMeta): __name__ = 'account.move.reconciliation' @classmethod @@ -285,9 +280,8 @@ class Reconciliation: Invoice.process(invoices) -class RenewFiscalYear: +class RenewFiscalYear(metaclass=PoolMeta): __name__ = 'account.fiscalyear.renew' - __metaclass__ = PoolMeta @property def invoice_sequence_fields(self): @@ -306,7 +300,7 @@ class RenewFiscalYear: for field in self.invoice_sequence_fields: sequence = getattr(invoice_sequence, field, None) sequences[sequence.id] = sequence - copies = Sequence.copy(sequences.values(), default={ + copies = Sequence.copy(list(sequences.values()), default={ 'next_number': 1, }) diff --git a/invoice.py b/invoice.py index 5b1025c4fb..3e9981b4f8 100644 --- a/invoice.py +++ b/invoice.py @@ -576,7 +576,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): for key in computed_taxes: if key not in tax_keys: self.tax_amount += computed_taxes[key]['amount'] - value = InvoiceTax.default_get(InvoiceTax._fields.keys()) + value = InvoiceTax.default_get(list(InvoiceTax._fields.keys())) value.update(computed_taxes[key]) invoice_tax = InvoiceTax(**value) if invoice_tax.tax: @@ -680,7 +680,7 @@ class Invoice(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 @@ -880,7 +880,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): def _compute_taxes(self): taxes = self._get_taxes() - for tax in taxes.itervalues(): + for tax in taxes.values(): tax['invoice'] = self.id return taxes @@ -895,7 +895,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): continue computed_taxes = invoice._compute_taxes() if not invoice.taxes: - to_create.extend([tax for tax in computed_taxes.values()]) + to_create.extend(computed_taxes.values()) else: tax_keys = [] for tax in invoice.taxes: @@ -1119,7 +1119,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): ] def get_origins(self, name): - return ', '.join(set(itertools.ifilter(None, + return ', '.join(set(filter(None, (l.origin_name for l in self.lines)))) @classmethod @@ -1209,7 +1209,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): if not l.reconciliation] best = Result([], self.total_amount) - for n in xrange(len(lines), 0, -1): + for n in range(len(lines), 0, -1): for comb_lines in combinations(lines, n): remainder = sum((l.debit - l.credit) for l in comb_lines) remainder -= amount @@ -1287,7 +1287,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): def add_payment_lines(cls, payments): "Add value lines to the key invoice from the payment dictionary." to_write = [] - for invoice, lines in payments.iteritems(): + for invoice, lines in payments.items(): if invoice.state == 'paid': cls.raise_user_error('modify_payment_lines_invoice_paid', { 'invoice': invoice.rec_name, @@ -1304,7 +1304,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): PaymentLine = pool.get('account.invoice-account.move.line') payments = defaultdict(list) - ids = map(int, lines) + ids = list(map(int, lines)) for sub_ids in grouped_slice(ids): payment_lines = PaymentLine.search([ ('line', 'in', list(sub_ids)), @@ -1313,7 +1313,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): payments[payment_line.invoice].append(payment_line.line) to_write = [] - for invoice, lines in payments.iteritems(): + for invoice, lines in payments.items(): if invoice.state == 'paid': cls.raise_user_error('modify_payment_lines_invoice_paid', { 'invoice': invoice.rec_name, @@ -1360,7 +1360,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): cls.update_taxes(new_invoices) if refund: cls.post(new_invoices) - for invoice, new_invoice in itertools.izip(invoices, new_invoices): + for invoice, new_invoice in zip(invoices, new_invoices): if new_invoice.state == 'posted': MoveLine.reconcile([l for l in invoice.lines_to_pay if not l.reconciliation] + @@ -1887,7 +1887,7 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin): def get_invoice_taxes(self, name): if not self.invoice: return - taxes_keys = self._get_taxes().keys() + taxes_keys = list(self._get_taxes().keys()) taxes = [] for tax in self.invoice.taxes: if tax.manual: diff --git a/party.py b/party.py index d1cd29d056..761034bf48 100644 --- a/party.py +++ b/party.py @@ -13,14 +13,12 @@ supplier_payment_term = fields.Many2One( 'account.invoice.payment_term', "Supplier Payment Term") -class Address: - __metaclass__ = PoolMeta +class Address(metaclass=PoolMeta): __name__ = 'party.address' invoice = fields.Boolean('Invoice') -class ContactMechanism: - __metaclass__ = PoolMeta +class ContactMechanism(metaclass=PoolMeta): __name__ = 'party.contact_mechanism' invoice = fields.Boolean('Invoice') @@ -91,8 +89,7 @@ class PartyPaymentTerm(ModelSQL, ValueMixin): parent='party', fields=fields) -class PartyReplace: - __metaclass__ = PoolMeta +class PartyReplace(metaclass=PoolMeta): __name__ = 'party.replace' @classmethod @@ -103,8 +100,7 @@ class PartyReplace: ] -class PartyErase: - __metaclass__ = PoolMeta +class PartyErase(metaclass=PoolMeta): __name__ = 'party.erase' @classmethod diff --git a/setup.py b/setup.py index 5a273f46ef..52fc95627a 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, @@ -122,10 +120,4 @@ setup(name=name, test_suite='tests', test_loader='trytond.test_loader:Loader', tests_require=tests_require, - use_2to3=True, - convert_2to3_doctests=[ - 'tests/scenario_invoice.rst', - 'tests/scenario_invoice_alternate_currency.rst', - 'tests/scenario_invoice_supplier.rst', - ], ) diff --git a/tests/scenario_invoice.rst b/tests/scenario_invoice.rst index 13e7d7e508..eabb4bc7b0 100644 --- a/tests/scenario_invoice.rst +++ b/tests/scenario_invoice.rst @@ -158,9 +158,9 @@ Post invoice:: >>> invoice.click('post') >>> invoice.state - u'posted' + 'posted' >>> invoice.tax_identifier.code - u'BE0897290877' + 'BE0897290877' >>> invoice.untaxed_amount Decimal('220.00') >>> invoice.tax_amount @@ -206,7 +206,7 @@ Credit invoice with refund:: >>> credit.execute('credit') >>> invoice.reload() >>> invoice.state - u'paid' + 'paid' >>> invoice.reconciled == today True >>> receivable.reload() @@ -295,7 +295,7 @@ Pay invoice:: >>> pay.execute('pay') >>> invoice.state - u'paid' + 'paid' Create empty invoice:: @@ -304,7 +304,7 @@ Create empty invoice:: >>> invoice.payment_term = payment_term >>> invoice.click('post') >>> invoice.state - u'paid' + 'paid' Create some complex invoice and test its taxes base rounding:: @@ -355,14 +355,14 @@ Create a paid invoice:: 'end' >>> invoice.tax_identifier >>> invoice.state - u'paid' + 'paid' The invoice is posted when the reconciliation is deleted:: >>> invoice.payment_lines[0].reconciliation.delete() >>> invoice.reload() >>> invoice.state - u'posted' + 'posted' >>> invoice.tax_identifier Credit invoice with non line lines:: diff --git a/tests/scenario_invoice_alternate_currency.rst b/tests/scenario_invoice_alternate_currency.rst index 6e7ee55409..e42a6d151e 100644 --- a/tests/scenario_invoice_alternate_currency.rst +++ b/tests/scenario_invoice_alternate_currency.rst @@ -135,7 +135,7 @@ Create invoice with alternate currency:: Decimal('460.00') >>> invoice.click('post') >>> invoice.state - u'posted' + 'posted' >>> invoice.untaxed_amount Decimal('420.00') >>> invoice.tax_amount @@ -163,7 +163,7 @@ Pay the invoice with rate change:: True >>> pay.execute('pay') >>> invoice.state - u'paid' + 'paid' Create negative tax:: @@ -191,7 +191,7 @@ Create invoice with alternate currency and negative taxes:: Decimal('360.00') >>> invoice.click('post') >>> invoice.state - u'posted' + 'posted' >>> invoice.untaxed_amount Decimal('400.00') >>> invoice.tax_amount diff --git a/tests/scenario_invoice_supplier.rst b/tests/scenario_invoice_supplier.rst index 7fabc928dd..63f10db925 100644 --- a/tests/scenario_invoice_supplier.rst +++ b/tests/scenario_invoice_supplier.rst @@ -121,23 +121,23 @@ Create invoice:: Decimal('120.00') >>> invoice.save() >>> invoice.state - u'draft' + 'draft' >>> bool(invoice.move) False >>> invoice.click('validate_invoice') >>> invoice.state - u'validated' + 'validated' >>> bool(invoice.move) True >>> invoice.move.state - u'draft' + 'draft' >>> invoice.click('post') >>> invoice.state - u'posted' + 'posted' >>> bool(invoice.move) True >>> invoice.move.state - u'posted' + 'posted' >>> invoice.untaxed_amount Decimal('110.00') >>> invoice.tax_amount @@ -184,7 +184,7 @@ Credit invoice:: >>> credit_note, = Invoice.find( ... [('type', '=', 'in'), ('id', '!=', invoice.id)]) >>> credit_note.state - u'draft' + 'draft' >>> credit_note.untaxed_amount == -invoice.untaxed_amount True >>> credit_note.tax_amount == -invoice.tax_amount @@ -210,7 +210,7 @@ Cancel draft invoice:: >>> invoice_draft.click('cancel') >>> invoice_draft.state - u'cancel' + 'cancel' >>> invoice_draft.move >>> invoice_draft.reconciled @@ -218,7 +218,7 @@ Cancel posted invoice:: >>> invoice.click('cancel') >>> invoice.state - u'cancel' + 'cancel' >>> invoice.cancel_move is not None True >>> invoice.reconciled == today diff --git a/tests/test_account_invoice.py b/tests/test_account_invoice.py index 4b602d478a..2ee4106168 100644 --- a/tests/test_account_invoice.py +++ b/tests/test_account_invoice.py @@ -135,14 +135,14 @@ class AccountInvoiceTestCase(ModuleTestCase): }])] }]) terms = remainder_term.compute(Decimal('0.0'), cu1, - date=datetime.date(2016, 05, 17)) + date=datetime.date(2016, 5, 17)) self.assertEqual(terms, [ - (datetime.date(2016, 05, 17), Decimal('0.0')), + (datetime.date(2016, 5, 17), Decimal('0.0')), ]) terms = percent_term.compute(Decimal('0.0'), cu1, - date=datetime.date(2016, 05, 17)) + date=datetime.date(2016, 5, 17)) self.assertEqual(terms, [ - (datetime.date(2016, 05, 17), Decimal('0.0')), + (datetime.date(2016, 5, 17), Decimal('0.0')), ]) @with_transaction() 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://}