Skip to content

Commit

Permalink
require python 3.6+
Browse files Browse the repository at this point in the history
  • Loading branch information
alexanderlukanin13 committed Oct 23, 2022
1 parent 5fbc994 commit 5b0fa35
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 179 deletions.
5 changes: 1 addition & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
language: python
python:
- 2.7
- 3.4
- 3.5
- 3.6
- 3.7
- 3.8
- 3.9
- pypy
- 3.10
- pypy3
install: pip install tox-travis
script: tox
72 changes: 28 additions & 44 deletions coolname/impl.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
"""
Do not import anything directly from this module.
"""


import hashlib
import itertools
import os
Expand All @@ -15,18 +13,18 @@
from .exceptions import ConfigurationError, InitializationError


class AbstractNestedList(object):
class AbstractNestedList:

def __init__(self, lists):
super(AbstractNestedList, self).__init__()
super().__init__()
self._lists = [WordList(x) if x.__class__ is list else x
for x in lists]
# If this is set to True in a subclass,
# then subclass yields sequences instead of single words.
self.multiword = any(x.multiword for x in self._lists)

def __str__(self):
return '{}({}, len={})'.format(self.__class__.__name__, len(self._lists), self.length)
return f'{self.__class__.__name__}({len(self._lists)}, len={self.length})'

def __repr__(self):
return self.__str__()
Expand All @@ -39,27 +37,18 @@ def squash(self, hard, cache):
return self

def _dump(self, stream, indent='', object_ids=False):
stream.write(indent + _unicode(self) +
(' [id={}]'.format(id(self)) if object_ids else '') +
stream.write(indent + str(self) +
(f' [id={id(self)}]' if object_ids else '') +
'\n')
indent += ' '
for sublist in self._lists:
sublist._dump(stream, indent, object_ids=object_ids)


# Poor man's `six`
try:
_unicode = unicode
_str_types = (str, _unicode) # pragma: nocover
except NameError:
_unicode = str
_str_types = str


# Convert value to bytes, for hashing
# (used to calculate WordList or PhraseList hash)
def _to_bytes(value):
if isinstance(value, _unicode):
if isinstance(value, str):
return value.encode('utf-8')
elif isinstance(value, tuple):
return str(value).encode('utf-8')
Expand Down Expand Up @@ -89,7 +78,7 @@ def squash(self, hard, cache):

@property
def _hash(self):
if self.__hash is not None:
if self.__hash:
return self.__hash
md5 = hashlib.md5()
md5.update(_to_bytes(str(len(self))))
Expand All @@ -107,11 +96,11 @@ class PhraseList(_BasicList):
"""List of phrases (sequences of one or more words)."""

def __init__(self, sequence=None):
super(PhraseList, self).__init__(tuple(_split_phrase(x)) for x in sequence)
super().__init__(tuple(_split_phrase(x)) for x in sequence)
self.multiword = True


class WordAsPhraseWrapper(object):
class WordAsPhraseWrapper:

multiword = True

Expand All @@ -129,16 +118,16 @@ def squash(self, hard, cache):
return self

def __str__(self):
return '{}({})'.format(self.__class__.__name__, str(self._list))
return f'{self.__class__.__name__}({self._list})'

def __repr__(self):
return '{}({})'.format(self.__class__.__name__, repr(self._list))
return f'{self.__class__.__name__}({self._list!r})'


class NestedList(AbstractNestedList):

def __init__(self, lists):
super(NestedList, self).__init__(lists)
super().__init__(lists)
# If user mixes WordList and PhraseList in the same NestedList,
# we need to make sure that __getitem__ always returns tuple.
# For that, we wrap WordList instances.
Expand All @@ -163,7 +152,7 @@ def squash(self, hard, cache):
# If we have 4 branches which finally point to the same list of nouns,
# why not using the same WordList instance for all 4 branches?
# This optimization is also applied to PhraseLists, just in case.
result = super(NestedList, self).squash(hard, cache)
result = super().squash(hard, cache)
if result is self and hard:
for cls in (WordList, PhraseList):
if all(isinstance(x, cls) for x in self._lists):
Expand All @@ -181,7 +170,7 @@ def squash(self, hard, cache):
class CartesianList(AbstractNestedList):

def __init__(self, lists):
super(CartesianList, self).__init__(lists)
super().__init__(lists)
self.length = 1
for x in self._lists:
self.length *= x.length
Expand Down Expand Up @@ -210,21 +199,21 @@ def __getitem__(self, i):
class Scalar(AbstractNestedList):

def __init__(self, value):
super(Scalar, self).__init__([])
super().__init__([])
self.value = value
self.length = 1

def __getitem__(self, i):
return self.value

def __str__(self):
return '{}(value={!r})'.format(self.__class__.__name__, self.value)
return f'{self.__class__.__name__}(value={self.value!r})'

def random(self):
return self.value


class RandomGenerator(object):
class RandomGenerator:
"""
This class provides random name generation interface.
Expand Down Expand Up @@ -258,31 +247,28 @@ def __init__(self, config, rand=None):
try:
ensure_unique = config['all'][_CONF.FIELD.ENSURE_UNIQUE]
if not isinstance(ensure_unique, bool):
raise ValueError('expected boolean, got {!r}'.format(ensure_unique))
raise ValueError(f'expected boolean, got {ensure_unique!r}')
self._ensure_unique = ensure_unique
except KeyError:
self._ensure_unique = False
except ValueError as ex:
raise ConfigurationError('Invalid {} value: {}'
.format(_CONF.FIELD.ENSURE_UNIQUE, ex))
raise ConfigurationError(f'Invalid {_CONF.FIELD.ENSURE_UNIQUE} value: {ex}')
# Should we avoid duplicating prefixes?
try:
self._check_prefix = int(config['all'][_CONF.FIELD.ENSURE_UNIQUE_PREFIX])
if self._check_prefix <= 0:
raise ValueError('expected a positive integer, got {!r}'.format(self._check_prefix))
raise ValueError(f'expected a positive integer, got {self._check_prefix!r}')
except KeyError:
self._check_prefix = None
except ValueError as ex:
raise ConfigurationError('Invalid {} value: {}'
.format(_CONF.FIELD.ENSURE_UNIQUE_PREFIX, ex))
raise ConfigurationError(f'Invalid {_CONF.FIELD.ENSURE_UNIQUE_PREFIX} value: {ex}')
# Get max slug length
try:
self._max_slug_length = int(config['all'][_CONF.FIELD.MAX_SLUG_LENGTH])
except KeyError:
self._max_slug_length = None
except ValueError as ex:
raise ConfigurationError('Invalid {} value: {}'
.format(_CONF.FIELD.MAX_SLUG_LENGTH, ex))
raise ConfigurationError(f'Invalid {_CONF.FIELD.MAX_SLUG_LENGTH} value: {ex}')
# Make sure that generate() does not go into long loop.
# Default generator is a special case, we don't need check.
if (not config['all'].get('__nocheck') and
Expand Down Expand Up @@ -394,9 +380,9 @@ def _is_str(value):

# Translate phrases defined as strings to tuples
def _split_phrase(x):
if isinstance(x, _str_types):
return re.split(_unicode(r'\s+'), x.strip())
else:
try:
return re.split(r'\s+', x.strip())
except AttributeError: # Not str
return x


Expand All @@ -411,12 +397,10 @@ def _validate_config(config):
for key, listdef in list(config.items()):
# Check if section is a list
if not isinstance(listdef, dict):
raise ValueError('Value at key {!r} is not a dict'
.format(key))
raise ValueError(f'Value at key {key!r} is not a dict')
# Check if it has correct type
if _CONF.FIELD.TYPE not in listdef:
raise ValueError('Config at key {!r} has no {!r}'
.format(key, _CONF.FIELD.TYPE))
raise ValueError(f'Config at key {key!r} has no {_CONF.FIELD.TYPE!r}')
# Nested or Cartesian
if listdef[_CONF.FIELD.TYPE] in (_CONF.TYPE.NESTED, _CONF.TYPE.CARTESIAN):
sublists = listdef.get(_CONF.FIELD.LISTS)
Expand Down Expand Up @@ -480,7 +464,7 @@ def _validate_config(config):
max_length = None
for phrase in phrases:
phrase = _split_phrase(phrase) # str -> sequence, if necessary
if not isinstance(phrase, (tuple, list)) or not all(isinstance(x, _str_types) for x in phrase):
if not isinstance(phrase, (tuple, list)) or not all(isinstance(x, str) for x in phrase):
raise ValueError('Config at key {!r} has invalid {!r}: '
'must be all string/tuple/list'
.format(key, _CONF.FIELD.PHRASES))
Expand Down
3 changes: 0 additions & 3 deletions docs/classes-and-functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
Classes and functions
=====================

Functions and methods accept and return Unicode strings, that is,
:class:`unicode` in Python 2 and :py:class:`str` in Python 3.

.. py:module:: coolname
Default generator
Expand Down
49 changes: 7 additions & 42 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,40 +27,11 @@ def compile_init_py():
sys.path.remove(current_path)
config_path = os.path.join(current_path, 'coolname', 'data')
config = load_config(config_path)
# Eliminate u'' literals if setup.py is executed from Python 2
def _to_str(obj):
if isinstance(obj, dict):
return {str(x): _to_str(y) for x, y in obj.items()}
elif isinstance(obj, list):
return [_to_str(x) for x in obj]
elif isinstance(obj, tuple):
return tuple(str(x) for x in obj)
elif obj.__class__.__name__ == 'unicode':
return str(obj)
else:
return obj
config = _to_str(config)
# Write to data/__init__.py to be used from .egg
with codecs.open(os.path.join(config_path, '__init__.py'), 'w', encoding='utf-8') as file:
file.write(_INIT_TEMPLATE.format(config))


_INIT_TEMPLATE = '''
# THIS FILE IS AUTO-GENERATED, DO NOT EDIT
config = {!r}
# Python 2 compatibility - all words must be unicode
# (this is to make Python 2 and 3 both work from the same __init__.py code)
try:
for listdef in config.values():
if listdef['type'] == 'words':
listdef['words'] = [unicode(x) for x in listdef['words']]
elif listdef['type'] == 'phrases':
listdef['phrases'] = [tuple(unicode(y) for y in x) for x in listdef['phrases']]
elif listdef['type'] == 'const':
listdef['value'] = unicode(listdef['value'])
except NameError:
pass
'''.lstrip()
file.write(f'''# THIS FILE IS AUTO-GENERATED, DO NOT EDIT
config = {config!r}
''')


def customize(cls):
Expand Down Expand Up @@ -88,14 +59,11 @@ def run(self, *args, **kwargs):
history = re.sub(r':\w+:`(\w+(?:\.\w+)*)`', r'``\1``', history)


test_requirements = [
'mock==3.0.5',
'six==1.15.0'
]
test_requirements = []

setup(
name='coolname',
version='1.1.0',
version='2.0.0',
description="Random name and slug generator",
long_description=readme + '\n\n' + history,
author="Alexander Lukanin",
Expand All @@ -114,19 +82,16 @@ def run(self, *args, **kwargs):
zip_safe=True,
keywords='coolname',
classifiers=[
'Development Status :: 4 - Beta',
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Natural Language :: English',
"Programming Language :: Python :: 2",
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
],
test_suite='tests',
tests_require=test_requirements
Expand Down
18 changes: 2 additions & 16 deletions tests/common.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import os.path as op
import unittest
import six


try:
from unittest import mock
from unittest.mock import patch
except ImportError:
import mock
from mock import patch

from unittest import mock
from unittest.mock import patch

TESTS_DIR = op.dirname(op.abspath(__file__))
PROJECT_DIR = op.abspath(op.join(TESTS_DIR, '..'))
Expand All @@ -20,12 +12,6 @@ class TestCase(unittest.TestCase):
pass


if six.PY2:
def assertRaisesRegex(self, *args, **kwargs):
return six.assertRaisesRegex(self, *args, **kwargs)
TestCase.assertRaisesRegex = assertRaisesRegex


class FakeRandom(object):
"""Generates 0, 1, 2..."""

Expand Down
4 changes: 2 additions & 2 deletions tests/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@


def main(argv):
if sys.version_info[:2] < (3, 3):
sys.stderr.write('This script requires Python 3.3+\n')
if sys.version_info[:2] < (3, 6):
sys.stderr.write('This script requires Python 3.6+\n')
return 1
parser = argparse.ArgumentParser(description='Generate slug to stdout')
parser.add_argument('length', default=None, nargs='?', type=int, help='Number of words')
Expand Down
6 changes: 0 additions & 6 deletions tests/import_coolname_and_print_slugs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@
import itertools
import sys

# Ugly hack for Python 2
try:
range = xrange
except NameError:
pass


class NotRandom(object):

Expand Down
Loading

0 comments on commit 5b0fa35

Please sign in to comment.