diff options
author | Seth Morton <seth.m.morton@gmail.com> | 2019-11-10 09:49:22 -0800 |
---|---|---|
committer | Seth Morton <seth.m.morton@gmail.com> | 2019-11-10 09:49:22 -0800 |
commit | 8de60b9bcb7a3c80aaf7aeab525aa309fafe2756 (patch) | |
tree | eedb79a08e54bb3c0669532087bd3b93aaf4d5e7 | |
parent | d4d56d552fea35c0ba81a8a0ef63d07bde0959e4 (diff) | |
parent | 2efa5026922bd16a2e895766f0d477dedd879209 (diff) | |
download | natsort-8de60b9bcb7a3c80aaf7aeab525aa309fafe2756.tar.gz |
Merge branch 'drop-python2-support'
38 files changed, 115 insertions, 570 deletions
diff --git a/.travis.yml b/.travis.yml index 439d190..ab3ca74 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ cache: - directories: - $HOME/.pyenv_cache python: - - 2.7 - 3.4 - 3.5 - 3.6 @@ -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 4e7e054..0e76d6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ Unreleased - Removed dependency on `sudo` in TravisCI configuration ([@hugovk](https://github.com/hugovk), issue #99) - Documentation typos ([@jdufresne](https://github.com/jdufresne), issue #94) ([@cpburnz](https://github.com/cpburnz), issue #95) +### Removed + - Support for Python 2 + [6.0.0] - 2019-02-04 --- @@ -332,7 +332,7 @@ from the command line with ``python -m natsort``. Requirements ------------ -``natsort`` requires Python version 2.7 or Python 3.4 or greater. +``natsort`` requires Python 3.4 or greater. Optional Dependencies --------------------- @@ -433,7 +433,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.txt b/dev/requirements.txt index e9af60b..aa7f471 100644 --- a/dev/requirements.txt +++ b/dev/requirements.txt @@ -5,5 +5,3 @@ pytest-mock >= 1.1 hypothesis >= 3.8.0 pytest-faulthandler; platform_python_implementation == 'CPython' semver -# These packages are standard on newer python versions. -pathlib; python_version < '3.4' diff --git a/docs/conf.py b/docs/conf.py index b746a90..c1ea2ba 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/docs/intro.rst b/docs/intro.rst index 313b410..cee463d 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -327,7 +327,7 @@ called from the command line with ``python -m natsort``. Requirements ------------ -:mod:`natsort` requires Python version 2.7 or Python 3.4 or greater. +:mod:`natsort` requires Python 3.4 or greater. Optional Dependencies --------------------- @@ -429,7 +429,7 @@ Deprecation Schedule Dropping Python 2.7 Support +++++++++++++++++++++++++++ -:mod:`natsort` version 7.0.0 will drop support for Python 2.7. +:mod:`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/natsort/__init__.py b/natsort/__init__.py index c27c7de..d33146d 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.1.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 58641e4..49aa4c5 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] @@ -619,60 +605,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 de28f3b..b2a732e 100644 --- a/natsort/unicode_numeric_hex.py +++ b/natsort/unicode_numeric_hex.py @@ -1735,12 +1735,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)) @@ -6,7 +6,7 @@ setup( version='6.1.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 @@ -5,7 +5,7 @@ [tox] envlist = - flake8, py27, py34, py35, py36, py37, pypy + flake8, py34, py35, py36, py37 # Other valid evironments are: # docs # release |