summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSeth M Morton <seth.m.morton@gmail.com>2015-06-25 00:00:09 -0700
committerSeth M Morton <seth.m.morton@gmail.com>2015-06-25 00:00:49 -0700
commitbc7744fc77f876887f8c63829b221857ce9b89ed (patch)
tree30e9b990a4dbbadabf3508f587eb0b026cacb74d
parent16dac3fd494a4221dd3c03ab2d0b15810874cc4a (diff)
parentdc73897013071b1d27390cd65a98098bd7e18dd4 (diff)
downloadnatsort-4.0.2.tar.gz
natsort version 4.0.2 releasev4.0.2
- Added back Python 2.6 and Python 3.2 compatibility. Unit testing is now performed for these versions. - Consolidated under-the-hood compatibility functionality.
-rw-r--r--.travis.yml2
-rw-r--r--MANIFEST.in7
-rw-r--r--README.rst11
-rw-r--r--docs/source/changelog.rst7
-rw-r--r--docs/source/intro.rst4
-rw-r--r--natsort/__init__.py31
-rw-r--r--natsort/__main__.py10
-rw-r--r--natsort/_version.py10
-rw-r--r--natsort/compat/__init__.py0
-rw-r--r--natsort/compat/fake_fastnumbers.py (renamed from natsort/fake_fastnumbers.py)8
-rw-r--r--natsort/compat/fastnumbers.py28
-rw-r--r--natsort/compat/locale.py59
-rw-r--r--natsort/compat/pathlib.py15
-rw-r--r--natsort/compat/py23.py (renamed from natsort/py23compat.py)47
-rw-r--r--natsort/locale_help.py95
-rw-r--r--natsort/natsort.py18
-rw-r--r--natsort/ns_enum.py9
-rw-r--r--natsort/unicode_numbers.py12
-rw-r--r--natsort/utils.py72
-rw-r--r--setup.cfg9
-rw-r--r--setup.py10
-rw-r--r--test_natsort/compat/__init__.py0
-rw-r--r--test_natsort/compat/hypothesis.py27
-rw-r--r--test_natsort/compat/locale.py50
-rw-r--r--test_natsort/compat/mock.py12
-rw-r--r--test_natsort/profile_natsorted.py2
-rw-r--r--test_natsort/slow_splitters.py2
-rw-r--r--test_natsort/stress_natsort.py2
-rw-r--r--test_natsort/test_fake_fastnumbers.py22
-rw-r--r--test_natsort/test_locale_help.py78
-rw-r--r--test_natsort/test_main.py39
-rw-r--r--test_natsort/test_natsort.py30
-rw-r--r--test_natsort/test_unicode_numbers.py9
-rw-r--r--test_natsort/test_utils.py159
-rw-r--r--tox.ini12
35 files changed, 600 insertions, 308 deletions
diff --git a/.travis.yml b/.travis.yml
index bebdb51..53efa3f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,8 @@
language: python
python:
+- 2.6
- 2.7
+- 3.2
- 3.3
- 3.4
env:
diff --git a/MANIFEST.in b/MANIFEST.in
index e852e41..b4422d9 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -4,18 +4,21 @@ include natsort/natsort.py
include natsort/_version.py
include natsort/__main__.py
include natsort/__init__.py
-include natsort/py23compat.py
include natsort/locale_help.py
-include natsort/fake_fastnumbers.py
include natsort/utils.py
+include natsort/ns_enum.py
+include natsort/unicode_numbers.py
+graft natsort/compat
include test_natsort/profile_natsorted.py
include test_natsort/stress_natsort.py
+include test_natsort/slow_splitters.py
include test_natsort/test_natsort.py
include test_natsort/test_locale_help.py
include test_natsort/test_fake_fastnumbers.py
include test_natsort/test_main.py
include test_natsort/test_utils.py
include test_natsort/test_unicode_numbers.py
+graft test_natsort/compat
include setup.py
include setup.cfg
prune natsort/__pycache__
diff --git a/README.rst b/README.rst
index e327e38..2726bc6 100644
--- a/README.rst
+++ b/README.rst
@@ -142,9 +142,7 @@ from the command line with ``python -m natsort``.
Requirements
------------
-``natsort`` requires Python version 2.7 or greater or Python 3.3 or greater.
-Python 2.6 and 3.2 are no longer officially supported (no unit tests are performed)
-but it should work.
+``natsort`` requires Python version 2.7 or greater or Python 3.2 or greater.
.. _optional:
@@ -227,6 +225,13 @@ History
These are the last three entries of the changelog. See the package documentation
for the complete `changelog <http://pythonhosted.org//natsort/changelog.html>`_.
+06-24-2015 v. 4.0.2
+'''''''''''''''''''
+
+ - Added back Python 2.6 and Python 3.2 compatibility. Unit testing is now
+ performed for these versions.
+ - Consolidated under-the-hood compatibility functionality.
+
06-04-2015 v. 4.0.1
'''''''''''''''''''
diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst
index 94b2487..42942de 100644
--- a/docs/source/changelog.rst
+++ b/docs/source/changelog.rst
@@ -3,6 +3,13 @@
Changelog
---------
+06-24-2015 v. 4.0.2
+'''''''''''''''''''
+
+ - Added back Python 2.6 and Python 3.2 compatibility. Unit testing is now
+ performed for these versions.
+ - Consolidated under-the-hood compatibility functionality.
+
06-04-2015 v. 4.0.1
'''''''''''''''''''
diff --git a/docs/source/intro.rst b/docs/source/intro.rst
index 17c6975..a27680c 100644
--- a/docs/source/intro.rst
+++ b/docs/source/intro.rst
@@ -160,9 +160,7 @@ If you want to build this documentation, enter::
python setup.py build_sphinx
-:mod:`natsort` requires Python version 2.7 or greater or Python 3.3 or greater.
-Python 2.6 and 3.2 are no longer officially supported (no unit tests are performed)
-but it should work.
+:mod:`natsort` requires Python version 2.7 or greater or Python 3.2 or greater.
The most efficient sorting can occur if you install the
`fastnumbers <https://pypi.python.org/pypi/fastnumbers>`_ package (it helps
diff --git a/natsort/__init__.py b/natsort/__init__.py
index 4e6cc82..2e48601 100644
--- a/natsort/__init__.py
+++ b/natsort/__init__.py
@@ -1,14 +1,29 @@
# -*- coding: utf-8 -*-
-from __future__ import (print_function, division,
- unicode_literals, absolute_import)
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
# Local imports.
-from natsort.natsort import (natsort_key, natsort_keygen, ns,
- natsorted, humansorted, versorted,
- realsorted, index_realsorted,
- index_natsorted, index_versorted,
- index_humansorted, order_by_index,
- decoder, as_ascii, as_utf8)
+from natsort.natsort import (
+ natsort_key,
+ natsort_keygen,
+ natsorted,
+ versorted,
+ humansorted,
+ realsorted,
+ index_natsorted,
+ index_versorted,
+ index_humansorted,
+ index_realsorted,
+ order_by_index,
+ decoder,
+ as_ascii,
+ as_utf8,
+ ns,
+)
from natsort._version import __version__
__all__ = [
diff --git a/natsort/__main__.py b/natsort/__main__.py
index e86097d..03c4551 100644
--- a/natsort/__main__.py
+++ b/natsort/__main__.py
@@ -1,6 +1,10 @@
# -*- coding: utf-8 -*-
-from __future__ import (print_function, division,
- unicode_literals, absolute_import)
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
# Std. lib imports.
import sys
@@ -9,7 +13,7 @@ import sys
from natsort.natsort import natsorted, ns
from natsort.utils import _regex_and_num_function_chooser
from natsort._version import __version__
-from natsort.py23compat import py23_str
+from natsort.compat.py23 import py23_str
def main():
diff --git a/natsort/_version.py b/natsort/_version.py
index 33e7460..035ff28 100644
--- a/natsort/_version.py
+++ b/natsort/_version.py
@@ -1,5 +1,9 @@
# -*- coding: utf-8 -*-
-from __future__ import (print_function, division,
- unicode_literals, absolute_import)
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
-__version__ = '4.0.1'
+__version__ = '4.0.2'
diff --git a/natsort/compat/__init__.py b/natsort/compat/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/natsort/compat/__init__.py
diff --git a/natsort/fake_fastnumbers.py b/natsort/compat/fake_fastnumbers.py
index e934313..6fd954a 100644
--- a/natsort/fake_fastnumbers.py
+++ b/natsort/compat/fake_fastnumbers.py
@@ -4,8 +4,12 @@ This module is intended to replicate some of the functionality
from the fastnumbers module in the event that module is not
installed.
"""
-from __future__ import (print_function, division,
- unicode_literals, absolute_import)
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
# Std. lib imports.
import sys
diff --git a/natsort/compat/fastnumbers.py b/natsort/compat/fastnumbers.py
new file mode 100644
index 0000000..c2c603b
--- /dev/null
+++ b/natsort/compat/fastnumbers.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
+
+# If the user has fastnumbers installed, they will get great speed
+# benefits. If not, we use the simulated functions that come with natsort.
+try:
+ from fastnumbers import (
+ fast_float,
+ fast_int,
+ isint,
+ isfloat,
+ )
+ import fastnumbers
+ v = list(map(int, fastnumbers.__version__.split('.')))
+ if not (v[0] >= 0 and v[1] >= 5): # Require >= version 0.5.0.
+ raise ImportError
+except ImportError:
+ from natsort.compat.fake_fastnumbers import (
+ fast_float,
+ fast_int,
+ isint,
+ isfloat,
+ )
diff --git a/natsort/compat/locale.py b/natsort/compat/locale.py
new file mode 100644
index 0000000..618f666
--- /dev/null
+++ b/natsort/compat/locale.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
+
+# Std. lib imports
+import sys
+
+# Local imports.
+from natsort.compat.py23 import PY_VERSION, cmp_to_key
+
+# Make the strxfrm function from strcoll on Python2
+# It can be buggy (especially on BSD-based systems),
+# so prefer PyICU if available.
+try:
+ import PyICU
+ from locale import getlocale
+
+ # If using PyICU, get the locale from the current global locale,
+ # then create a sort key from that
+ def get_pyicu_transform(l, _d={}):
+ if l not in _d:
+ if l == (None, None):
+ c = PyICU.Collator.createInstance(PyICU.Locale())
+ else:
+ loc = '.'.join(l)
+ c = PyICU.Collator.createInstance(PyICU.Locale(loc))
+ _d[l] = c.getSortKey
+ return _d[l]
+ use_pyicu = True
+ null_string = b''
+
+ def dumb_sort():
+ return False
+except ImportError:
+ if sys.version[0] == '2':
+ from locale import strcoll
+ strxfrm = cmp_to_key(strcoll)
+ null_string = strxfrm('')
+ else:
+ from locale import strxfrm
+ null_string = ''
+ use_pyicu = False
+
+ # On some systems, locale is broken and does not sort in the expected
+ # order. We will try to detect this and compensate.
+ def dumb_sort():
+ return strxfrm('A') < strxfrm('a')
+
+
+if PY_VERSION >= 3.3:
+ def _low(x):
+ return x.casefold()
+else:
+ def _low(x):
+ return x.lower()
diff --git a/natsort/compat/pathlib.py b/natsort/compat/pathlib.py
new file mode 100644
index 0000000..f0ab7eb
--- /dev/null
+++ b/natsort/compat/pathlib.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
+
+try:
+ from pathlib import PurePath # PurePath is the base object for Paths.
+except ImportError: # pragma: no cover
+ PurePath = object # To avoid NameErrors.
+ has_pathlib = False
+else:
+ has_pathlib = True
diff --git a/natsort/py23compat.py b/natsort/compat/py23.py
index 3c9f88b..358eecf 100644
--- a/natsort/py23compat.py
+++ b/natsort/compat/py23.py
@@ -1,12 +1,17 @@
# -*- coding: utf-8 -*-
-from __future__ import (print_function, division,
- unicode_literals, absolute_import)
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
import functools
import sys
# These functions are used to make the doctests compatible between
-# python2 and python3. This code is pretty much lifted from the iPython
+# python2 and python3, and also provide uniform functionality between
+# the two versions. This code is pretty much lifted from the iPython
# project's py3compat.py file. Credit to the iPython devs.
# Numeric form of version
@@ -32,6 +37,42 @@ else:
py23_zip = itertools.izip
+# cmp_to_key was not created till 2.7, so require this for 2.6
+try:
+ from functools import cmp_to_key
+except ImportError: # pragma: no cover
+ def cmp_to_key(mycmp):
+ """Convert a cmp= function into a key= function"""
+ class K(object):
+ __slots__ = ['obj']
+
+ def __init__(self, obj):
+ self.obj = obj
+
+ def __lt__(self, other):
+ return mycmp(self.obj, other.obj) < 0
+
+ def __gt__(self, other):
+ return mycmp(self.obj, other.obj) > 0
+
+ def __eq__(self, other):
+ return mycmp(self.obj, other.obj) == 0
+
+ def __le__(self, other):
+ return mycmp(self.obj, other.obj) <= 0
+
+ def __ge__(self, other):
+ return mycmp(self.obj, other.obj) >= 0
+
+ def __ne__(self, other):
+ return mycmp(self.obj, other.obj) != 0
+
+ def __hash__(self):
+ raise TypeError('hash not implemented')
+
+ return K
+
+
# This function is intended to decorate other functions that will modify
# either a string directly, or a function's docstring.
def _modify_str_or_docstring(str_change_func):
diff --git a/natsort/locale_help.py b/natsort/locale_help.py
index 789b50b..bf80168 100644
--- a/natsort/locale_help.py
+++ b/natsort/locale_help.py
@@ -4,98 +4,23 @@ This module is intended to help combine some locale functions
together for natsort consumption. It also accounts for Python2
and Python3 differences.
"""
-from __future__ import (print_function, division,
- unicode_literals, absolute_import)
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
# Std. lib imports.
-import sys
from itertools import chain
from locale import localeconv
# Local imports.
-from natsort.py23compat import PY_VERSION
-
-# We need cmp_to_key for Python2 because strxfrm is broken for unicode.
-try:
- from functools import cmp_to_key
-# cmp_to_key was not created till 2.7.
-except ImportError: # pragma: no cover
- def cmp_to_key(mycmp):
- """Convert a cmp= function into a key= function"""
- class K(object):
- __slots__ = ['obj']
-
- def __init__(self, obj):
- self.obj = obj
-
- def __lt__(self, other):
- return mycmp(self.obj, other.obj) < 0
-
- def __gt__(self, other):
- return mycmp(self.obj, other.obj) > 0
-
- def __eq__(self, other):
- return mycmp(self.obj, other.obj) == 0
-
- def __le__(self, other):
- return mycmp(self.obj, other.obj) <= 0
-
- def __ge__(self, other):
- return mycmp(self.obj, other.obj) >= 0
-
- def __ne__(self, other):
- return mycmp(self.obj, other.obj) != 0
-
- def __hash__(self):
- raise TypeError('hash not implemented')
-
- return K
-
-# Make the strxfrm function from strcoll on Python2
-# It can be buggy (especially on BSD-based systems),
-# so prefer PyICU if available.
-try:
- import PyICU
- from locale import getlocale
-
- # If using PyICU, get the locale from the current global locale,
- # then create a sort key from that
- def get_pyicu_transform(l, _d={}):
- if l not in _d:
- if l == (None, None):
- c = PyICU.Collator.createInstance(PyICU.Locale())
- else:
- loc = '.'.join(l)
- c = PyICU.Collator.createInstance(PyICU.Locale(loc))
- _d[l] = c.getSortKey
- return _d[l]
- use_pyicu = True
- null_string = b''
-
- def dumb_sort():
- return False
-except ImportError:
- if sys.version[0] == '2':
- from locale import strcoll
- strxfrm = cmp_to_key(strcoll)
- null_string = strxfrm('')
- else:
- from locale import strxfrm
- null_string = ''
- use_pyicu = False
-
- # On some systems, locale is broken and does not sort in the expected
- # order. We will try to detect this and compensate.
- def dumb_sort():
- return strxfrm('A') < strxfrm('a')
-
-
-if PY_VERSION >= 3.3:
- def _low(x):
- return x.casefold()
+from natsort.compat.locale import use_pyicu, _low
+if use_pyicu:
+ from natsort.compat.locale import get_pyicu_transform, getlocale
else:
- def _low(x):
- return x.lower()
+ from natsort.compat.locale import strxfrm
def groupletters(x):
diff --git a/natsort/natsort.py b/natsort/natsort.py
index 78c0c24..6f90bf9 100644
--- a/natsort/natsort.py
+++ b/natsort/natsort.py
@@ -9,11 +9,13 @@ You can mix types with natsorted. This can get around the new
descend into lists of lists so you can sort by the sublist contents.
See the README or the natsort homepage for more details.
-
"""
-
-from __future__ import (print_function, division,
- unicode_literals, absolute_import)
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
# Std lib. imports.
import re
@@ -22,9 +24,13 @@ from functools import partial
from warnings import warn
# Local imports.
-from natsort.utils import _natsort_key, _args_to_enum, _do_decoding
from natsort.ns_enum import ns
-from natsort.py23compat import u_format
+from natsort.compat.py23 import u_format
+from natsort.utils import (
+ _natsort_key,
+ _args_to_enum,
+ _do_decoding,
+)
# Make sure the doctest works for either python2 or python3
__doc__ = u_format(__doc__)
diff --git a/natsort/ns_enum.py b/natsort/ns_enum.py
index ebe3374..fdaaf88 100644
--- a/natsort/ns_enum.py
+++ b/natsort/ns_enum.py
@@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-
"""This module defines the "ns" enum for natsort."""
-
-from __future__ import (print_function, division,
- unicode_literals, absolute_import)
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
class ns(object):
diff --git a/natsort/unicode_numbers.py b/natsort/unicode_numbers.py
index a0e8359..c93ed56 100644
--- a/natsort/unicode_numbers.py
+++ b/natsort/unicode_numbers.py
@@ -1,17 +1,19 @@
# -*- coding: utf-8 -*-
"""
Contains all possible non-ASCII unicode numbers.
-
"""
-
-from __future__ import (print_function, division,
- unicode_literals, absolute_import)
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
# Std. lib imports.
import unicodedata
# Local imports.
-from natsort.py23compat import py23_unichr
+from natsort.compat.py23 import py23_unichr
# Rather than determine this on the fly, which would incur a startup
diff --git a/natsort/utils.py b/natsort/utils.py
index 6d6fd99..97dfbc1 100644
--- a/natsort/utils.py
+++ b/natsort/utils.py
@@ -2,11 +2,13 @@
"""
Utilities and definitions for natsort, mostly all used to define
the _natsort_key function.
-
"""
-
-from __future__ import (print_function, division,
- unicode_literals, absolute_import)
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
# Std. lib imports.
import re
@@ -18,32 +20,26 @@ from itertools import islice
from locale import localeconv
# Local imports.
-from natsort.locale_help import (locale_convert, grouper,
- null_string, use_pyicu, dumb_sort)
-from natsort.py23compat import py23_str, py23_zip, PY_VERSION
from natsort.ns_enum import ns, _ns
from natsort.unicode_numbers import digits, numeric
-
-# If the user has fastnumbers installed, they will get great speed
-# benefits. If not, we simulate the functions here.
-try:
- from fastnumbers import fast_float, fast_int, isint, isfloat
- import fastnumbers
- v = list(map(int, fastnumbers.__version__.split('.')))
- if not (v[0] >= 0 and v[1] >= 5): # Require >= version 0.5.0.
- raise ImportError
-except ImportError:
- from natsort.fake_fastnumbers import fast_float, fast_int, isint, isfloat
-
-# If the user has pathlib installed, the ns.PATH option will convert
-# Path objects to str before sorting.
-try:
- from pathlib import PurePath # PurePath is the base object for Paths.
-except ImportError: # pragma: no cover
- PurePath = object # To avoid NameErrors.
- has_pathlib = False
-else:
- has_pathlib = True
+from natsort.locale_help import locale_convert, grouper
+from natsort.compat.pathlib import PurePath, has_pathlib
+from natsort.compat.py23 import (
+ py23_str,
+ py23_zip,
+ PY_VERSION,
+)
+from natsort.compat.locale import (
+ dumb_sort,
+ use_pyicu,
+ null_string,
+)
+from natsort.compat.fastnumbers import (
+ fast_float,
+ fast_int,
+ isint,
+ isfloat,
+)
# Group algorithm types for easy extraction
_NUMBER_ALGORITHMS = ns.FLOAT | ns.INT | ns.UNSIGNED | ns.SIGNED | ns.NOEXP
@@ -51,35 +47,35 @@ _ALL_BUT_PATH = (ns.F | ns.I | ns.U | ns.S | ns.N | ns.L |
ns.IC | ns.LF | ns.G | ns.UG | ns.TYPESAFE)
# The regex that locates floats - include Unicode numerals.
-_float_sign_exp_re = r'([-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?|[{}])'
+_float_sign_exp_re = r'([-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?|[{0}])'
_float_sign_exp_re = _float_sign_exp_re.format(numeric)
_float_sign_exp_re = re.compile(_float_sign_exp_re, flags=re.U)
-_float_nosign_exp_re = r'([0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?|[{}])'
+_float_nosign_exp_re = r'([0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?|[{0}])'
_float_nosign_exp_re = _float_nosign_exp_re.format(numeric)
_float_nosign_exp_re = re.compile(_float_nosign_exp_re, flags=re.U)
-_float_sign_noexp_re = r'([-+]?[0-9]*\.?[0-9]+|[{}])'
+_float_sign_noexp_re = r'([-+]?[0-9]*\.?[0-9]+|[{0}])'
_float_sign_noexp_re = _float_sign_noexp_re.format(numeric)
_float_sign_noexp_re = re.compile(_float_sign_noexp_re, flags=re.U)
-_float_nosign_noexp_re = r'([0-9]*\.?[0-9]+|[{}])'
+_float_nosign_noexp_re = r'([0-9]*\.?[0-9]+|[{0}])'
_float_nosign_noexp_re = _float_nosign_noexp_re.format(numeric)
_float_nosign_noexp_re = re.compile(_float_nosign_noexp_re, flags=re.U)
-_float_sign_exp_re_c = r'([-+]?[0-9]*[.,]?[0-9]+(?:[eE][-+]?[0-9]+)?)|[{}]'
+_float_sign_exp_re_c = r'([-+]?[0-9]*[.,]?[0-9]+(?:[eE][-+]?[0-9]+)?)|[{0}]'
_float_sign_exp_re_c = _float_sign_exp_re_c.format(numeric)
_float_sign_exp_re_c = re.compile(_float_sign_exp_re_c, flags=re.U)
-_float_nosign_exp_re_c = r'([0-9]*[.,]?[0-9]+(?:[eE][-+]?[0-9]+)?|[{}])'
+_float_nosign_exp_re_c = r'([0-9]*[.,]?[0-9]+(?:[eE][-+]?[0-9]+)?|[{0}])'
_float_nosign_exp_re_c = _float_nosign_exp_re_c.format(numeric)
_float_nosign_exp_re_c = re.compile(_float_nosign_exp_re_c, flags=re.U)
-_float_sign_noexp_re_c = r'([-+]?[0-9]*[.,]?[0-9]+|[{}])'
+_float_sign_noexp_re_c = r'([-+]?[0-9]*[.,]?[0-9]+|[{0}])'
_float_sign_noexp_re_c = _float_sign_noexp_re_c.format(numeric)
_float_sign_noexp_re_c = re.compile(_float_sign_noexp_re_c, flags=re.U)
-_float_nosign_noexp_re_c = r'([0-9]*[.,]?[0-9]+|[{}])'
+_float_nosign_noexp_re_c = r'([0-9]*[.,]?[0-9]+|[{0}])'
_float_nosign_noexp_re_c = _float_nosign_noexp_re_c.format(numeric)
_float_nosign_noexp_re_c = re.compile(_float_nosign_noexp_re_c, flags=re.U)
# Integer regexes - include Unicode digits.
-_int_nosign_re = r'([0-9]+|[{}])'.format(digits)
+_int_nosign_re = r'([0-9]+|[{0}])'.format(digits)
_int_nosign_re = re.compile(_int_nosign_re, flags=re.U)
-_int_sign_re = r'([-+]?[0-9]+|[{}])'.format(digits)
+_int_sign_re = r'([-+]?[0-9]+|[{0}])'.format(digits)
_int_sign_re = re.compile(_int_sign_re, flags=re.U)
# This dict will help select the correct regex and number conversion function.
diff --git a/setup.cfg b/setup.cfg
index 8498b36..2bde776 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,16 +6,23 @@ formats = zip,gztar
[pytest]
flakes-ignore =
- natsort/py23compat.py UndefinedName
+ natsort/compat/py23.py UndefinedName
natsort/__init__.py UnusedImport
+ natsort/compat/* UnusedImport
docs/source/conf.py ALL
test_natsort/test_natsort.py UnusedImport RedefinedWhileUnused
test_natsort/test_locale_help.py UnusedImport RedefinedWhileUnused
+ test_natsort/compat/* UnusedImport
pep8ignore =
+ natsort/ns_enum.py E126 E241 E123
test_natsort/test_natsort.py E501 E241 E221
test_natsort/test_utils.py E501 E241 E221
test_natsort/test_locale_help.py E501 E241 E221
test_natsort/test_main.py E501 E241 E221
test_natsort/profile_natsorted.py ALL
docs/source/conf.py ALL
+
+[flake8]
+max-line-length = 160
+ignore = E231,E302
diff --git a/setup.py b/setup.py
index c8c4d21..b9ef737 100644
--- a/setup.py
+++ b/setup.py
@@ -25,6 +25,7 @@ class PyTest(TestCommand):
'--cov-report', 'term-missing',
'--flakes',
'--pep8',
+ '-s',
# '--failed',
# '-v',
])
@@ -61,10 +62,13 @@ REQUIRES = 'argparse' if sys.version[:3] in ('2.6', '3.0', '3.1') else ''
# Testing needs pytest, and mock if less than python 3.3
TESTS_REQUIRE = ['pytest', 'pytest-pep8', 'pytest-flakes',
- 'pytest-cov', 'hypothesis']
-if sys.version[0] == 2 or (sys.version[3] == '3' and int(sys.version[2]) < 3):
+ 'pytest-cov', 'pytest-cache', 'hypothesis']
+
+if (sys.version.startswith('2') or
+ (sys.version.startswith('3') and int(sys.version.split('.')[1]) < 3)):
TESTS_REQUIRE.append('mock')
-if sys.version[0] == 2 or (sys.version[3] == '3' and int(sys.version[2]) < 4):
+if (sys.version.startswith('2') or
+ (sys.version.startswith('3') and int(sys.version.split('.')[1]) < 4)):
TESTS_REQUIRE.append('pathlib')
# The setup parameters
diff --git a/test_natsort/compat/__init__.py b/test_natsort/compat/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test_natsort/compat/__init__.py
diff --git a/test_natsort/compat/hypothesis.py b/test_natsort/compat/hypothesis.py
new file mode 100644
index 0000000..ade7277
--- /dev/null
+++ b/test_natsort/compat/hypothesis.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
+import sys
+import compat.mock
+
+major_minor = sys.version_info[:2]
+
+# Use hypothesis if not on python 2.6.
+if major_minor != (2, 6):
+ use_hypothesis = True
+ from hypothesis import assume, given, example
+ from hypothesis.specifiers import (
+ integers_in_range,
+ integers_from,
+ sampled_from,
+ )
+# Otherwise mock these imports, because hypothesis
+# is incompatible with python 2.6.
+else:
+ example = integers_in_range = integers_from = \
+ sampled_from = assume = given = compat.mock.MagicMock()
+ use_hypothesis = False
diff --git a/test_natsort/compat/locale.py b/test_natsort/compat/locale.py
new file mode 100644
index 0000000..1517c9c
--- /dev/null
+++ b/test_natsort/compat/locale.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
+
+# Std. lib imports.
+import locale
+
+# Local imports
+from natsort.locale_help import use_pyicu
+from natsort.compat.py23 import py23_str
+
+
+def load_locale(x):
+ """ Convenience to load a locale, trying ISO8859-1 first."""
+ try:
+ locale.setlocale(locale.LC_ALL, str('{0}.ISO8859-1'.format(x)))
+ except:
+ locale.setlocale(locale.LC_ALL, str('{0}.UTF-8'.format(x)))
+
+# Check if de_DE is installed.
+try:
+ load_locale('de_DE')
+ has_locale_de_DE = True
+except locale.Error:
+ has_locale_de_DE = False
+
+# Make a function that will return the appropriate
+# strxfrm for the current locale.
+if use_pyicu:
+ from natsort.locale_help import get_pyicu_transform
+ from locale import getlocale
+
+ def get_strxfrm():
+ return get_pyicu_transform(getlocale())
+else:
+ from natsort.locale_help import strxfrm
+
+ def get_strxfrm():
+ return strxfrm
+
+# Depending on the python version, use lower or casefold
+# to make a string lowercase.
+try:
+ low = py23_str.casefold
+except AttributeError:
+ low = py23_str.lower
diff --git a/test_natsort/compat/mock.py b/test_natsort/compat/mock.py
new file mode 100644
index 0000000..9ddb302
--- /dev/null
+++ b/test_natsort/compat/mock.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+from __future__ import (
+ print_function,
+ division,
+ unicode_literals,
+ absolute_import
+)
+# Load mock functions from the right place.
+try:
+ from unittest.mock import MagicMock, patch, call
+except ImportError:
+ from mock import MagicMock, patch, call
diff --git a/test_natsort/profile_natsorted.py b/test_natsort/profile_natsorted.py
index 802fe5f..130b652 100644
--- a/test_natsort/profile_natsorted.py
+++ b/test_natsort/profile_natsorted.py
@@ -10,7 +10,7 @@ import sys
sys.path.insert(0, '.')
from natsort import natsorted, index_natsorted
-from natsort.py23compat import py23_range
+from natsort.compat.py23 import py23_range
# Sample lists to sort
diff --git a/test_natsort/slow_splitters.py b/test_natsort/slow_splitters.py
index 6352dd7..61e8025 100644
--- a/test_natsort/slow_splitters.py
+++ b/test_natsort/slow_splitters.py
@@ -3,7 +3,7 @@
from __future__ import unicode_literals
import unicodedata
-from natsort.py23compat import PY_VERSION
+from natsort.compat.py23 import PY_VERSION
if PY_VERSION >= 3.0:
long = int
diff --git a/test_natsort/stress_natsort.py b/test_natsort/stress_natsort.py
index 604a33d..d9815eb 100644
--- a/test_natsort/stress_natsort.py
+++ b/test_natsort/stress_natsort.py
@@ -8,7 +8,7 @@ from string import printable
from copy import copy
from pytest import fail
from natsort import natsorted
-from natsort.py23compat import py23_range
+from natsort.compat.py23 import py23_range
def test_random():
diff --git a/test_natsort/test_fake_fastnumbers.py b/test_natsort/test_fake_fastnumbers.py
index ff7e42c..594d060 100644
--- a/test_natsort/test_fake_fastnumbers.py
+++ b/test_natsort/test_fake_fastnumbers.py
@@ -4,11 +4,21 @@ Test the fake fastnumbers module.
"""
from __future__ import unicode_literals
+import pytest
import unicodedata
from math import isnan
-from hypothesis import given, assume
-from natsort.fake_fastnumbers import fast_float, fast_int, isfloat, isint
-from natsort.py23compat import py23_str
+from natsort.compat.py23 import py23_str
+from natsort.compat.fake_fastnumbers import (
+ fast_float,
+ fast_int,
+ isfloat,
+ isint,
+)
+from compat.hypothesis import (
+ assume,
+ given,
+ use_hypothesis,
+)
def is_float(x):
@@ -38,10 +48,10 @@ def is_int(x):
else:
return True
-
# Each test has an "example" version for demonstrative purposes,
# and a test that uses the hypothesis module.
+
def test_fast_float_converts_float_string_to_float_example():
assert fast_float('45.8') == 45.8
assert fast_float('-45') == -45.0
@@ -49,6 +59,7 @@ def test_fast_float_converts_float_string_to_float_example():
assert isnan(fast_float('nan'))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(float)
def test_fast_float_converts_float_string_to_float(x):
assume(not isnan(x)) # But inf is included
@@ -59,6 +70,7 @@ def test_fast_float_leaves_string_as_is_example():
assert fast_float('invalid') == 'invalid'
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(py23_str)
def test_fast_float_leaves_string_as_is(x):
assume(not is_float(x))
@@ -71,6 +83,7 @@ def test_fast_int_leaves_float_string_as_is_example():
assert fast_int('inf') == 'inf'
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(float)
def test_fast_int_leaves_float_string_as_is(x):
assume(not x.is_integer())
@@ -91,6 +104,7 @@ def test_fast_int_leaves_string_as_is_example():
assert fast_int('invalid') == 'invalid'
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(py23_str)
def test_fast_int_leaves_string_as_is(x):
assume(not is_int(x))
diff --git a/test_natsort/test_locale_help.py b/test_natsort/test_locale_help.py
index 95c3000..63ff96b 100644
--- a/test_natsort/test_locale_help.py
+++ b/test_natsort/test_locale_help.py
@@ -5,26 +5,24 @@ Test the locale help module module.
from __future__ import unicode_literals
import locale
+import pytest
from math import isnan
from itertools import chain
-from natsort.fake_fastnumbers import fast_float, isfloat, isint
-from natsort.locale_help import grouper, locale_convert, use_pyicu
-from natsort.py23compat import py23_str
-from hypothesis import given, assume, example
-
-if use_pyicu:
- from natsort.locale_help import get_pyicu_transform
- from locale import getlocale
- strxfrm = get_pyicu_transform(getlocale())
-else:
- from natsort.locale_help import strxfrm
-
-
-def load_locale(x):
- try:
- locale.setlocale(locale.LC_ALL, str('{}.ISO8859-1'.format(x)))
- except:
- locale.setlocale(locale.LC_ALL, str('{}.UTF-8'.format(x)))
+from natsort.compat.fake_fastnumbers import fast_float, isfloat
+from natsort.locale_help import grouper, locale_convert
+from natsort.compat.py23 import py23_str
+from natsort.compat.locale import use_pyicu
+from compat.locale import (
+ load_locale,
+ has_locale_de_DE,
+ get_strxfrm,
+ low,
+)
+from compat.hypothesis import (
+ assume,
+ given,
+ use_hypothesis,
+)
# Each test has an "example" version for demonstrative purposes,
@@ -36,13 +34,10 @@ def test_grouper_returns_letters_with_lowercase_transform_of_letter_example():
assert grouper('hello', (fast_float, isfloat)) == 'hheelllloo'
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(py23_str)
def test_grouper_returns_letters_with_lowercase_transform_of_letter(x):
assume(type(fast_float(x)) is not float)
- try:
- low = py23_str.casefold
- except AttributeError:
- low = py23_str.lower
assert grouper(x, (fast_float, isfloat)) == ''.join(chain.from_iterable([low(y), y] for y in x))
@@ -50,6 +45,7 @@ def test_grouper_returns_float_string_as_float_example():
assert grouper('45.8e-2', (fast_float, isfloat)) == 45.8e-2
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(float)
def test_grouper_returns_float_string_as_float(x):
assume(not isnan(x))
@@ -62,6 +58,7 @@ def test_locale_convert_transforms_float_string_to_float_example():
locale.setlocale(locale.LC_NUMERIC, str(''))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(float)
def test_locale_convert_transforms_float_string_to_float(x):
assume(not isnan(x))
@@ -72,62 +69,41 @@ def test_locale_convert_transforms_float_string_to_float(x):
def test_locale_convert_transforms_nonfloat_string_to_strxfrm_string_example():
load_locale('en_US')
- if use_pyicu:
- from natsort.locale_help import get_pyicu_transform
- from locale import getlocale
- strxfrm = get_pyicu_transform(getlocale())
- else:
- from natsort.locale_help import strxfrm
+ strxfrm = get_strxfrm()
assert locale_convert('45,8', (fast_float, isfloat), False) == strxfrm('45,8')
assert locale_convert('hello', (fast_float, isfloat), False) == strxfrm('hello')
locale.setlocale(locale.LC_NUMERIC, str(''))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(py23_str)
def test_locale_convert_transforms_nonfloat_string_to_strxfrm_string(x):
assume(type(fast_float(x)) is not float)
load_locale('en_US')
- if use_pyicu:
- from natsort.locale_help import get_pyicu_transform
- from locale import getlocale
- strxfrm = get_pyicu_transform(getlocale())
- else:
- from natsort.locale_help import strxfrm
+ strxfrm = get_strxfrm()
assert locale_convert(x, (fast_float, isfloat), False) == strxfrm(x)
locale.setlocale(locale.LC_NUMERIC, str(''))
def test_locale_convert_with_groupletters_transforms_nonfloat_string_to_strxfrm_string_with_grouped_letters_example():
load_locale('en_US')
- if use_pyicu:
- from natsort.locale_help import get_pyicu_transform
- from locale import getlocale
- strxfrm = get_pyicu_transform(getlocale())
- else:
- from natsort.locale_help import strxfrm
+ strxfrm = get_strxfrm()
assert locale_convert('hello', (fast_float, isfloat), True) == strxfrm('hheelllloo')
assert locale_convert('45,8', (fast_float, isfloat), True) == strxfrm('4455,,88')
locale.setlocale(locale.LC_NUMERIC, str(''))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(py23_str)
def test_locale_convert_with_groupletters_transforms_nonfloat_string_to_strxfrm_string_with_grouped_letters(x):
assume(type(fast_float(x)) is not float)
load_locale('en_US')
- if use_pyicu:
- from natsort.locale_help import get_pyicu_transform
- from locale import getlocale
- strxfrm = get_pyicu_transform(getlocale())
- else:
- from natsort.locale_help import strxfrm
- try:
- low = py23_str.casefold
- except AttributeError:
- low = py23_str.lower
+ strxfrm = get_strxfrm()
assert locale_convert(x, (fast_float, isfloat), True) == strxfrm(''.join(chain.from_iterable([low(y), y] for y in x)))
locale.setlocale(locale.LC_NUMERIC, str(''))
+@pytest.mark.skipif(not has_locale_de_DE, reason='requires de_DE locale')
def test_locale_convert_transforms_float_string_to_float_with_de_locale_example():
load_locale('de_DE')
assert locale_convert('45.8', (fast_float, isfloat), False) == 45.8
@@ -135,6 +111,8 @@ def test_locale_convert_transforms_float_string_to_float_with_de_locale_example(
locale.setlocale(locale.LC_NUMERIC, str(''))
+@pytest.mark.skipif(not has_locale_de_DE, reason='requires de_DE locale')
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(float)
def test_locale_convert_transforms_float_string_to_float_with_de_locale(x):
assume(not isnan(x))
diff --git a/test_natsort/test_main.py b/test_natsort/test_main.py
index 0735f6d..4751c4a 100644
--- a/test_natsort/test_main.py
+++ b/test_natsort/test_main.py
@@ -3,19 +3,28 @@
Test the natsort command-line tool functions.
"""
from __future__ import print_function, unicode_literals
+import pytest
import re
import sys
from pytest import raises
-from hypothesis import given, assume
-from hypothesis.specifiers import integers_in_range, integers_from, sampled_from
-try:
- from unittest.mock import patch, call
-except ImportError:
- from mock import patch, call
-from natsort.__main__ import main, range_check, check_filter
-from natsort.__main__ import keep_entry_range, exclude_entry
-from natsort.__main__ import sort_and_print_entries
-from natsort.py23compat import py23_str
+from compat.mock import patch, call
+from compat.hypothesis import (
+ assume,
+ given,
+ integers_from,
+ integers_in_range,
+ sampled_from,
+ use_hypothesis,
+)
+from natsort.__main__ import (
+ main,
+ range_check,
+ check_filter,
+ keep_entry_range,
+ exclude_entry,
+ sort_and_print_entries,
+ py23_str,
+)
def test_main_passes_default_arguments_with_no_command_line_options():
@@ -162,12 +171,14 @@ def test_range_check_returns_range_as_is_but_with_floats_if_first_is_less_than_s
assert range_check(6.4, 30) == (6.4, 30.0)
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(x=int, y=int)
def test_range_check_returns_range_as_is_but_with_floats_if_first_is_less_than_second(x, y):
assume(x < y)
assert range_check(x, y) == (float(x), float(y))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(x=float, y=float)
def test_range_check_returns_range_as_is_but_with_floats_if_first_is_less_than_second2(x, y):
assume(x < y)
@@ -180,6 +191,7 @@ def test_range_check_raises_ValueError_if_second_is_less_than_first_example():
assert str(err.value) == 'low >= high'
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(x=float, y=float)
def test_range_check_raises_ValueError_if_second_is_less_than_first(x, y):
assume(x >= y)
@@ -199,6 +211,7 @@ def test_check_filter_converts_filter_numbers_to_floats_if_filter_is_valid_examp
assert check_filter([(6, 7), (2, 8)]) == [(6.0, 7.0), (2.0, 8.0)]
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(x=(int, int, float, float), y=(int, float, float, int))
def test_check_filter_converts_filter_numbers_to_floats_if_filter_is_valid(x, y):
assume(all(i < j for i, j in zip(x, y)))
@@ -211,6 +224,7 @@ def test_check_filter_raises_ValueError_if_filter_is_invalid_example():
assert str(err.value) == 'Error in --filter: low >= high'
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(x=(int, int, float, float), y=(int, float, float, int))
def test_check_filter_raises_ValueError_if_filter_is_invalid(x, y):
assume(any(i >= j for i, j in zip(x, y)))
@@ -223,6 +237,7 @@ def test_keep_entry_range_returns_True_if_any_portion_of_input_is_between_the_ra
assert keep_entry_range('a56b23c89', [0], [100], int, re.compile(r'\d+'))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given((py23_str, integers_in_range(1, 99), py23_str, integers_in_range(1, 99), py23_str))
def test_keep_entry_range_returns_True_if_any_portion_of_input_is_between_the_range_bounds(x):
s = ''.join(map(py23_str, x))
@@ -234,6 +249,7 @@ def test_keep_entry_range_returns_True_if_any_portion_of_input_is_between_any_ra
assert keep_entry_range('a56b23c89', [1, 88], [20, 90], int, re.compile(r'\d+'))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given((py23_str, integers_in_range(2, 89), py23_str, integers_in_range(2, 89), py23_str))
def test_keep_entry_range_returns_True_if_any_portion_of_input_is_between_any_range_bounds(x):
s = ''.join(map(py23_str, x))
@@ -245,6 +261,7 @@ def test_keep_entry_range_returns_False_if_no_portion_of_input_is_between_the_ra
assert not keep_entry_range('a56b23c89', [1], [20], int, re.compile(r'\d+'))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given((py23_str, integers_from(21), py23_str, integers_from(21), py23_str))
def test_keep_entry_range_returns_False_if_no_portion_of_input_is_between_the_range_bounds(x):
s = ''.join(map(py23_str, x))
@@ -256,6 +273,7 @@ def test_exclude_entry_returns_True_if_exlcude_parameters_are_not_in_input_examp
assert exclude_entry('a56b23c89', [100, 45], int, re.compile(r'\d+'))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given((py23_str, integers_from(0), py23_str, integers_from(0), py23_str))
def test_exclude_entry_returns_True_if_exlcude_parameters_are_not_in_input(x):
s = ''.join(map(py23_str, x))
@@ -267,6 +285,7 @@ def test_exclude_entry_returns_False_if_exlcude_parameters_are_in_input_example(
assert not exclude_entry('a56b23c89', [23], int, re.compile(r'\d+'))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given((py23_str, sampled_from([23, 45, 87]), py23_str, sampled_from([23, 45, 87]), py23_str))
def test_exclude_entry_returns_False_if_exlcude_parameters_are_in_input(x):
s = ''.join(map(py23_str, x))
diff --git a/test_natsort/test_natsort.py b/test_natsort/test_natsort.py
index 382db24..eac8c39 100644
--- a/test_natsort/test_natsort.py
+++ b/test_natsort/test_natsort.py
@@ -4,24 +4,33 @@ Here are a collection of examples of how this module can be used.
See the README or the natsort homepage for more details.
"""
from __future__ import unicode_literals, print_function
+import pytest
import sys
import warnings
import locale
from operator import itemgetter
from pytest import raises
-from natsort import natsorted, index_natsorted, natsort_key, versorted, index_versorted
-from natsort import humansorted, index_humansorted, natsort_keygen, order_by_index, ns
-from natsort import realsorted, index_realsorted, decoder, as_ascii, as_utf8
+from natsort import (
+ natsorted,
+ index_natsorted,
+ natsort_key,
+ versorted,
+ index_versorted,
+ humansorted,
+ index_humansorted,
+ natsort_keygen,
+ order_by_index,
+ ns,
+ realsorted,
+ index_realsorted,
+ decoder,
+ as_ascii,
+ as_utf8,
+)
+from compat.locale import load_locale, has_locale_de_DE
from natsort.utils import _natsort_key
-def load_locale(x):
- try:
- locale.setlocale(locale.LC_ALL, str('{}.ISO8859-1'.format(x)))
- except:
- locale.setlocale(locale.LC_ALL, str('{}.UTF-8'.format(x)))
-
-
def test_decoder_returns_function_that_can_decode_bytes_but_return_non_bytes_as_is():
f = decoder('latin1')
a = 'bytes'
@@ -291,6 +300,7 @@ def test_natsorted_with_LOCALE_and_en_setting_returns_results_sorted_by_en_langu
locale.setlocale(locale.LC_ALL, str(''))
+@pytest.mark.skipif(not has_locale_de_DE, reason='requires de_DE locale')
def test_natsorted_with_LOCALE_and_de_setting_returns_results_sorted_by_de_language():
load_locale('de_DE')
a = ['c', 'รค', 'b', 'a5,6', 'a5,50']
diff --git a/test_natsort/test_unicode_numbers.py b/test_natsort/test_unicode_numbers.py
index f3e8de7..25d4679 100644
--- a/test_natsort/test_unicode_numbers.py
+++ b/test_natsort/test_unicode_numbers.py
@@ -4,8 +4,13 @@ Test the Unicode numbers module.
"""
from __future__ import unicode_literals
import unicodedata
-from natsort.py23compat import py23_range, py23_unichr
-from natsort.unicode_numbers import numeric_chars, numeric, digit_chars, digits
+from natsort.compat.py23 import py23_range, py23_unichr
+from natsort.unicode_numbers import (
+ numeric_chars,
+ numeric,
+ digit_chars,
+ digits,
+)
def test_numeric_chars_contains_only_valid_unicode_numeric_characters():
diff --git a/test_natsort/test_utils.py b/test_natsort/test_utils.py
index 3668511..01de5d3 100644
--- a/test_natsort/test_utils.py
+++ b/test_natsort/test_utils.py
@@ -5,30 +5,57 @@ from __future__ import unicode_literals
import sys
import locale
import pathlib
+import pytest
import string
from math import isnan
from operator import itemgetter
from itertools import chain
from pytest import raises
-from hypothesis import given, assume, example
-from hypothesis.specifiers import sampled_from
from natsort.ns_enum import ns
-from natsort.utils import _number_extracter, _py3_safe, _natsort_key, _args_to_enum
-from natsort.utils import _float_sign_exp_re, _float_nosign_exp_re, _float_sign_noexp_re
-from natsort.utils import _float_nosign_noexp_re, _int_nosign_re, _int_sign_re, _do_decoding
-from natsort.utils import _path_splitter, _fix_nan
-from natsort.locale_help import use_pyicu, null_string, locale_convert, dumb_sort
-from natsort.py23compat import py23_str
-from slow_splitters import int_splitter, float_splitter, sep_inserter
-
-try:
- from fastnumbers import fast_float, fast_int, isint
- import fastnumbers
- v = list(map(int, fastnumbers.__version__.split('.')))
- if not (v[0] >= 0 and v[1] >= 5): # Require >= version 0.5.0.
- raise ImportError
-except ImportError:
- from natsort.fake_fastnumbers import fast_float, fast_int, isint
+from natsort.utils import (
+ _number_extracter,
+ _py3_safe,
+ _natsort_key,
+ _args_to_enum,
+ _float_sign_exp_re,
+ _float_nosign_exp_re,
+ _float_sign_noexp_re,
+ _float_nosign_noexp_re,
+ _int_nosign_re,
+ _int_sign_re,
+ _do_decoding,
+ _path_splitter,
+ _fix_nan,
+)
+from natsort.locale_help import locale_convert
+from natsort.compat.py23 import py23_str
+from natsort.compat.locale import (
+ use_pyicu,
+ null_string,
+ dumb_sort,
+)
+from natsort.compat.fastnumbers import (
+ fast_float,
+ fast_int,
+ isint,
+)
+from slow_splitters import (
+ int_splitter,
+ float_splitter,
+ sep_inserter,
+)
+from compat.locale import (
+ load_locale,
+ get_strxfrm,
+ low,
+)
+from compat.hypothesis import (
+ assume,
+ given,
+ example,
+ sampled_from,
+ use_hypothesis,
+)
if sys.version[0] == '3':
long = int
@@ -36,13 +63,6 @@ if sys.version[0] == '3':
ichain = chain.from_iterable
-def load_locale(x):
- try:
- locale.setlocale(locale.LC_ALL, str('{}.ISO8859-1'.format(x)))
- except:
- locale.setlocale(locale.LC_ALL, str('{}.UTF-8'.format(x)))
-
-
def test_do_decoding_decodes_bytes_string_to_unicode():
assert type(_do_decoding(b'bytes', 'ascii')) is py23_str
assert _do_decoding(b'bytes', 'ascii') == 'bytes'
@@ -156,6 +176,7 @@ def test_py3_safe_with_use_locale_inserts_null_string_between_two_numbers_exampl
assert _py3_safe([5, 9], True, isint) == [5, null_string, 9]
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([py23_str, int])
def test_py3_safe_inserts_empty_string_between_two_numbers(x):
assume(bool(x))
@@ -167,6 +188,7 @@ def test_path_splitter_splits_path_string_by_separator_example():
assert _path_splitter(z) == list(pathlib.Path(z).parts)
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([sampled_from(string.ascii_letters)])
def test_path_splitter_splits_path_string_by_separator(x):
assume(len(x) > 1)
@@ -181,6 +203,7 @@ def test_path_splitter_splits_path_string_by_separator_and_removes_extension_exa
assert _path_splitter(z) == y[:-1] + [pathlib.Path(z).stem] + [pathlib.Path(z).suffix]
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([sampled_from(string.ascii_letters)])
def test_path_splitter_splits_path_string_by_separator_and_removes_extension(x):
assume(len(x) > 2)
@@ -195,6 +218,7 @@ def test_number_extracter_raises_TypeError_if_given_a_number_example():
assert _number_extracter(50.0, _float_sign_exp_re, *float_nosafe_nolocale_nogroup)
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(float)
def test_number_extracter_raises_TypeError_if_given_a_number(x):
with raises(TypeError):
@@ -205,6 +229,7 @@ def test_number_extracter_includes_plus_sign_and_exponent_in_float_definition_fo
assert _number_extracter('a5+5.034e-1', _float_sign_exp_re, *float_nosafe_nolocale_nogroup) == ['a', 5.0, 0.5034]
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test_number_extracter_includes_plus_sign_and_exponent_in_float_definition_for_signed_exp_floats(x):
assume(len(x) <= 10)
@@ -217,6 +242,7 @@ def test_number_extracter_excludes_plus_sign_in_float_definition_but_includes_ex
assert _number_extracter('a5+5.034e-1', _float_nosign_exp_re, *float_nosafe_nolocale_nogroup) == ['a', 5.0, '+', 0.5034]
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test_number_extracter_excludes_plus_sign_in_float_definition_but_includes_exponent_for_unsigned_exp_floats(x):
assume(len(x) <= 10)
@@ -229,6 +255,7 @@ def test_number_extracter_includes_plus_and_minus_sign_in_float_definition_but_e
assert _number_extracter('a5+5.034e-1', _float_sign_noexp_re, *float_nosafe_nolocale_nogroup) == ['a', 5.0, 5.034, 'e', -1.0]
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test_number_extracter_includes_plus_and_minus_sign_in_float_definition_but_excludes_exponent_for_signed_noexp_floats(x):
assume(len(x) <= 10)
@@ -241,6 +268,7 @@ def test_number_extracter_excludes_plus_sign_and_exponent_in_float_definition_fo
assert _number_extracter('a5+5.034e-1', _float_nosign_noexp_re, *float_nosafe_nolocale_nogroup) == ['a', 5.0, '+', 5.034, 'e-', 1.0]
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test_number_extracter_excludes_plus_sign_and_exponent_in_float_definition_for_unsigned_noexp_floats(x):
assume(len(x) <= 10)
@@ -253,6 +281,7 @@ def test_number_extracter_excludes_plus_and_minus_sign_in_int_definition_for_uns
assert _number_extracter('a5+5.034e-1', _int_nosign_re, *int_nosafe_nolocale_nogroup) == ['a', 5, '+', 5, '.', 34, 'e-', 1]
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
@example([10000000000000000000000000000000000000000000000000000000000000000000000000,
100000000000000000000000000000000000000000000000000000000000000000000000000,
@@ -267,6 +296,7 @@ def test_number_extracter_includes_plus_and_minus_sign_in_int_definition_for_sig
assert _number_extracter('a5+5.034e-1', _int_sign_re, *int_nosafe_nolocale_nogroup) == ['a', 5, 5, '.', 34, 'e', -1]
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test_number_extracter_includes_plus_and_minus_sign_in_int_definition_for_signed_ints(x):
assume(len(x) <= 10)
@@ -278,6 +308,7 @@ def test_number_extracter_inserts_empty_string_between_floats_for_py3safe_option
assert _number_extracter('a5+5.034e-1', _float_sign_exp_re, *float_safe_nolocale_nogroup) == ['a', 5.0, '', 0.5034]
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test_number_extracter_inserts_empty_string_between_floats_for_py3safe_option(x):
assume(len(x) <= 10)
@@ -290,6 +321,7 @@ def test_number_extracter_inserts_empty_string_between_ints_for_py3safe_option_e
assert _number_extracter('a5+5.034e-1', _int_sign_re, *int_safe_nolocale_nogroup) == ['a', 5, '', 5, '.', 34, 'e', -1]
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test_number_extracter_inserts_empty_string_between_ints_for_py3safe_option(x):
assume(len(x) <= 10)
@@ -313,14 +345,11 @@ def test_number_extracter_doubles_letters_with_lowercase_version_with_grouplette
assert _number_extracter('A5+5.034E-1', _float_sign_exp_re, *float_nosafe_nolocale_group) == ['aA', 5.0, 0.5034]
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test_number_extracter_doubles_letters_with_lowercase_version_with_groupletters_for_float(x):
assume(len(x) <= 10)
assume(not any(type(y) == float and isnan(y) for y in x))
- try:
- low = py23_str.casefold
- except AttributeError:
- low = py23_str.lower
s = ''.join(repr(y) if type(y) in (float, long, int) else y for y in x)
t = float_splitter(s, True, True, False, '')
t = [''.join([low(z) + z for z in y]) if type(y) != float else y for y in t]
@@ -331,13 +360,10 @@ def test_number_extracter_doubles_letters_with_lowercase_version_with_grouplette
assert _number_extracter('A5+5.034E-1', _int_nosign_re, *int_nosafe_nolocale_group) == ['aA', 5, '++', 5, '..', 34, 'eE--', 1]
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test_number_extracter_doubles_letters_with_lowercase_version_with_groupletters_for_int(x):
assume(len(x) <= 10)
- try:
- low = py23_str.casefold
- except AttributeError:
- low = py23_str.lower
s = ''.join(repr(y) if type(y) in (float, long, int) else y for y in x)
t = int_splitter(s, False, False, '')
t = [''.join([low(z) + z for z in y]) if type(y) not in (int, long) else y for y in t]
@@ -346,58 +372,56 @@ def test_number_extracter_doubles_letters_with_lowercase_version_with_grouplette
def test_number_extracter_extracts_numbers_and_strxfrms_strings_with_use_locale_example():
load_locale('en_US')
- if use_pyicu:
- from natsort.locale_help import get_pyicu_transform
- from locale import getlocale
- strxfrm = get_pyicu_transform(getlocale())
- else:
- from natsort.locale_help import strxfrm
+ strxfrm = get_strxfrm()
assert _number_extracter('A5+5.034E-1', _int_nosign_re, *int_nosafe_locale_nogroup) == [strxfrm('A'), 5, strxfrm('+'), 5, strxfrm('.'), 34, strxfrm('E-'), 1]
locale.setlocale(locale.LC_NUMERIC, str(''))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test_number_extracter_extracts_numbers_and_strxfrms_strings_with_use_locale(x):
assume(len(x) <= 10)
load_locale('en_US')
s = ''.join(repr(y) if type(y) in (float, long, int) else y for y in x)
t = int_splitter(s, False, False, null_string)
- t = [y if i == 0 and y is null_string else locale_convert(y, (fast_int, isint), False) for i, y in enumerate(t)]
- assert _number_extracter(s, _int_nosign_re, *int_nosafe_locale_nogroup) == t
+ try: # Account for locale bug on Python 3.2
+ t = [y if i == 0 and y is null_string else locale_convert(y, (fast_int, isint), False) for i, y in enumerate(t)]
+ assert _number_extracter(s, _int_nosign_re, *int_nosafe_locale_nogroup) == t
+ except OverflowError:
+ pass
locale.setlocale(locale.LC_NUMERIC, str(''))
def test_number_extracter_extracts_numbers_and_strxfrms_letter_doubled_strings_with_use_locale_and_groupletters_example():
load_locale('en_US')
- if use_pyicu:
- from natsort.locale_help import get_pyicu_transform
- from locale import getlocale
- strxfrm = get_pyicu_transform(getlocale())
- else:
- from natsort.locale_help import strxfrm
+ strxfrm = get_strxfrm()
assert _number_extracter('A5+5.034E-1', _int_nosign_re, *int_nosafe_locale_group) == [strxfrm('aA'), 5, strxfrm('++'), 5, strxfrm('..'), 34, strxfrm('eE--'), 1]
locale.setlocale(locale.LC_NUMERIC, str(''))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test_number_extracter_extracts_numbers_and_strxfrms_letter_doubled_strings_with_use_locale_and_groupletters(x):
assume(len(x) <= 10)
load_locale('en_US')
s = ''.join(repr(y) if type(y) in (float, long, int) else y for y in x)
t = int_splitter(s, False, False, null_string)
- t = [y if i == 0 and y is null_string else locale_convert(y, (fast_int, isint), True) for i, y in enumerate(t)]
- assert _number_extracter(s, _int_nosign_re, *int_nosafe_locale_group) == t
+ try: # Account for locale bug on Python 3.2
+ t = [y if i == 0 and y is null_string else locale_convert(y, (fast_int, isint), True) for i, y in enumerate(t)]
+ assert _number_extracter(s, _int_nosign_re, *int_nosafe_locale_group) == t
+ except OverflowError:
+ pass
locale.setlocale(locale.LC_NUMERIC, str(''))
def test__natsort_key_with_nan_input_transforms_nan_to_negative_inf():
- assert _natsort_key('nan', None, ns.FLOAT) == (u'', float('-inf'))
- assert _natsort_key(float('nan'), None, 0) == (u'', float('-inf'))
+ assert _natsort_key('nan', None, ns.FLOAT) == ('', float('-inf'))
+ assert _natsort_key(float('nan'), None, 0) == ('', float('-inf'))
def test__natsort_key_with_nan_input_and_NANLAST_transforms_nan_to_positive_inf():
- assert _natsort_key('nan', None, ns.FLOAT | ns.NANLAST) == (u'', float('+inf'))
- assert _natsort_key(float('nan'), None, ns.NANLAST) == (u'', float('+inf'))
+ assert _natsort_key('nan', None, ns.FLOAT | ns.NANLAST) == ('', float('+inf'))
+ assert _natsort_key(float('nan'), None, ns.NANLAST) == ('', float('+inf'))
assert ns.NL == ns.NANLAST
@@ -405,6 +429,7 @@ def test__natsort_key_with_nan_input_and_NANLAST_transforms_nan_to_positive_inf(
# They only confirm that _natsort_key uses the above building blocks.
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_float_and_signed_splits_input_into_string_and_signed_float_with_exponent(x):
assume(len(x) <= 10)
@@ -415,6 +440,7 @@ def test__natsort_key_with_float_and_signed_splits_input_into_string_and_signed_
assert _natsort_key(s, None, ns.F | ns.S) == tuple(_number_extracter(s, _float_sign_exp_re, *float_nosafe_nolocale_nogroup))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_real_splits_input_into_string_and_signed_float_with_exponent(x):
assume(len(x) <= 10)
@@ -424,6 +450,7 @@ def test__natsort_key_with_real_splits_input_into_string_and_signed_float_with_e
assert _natsort_key(s, None, ns.R) == tuple(_number_extracter(s, _float_sign_exp_re, *float_nosafe_nolocale_nogroup))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_real_matches_signed_float(x):
assume(len(x) <= 10)
@@ -432,6 +459,7 @@ def test__natsort_key_with_real_matches_signed_float(x):
assert _natsort_key(s, None, ns.R) == _natsort_key(s, None, ns.F | ns.S)
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_float_and_signed_and_noexp_splits_input_into_string_and_signed_float_without_exponent(x):
assume(len(x) <= 10)
@@ -441,6 +469,7 @@ def test__natsort_key_with_float_and_signed_and_noexp_splits_input_into_string_a
assert _natsort_key(s, None, ns.F | ns.S | ns.N) == tuple(_number_extracter(s, _float_sign_noexp_re, *float_nosafe_nolocale_nogroup))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_float_and_unsigned_splits_input_into_string_and_unsigned_float(x):
assume(len(x) <= 10)
@@ -452,6 +481,7 @@ def test__natsort_key_with_float_and_unsigned_splits_input_into_string_and_unsig
assert _natsort_key(s, None, ns.F) == tuple(_number_extracter(s, _float_nosign_exp_re, *float_nosafe_nolocale_nogroup))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_float_and_noexp_splits_input_into_string_and_unsigned_float_without_exponent(x):
assume(len(x) <= 10)
@@ -460,6 +490,7 @@ def test__natsort_key_with_float_and_noexp_splits_input_into_string_and_unsigned
assert _natsort_key(s, None, ns.F | ns.N) == tuple(_number_extracter(s, _float_nosign_noexp_re, *float_nosafe_nolocale_nogroup))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_int_splits_input_into_string_and_unsigned_int(x):
assume(len(x) <= 10)
@@ -473,6 +504,7 @@ def test__natsort_key_with_int_splits_input_into_string_and_unsigned_int(x):
assert _natsort_key(s, None, ns.I | ns.NOEXP) == tuple(_number_extracter(s, _int_nosign_re, *int_nosafe_nolocale_nogroup))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_int_splits_and_signed_input_into_string_and_signed_int(x):
assume(len(x) <= 10)
@@ -482,6 +514,7 @@ def test__natsort_key_with_int_splits_and_signed_input_into_string_and_signed_in
assert _natsort_key(s, None, ns.SIGNED) == tuple(_number_extracter(s, _int_sign_re, *int_nosafe_nolocale_nogroup))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_version_or_digit_matches_usigned_int(x):
assume(len(x) <= 10)
@@ -491,6 +524,7 @@ def test__natsort_key_with_version_or_digit_matches_usigned_int(x):
assert _natsort_key(s, None, ns.DIGIT) == _natsort_key(s, None, ns.VERSION)
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_key_applies_key_function_before_splitting(x):
assume(len(x) <= 10)
@@ -499,6 +533,7 @@ def test__natsort_key_with_key_applies_key_function_before_splitting(x):
assert _natsort_key(s, lambda x: x.upper(), ns.I) == tuple(_number_extracter(s.upper(), _int_nosign_re, *int_nosafe_nolocale_nogroup))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_tuple_input_returns_nested_tuples(x):
# Iterables are parsed recursively so you can sort lists of lists.
@@ -509,6 +544,7 @@ def test__natsort_key_with_tuple_input_returns_nested_tuples(x):
assert _natsort_key((s, s), None, ns.I) == (t, t)
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_tuple_input_but_itemgetter_key_returns_split_second_element(x):
# A key is applied before recursion, but not in the recursive calls.
@@ -519,6 +555,7 @@ def test__natsort_key_with_tuple_input_but_itemgetter_key_returns_split_second_e
assert _natsort_key((s, s), itemgetter(1), ns.I) == t
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given(float)
def test__natsort_key_with_numeric_input_returns_number_with_leading_empty_string(x):
assume(not isnan(x))
@@ -527,6 +564,7 @@ def test__natsort_key_with_numeric_input_returns_number_with_leading_empty_strin
assert _natsort_key(x, None, ns.I) == ('', x)
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_TYPESAFE_inserts_spaces_between_numbers(x):
# Turn on TYPESAFE to put a '' between adjacent numbers
@@ -543,6 +581,7 @@ def test__natsort_key_with_invalid_alg_input_raises_ValueError():
assert str(err.value) == "_natsort_key: 'alg' argument must be from the enum 'ns', got 1"
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_IGNORECASE_lowercases_text(x):
assume(len(x) <= 10)
@@ -554,6 +593,7 @@ def test__natsort_key_with_IGNORECASE_lowercases_text(x):
assert _natsort_key(s, None, ns.IGNORECASE) == tuple(_number_extracter(s.lower(), _int_nosign_re, *int_nosafe_nolocale_nogroup))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_LOWERCASEFIRST_inverts_text_case(x):
assume(len(x) <= 10)
@@ -562,12 +602,9 @@ def test__natsort_key_with_LOWERCASEFIRST_inverts_text_case(x):
assert _natsort_key(s, None, ns.LOWERCASEFIRST) == tuple(_number_extracter(s.swapcase(), _int_nosign_re, *int_nosafe_nolocale_nogroup))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_GROUPLETTERS_doubles_text_with_lowercase_letter_first(x):
- try:
- low = py23_str.casefold
- except AttributeError:
- low = py23_str.lower
assume(len(x) <= 10)
assume(not any(type(y) == float and isnan(y) for y in x))
s = ''.join(ichain([repr(y)] if type(y) in (float, long, int) else [low(y), y] for y in x))
@@ -576,12 +613,9 @@ def test__natsort_key_with_GROUPLETTERS_doubles_text_with_lowercase_letter_first
assert _natsort_key(s, None, ns.GROUPLETTERS) == tuple(''.join(low(z) + z for z in y) if type(y) not in (float, long, int) else y for y in t)
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_GROUPLETTERS_and_LOWERCASEFIRST_inverts_text_first_then_doubles_letters_with_lowercase_letter_first(x):
- try:
- low = py23_str.casefold
- except AttributeError:
- low = py23_str.lower
assume(len(x) <= 10)
assume(not any(type(y) == float and isnan(y) for y in x))
s = ''.join(ichain([repr(y)] if type(y) in (float, long, int) else [low(y), y] for y in x))
@@ -600,6 +634,7 @@ def test__natsort_key_with_bytes_input_only_applies_LOWERCASEFIRST_or_IGNORECASE
assert True
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_LOCALE_transforms_floats_according_to_the_current_locale_and_strxfrms_strings(x):
# Locale aware sorting
@@ -614,6 +649,7 @@ def test__natsort_key_with_LOCALE_transforms_floats_according_to_the_current_loc
locale.setlocale(locale.LC_NUMERIC, str(''))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_LOCALE_and_UNGROUPLETTERS_places_space_before_string_with_capital_first_letter(x):
# Locale aware sorting
@@ -639,6 +675,7 @@ def test__natsort_key_with_LOCALE_and_UNGROUPLETTERS_places_space_before_string_
locale.setlocale(locale.LC_NUMERIC, str(''))
+@pytest.mark.skipif(not use_hypothesis, reason='requires python2.7 or greater')
@given([float, py23_str, int])
def test__natsort_key_with_UNGROUPLETTERS_does_nothing_without_LOCALE(x):
assume(len(x) <= 10)
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..1c5607d
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,12 @@
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist =
+ py26, py27, py32, py33, py34, pypy
+
+[testenv]
+commands = {envpython} setup.py test
+deps = pytest