summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml11
-rw-r--r--CHANGELOG.md3
-rw-r--r--README.rst7
-rw-r--r--dev/requirements.in1
-rw-r--r--dev/requirements.txt14
-rw-r--r--docs/conf.py16
-rw-r--r--docs/examples.rst25
-rw-r--r--natsort/__init__.py7
-rw-r--r--natsort/__main__.py6
-rw-r--r--natsort/compat/fake_fastnumbers.py8
-rw-r--r--natsort/compat/fastnumbers.py1
-rw-r--r--natsort/compat/locale.py39
-rw-r--r--natsort/compat/pathlib.py10
-rw-r--r--natsort/compat/py23.py101
-rw-r--r--natsort/natsort.py94
-rw-r--r--natsort/ns_enum.py1
-rw-r--r--natsort/unicode_numbers.py4
-rw-r--r--natsort/unicode_numeric_hex.py5
-rw-r--r--natsort/utils.py113
-rw-r--r--setup.cfg6
-rw-r--r--setup.py2
-rw-r--r--tests/profile_natsorted.py7
-rw-r--r--tests/test_fake_fastnumbers.py7
-rw-r--r--tests/test_final_data_transform_factory.py6
-rw-r--r--tests/test_input_string_transform_factory.py14
-rw-r--r--tests/test_main.py1
-rw-r--r--tests/test_natsort_cmp.py83
-rw-r--r--tests/test_natsort_key.py9
-rw-r--r--tests/test_natsort_keygen.py6
-rw-r--r--tests/test_natsorted.py16
-rw-r--r--tests/test_natsorted_convenience.py14
-rw-r--r--tests/test_parse_bytes_function.py1
-rw-r--r--tests/test_parse_number_function.py1
-rw-r--r--tests/test_parse_string_function.py6
-rw-r--r--tests/test_regex.py1
-rw-r--r--tests/test_string_component_transform_factory.py10
-rw-r--r--tests/test_unicode_numbers.py6
-rw-r--r--tests/test_utils.py40
-rw-r--r--tox.ini6
39 files changed, 121 insertions, 587 deletions
diff --git a/.travis.yml b/.travis.yml
index 2ccf165..ff44464 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,6 @@ cache:
- directories:
- $HOME/.pyenv_cache
python:
- - 2.7
- 3.5
- 3.6
- 3.7
@@ -18,16 +17,8 @@ env: WITH_EXTRAS=""
jobs:
include:
- # For Python 2.7 and 3.7, do some extra configurations.
+ # For Python 3.7 do some extra configurations.
# Linux with both "icu" and "fastnumbers"
- - python: "2.7"
- env: WITH_EXTRAS="fast,icu"
- addons:
- apt:
- packages:
- - libicu-dev
- - language-pack-de
- - language-pack-en
- python: "3.7"
env: WITH_EXTRAS="fast,icu"
addons:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d230e19..7c17af8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,9 @@ Unreleased
### Removed
- Introduction page in documentation
+### Removed
+ - Support for Python 2
+
[6.1.0] - 2019-11-09
---
diff --git a/README.rst b/README.rst
index a88203d..fe395f5 100644
--- a/README.rst
+++ b/README.rst
@@ -347,9 +347,8 @@ from the command line with ``python -m natsort``.
Requirements
------------
-``natsort`` requires Python version 2.7 or Python 3.5 or greater. Python 3.4 is
-unofficially supported, meaning that support has not been removed, but it is no
-longer tested.
+``natsort`` requires Python 3.5 or greater. Python 3.4 is unofficially supported,
+meaning that support has not been removed, but it is no longer tested.
Optional Dependencies
---------------------
@@ -450,7 +449,7 @@ Deprecation Schedule
Dropping Python 2.7 Support
+++++++++++++++++++++++++++
-``natsort`` version 7.0.0 will drop support for Python 2.7.
+``natsort`` version 7.0.0 drops support for Python 2.7.
The version 6.X branch will remain as a "long term support" branch where bug
fixes are applied so that users who cannot update from Python 2.7 will not be
diff --git a/dev/requirements.in b/dev/requirements.in
index 4b82ff2..c30d7ab 100644
--- a/dev/requirements.in
+++ b/dev/requirements.in
@@ -5,4 +5,3 @@ pytest-mock
hypothesis
# pytest-faulthandler; platform_python_implementation == 'CPython'
semver
-pathlib; python_version < '3.4' \ No newline at end of file
diff --git a/dev/requirements.txt b/dev/requirements.txt
index 769af62..a7294a8 100644
--- a/dev/requirements.txt
+++ b/dev/requirements.txt
@@ -6,26 +6,18 @@
#
atomicwrites==1.3.0 # via pytest
attrs==19.3.0 # via hypothesis, pytest
-configparser==4.0.2 # via importlib-metadata
-contextlib2==0.6.0.post1 # via importlib-metadata
coverage==4.5.4
-enum34==1.1.6 # via hypothesis
-funcsigs==1.0.2 # via mock, pytest
hypothesis==4.44.2
importlib-metadata==0.23 # via pluggy, pytest
-mock==3.0.5 # via pytest-mock
-more-itertools==5.0.0 # via pytest, zipp
+more-itertools==7.2.0 # via pytest, zipp
packaging==19.2 # via pytest
-pathlib2==2.3.5 # via importlib-metadata, pytest
-pathlib==1.0.1 ; python_version < "3.4"
pluggy==0.13.0 # via pytest
py==1.8.0 # via pytest
pyparsing==2.4.5 # via packaging
pytest-cov==2.8.1
pytest-mock==1.11.2
-pytest==4.6.6
-scandir==1.10.0 # via pathlib2
+pytest==5.2.2
semver==2.9.0
-six==1.13.0 # via mock, more-itertools, packaging, pathlib2, pytest
+six==1.13.0 # via packaging
wcwidth==0.1.7 # via pytest
zipp==0.6.0 # via importlib-metadata
diff --git a/docs/conf.py b/docs/conf.py
index 31f1fed..bd6c2e0 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -49,9 +49,9 @@ source_suffix = ['.rst', '.md']
master_doc = 'index'
# General information about the project.
-project = u'natsort'
+project = 'natsort'
# noinspection PyShadowingBuiltins
-copyright = u'2014, Seth M. Morton'
+copyright = '2014, Seth M. Morton'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -210,8 +210,8 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- ('index', 'natsort.tex', u'natsort Documentation',
- u'Seth M. Morton', 'manual'),
+ ('index', 'natsort.tex', 'natsort Documentation',
+ 'Seth M. Morton', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -240,8 +240,8 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
- ('index', 'natsort', u'natsort Documentation',
- [u'Seth M. Morton'], 1)
+ ('index', 'natsort', 'natsort Documentation',
+ ['Seth M. Morton'], 1)
]
# If true, show URL addresses after external links.
@@ -254,8 +254,8 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- ('index', 'natsort', u'natsort Documentation',
- u'Seth M. Morton', 'natsort', 'One line description of project.',
+ ('index', 'natsort', 'natsort Documentation',
+ 'Seth M. Morton', 'natsort', 'One line description of project.',
'Miscellaneous'),
]
diff --git a/docs/examples.rst b/docs/examples.rst
index f09f8e5..2c56a87 100644
--- a/docs/examples.rst
+++ b/docs/examples.rst
@@ -314,31 +314,6 @@ need to pass a key to the :meth:`list.sort` method. The function
:func:`~natsort_keygen` has the same API as :func:`~natsorted` (minus the
`reverse` option).
-Natural Sorting with ``cmp`` (Python 2 only)
---------------------------------------------
-
-.. note::
- This is a Python2-only feature! The :func:`natcmp` function is not
- exposed on Python3. Because this documentation is built with
- Python3, you will not find :func:`natcmp` in the API.
-
-If you are using a legacy codebase that requires you to use :func:`cmp` instead
-of a key-function, you can use :func:`~natcmp`.
-
-.. code-block:: pycon
-
- >>> import sys
- >>> a = ['2 ft 7 in', '1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '7 ft 6 in']
- >>> if sys.version_info[0] == 2:
- ... from natsort import natcmp
- ... sorted(a, cmp=natcmp)
- ... else:
- ... natsorted(a) # so docstrings don't fail
- ['1 ft 5 in', '2 ft 7 in', '2 ft 11 in', '7 ft 6 in', '10 ft 2 in']
-
-:func:`natcmp` also accepts an ``alg`` argument so you can customize your
-sorting experience.
-
Sorting Multiple Lists According to a Single List
-------------------------------------------------
diff --git a/natsort/__init__.py b/natsort/__init__.py
index 6cb28f5..4a838fc 100644
--- a/natsort/__init__.py
+++ b/natsort/__init__.py
@@ -1,7 +1,4 @@
# -*- coding: utf-8 -*-
-from __future__ import absolute_import, division, print_function, unicode_literals
-
-import sys
from natsort.natsort import (
as_ascii,
@@ -21,9 +18,6 @@ from natsort.natsort import (
)
from natsort.utils import chain_functions
-if float(sys.version[:3]) < 3:
- from natsort.natsort import natcmp
-
__version__ = "6.2.0"
__all__ = [
@@ -37,7 +31,6 @@ __all__ = [
"index_realsorted",
"order_by_index",
"decoder",
- "natcmp",
"as_ascii",
"as_utf8",
"ns",
diff --git a/natsort/__main__.py b/natsort/__main__.py
index b52bf36..e2f3299 100644
--- a/natsort/__main__.py
+++ b/natsort/__main__.py
@@ -1,10 +1,8 @@
# -*- coding: utf-8 -*-
-from __future__ import absolute_import, division, print_function, unicode_literals
import sys
import natsort
-from natsort.compat.py23 import py23_str
from natsort.utils import regex_chooser
@@ -191,7 +189,7 @@ def check_filters(filters):
try:
return [range_check(f[0], f[1]) for f in filters]
except ValueError as err:
- raise ValueError("Error in --filter: " + py23_str(err))
+ raise ValueError("Error in --filter: " + str(err))
def keep_entry_range(entry, lows, highs, converter, regex):
@@ -310,6 +308,6 @@ if __name__ == "__main__":
try:
main()
except ValueError as a:
- sys.exit(py23_str(a))
+ sys.exit(str(a))
except KeyboardInterrupt:
sys.exit(1)
diff --git a/natsort/compat/fake_fastnumbers.py b/natsort/compat/fake_fastnumbers.py
index 27ce027..9c0258f 100644
--- a/natsort/compat/fake_fastnumbers.py
+++ b/natsort/compat/fake_fastnumbers.py
@@ -3,19 +3,13 @@
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 absolute_import, division, print_function, unicode_literals
# Std. lib imports.
import unicodedata
# Local imports.
-from natsort.compat.py23 import PY_VERSION
from natsort.unicode_numbers import decimal_chars
-if PY_VERSION >= 3:
- long = int
-
-
NAN_INF = [
"INF",
"INf",
@@ -112,7 +106,7 @@ def fast_int(
"""
if x[0] in _first_char:
try:
- return long(x)
+ return int(x)
except ValueError:
try:
return _uni(x, key(x)) if len(x) == 1 else key(x)
diff --git a/natsort/compat/fastnumbers.py b/natsort/compat/fastnumbers.py
index b1468b3..b6b0bb1 100644
--- a/natsort/compat/fastnumbers.py
+++ b/natsort/compat/fastnumbers.py
@@ -3,7 +3,6 @@
Interface for natsort to access fastnumbers functions without
having to worry if it is actually installed.
"""
-from __future__ import absolute_import, division, print_function, unicode_literals
from distutils.version import StrictVersion
diff --git a/natsort/compat/locale.py b/natsort/compat/locale.py
index 41abea6..ccb5592 100644
--- a/natsort/compat/locale.py
+++ b/natsort/compat/locale.py
@@ -3,23 +3,17 @@
Interface for natsort to access locale functionality without
having to worry about if it is using PyICU or the built-in locale.
"""
-from __future__ import absolute_import, division, print_function, unicode_literals
# Std. lib imports.
import sys
-from functools import cmp_to_key
-
-# Local imports.
-from natsort.compat.py23 import PY_VERSION, py23_unichr
# This string should be sorted after any other byte string because
# it contains the max unicode character repeated 20 times.
# You would need some odd data to come after that.
null_string = ""
-null_string_max = py23_unichr(sys.maxunicode) * 20
+null_string_max = chr(sys.maxunicode) * 20
-# Make the strxfrm function from strcoll on Python2
-# It can be buggy (especially on BSD-based systems),
+# strxfrm can be buggy (especially on BSD-based systems),
# so prefer icu if available.
try: # noqa: C901
import icu
@@ -56,33 +50,10 @@ try: # noqa: C901
except ImportError:
import locale
+ from locale import strxfrm
- if PY_VERSION < 3:
- from locale import strcoll
-
- sentinel = object()
-
- def custom_strcoll(a, b, last=sentinel):
- """strcoll that can handle a sentinel that is always last."""
- if a is last:
- return 0 if a is b else 1
- elif b is last: # a cannot also be sentinel b/c above logic
- return -1
- else: # neither are sentinel
- return strcoll(a, b)
-
- strxfrm = cmp_to_key(custom_strcoll)
- null_string_locale = strxfrm("")
- null_string_locale_max = strxfrm(sentinel)
- else:
- from locale import strxfrm
-
- null_string_locale = ""
-
- # This string should be sorted after any other byte string because
- # it contains the max unicode character repeated 20 times.
- # You would need some odd data to come after that.
- null_string_locale_max = py23_unichr(sys.maxunicode) * 20
+ null_string_locale = null_string
+ null_string_locale_max = null_string_max
# On some systems, locale is broken and does not sort in the expected
# order. We will try to detect this and compensate.
diff --git a/natsort/compat/pathlib.py b/natsort/compat/pathlib.py
deleted file mode 100644
index a189f37..0000000
--- a/natsort/compat/pathlib.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, division, print_function, unicode_literals
-
-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/compat/py23.py b/natsort/compat/py23.py
deleted file mode 100644
index 58f7487..0000000
--- a/natsort/compat/py23.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Compatibility layer for Python 2 and Python 3.
-
-Probably could have used six...
-This file will light up most linters...
-"""
-from __future__ import absolute_import, division, print_function, unicode_literals
-
-import functools
-import sys
-
-# These functions are used to make the doctests compatible between
-# 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
-PY_VERSION = float(sys.version[:3])
-NEWPY = PY_VERSION >= 3.3
-
-# Assume all strings are Unicode in Python 2
-py23_str = str if PY_VERSION >= 3 else unicode
-
-# Use the range iterator always
-py23_range = range if PY_VERSION >= 3 else xrange
-
-# Uniform base string type
-py23_basestring = str if PY_VERSION >= 3 else basestring
-
-# Iniform int type
-py23_int = (int,) if PY_VERSION >= 3 else (int, long)
-
-# unichr function
-py23_unichr = chr if PY_VERSION >= 3 else unichr
-
-# Proper lower-casing of letters.
-py23_lower = py23_str.casefold if NEWPY else py23_str.lower
-
-
-def _py23_cmp(a, b):
- return (a > b) - (a < b)
-
-
-py23_cmp = _py23_cmp if PY_VERSION >= 3 else cmp
-
-# zip as an iterator
-if PY_VERSION >= 3:
- py23_zip = zip
- py23_map = map
- py23_filter = filter
-else:
- import itertools
-
- py23_zip = itertools.izip
- py23_map = itertools.imap
- py23_filter = itertools.ifilter
-
-
-# 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):
- @functools.wraps(str_change_func)
- def wrapper(func_or_str):
- if isinstance(func_or_str, py23_basestring):
- func = None
- doc = func_or_str
- else:
- func = func_or_str
- doc = func.__doc__
-
- if doc is not None:
- doc = str_change_func(doc)
-
- if func:
- func.__doc__ = doc
- return func
- return doc
-
- return wrapper
-
-
-# Properly modify a doctstring to either have the unicode literal or not.
-if PY_VERSION >= 3:
- # Abstract u'abc' syntax:
- @_modify_str_or_docstring
- def u_format(s):
- """"{u}'abc'" --> "'abc'" (Python 3)
-
- Accepts a string or a function, so it can be used as a decorator."""
- return s.format(u="")
-
-
-else:
- # Abstract u'abc' syntax:
- @_modify_str_or_docstring
- def u_format(s):
- """"{u}'abc'" --> "u'abc'" (Python 2)
-
- Accepts a string or a function, so it can be used as a decorator."""
- return s.format(u="u")
diff --git a/natsort/natsort.py b/natsort/natsort.py
index ecc4edd..9dcb6eb 100644
--- a/natsort/natsort.py
+++ b/natsort/natsort.py
@@ -5,19 +5,15 @@ natsort public API.
The majority of the "work" is defined in utils.py.
"""
-from __future__ import absolute_import, division, print_function, unicode_literals
-import sys
from functools import partial
from operator import itemgetter
import natsort.compat.locale
from natsort import utils
-from natsort.compat.py23 import py23_cmp, py23_str, u_format
from natsort.ns_enum import NS_DUMB, ns
-@u_format
def decoder(encoding):
"""
Return a function that can be used to decode bytes to unicode.
@@ -59,7 +55,6 @@ def decoder(encoding):
return partial(utils.do_decoding, encoding=encoding)
-@u_format
def as_ascii(s):
"""
Function to decode an input with the ASCII codec, or return as-is.
@@ -83,7 +78,6 @@ def as_ascii(s):
return utils.do_decoding(s, "ascii")
-@u_format
def as_utf8(s):
"""
Function to decode an input with the UTF-8 codec, or return as-is.
@@ -107,7 +101,6 @@ def as_utf8(s):
return utils.do_decoding(s, "utf-8")
-@u_format
def natsort_keygen(key=None, alg=ns.DEFAULT):
"""
Generate a key to sort strings and numbers naturally.
@@ -151,14 +144,14 @@ def natsort_keygen(key=None, alg=ns.DEFAULT):
>>> a = ['num5.10', 'num-3', 'num5.3', 'num2']
>>> a.sort(key=natsort_keygen(alg=ns.REAL))
>>> a
- [{u}'num-3', {u}'num2', {u}'num5.10', {u}'num5.3']
+ ['num-3', 'num2', 'num5.10', 'num5.3']
"""
try:
ns.DEFAULT | alg
except TypeError:
msg = "natsort_keygen: 'alg' argument must be from the enum 'ns'"
- raise ValueError(msg + ", got {}".format(py23_str(alg)))
+ raise ValueError(msg + ", got {}".format(str(alg)))
# Add the NS_DUMB option if the locale library is broken.
if alg & ns.LOCALEALPHA and natsort.compat.locale.dumb_sort():
@@ -218,7 +211,6 @@ natsort_keygen
"""
-@u_format
def natsorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
"""
Sorts an iterable naturally.
@@ -260,14 +252,13 @@ def natsorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
>>> a = ['num3', 'num5', 'num2']
>>> natsorted(a)
- [{u}'num2', {u}'num3', {u}'num5']
+ ['num2', 'num3', 'num5']
"""
key = natsort_keygen(key, alg)
return sorted(seq, reverse=reverse, key=key)
-@u_format
def humansorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
"""
Convenience function to properly sort non-numeric characters.
@@ -312,15 +303,14 @@ def humansorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
>>> a = ['Apple', 'Banana', 'apple', 'banana']
>>> natsorted(a)
- [{u}'Apple', {u}'Banana', {u}'apple', {u}'banana']
+ ['Apple', 'Banana', 'apple', 'banana']
>>> humansorted(a)
- [{u}'apple', {u}'Apple', {u}'banana', {u}'Banana']
+ ['apple', 'Apple', 'banana', 'Banana']
"""
return natsorted(seq, key, reverse, alg | ns.LOCALE)
-@u_format
def realsorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
"""
Convenience function to properly sort signed floats.
@@ -366,15 +356,14 @@ def realsorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
>>> a = ['num5.10', 'num-3', 'num5.3', 'num2']
>>> natsorted(a)
- [{u}'num2', {u}'num5.3', {u}'num5.10', {u}'num-3']
+ ['num2', 'num5.3', 'num5.10', 'num-3']
>>> realsorted(a)
- [{u}'num-3', {u}'num2', {u}'num5.10', {u}'num5.3']
+ ['num-3', 'num2', 'num5.10', 'num5.3']
"""
return natsorted(seq, key, reverse, alg | ns.REAL)
-@u_format
def index_natsorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
"""
Determine the list of the indexes used to sort the input sequence.
@@ -426,9 +415,9 @@ def index_natsorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
[2, 0, 1]
>>> # Sort both lists by the sort order of a
>>> order_by_index(a, index)
- [{u}'num2', {u}'num3', {u}'num5']
+ ['num2', 'num3', 'num5']
>>> order_by_index(b, index)
- [{u}'baz', {u}'foo', {u}'bar']
+ ['baz', 'foo', 'bar']
"""
if key is None:
@@ -444,7 +433,6 @@ def index_natsorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
return [x for x, _ in index_seq_pair]
-@u_format
def index_humansorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
"""
This is a wrapper around ``index_natsorted(seq, alg=ns.LOCALE)``.
@@ -494,7 +482,6 @@ def index_humansorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
return index_natsorted(seq, key, reverse, alg | ns.LOCALE)
-@u_format
def index_realsorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
"""
This is a wrapper around ``index_natsorted(seq, alg=ns.REAL)``.
@@ -541,7 +528,6 @@ def index_realsorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
# noinspection PyShadowingBuiltins,PyUnresolvedReferences
-@u_format
def order_by_index(seq, index, iter=False):
"""
Order a given sequence by an index sequence.
@@ -593,9 +579,9 @@ def order_by_index(seq, index, iter=False):
[2, 0, 1]
>>> # Sort both lists by the sort order of a
>>> order_by_index(a, index)
- [{u}'num2', {u}'num3', {u}'num5']
+ ['num2', 'num3', 'num5']
>>> order_by_index(b, index)
- [{u}'baz', {u}'foo', {u}'bar']
+ ['baz', 'foo', 'bar']
"""
return (seq[i] for i in index) if iter else [seq[i] for i in index]
@@ -618,61 +604,3 @@ def numeric_regex_chooser(alg):
"""
# Remove the leading and trailing parens
return utils.regex_chooser(alg).pattern[1:-1]
-
-
-if float(sys.version[:3]) < 3:
- # pylint: disable=unused-variable
- # noinspection PyUnresolvedReferences,PyPep8Naming
- class natcmp(object): # noqa: N801
- """
- Compare two objects using a key and an algorithm.
-
- Parameters
- ----------
- x : object
- First object to compare.
-
- y : object
- Second object to compare.
-
- alg : ns enum, optional
- This option is used to control which algorithm `natsort`
- uses when sorting. For details into these options, please see
- the :class:`ns` class documentation. The default is `ns.INT`.
-
- Returns
- -------
- out: int
- 0 if x and y are equal, 1 if x > y, -1 if y > x.
-
- See Also
- --------
- natsort_keygen : Generates a key that makes natural sorting possible.
-
- Examples
- --------
- Use `natcmp` just like the builtin `cmp`::
-
- >>> one = 1
- >>> two = 2
- >>> natcmp(one, two)
- -1
- """
-
- cached_keys = {}
-
- def __new__(cls, x, y, alg=ns.DEFAULT):
- try:
- ns.DEFAULT | alg
- except TypeError:
- msg = "natsort_keygen: 'alg' argument must be from the enum 'ns'"
- raise ValueError(msg + ", got {}".format(py23_str(alg)))
-
- # Add the _DUMB option if the locale library is broken.
- if alg & ns.LOCALEALPHA and natsort.compat.locale.dumb_sort():
- alg |= NS_DUMB
-
- if alg not in cls.cached_keys:
- cls.cached_keys[alg] = natsort_keygen(alg=alg)
-
- return py23_cmp(cls.cached_keys[alg](x), cls.cached_keys[alg](y))
diff --git a/natsort/ns_enum.py b/natsort/ns_enum.py
index 6eebef6..0c1696d 100644
--- a/natsort/ns_enum.py
+++ b/natsort/ns_enum.py
@@ -3,7 +3,6 @@
This module defines the "ns" enum for natsort is used to determine
what algorithm natsort uses.
"""
-from __future__ import absolute_import, division, print_function, unicode_literals
import collections
diff --git a/natsort/unicode_numbers.py b/natsort/unicode_numbers.py
index 28cbafc..7800fd2 100644
--- a/natsort/unicode_numbers.py
+++ b/natsort/unicode_numbers.py
@@ -2,11 +2,9 @@
"""
Pre-determine the collection of unicode decimals, digits, and numerals.
"""
-from __future__ import absolute_import, division, print_function, unicode_literals
import unicodedata
-from natsort.compat.py23 import py23_unichr
from natsort.unicode_numeric_hex import numeric_hex
# Convert each hex into the literal Unicode character.
@@ -16,7 +14,7 @@ from natsort.unicode_numeric_hex import numeric_hex
numeric_chars = []
for a in numeric_hex:
try:
- character = py23_unichr(a)
+ character = chr(a)
except ValueError: # pragma: no cover
break
if unicodedata.numeric(character, None) is None:
diff --git a/natsort/unicode_numeric_hex.py b/natsort/unicode_numeric_hex.py
index 991d3e3..92aecf1 100644
--- a/natsort/unicode_numeric_hex.py
+++ b/natsort/unicode_numeric_hex.py
@@ -1836,12 +1836,11 @@ numeric_hex = (
# Some code that can be used to create the above list of hex numbers.
if __name__ == "__main__":
import unicodedata
- from natsort.compat.py23 import py23_range, py23_unichr
hex_chars = []
- for i in py23_range(0x110000):
+ for i in range(0x110000):
try:
- a = py23_unichr(i)
+ a = chr(i)
except ValueError:
break
if a in "0123456789":
diff --git a/natsort/utils.py b/natsort/utils.py
index 61387d3..4f5ddfb 100644
--- a/natsort/utils.py
+++ b/natsort/utils.py
@@ -38,39 +38,21 @@ that ensures "val" is a local variable instead of global variable
and thus has a slightly improved performance at runtime.
"""
-from __future__ import absolute_import, division, print_function, unicode_literals
import re
-from collections import deque
from functools import partial, reduce
from itertools import chain as ichain
from operator import methodcaller
-from os import curdir as os_curdir
-from os import pardir as os_pardir
-from os import sep as os_sep
-from os.path import split as path_split
-from os.path import splitext as path_splitext
+from pathlib import PurePath
from unicodedata import normalize
from natsort.compat.fastnumbers import fast_float, fast_int
from natsort.compat.locale import get_decimal_point, get_strxfrm, get_thousands_sep
-from natsort.compat.pathlib import PurePath, has_pathlib
-from natsort.compat.py23 import (
- NEWPY,
- PY_VERSION,
- py23_filter,
- py23_map,
- py23_str,
- u_format,
-)
from natsort.ns_enum import NS_DUMB, ns
from natsort.unicode_numbers import digits_no_decimals, numeric_no_decimals
-if PY_VERSION >= 3:
- long = int
-
-class NumericalRegularExpressions(object):
+class NumericalRegularExpressions:
"""
Container of regular expressions that match numbers.
@@ -176,11 +158,7 @@ def _normalize_input_factory(alg):
"""
normalization_form = "NFKD" if alg & ns.COMPATIBILITYNORMALIZE else "NFD"
- wrapped = partial(normalize, normalization_form)
- if NEWPY:
- return wrapped
- else:
- return lambda x, _f=wrapped: _f(x) if isinstance(x, py23_str) else x
+ return partial(normalize, normalization_form)
def natsort_key(val, key, string_func, bytes_func, num_func):
@@ -390,8 +368,8 @@ def parse_string_factory(
x = normalize_input(x)
x, original = input_transform(x), original_func(x)
x = splitter(x) # Split string into components.
- x = py23_filter(None, x) # Remove empty strings.
- x = py23_map(component_transform, x) # Apply transform on components.
+ x = filter(None, x) # Remove empty strings.
+ x = map(component_transform, x) # Apply transform on components.
x = sep_inserter(x, sep) # Insert '' between numbers.
return final_transform(x, original) # Apply the final transform.
@@ -422,7 +400,7 @@ def parse_path_factory(str_split):
parse_string_factory
"""
- return lambda x: tuple(py23_map(str_split, path_splitter(x)))
+ return lambda x: tuple(map(str_split, path_splitter(x)))
def sep_inserter(iterable, sep):
@@ -446,7 +424,7 @@ def sep_inserter(iterable, sep):
# Get the first element. A StopIteration indicates an empty iterable.
# Since we are controlling the types of the input, 'type' is used
# instead of 'isinstance' for the small speed advantage it offers.
- types = (int, float, long)
+ types = (int, float)
first = next(iterable)
if type(first) in types:
yield sep
@@ -500,10 +478,7 @@ def input_string_transform_factory(alg):
function_chain.append(methodcaller("swapcase"))
if alg & ns.IGNORECASE:
- if NEWPY:
- function_chain.append(methodcaller("casefold"))
- else:
- function_chain.append(methodcaller("lower"))
+ function_chain.append(methodcaller("casefold"))
if alg & ns.LOCALENUM:
# Create a regular expression that will remove thousands separators.
@@ -637,11 +612,10 @@ def final_data_transform_factory(alg, sep, pre_sep):
return lambda split_val, val: tuple(split_val)
-lower_function = methodcaller("casefold" if NEWPY else "lower")
+lower_function = methodcaller("casefold")
# noinspection PyIncorrectDocstring
-@u_format
def groupletters(x, _low=lower_function):
"""
Double all characters, making doubled letters lowercase.
@@ -658,7 +632,7 @@ def groupletters(x, _low=lower_function):
--------
>>> groupletters("Apple")
- {u}'aAppppllee'
+ 'aAppppllee'
"""
return "".join(ichain.from_iterable((_low(y), y) for y in x))
@@ -725,7 +699,6 @@ def do_decoding(s, encoding):
# noinspection PyIncorrectDocstring
-@u_format
def path_splitter(s, _d_match=re.compile(r"\.\d").match):
"""
Split a string into its path components.
@@ -745,47 +718,25 @@ def path_splitter(s, _d_match=re.compile(r"\.\d").match):
--------
>>> tuple(path_splitter("this/thing.ext"))
- ({u}'this', {u}'thing', {u}'.ext')
-
- """
- if has_pathlib and isinstance(s, PurePath):
- s = py23_str(s)
- path_parts = deque()
- p_appendleft = path_parts.appendleft
- # Continue splitting the path from the back until we have reached
- # '..' or '.', or until there is nothing left to split.
- path_location = s
- while path_location != os_curdir and path_location != os_pardir:
- parent_path = path_location
- path_location, child_path = path_split(parent_path)
- if path_location == parent_path:
- break
- p_appendleft(child_path)
-
- # This last append is the base path.
- # Only append if the string is non-empty.
- # Make sure the proper path separator for this OS is used
- # no matter what was actually given.
- if path_location:
- p_appendleft(py23_str(os_sep))
-
- # Now, split off the file extensions using a similar method to above.
- # Continue splitting off file extensions until we reach a decimal number
- # or there are no more extensions.
- # We are not using built-in functionality of PathLib here because of
- # the recursive splitting up to a decimal.
- base = path_parts.pop()
- base_parts = deque()
- b_appendleft = base_parts.appendleft
- while True:
- front = base
- base, ext = path_splitext(front)
- if _d_match(ext) or not ext:
- # Reset base to before the split if the split is invalid.
- base = front
- break
- b_appendleft(ext)
- b_appendleft(base)
-
- # Return the split parent paths and then the split basename.
- return ichain(path_parts, base_parts)
+ ('this', 'thing', '.ext')
+
+ """
+ if not isinstance(s, PurePath):
+ s = PurePath(s)
+
+ # Split the path into parts.
+ *path_parts, base = s.parts
+
+ # Now, split off the file extensions until we reach a decimal number at
+ # the beginning of the suffix or there are no more extensions.
+ suffixes = PurePath(base).suffixes
+ try:
+ digit_index = next(i for i, x in enumerate(reversed(suffixes)) if _d_match(x))
+ except StopIteration:
+ pass
+ else:
+ digit_index = len(suffixes) - digit_index
+ suffixes = suffixes[digit_index:]
+
+ base = base.replace("".join(suffixes), "")
+ return filter(None, ichain(path_parts, [base], suffixes))
diff --git a/setup.cfg b/setup.cfg
index 686ce26..582fcd7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -24,8 +24,6 @@ classifiers =
License :: OSI Approved :: MIT License
Natural Language :: English
Programming Language :: Python
- Programming Language :: Python :: 2
- Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
@@ -69,6 +67,6 @@ exclude =
build,
dist,
docs,
- .venv,
- natsort/compat/py23.py
+ .venv
+
diff --git a/setup.py b/setup.py
index 220eadc..e958aaf 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ setup(
version='6.2.0',
packages=find_packages(),
entry_points={'console_scripts': ['natsort = natsort.__main__:main']},
- python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
+ python_requires=">=3.4",
extras_require={
'fast': ["fastnumbers >= 2.0.0"],
'icu': ["PyICU >= 1.0.0"]
diff --git a/tests/profile_natsorted.py b/tests/profile_natsorted.py
index ec7037f..a2b1a5c 100644
--- a/tests/profile_natsorted.py
+++ b/tests/profile_natsorted.py
@@ -3,7 +3,6 @@
This file contains functions to profile natsorted with different
inputs and different settings.
"""
-from __future__ import print_function
import cProfile
import locale
@@ -11,11 +10,9 @@ import sys
try:
from natsort import ns, natsort_keygen
- from natsort.compat.py23 import py23_range
except ImportError:
sys.path.insert(0, ".")
from natsort import ns, natsort_keygen
- from natsort.compat.py23 import py23_range
locale.setlocale(locale.LC_ALL, "en_US.UTF-8")
@@ -37,7 +34,7 @@ locale_key = natsort_keygen(alg=ns.LOCALE)
def prof_time_to_generate():
print("*** Generate Plain Key ***")
- for _ in py23_range(100000):
+ for _ in range(100000):
natsort_keygen()
@@ -46,7 +43,7 @@ cProfile.run("prof_time_to_generate()", sort="time")
def prof_parsing(a, msg, key=basic_key):
print(msg)
- for _ in py23_range(100000):
+ for _ in range(100000):
key(a)
diff --git a/tests/test_fake_fastnumbers.py b/tests/test_fake_fastnumbers.py
index 1c0da66..c75bb11 100644
--- a/tests/test_fake_fastnumbers.py
+++ b/tests/test_fake_fastnumbers.py
@@ -2,7 +2,6 @@
"""\
Test the fake fastnumbers module.
"""
-from __future__ import unicode_literals
import unicodedata
from math import isnan
@@ -10,10 +9,6 @@ from math import isnan
from hypothesis import given
from hypothesis.strategies import floats, integers, text
from natsort.compat.fake_fastnumbers import fast_float, fast_int
-from natsort.compat.py23 import PY_VERSION
-
-if PY_VERSION >= 3:
- long = int
def is_float(x):
@@ -39,7 +34,7 @@ def is_int(x):
return x.is_integer()
except AttributeError:
try:
- long(x)
+ int(x)
except ValueError:
try:
unicodedata.digit(x)
diff --git a/tests/test_final_data_transform_factory.py b/tests/test_final_data_transform_factory.py
index fb2eb25..dc6e1db 100644
--- a/tests/test_final_data_transform_factory.py
+++ b/tests/test_final_data_transform_factory.py
@@ -1,11 +1,9 @@
# -*- coding: utf-8 -*-
"""These test the utils.py functions."""
-from __future__ import unicode_literals
import pytest
from hypothesis import example, given
from hypothesis.strategies import floats, integers, text
-from natsort.compat.py23 import py23_str
from natsort.ns_enum import NS_DUMB, ns
from natsort.utils import final_data_transform_factory
@@ -16,7 +14,7 @@ from natsort.utils import final_data_transform_factory
def test_final_data_transform_factory_default(x, y, alg):
final_data_transform_func = final_data_transform_factory(alg, "", "::")
value = (x, y)
- original_value = "".join(map(py23_str, value))
+ original_value = "".join(map(str, value))
result = final_data_transform_func(value, original_value)
assert result == value
@@ -39,7 +37,7 @@ def test_final_data_transform_factory_default(x, y, alg):
def test_final_data_transform_factory_ungroup_and_locale(x, y, alg, func):
final_data_transform_func = final_data_transform_factory(alg, "", "::")
value = (x, y)
- original_value = "".join(map(py23_str, value))
+ original_value = "".join(map(str, value))
result = final_data_transform_func(value, original_value)
if x:
expected = ((func(original_value[:1]),), value)
diff --git a/tests/test_input_string_transform_factory.py b/tests/test_input_string_transform_factory.py
index f2e9a7d..27dd725 100644
--- a/tests/test_input_string_transform_factory.py
+++ b/tests/test_input_string_transform_factory.py
@@ -1,23 +1,13 @@
# -*- coding: utf-8 -*-
"""These test the utils.py functions."""
-from __future__ import unicode_literals
import pytest
from hypothesis import example, given
from hypothesis.strategies import integers, text
-from natsort.compat.py23 import NEWPY
from natsort.ns_enum import NS_DUMB, ns
from natsort.utils import input_string_transform_factory
-def lower(x):
- """Call the appropriate lower method for the Python version."""
- if NEWPY:
- return x.casefold()
- else:
- return x.lower()
-
-
def thousands_separated_int(n):
"""Insert thousands separators in an int."""
new_int = ""
@@ -38,11 +28,11 @@ def test_input_string_transform_factory_is_no_op_for_no_alg_options(x):
@pytest.mark.parametrize(
"alg, example_func",
[
- (ns.IGNORECASE, lower),
+ (ns.IGNORECASE, lambda x: x.casefold()),
(NS_DUMB, lambda x: x.swapcase()),
(ns.LOWERCASEFIRST, lambda x: x.swapcase()),
(NS_DUMB | ns.LOWERCASEFIRST, lambda x: x), # No-op
- (ns.IGNORECASE | ns.LOWERCASEFIRST, lambda x: lower(x.swapcase())),
+ (ns.IGNORECASE | ns.LOWERCASEFIRST, lambda x: x.swapcase().casefold()),
],
)
@given(x=text())
diff --git a/tests/test_main.py b/tests/test_main.py
index 4559b92..1b01fbf 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -2,7 +2,6 @@
"""\
Test the natsort command-line tool functions.
"""
-from __future__ import print_function, unicode_literals
import re
import sys
diff --git a/tests/test_natsort_cmp.py b/tests/test_natsort_cmp.py
deleted file mode 100644
index 41a252f..0000000
--- a/tests/test_natsort_cmp.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# -*- coding: utf-8 -*-
-# pylint: disable=unused-variable
-"""These test the natcmp() function.
-
-Note that these tests are only relevant for Python version < 3.
-"""
-from functools import partial
-
-import pytest
-from hypothesis import given
-from hypothesis.strategies import floats, integers, lists
-from natsort import ns
-from natsort.compat.py23 import PY_VERSION, py23_cmp
-
-if PY_VERSION < 3:
- from natsort import natcmp
-
-
-class Comparable(object):
- """Stub class for testing natcmp functionality."""
-
- def __init__(self, value):
- self.value = value
-
- def __cmp__(self, other):
- return natcmp(self.value, other.value)
-
-
-@pytest.mark.skipif(PY_VERSION >= 3.0, reason="cmp() deprecated in Python 3")
-class TestNatCmp:
-
- def test_classes_can_be_compared(self):
- one = Comparable("1")
- two = Comparable("2")
- another_two = Comparable("2")
- ten = Comparable("10")
- assert ten > two == another_two > one
-
- def test_keys_are_being_cached(self, mocker):
- natcmp.cached_keys = {}
- assert len(natcmp.cached_keys) == 0
- natcmp(0, 0)
- assert len(natcmp.cached_keys) == 1
- natcmp(0, 0)
- assert len(natcmp.cached_keys) == 1
-
- with mocker.patch("natsort.compat.locale.dumb_sort", return_value=False):
- natcmp(0, 0, alg=ns.L)
- assert len(natcmp.cached_keys) == 2
- natcmp(0, 0, alg=ns.L)
- assert len(natcmp.cached_keys) == 2
-
- with mocker.patch("natsort.compat.locale.dumb_sort", return_value=True):
- natcmp(0, 0, alg=ns.L)
- assert len(natcmp.cached_keys) == 3
- natcmp(0, 0, alg=ns.L)
- assert len(natcmp.cached_keys) == 3
-
- def test_illegal_algorithm_raises_error(self):
- with pytest.raises(ValueError):
- natcmp(0, 0, alg="Just random stuff")
-
- def test_classes_can_utilize_max_or_min(self):
- comparables = [Comparable(i) for i in range(10)]
-
- assert max(comparables) == comparables[-1]
- assert min(comparables) == comparables[0]
-
- @given(integers(), integers())
- def test_natcmp_works_the_same_for_integers_as_cmp(self, x, y):
- assert py23_cmp(x, y) == natcmp(x, y)
-
- @given(floats(allow_nan=False), floats(allow_nan=False))
- def test_natcmp_works_the_same_for_floats_as_cmp(self, x, y):
- assert py23_cmp(x, y) == natcmp(x, y)
-
- @given(lists(elements=integers()))
- def test_sort_strings_with_numbers(self, a_list):
- strings = [str(var) for var in a_list]
- # noinspection PyArgumentList
- natcmp_sorted = sorted(strings, cmp=partial(natcmp, alg=ns.SIGNED))
-
- assert sorted(a_list) == [int(var) for var in natcmp_sorted]
diff --git a/tests/test_natsort_key.py b/tests/test_natsort_key.py
index e0c442e..6b56bb1 100644
--- a/tests/test_natsort_key.py
+++ b/tests/test_natsort_key.py
@@ -1,19 +1,13 @@
# -*- coding: utf-8 -*-
"""These test the utils.py functions."""
-from __future__ import unicode_literals
-import pytest
from hypothesis import given
from hypothesis.strategies import binary, floats, integers, lists, text
-from natsort.compat.py23 import PY_VERSION, py23_str
from natsort.utils import natsort_key
-if PY_VERSION >= 3:
- long = int
-
def str_func(x):
- if isinstance(x, py23_str):
+ if isinstance(x, str):
return x
else:
raise TypeError("Not a str!")
@@ -28,7 +22,6 @@ def test_natsort_key_with_numeric_input_takes_number_path(x):
assert natsort_key(x, None, str_func, fail, lambda y: y) is x
-@pytest.mark.skipif(PY_VERSION < 3, reason="only valid on python3")
@given(binary().filter(bool))
def test_natsort_key_with_bytes_input_takes_bytes_path(x):
assert natsort_key(x, None, str_func, lambda y: y, fail) is x
diff --git a/tests/test_natsort_keygen.py b/tests/test_natsort_keygen.py
index 0cf5fa6..96c4710 100644
--- a/tests/test_natsort_keygen.py
+++ b/tests/test_natsort_keygen.py
@@ -3,14 +3,12 @@
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 print_function, unicode_literals
import os
import pytest
from natsort import natsort_key, natsort_keygen, natsorted, ns
from natsort.compat.locale import get_strxfrm, null_string_locale
-from natsort.compat.py23 import PY_VERSION, py23_str
@pytest.fixture
@@ -74,7 +72,7 @@ def test_natsort_keygen_returns_natsort_key_that_parses_input(alg, expected):
ns.PATH | ns.GROUPLETTERS,
(
(("", 6, "aA--", 5, "..", 34, "ee++", 1),),
- ((2 * py23_str(os.sep),), ("fFoollddeerr ((", 1, "))"), ("fFoooo",)),
+ ((2 * os.sep,), ("fFoollddeerr ((", 1, "))"), ("fFoooo",)),
(("", 56.7),),
),
),
@@ -95,7 +93,6 @@ def test_natsort_keygen_handles_arbitrary_input(arbitrary_input, alg, expected):
(ns.PATH | ns.GROUPLETTERS, ((b"6A-5.034e+1",),)),
],
)
-@pytest.mark.skipif(PY_VERSION < 3.0, reason="special bytes handling only on Python3")
def test_natsort_keygen_handles_bytes_input(bytes_input, alg, expected):
ns_key = natsort_keygen(alg=alg)
assert ns_key(bytes_input) == expected
@@ -161,7 +158,6 @@ def test_natsort_keygen_with_locale(mocker, arbitrary_input, alg, expected, is_d
"alg, is_dumb",
[(ns.LOCALE, False), (ns.LOCALE, True), (ns.LOCALE | ns.CAPITALFIRST, False)],
)
-@pytest.mark.skipif(PY_VERSION < 3.0, reason="special bytes handling only on Python3")
@pytest.mark.usefixtures("with_locale_en_us")
def test_natsort_keygen_with_locale_bytes(mocker, bytes_input, alg, is_dumb):
expected = (b"6A-5.034e+1",)
diff --git a/tests/test_natsorted.py b/tests/test_natsorted.py
index f21cec8..3ae97a2 100644
--- a/tests/test_natsorted.py
+++ b/tests/test_natsorted.py
@@ -3,13 +3,11 @@
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 print_function, unicode_literals
from operator import itemgetter
import pytest
from natsort import as_utf8, natsorted, ns
-from natsort.compat.py23 import PY_VERSION
from pytest import raises
@@ -101,7 +99,6 @@ def test_natsorted_handles_nan(alg, expected, slc):
assert natsorted(given, alg=alg)[slc] == expected[slc]
-@pytest.mark.skipif(PY_VERSION < 3.0, reason="error is only raised on Python 3")
def test_natsorted_with_mixed_bytes_and_str_input_raises_type_error():
with raises(TypeError, match="bytes"):
natsorted(["รค", b"b"])
@@ -135,12 +132,14 @@ def test_natsorted_returns_list_in_reversed_order_with_reverse_option(float_list
def test_natsorted_handles_filesystem_paths():
given = [
"/p/Folder (10)/file.tar.gz",
- "/p/Folder/file.tar.gz",
"/p/Folder (1)/file (1).tar.gz",
+ "/p/Folder/file.x1.9.tar.gz",
"/p/Folder (1)/file.tar.gz",
+ "/p/Folder/file.x1.10.tar.gz",
]
expected_correct = [
- "/p/Folder/file.tar.gz",
+ "/p/Folder/file.x1.10.tar.gz",
+ "/p/Folder/file.x1.9.tar.gz",
"/p/Folder (1)/file.tar.gz",
"/p/Folder (1)/file (1).tar.gz",
"/p/Folder (10)/file.tar.gz",
@@ -149,12 +148,13 @@ def test_natsorted_handles_filesystem_paths():
"/p/Folder (1)/file (1).tar.gz",
"/p/Folder (1)/file.tar.gz",
"/p/Folder (10)/file.tar.gz",
- "/p/Folder/file.tar.gz",
+ "/p/Folder/file.x1.10.tar.gz",
+ "/p/Folder/file.x1.9.tar.gz",
]
# Is incorrect by default.
- assert natsorted(given) == expected_incorrect
+ assert natsorted(given, alg=ns.FLOAT) == expected_incorrect
# Need ns.PATH to make it correct.
- assert natsorted(given, alg=ns.PATH) == expected_correct
+ assert natsorted(given, alg=ns.FLOAT | ns.PATH) == expected_correct
def test_natsorted_handles_numbers_and_filesystem_paths_simultaneously():
diff --git a/tests/test_natsorted_convenience.py b/tests/test_natsorted_convenience.py
index 876a4e7..7a91556 100644
--- a/tests/test_natsorted_convenience.py
+++ b/tests/test_natsorted_convenience.py
@@ -3,7 +3,6 @@
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 print_function, unicode_literals
from operator import itemgetter
@@ -21,7 +20,6 @@ from natsort import (
order_by_index,
realsorted,
)
-from natsort.compat.py23 import PY_VERSION
@pytest.fixture
@@ -45,15 +43,9 @@ def test_decoder_returns_function_that_can_decode_bytes_but_return_non_bytes_as_
int_obj = 14
assert func(b"bytes") == str_obj
assert func(int_obj) is int_obj # returns as-is, same object ID
- if PY_VERSION >= 3:
- assert (
- func(str_obj) is str_obj
- ) # same object returned on Python3 b/c only bytes has decode
- else:
- assert func(str_obj) is not str_obj
- assert (
- func(str_obj) == str_obj
- ) # not same object on Python2 because str can decode
+ assert (
+ func(str_obj) is str_obj
+ ) # same object returned b/c only bytes has decode
def test_as_ascii_converts_bytes_to_ascii():
diff --git a/tests/test_parse_bytes_function.py b/tests/test_parse_bytes_function.py
index 49f54ae..6637cbd 100644
--- a/tests/test_parse_bytes_function.py
+++ b/tests/test_parse_bytes_function.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
"""These test the utils.py functions."""
-from __future__ import unicode_literals
import pytest
from hypothesis import given
diff --git a/tests/test_parse_number_function.py b/tests/test_parse_number_function.py
index 7f22ef4..ab3e5b6 100644
--- a/tests/test_parse_number_function.py
+++ b/tests/test_parse_number_function.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
"""These test the utils.py functions."""
-from __future__ import unicode_literals
import pytest
from hypothesis import given
diff --git a/tests/test_parse_string_function.py b/tests/test_parse_string_function.py
index 2c7729b..9084248 100644
--- a/tests/test_parse_string_function.py
+++ b/tests/test_parse_string_function.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
"""These test the utils.py functions."""
-from __future__ import unicode_literals
import unicodedata
@@ -8,7 +7,6 @@ import pytest
from hypothesis import given
from hypothesis.strategies import floats, integers, lists, text
from natsort.compat.fastnumbers import fast_float
-from natsort.compat.py23 import py23_str
from natsort.ns_enum import NS_DUMB, ns
from natsort.utils import NumericalRegularExpressions as NumRegex
from natsort.utils import parse_string_factory
@@ -79,10 +77,10 @@ def test_parse_string_factory_invariance(x, parse_string_func, orig_func):
# What is relevant is that the form of the output matches the invariant
# that even elements are string and odd are numerical. That each component
# function is doing what it should is tested elsewhere.
- value = "".join(map(py23_str, x)) # Convert the input to a single string.
+ value = "".join(map(str, x)) # Convert the input to a single string.
result = parse_string_func(value)
result_types = list(map(type, result))
- expected_types = [py23_str if i % 2 == 0 else float for i in range(len(result))]
+ expected_types = [str if i % 2 == 0 else float for i in range(len(result))]
assert result_types == expected_types
# The result is in our CustomTuple.
diff --git a/tests/test_regex.py b/tests/test_regex.py
index a6da62d..8d12969 100644
--- a/tests/test_regex.py
+++ b/tests/test_regex.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
"""These test the splitting regular expressions."""
-from __future__ import unicode_literals
import pytest
from natsort import ns, numeric_regex_chooser
diff --git a/tests/test_string_component_transform_factory.py b/tests/test_string_component_transform_factory.py
index 4754072..c48138f 100644
--- a/tests/test_string_component_transform_factory.py
+++ b/tests/test_string_component_transform_factory.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
"""These test the utils.py functions."""
-from __future__ import unicode_literals
from functools import partial
@@ -9,7 +8,6 @@ from hypothesis import example, given
from hypothesis.strategies import floats, integers, text
from natsort.compat.fastnumbers import fast_float, fast_int
from natsort.compat.locale import get_strxfrm
-from natsort.compat.py23 import py23_range, py23_str, py23_unichr
from natsort.ns_enum import NS_DUMB, ns
from natsort.utils import groupletters, string_component_transform_factory
@@ -18,7 +16,7 @@ from natsort.utils import groupletters, string_component_transform_factory
# raised by strxfrm). Let's filter them out.
try:
bad_uni_chars = frozenset(
- py23_unichr(x) for x in py23_range(0X10fefd, 0X10ffff + 1)
+ chr(x) for x in range(0X10fefd, 0X10ffff + 1)
)
except ValueError:
# Narrow unicode build... no worries.
@@ -49,8 +47,8 @@ def no_null(x):
partial(fast_int, key=lambda x: get_strxfrm()(groupletters(x))),
),
(
- NS_DUMB | ns.LOCALE,
- partial(fast_int, key=lambda x: get_strxfrm()(groupletters(x))),
+ NS_DUMB | ns.LOCALE,
+ partial(fast_int, key=lambda x: get_strxfrm()(groupletters(x))),
),
(
ns.GROUPLETTERS | ns.LOCALE | ns.FLOAT | ns.NANLAST,
@@ -72,7 +70,7 @@ def no_null(x):
def test_string_component_transform_factory(x, alg, example_func):
string_component_transform_func = string_component_transform_factory(alg)
try:
- assert string_component_transform_func(py23_str(x)) == example_func(py23_str(x))
+ assert string_component_transform_func(str(x)) == example_func(str(x))
except ValueError as e: # handle broken locale lib on BSD.
if "is not in range" not in str(e):
raise
diff --git a/tests/test_unicode_numbers.py b/tests/test_unicode_numbers.py
index 582a8f0..a8ee3c7 100644
--- a/tests/test_unicode_numbers.py
+++ b/tests/test_unicode_numbers.py
@@ -2,11 +2,9 @@
"""\
Test the Unicode numbers module.
"""
-from __future__ import unicode_literals
import unicodedata
-from natsort.compat.py23 import py23_range, py23_unichr
from natsort.unicode_numbers import (
decimal_chars,
decimals,
@@ -40,9 +38,9 @@ def test_numeric_chars_contains_all_valid_unicode_numeric_and_digit_characters()
set_numeric_chars = set(numeric_chars)
set_digit_chars = set(digit_chars)
set_decimal_chars = set(decimal_chars)
- for i in py23_range(0X110000):
+ for i in range(0X110000):
try:
- a = py23_unichr(i)
+ a = chr(i)
except ValueError:
break
if a in "0123456789":
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 34e60ff..d559803 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""These test the utils.py functions."""
-from __future__ import unicode_literals
+import os
import pathlib
import string
from itertools import chain
@@ -11,12 +11,11 @@ import pytest
from hypothesis import given
from hypothesis.strategies import integers, lists, sampled_from, text
from natsort import utils
-from natsort.compat.py23 import py23_cmp, py23_int, py23_lower, py23_str
from natsort.ns_enum import ns
def test_do_decoding_decodes_bytes_string_to_unicode():
- assert type(utils.do_decoding(b"bytes", "ascii")) is py23_str
+ assert type(utils.do_decoding(b"bytes", "ascii")) is str
assert utils.do_decoding(b"bytes", "ascii") == "bytes"
assert utils.do_decoding(b"bytes", "ascii") == b"bytes".decode("ascii")
@@ -100,7 +99,7 @@ def test_groupletters_returns_letters_with_lowercase_transform_of_letter_example
@given(text().filter(bool))
def test_groupletters_returns_letters_with_lowercase_transform_of_letter(x):
assert utils.groupletters(x) == "".join(
- chain.from_iterable([py23_lower(y), y] for y in x)
+ chain.from_iterable([y.casefold(), y] for y in x)
)
@@ -124,44 +123,35 @@ def test_sep_inserter_inserts_separator_between_two_numbers(x):
result = list(utils.sep_inserter(iter(x), ""))
for i, pos in enumerate(result[1:-1], 1):
if pos == "":
- assert isinstance(result[i - 1], py23_int)
- assert isinstance(result[i + 1], py23_int)
+ assert isinstance(result[i - 1], int)
+ assert isinstance(result[i + 1], int)
def test_path_splitter_splits_path_string_by_separator_example():
- z = "/this/is/a/path"
- assert tuple(utils.path_splitter(z)) == tuple(pathlib.Path(z).parts)
- z = pathlib.Path("/this/is/a/path")
- assert tuple(utils.path_splitter(z)) == tuple(pathlib.Path(z).parts)
+ given = "/this/is/a/path"
+ expected = (os.sep, "this", "is", "a", "path")
+ assert tuple(utils.path_splitter(given)) == tuple(expected)
+ given = pathlib.Path(given)
+ assert tuple(utils.path_splitter(given)) == tuple(expected)
@given(lists(sampled_from(string.ascii_letters), min_size=2).filter(all))
def test_path_splitter_splits_path_string_by_separator(x):
- z = py23_str(pathlib.Path(*x))
+ z = str(pathlib.Path(*x))
assert tuple(utils.path_splitter(z)) == tuple(pathlib.Path(z).parts)
def test_path_splitter_splits_path_string_by_separator_and_removes_extension_example():
- z = "/this/is/a/path/file.exe"
- y = tuple(pathlib.Path(z).parts)
- assert tuple(utils.path_splitter(z)) == y[:-1] + (
- pathlib.Path(z).stem,
- pathlib.Path(z).suffix,
- )
+ given = "/this/is/a/path/file.x1.10.tar.gz"
+ expected = (os.sep, "this", "is", "a", "path", "file.x1.10", ".tar", ".gz")
+ assert tuple(utils.path_splitter(given)) == tuple(expected)
@given(lists(sampled_from(string.ascii_letters), min_size=3).filter(all))
def test_path_splitter_splits_path_string_by_separator_and_removes_extension(x):
- z = py23_str(pathlib.Path(*x[:-2])) + "." + x[-1]
+ z = str(pathlib.Path(*x[:-2])) + "." + x[-1]
y = tuple(pathlib.Path(z).parts)
assert tuple(utils.path_splitter(z)) == y[:-1] + (
pathlib.Path(z).stem,
pathlib.Path(z).suffix,
)
-
-
-@given(integers())
-def test_py23_cmp(x):
- assert py23_cmp(x, x) == 0
- assert py23_cmp(x, x + 1) < 0
- assert py23_cmp(x, x - 1) > 0
diff --git a/tox.ini b/tox.ini
index bec5a24..3d0ce33 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,7 +5,7 @@
[tox]
envlist =
- flake8, py27, py35, py36, py37, py38
+ flake8, py35, py36, py37, py38
# Other valid evironments are:
# docs
# release
@@ -23,8 +23,8 @@ extras =
{env:WITH_EXTRAS:}
commands =
# Doctests
- py36: {envpython} -m doctest -o IGNORE_EXCEPTION_DETAIL docs/howitworks.rst
- !py27: pytest README.rst docs/examples.rst
+ {envpython} -m doctest -o IGNORE_EXCEPTION_DETAIL docs/howitworks.rst
+ pytest README.rst docs/examples.rst
pytest --doctest-modules {envsitepackagesdir}/natsort
# Full test suite. Allow the user to pass command-line objects.
pytest --hypothesis-profile=slow-tests --tb=short --cov {envsitepackagesdir}/natsort --cov-report term-missing {posargs:}