From deb6bdeea67eb5657455ddf7966ae6b3e6389c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 26 Jun 2018 19:11:58 +0200 Subject: [PATCH] Remove support for Python 2.7 issue7497 review53261002 --- .drone.yml | 6 ------ CHANGELOG | 1 + INSTALL | 2 +- party.py | 6 ++---- setup.py | 22 +++++++--------------- tests/test_web_user.py | 5 +---- tox.ini | 6 +++--- user.py | 37 ++++++++++++++++++------------------- 8 files changed, 33 insertions(+), 52 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 587955fe81..73cc76130b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Remove support for Python 2.7 * Add remove and reset session methods Version 4.8.0 - 2018-04-23 diff --git a/INSTALL b/INSTALL index 071999329d..d3fd3b6dcc 100644 --- a/INSTALL +++ b/INSTALL @@ -4,7 +4,7 @@ Installing trytond_web_user Prerequisites ------------- - * Python 2.7 or later (http://www.python.org/) + * Python 3.4 or later (http://www.python.org/) * trytond (http://www.tryton.org/) Installation diff --git a/party.py b/party.py index a9cd052940..f04a585b66 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 @@ -16,8 +15,7 @@ class PartyReplace: ] -class PartyErase: - __metaclass__ = PoolMeta +class PartyErase(metaclass=PoolMeta): __name__ = 'party.erase' def to_erase(self, party_id): diff --git a/setup.py b/setup.py index 1a85ccdca0..28602a2204 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')) @@ -54,11 +52,6 @@ for dep in info.get('depends', []): requires.append(get_require_version('trytond')) tests_require = [] -try: - from unittest import mock - mock.__version__ -except ImportError: - tests_require.append('mock') dependency_links = [] if minor_version % 2: dependency_links.append('https://trydevpi.tryton.org/') @@ -106,7 +99,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', @@ -115,6 +107,7 @@ setup(name=name, 'Topic :: Office/Business', ], license='GPL-3', + python_requires='>=3.4', install_requires=requires, extras_require={ 'BCrypt': ['bcrypt'], @@ -129,5 +122,4 @@ setup(name=name, test_suite='tests', test_loader='trytond.test_loader:Loader', tests_require=tests_require, - use_2to3=True, ) diff --git a/tests/test_web_user.py b/tests/test_web_user.py index c34751f3eb..3ec1de7b66 100644 --- a/tests/test_web_user.py +++ b/tests/test_web_user.py @@ -3,10 +3,7 @@ import unittest -try: - from unittest.mock import patch, ANY -except ImportError: - from mock import patch, ANY +from unittest.mock import patch, ANY from trytond.tests.test_tryton import ModuleTestCase, with_transaction from trytond.tests.test_tryton import suite as test_suite 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://} diff --git a/user.py b/user.py index cfcbbe2065..67170ee944 100644 --- a/user.py +++ b/user.py @@ -5,8 +5,7 @@ import hashlib import random import string import time -import urllib -import urlparse +import urllib.parse from email.header import Header try: @@ -51,17 +50,17 @@ def _send_email(from_, users, email_func): def _add_params(url, **params): - parts = urlparse.urlsplit(url) - query = urlparse.parse_qsl(parts.query) + parts = urllib.parse.urlsplit(url) + query = urllib.parse.parse_qsl(parts.query) for key, value in sorted(params.items()): query.append((key, value)) parts = list(parts) - parts[3] = urllib.urlencode(query) - return urlparse.urlunsplit(parts) + parts[3] = urllib.parse.urlencode(query) + return urllib.parse.urlunsplit(parts) def _extract_params(url): - return urlparse.parse_qsl(urlparse.urlsplit(url).query) + return urllib.parse.parse_qsl(urllib.parse.urlsplit(url).query) class User(DeactivableMixin, ModelSQL, ModelView): @@ -213,35 +212,35 @@ class User(DeactivableMixin, ModelSQL, ModelView): def hash_sha1(cls, password): salt = ''.join(random.sample(string.ascii_letters + string.digits, 8)) salted_password = password + salt - if isinstance(salted_password, unicode): + if isinstance(salted_password, str): salted_password = salted_password.encode('utf-8') hash_ = hashlib.sha1(salted_password).hexdigest() return '$'.join(['sha1', hash_, salt]) @classmethod def check_sha1(cls, password, hash_): - if isinstance(password, unicode): + if isinstance(password, str): password = password.encode('utf-8') hash_method, hash_, salt = hash_.split('$', 2) salt = salt or '' - if isinstance(salt, unicode): + if isinstance(salt, str): salt = salt.encode('utf-8') assert hash_method == 'sha1' return hash_ == hashlib.sha1(password + salt).hexdigest() @classmethod def hash_bcrypt(cls, password): - if isinstance(password, unicode): + if isinstance(password, str): password = password.encode('utf-8') hash_ = bcrypt.hashpw(password, bcrypt.gensalt()).decode('utf-8') return '$'.join(['bcrypt', hash_]) @classmethod def check_bcrypt(cls, password, hash_): - if isinstance(password, unicode): + if isinstance(password, str): password = password.encode('utf-8') hash_method, hash_ = hash_.split('$', 1) - if isinstance(hash_, unicode): + if isinstance(hash_, str): hash_ = hash_.encode('utf-8') assert hash_method == 'bcrypt' return hash_ == bcrypt.hashpw(password, hash_) @@ -279,10 +278,10 @@ class User(DeactivableMixin, ModelSQL, ModelView): @classmethod def validate_email_url(cls, url): - parts = urlparse.urlsplit(url) + parts = urllib.parse.urlsplit(url) tokens = filter( - None, urlparse.parse_qs(parts.query).get('token', [None])) - return cls.validate_email_token(tokens) + None, urllib.parse.parse_qs(parts.query).get('token', [None])) + return cls.validate_email_token(list(tokens)) @classmethod def validate_email_token(cls, tokens): @@ -304,7 +303,7 @@ class User(DeactivableMixin, ModelSQL, ModelView): def reset(user): return not (user.reset_password_token_expire and user.reset_password_token_expire > now) - users = filter(reset, users) + users = list(filter(reset, users)) for user in users: user.set_reset_password_token() @@ -334,8 +333,8 @@ class User(DeactivableMixin, ModelSQL, ModelView): @classmethod def set_password_url(cls, url, password): - parts = urlparse.urlsplit(url) - query = urlparse.parse_qs(parts.query) + parts = urllib.parse.urlsplit(url) + query = urllib.parse.parse_qs(parts.query) email = query.get('email', [None])[0] token = query.get('token', [None])[0] return cls.set_password_token(email, token, password)