Commit 30a840d9 authored by Denis Laxalde's avatar Denis Laxalde
Browse files

Drop python2 support

This mostly consists in removing the dependency on "six" and updating
the code to use only Python3 idioms.

Notice that we previously used TemporaryDirectory from
cubicweb.devtools.testlib for compatibility with Python2. We now
directly import it from tempfile.
parent 6b3523f81f42
......@@ -21,7 +21,6 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
BuildArch: noarch
Requires: %{python}
Requires: %{python}-six >= 1.4.0
Requires: %{python}-logilab-common >= 1.4.0
Requires: %{python}-logilab-mtconverter >= 0.8.0
Requires: %{python}-rql >= 0.34.0
......@@ -29,7 +28,6 @@ Requires: %{python}-yams >= 0.45.0
Requires: %{python}-logilab-database >= 1.15.0
Requires: %{python}-passlib
Requires: %{python}-lxml
Requires: %{python}-unittest2 >= 0.7.0
Requires: %{python}-markdown
Requires: pytz
# the schema view uses `dot'; at least on el5, png output requires graphviz-gd
......
......@@ -27,8 +27,6 @@ import sys
import warnings
import zlib
from six import PY2, binary_type, text_type
from logilab.common.logging_ext import set_log_methods
from yams.constraints import BASE_CONVERTERS, BASE_CHECKERS
from yams.schema import role_name as rname
......@@ -40,11 +38,7 @@ from logilab.common.registry import ObjectNotFound, NoSelectableObject, Registry
from yams import ValidationError
from cubicweb._exceptions import * # noqa
if PY2:
# http://bugs.python.org/issue10211
from StringIO import StringIO as BytesIO
else:
from io import BytesIO
from io import BytesIO
# ignore the pygments UserWarnings
warnings.filterwarnings('ignore', category=UserWarning,
......@@ -63,12 +57,12 @@ CW_SOFTWARE_ROOT = __path__[0] # noqa
# '_' is available to mark internationalized string but should not be used to
# do the actual translation
_ = text_type
_ = str
class Binary(BytesIO):
"""class to hold binary data. Use BytesIO to prevent use of unicode data"""
_allowed_types = (binary_type, bytearray, buffer if PY2 else memoryview) # noqa: F405
_allowed_types = (bytes, bytearray, memoryview)
def __init__(self, buf=b''):
assert isinstance(buf, self._allowed_types), \
......@@ -144,7 +138,7 @@ class Binary(BytesIO):
def check_password(eschema, value):
return isinstance(value, (binary_type, Binary))
return isinstance(value, (bytes, Binary))
BASE_CHECKERS['Password'] = check_password
......@@ -153,7 +147,7 @@ BASE_CHECKERS['Password'] = check_password
def str_or_binary(value):
if isinstance(value, Binary):
return value
return binary_type(value)
return bytes(value)
BASE_CONVERTERS['Password'] = str_or_binary
......
......@@ -34,7 +34,7 @@ license = 'LGPL'
classifiers = [
'Environment :: Web Environment',
'Framework :: CubicWeb',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: JavaScript',
]
......
......@@ -21,8 +21,6 @@
from warnings import warn
from six import PY2, text_type
from logilab.common.decorators import cachedproperty
from yams import ValidationError
......@@ -32,21 +30,15 @@ from yams import ValidationError
class CubicWebException(Exception):
"""base class for cubicweb server exception"""
msg = ""
def __unicode__(self):
def __str__(self):
if self.msg:
if self.args:
return self.msg % tuple(self.args)
else:
return self.msg
else:
return u' '.join(text_type(arg) for arg in self.args)
def __str__(self):
res = self.__unicode__()
if PY2:
res = res.encode('utf-8')
return res
return u' '.join(str(arg) for arg in self.args)
class ConfigurationError(CubicWebException):
"""a misconfiguration error"""
......@@ -98,7 +90,7 @@ class UniqueTogetherError(RepositoryError):
def rtypes(self):
if 'rtypes' in self.kwargs:
return self.kwargs['rtypes']
cstrname = text_type(self.kwargs['cstrname'])
cstrname = str(self.kwargs['cstrname'])
cstr = self.session.find('CWUniqueTogetherConstraint', name=cstrname).one()
return sorted(rtype.name for rtype in cstr.relations)
......@@ -119,7 +111,7 @@ class Unauthorized(SecurityError):
msg1 = u'You are not allowed to perform %s operation on %s'
var = None
def __unicode__(self):
def __str__(self):
try:
if self.args and len(self.args) == 2:
return self.msg1 % self.args
......@@ -127,7 +119,7 @@ class Unauthorized(SecurityError):
return u' '.join(self.args)
return self.msg
except Exception as ex:
return text_type(ex)
return str(ex)
class Forbidden(SecurityError):
"""raised when a user tries to perform a forbidden action
......
......@@ -15,12 +15,8 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
import gc, types, weakref
from six import PY2
from cubicweb.schema import CubicWebRelationSchema, CubicWebEntitySchema
try:
from cubicweb.web.request import _NeedAuthAccessMock
......@@ -37,9 +33,6 @@ IGNORE_CLASSES = (
types.ModuleType, types.FunctionType, types.MethodType,
types.MemberDescriptorType, types.GetSetDescriptorType,
)
if PY2:
# weakref.WeakKeyDictionary fails isinstance check on Python 3.5.
IGNORE_CLASSES += (weakref.WeakKeyDictionary, )
if _NeedAuthAccessMock is not None:
IGNORE_CLASSES = IGNORE_CLASSES + (_NeedAuthAccessMock,)
......
......@@ -19,8 +19,7 @@
from base64 import b64encode, b64decode
from six.moves import cPickle as pickle
import pickle
from Crypto.Cipher import Blowfish
......@@ -38,7 +37,7 @@ def encrypt(data, seed):
string = pickle.dumps(data)
string = string + '*' * (8 - len(string) % 8)
string = b64encode(_cypherer(seed).encrypt(string))
return unicode(string)
return str(string)
def decrypt(string, seed):
......
......@@ -166,8 +166,6 @@ Here are all environment variables that may be used to configure *CubicWeb*:
Directory where pid files will be written
"""
from __future__ import print_function
import importlib
import logging
import logging.config
......@@ -182,8 +180,6 @@ import sys
from threading import Lock
from warnings import filterwarnings
from six import text_type
from logilab.common.decorators import cached
from logilab.common.logging_ext import set_log_methods, init_log
from logilab.common.configuration import (Configuration, Method,
......@@ -641,7 +637,7 @@ this option is set to yes",
self.adjust_sys_path()
self.load_defaults()
# will be properly initialized later by _gettext_init
self.translations = {'en': (text_type, lambda ctx, msgid: text_type(msgid) )}
self.translations = {'en': (str, lambda ctx, msgid: str(msgid) )}
self._site_loaded = set()
# don't register ReStructured Text directives by simple import, avoid pb
# with eg sphinx.
......@@ -990,7 +986,7 @@ the repository',
# set to true while creating an instance
self.creating = creating
super(CubicWebConfiguration, self).__init__(debugmode)
fake_gettext = (text_type, lambda ctx, msgid: text_type(msgid))
fake_gettext = (str, lambda ctx, msgid: str(msgid))
for lang in self.available_languages():
self.translations[lang] = fake_gettext
self._cubes = None
......
......@@ -18,8 +18,6 @@
"""the cubicweb-ctl tool, based on logilab.common.clcommands to
provide a pluggable commands system.
"""
from __future__ import print_function
# *ctl module should limit the number of import to be imported as quickly as
# possible (for cubicweb-ctl reactivity, necessary for instance for usable bash
# completion). So import locally in command helpers.
......
......@@ -18,8 +18,6 @@
import gettext
from six import PY3
class cwGNUTranslations(gettext.GNUTranslations):
# The encoding of a msgctxt and a msgid in a .mo file is
......@@ -85,8 +83,7 @@ class cwGNUTranslations(gettext.GNUTranslations):
else:
return msgid2
if PY3:
ugettext = gettext.GNUTranslations.gettext
ugettext = gettext.GNUTranslations.gettext
def upgettext(self, context, message):
ctxt_message_id = self.CONTEXT_ENCODING % (context, message)
......@@ -97,7 +94,7 @@ class cwGNUTranslations(gettext.GNUTranslations):
return self.ugettext(message)
if self._fallback:
return self._fallback.upgettext(context, message)
return unicode(message)
return str(message)
return tmsg
def unpgettext(self, context, msgid1, msgid2, n):
......@@ -108,9 +105,9 @@ class cwGNUTranslations(gettext.GNUTranslations):
if self._fallback:
return self._fallback.unpgettext(context, msgid1, msgid2, n)
if n == 1:
tmsg = unicode(msgid1)
tmsg = str(msgid1)
else:
tmsg = unicode(msgid2)
tmsg = str(msgid2)
return tmsg
......
......@@ -24,8 +24,6 @@ from os.path import join, dirname, realpath
from datetime import datetime, date, time, timedelta
from functools import reduce
from six import text_type, binary_type
from logilab.common.decorators import cached, clear_cache
from logilab.common.deprecation import class_deprecated
from logilab.common.modutils import clean_sys_modules
......@@ -218,9 +216,9 @@ class ViewsRegistry(CWRegistry):
"""
obj = self.select(oid, req, rset=rset, **kwargs)
res = obj.render(**kwargs)
if isinstance(res, text_type):
if isinstance(res, str):
return res.encode(req.encoding)
assert isinstance(res, binary_type)
assert isinstance(res, bytes)
return res
def possible_views(self, req, rset=None, **kwargs):
......
......@@ -16,18 +16,14 @@
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""Functions to help importing CSV data"""
from __future__ import absolute_import, print_function
import codecs
import csv as csvmod
from six import PY2, PY3, string_types
from logilab.common import shellutils
def count_lines(stream_or_filename):
if isinstance(stream_or_filename, string_types):
if isinstance(stream_or_filename, str):
f = open(stream_or_filename)
else:
f = stream_or_filename
......@@ -42,7 +38,7 @@ def count_lines(stream_or_filename):
def ucsvreader_pb(stream_or_path, encoding='utf-8', delimiter=',', quotechar='"',
skipfirst=False, withpb=True, skip_empty=True):
"""same as :func:`ucsvreader` but a progress bar is displayed as we iter on rows"""
if isinstance(stream_or_path, string_types):
if isinstance(stream_or_path, str):
stream = open(stream_or_path, 'rb')
else:
stream = stream_or_path
......@@ -68,19 +64,14 @@ def ucsvreader(stream, encoding='utf-8', delimiter=',', quotechar='"',
separators) will be skipped. This is useful for Excel exports which may be
full of such lines.
"""
if PY3:
stream = codecs.getreader(encoding)(stream)
stream = codecs.getreader(encoding)(stream)
it = iter(csvmod.reader(stream, delimiter=delimiter, quotechar=quotechar))
if not ignore_errors:
if skipfirst:
next(it)
for row in it:
if PY2:
decoded = [item.decode(encoding) for item in row]
else:
decoded = row
if not skip_empty or any(decoded):
yield decoded
if not skip_empty or any(row):
yield row
else:
if skipfirst:
try:
......@@ -97,9 +88,5 @@ def ucsvreader(stream, encoding='utf-8', delimiter=',', quotechar='"',
# Error in CSV, ignore line and continue
except csvmod.Error:
continue
if PY2:
decoded = [item.decode(encoding) for item in row]
else:
decoded = row
if not skip_empty or any(decoded):
yield decoded
if not skip_empty or any(row):
yield row
......@@ -22,9 +22,6 @@ from itertools import chain
import logging
from uuid import uuid4
from six import text_type
from six.moves import range
from cubicweb.dataimport import stores, pgstore
from cubicweb.server.schema2sql import eschema_sql_def
......@@ -70,7 +67,7 @@ class MassiveObjectStore(stores.RQLObjectStore):
"""
super(MassiveObjectStore, self).__init__(cnx)
self.uuid = text_type(uuid4()).replace('-', '')
self.uuid = str(uuid4()).replace('-', '')
self.slave_mode = slave_mode
if metagen is None:
metagen = stores.MetadataGenerator(cnx)
......
......@@ -17,17 +17,13 @@
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""Postgres specific store"""
from __future__ import print_function
import warnings
import os.path as osp
from io import StringIO
from time import asctime
from datetime import date, datetime, time
from collections import defaultdict
from six import string_types, integer_types, text_type, add_metaclass
from six.moves import cPickle as pickle, range
import pickle
from cubicweb.utils import make_uid
from cubicweb.server.sqlutils import SQL_PREFIX
......@@ -108,7 +104,7 @@ def _copyfrom_buffer_convert_None(value, **opts):
def _copyfrom_buffer_convert_number(value, **opts):
'''Convert a number into its string representation'''
return text_type(value)
return str(value)
def _copyfrom_buffer_convert_string(value, **opts):
'''Convert string value.
......@@ -140,8 +136,8 @@ def _copyfrom_buffer_convert_time(value, **opts):
# (types, converter) list.
_COPYFROM_BUFFER_CONVERTERS = [
(type(None), _copyfrom_buffer_convert_None),
(integer_types + (float,), _copyfrom_buffer_convert_number),
(string_types, _copyfrom_buffer_convert_string),
((int, float), _copyfrom_buffer_convert_number),
(str, _copyfrom_buffer_convert_string),
(datetime, _copyfrom_buffer_convert_datetime),
(date, _copyfrom_buffer_convert_date),
(time, _copyfrom_buffer_convert_time),
......@@ -185,7 +181,7 @@ def _create_copyfrom_buffer(data, columns=None, **convert_opts):
for types, converter in _COPYFROM_BUFFER_CONVERTERS:
if isinstance(value, types):
value = converter(value, **convert_opts)
assert isinstance(value, text_type)
assert isinstance(value, str)
break
else:
raise ValueError("Unsupported value type %s" % type(value))
......
......@@ -62,8 +62,6 @@ from datetime import datetime
from copy import copy
from itertools import count
from six import add_metaclass
import pytz
from logilab.common.decorators import cached
......@@ -362,8 +360,7 @@ class _MetaGeneratorBWCompatWrapper(object):
return self._mdgen.source
@add_metaclass(class_deprecated)
class MetaGenerator(object):
class MetaGenerator(object, metaclass=class_deprecated):
"""Class responsible for generating standard metadata for imported entities. You may want to
derive it to add application specific's metadata.
......
......@@ -20,16 +20,11 @@
import datetime as DT
from six import PY3
from logilab.common.testlib import TestCase, unittest_main
from cubicweb.dataimport import pgstore
if PY3:
long = int
class CreateCopyFromBufferTC(TestCase):
# test converters
......@@ -41,7 +36,7 @@ class CreateCopyFromBufferTC(TestCase):
def test_convert_number(self):
cnvt = pgstore._copyfrom_buffer_convert_number
self.assertEqual(u'42', cnvt(42))
self.assertEqual(u'42', cnvt(long(42)))
self.assertEqual(u'42', cnvt(int(42)))
self.assertEqual(u'42.42', cnvt(42.42))
def test_convert_string(self):
......@@ -68,9 +63,9 @@ class CreateCopyFromBufferTC(TestCase):
# test buffer
def test_create_copyfrom_buffer_tuple(self):
data = ((42, long(42), 42.42, u'éléphant', DT.date(666, 1, 13), DT.time(6, 6, 6),
data = ((42, int(42), 42.42, u'éléphant', DT.date(666, 1, 13), DT.time(6, 6, 6),
DT.datetime(666, 6, 13, 6, 6, 6)),
(6, long(6), 6.6, u'babar', DT.date(2014, 1, 14), DT.time(4, 2, 1),
(6, int(6), 6.6, u'babar', DT.date(2014, 1, 14), DT.time(4, 2, 1),
DT.datetime(2014, 1, 1, 0, 0, 0)))
results = pgstore._create_copyfrom_buffer(data)
# all columns
......
......@@ -17,8 +17,6 @@
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""Test tools for cubicweb"""
from __future__ import print_function
import os
import sys
import errno
......@@ -31,10 +29,9 @@ import getpass
from hashlib import sha1 # pylint: disable=E0611
from os.path import abspath, join, exists, split, isdir, dirname
from functools import partial
import pickle
import filelock
from six import text_type
from six.moves import cPickle as pickle
from logilab.common.decorators import cached, clear_cache
......@@ -93,7 +90,7 @@ DEFAULT_SOURCES = {
DEFAULT_PSQL_SOURCES = DEFAULT_SOURCES.copy()
DEFAULT_PSQL_SOURCES['system'] = DEFAULT_SOURCES['system'].copy()
DEFAULT_PSQL_SOURCES['system']['db-driver'] = 'postgres'
DEFAULT_PSQL_SOURCES['system']['db-user'] = text_type(getpass.getuser())
DEFAULT_PSQL_SOURCES['system']['db-user'] = getpass.getuser()
DEFAULT_PSQL_SOURCES['system']['db-password'] = None
# insert a dumb value as db-host to avoid unexpected connection to local server
DEFAULT_PSQL_SOURCES['system']['db-host'] = 'REPLACEME'
......@@ -395,7 +392,7 @@ class TestDataBaseHandler(object):
from cubicweb.repoapi import connect
repo = self.get_repo()
sources = self.config.read_sources_file()
login = text_type(sources['admin']['login'])
login = sources['admin']['login']
password = sources['admin']['password'] or 'xxx'
cnx = connect(repo, login, password=password)
return cnx
......
......@@ -18,8 +18,6 @@
"""additional cubicweb-ctl commands and command handlers for cubicweb and
cubicweb's cubes development
"""
from __future__ import print_function
# *ctl module should limit the number of import to be imported as quickly as
# possible (for cubicweb-ctl reactivity, necessary for instance for usable bash
# completion). So import locally in command helpers.
......@@ -33,8 +31,6 @@ import pkg_resources
from pytz import UTC
from six.moves import input
from logilab.common import STD_BLACKLIST
from logilab.common.modutils import clean_sys_modules
from logilab.common.fileutils import ensure_fs_mode
......@@ -717,7 +713,6 @@ layout, and a full featured cube with "full" layout.',
longdesc = input(
'Enter a long description (leave empty to reuse the short one): ')
dependencies = {
'six': '>= 1.4.0',
'cubicweb': '>= %s' % cubicwebversion,
}
if verbose:
......
......@@ -20,8 +20,6 @@
from contextlib import contextmanager
from six import string_types
from logilab.database import get_db_helper
from cubicweb.req import RequestSessionBase
......@@ -98,7 +96,7 @@ class FakeRequest(ConnectionCubicWebRequestBase):
def set_request_header(self, header, value, raw=False):
"""set an incoming HTTP header (for test purpose only)"""
if isinstance(value, string_types):
if isinstance(value, str):
value = [value]
if raw:
# adding encoded header is important, else page content
......
......@@ -17,9 +17,6 @@
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""This modules defines func / methods for creating test repositories"""
from __future__ import print_function
import logging
from random import randint, choice
......@@ -28,9 +25,6 @@ from datetime import datetime, date, time, timedelta
from decimal import Decimal
import inspect
from six import text_type, add_metaclass
from six.moves import range
from logilab.common import attrdict
from logilab.mtconverter import xml_escape
from yams.constraints import (SizeConstraint, StaticVocabularyConstraint,
......@@ -234,7 +228,7 @@ title
"""
for cst in self.eschema.rdef(attrname).constraints:
if isinstance(cst, StaticVocabularyConstraint):
return text_type(choice(cst.vocabulary()))
return choice(cst.vocabulary())
return None
# XXX nothing to do here
......@@ -270,8 +264,7 @@ class autoextend(type):
return type.__new__(mcs, name, bases, classdict)
@add_metaclass(autoextend)
class ValueGenerator(_ValueGenerator):
class ValueGenerator(_ValueGenerator, metaclass=autoextend):
pass
......@@ -359,7 +352,7 @@ def make_entity(etype, schema, vreg, index=0, choice_func=_default_choice_func,
fmt = vreg.property_value('ui.float-format')
value = fmt % value
else:
value = text_type(value)
value = value
return entity
......
......@@ -18,15 +18,12 @@
"""this module contains base classes and utilities for integration with running
http server
"""
from __future__ import print_function
import http.client
import random
import threading
import socket
from six.moves import range, http_client
from six.moves.urllib.parse import urlparse
from urllib.parse import urlparse
from cubicweb.devtools.testlib import CubicWebTC
......@@ -83,7 +80,7 @@ class _CubicWebServerTC(CubicWebTC):
passwd = user
response = self.web_get("login?__login=%s&__password=%s" %
(user, passwd))
assert response.status == http_client.SEE_OTHER, response.status