Hello community, here is the log from the commit of package python-natsort for openSUSE:Factory checked in at 2020-11-25 19:29:40 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-natsort (Old) and /work/SRC/openSUSE:Factory/.python-natsort.new.5913 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-natsort" Wed Nov 25 19:29:40 2020 rev:16 rq:850141 version:7.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-natsort/python-natsort.changes 2020-06-10 00:45:04.450433226 +0200 +++ /work/SRC/openSUSE:Factory/.python-natsort.new.5913/python-natsort.changes 2020-11-25 19:30:27.090562266 +0100 @@ -1,0 +2,21 @@ +Sat Nov 21 22:21:53 UTC 2020 - Arun Persaud <a...@gmx.de> + +- update to version 7.1.0: + * Added + + os_sorted, os_sort_keygen, and os_sort_key to better support + sorting like the file browser on the current operating system - + this closes the long-standing issue #41 + + Support for Python 3.9 (@swt2c, issue #119) + * Changed + + MacOS unit tests run on native Python + + Treate None like NaN internally to avoid TypeError (issue #117) + + No longer fail tests every time a new Python version is released + (issue #122) + * Fixed + + Various typos, missing figures, and out-of-date information in + the "How it works" + + Fix typo in CHANGELOG (@graingert, issue #113) + + Updated "How it works" to account for Pandas updates (@kuraga, + issue #116) + +------------------------------------------------------------------- Old: ---- natsort-7.0.1.tar.gz New: ---- natsort-7.1.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-natsort.spec ++++++ --- /var/tmp/diff_new_pack.TWZZwx/_old 2020-11-25 19:30:27.834562998 +0100 +++ /var/tmp/diff_new_pack.TWZZwx/_new 2020-11-25 19:30:27.838563003 +0100 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-natsort -Version: 7.0.1 +Version: 7.1.0 Release: 0 Summary: Natural sorting in Python License: MIT ++++++ natsort-7.0.1.tar.gz -> natsort-7.1.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/CHANGELOG.md new/natsort-7.1.0/CHANGELOG.md --- old/natsort-7.0.1/CHANGELOG.md 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/CHANGELOG.md 2020-11-21 02:38:43.000000000 +0100 @@ -1,6 +1,26 @@ Unreleased --- +[7.1.0] - 2020-11-19 +--- + +### Added + - ``os_sorted``, ``os_sort_keygen``, and ``os_sort_key`` to better support + sorting like the file browser on the current operating system - this + closes the long-standing issue #41 + - Support for Python 3.9 ([@swt2c](https://github.com/swt2c), issue #119) + +### Changed + - MacOS unit tests run on native Python + - Treate `None` like `NaN` internally to avoid `TypeError` (issue #117) + - No longer fail tests every time a new Python version is released (issue #122) + +### Fixed + - Various typos, missing figures, and out-of-date information in the "How it works" + - Fix typo in CHANGELOG ([@graingert](https://github.com/graingert), issue #113) + - Updated "How it works" to account for Pandas updates + ([@kuraga](https://github.com/kuraga), issue #116) + [7.0.1] - 2020-01-27 --- @@ -105,7 +125,7 @@ --- ### Changed - - Re-expose `natsort_key` as "public" and remove the associated `DepricationWarning` + - Re-expose `natsort_key` as "public" and remove the associated `DeprecationWarning` - Better developer documentation - Refactor tests (issue #66) - Bump allowed [`fastnumbers`](https://github.com/SethMMorton/fastnumbers) version @@ -530,6 +550,7 @@ - Sorting algorithm to support floats (including exponentials) and basic version number support <!---Comparison links--> +[7.1.0]: https://github.com/SethMMorton/natsort/compare/7.0.1...7.1.0 [7.0.1]: https://github.com/SethMMorton/natsort/compare/7.0.0...7.0.1 [7.0.0]: https://github.com/SethMMorton/natsort/compare/6.2.0...7.0.0 [6.2.0]: https://github.com/SethMMorton/natsort/compare/6.1.0...6.2.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/PKG-INFO new/natsort-7.1.0/PKG-INFO --- old/natsort-7.0.1/PKG-INFO 2020-01-28 08:46:26.000000000 +0100 +++ new/natsort-7.1.0/PKG-INFO 2020-11-21 02:39:03.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: natsort -Version: 7.0.1 +Version: 7.1.0 Summary: Simple yet flexible natural sorting in Python. Home-page: https://github.com/SethMMorton/natsort Author: Seth M. Morton @@ -109,6 +109,7 @@ -------------- - `Sorting Versions`_ + - `Sort Paths Like My File Browser (e.g. Windows Explorer on Windows)`_ - `Sorting by Real Numbers (i.e. Signed Floats)`_ - `Locale-Aware Sorting (or "Human Sorting")`_ - `Further Customizing Natsort`_ @@ -136,6 +137,30 @@ If you need to versions that use a more complicated scheme, please see `these examples <https://natsort.readthedocs.io/en/master/examples.html#rc-sorting>`_. + Sort Paths Like My File Browser (e.g. Windows Explorer on Windows) + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + Prior to ``natsort`` version 7.1.0, it was a common request to be able to + sort paths like Windows Explorer. As of ``natsort`` 7.1.0, the function + ``os_sorted`` has been added to provide users the ability to sort + in the order that their file browser might sort (e.g Windows Explorer on + Windows, Finder on MacOS, Dolphin/Nautilus/Thunar/etc. on Linux). + + .. code-block:: python + + import os + from natsort import os_sorted + print(os_sorted(os.listdir())) + # The directory sorted like your file browser might show + + Output will be different depending on the operating system you are on. + + For users **not** on Windows (e.g. MacOS/Linux) it is **strongly** recommended + to also install `PyICU <https://pypi.org/project/PyICU>`_, which will help + ``natsort`` give results that match most file browsers. If this is not installed, + it will fall back on Python's built-in ``locale`` module and will give good + results for most input, but will give poor restuls for special characters. + Sorting by Real Numbers (i.e. Signed Floats) ++++++++++++++++++++++++++++++++++++++++++++ @@ -494,17 +519,6 @@ to be shown. Alternatively, you can just set the environment variable ``PYTHONWARNINGS`` to "default::DeprecationWarning" and then run your code. - Dropped Pipenv for Development - ++++++++++++++++++++++++++++++ - - ``natsort`` version 6.0.0 no longer uses `Pipenv <https://pipenv.readthedocs.io/en/latest/>`_ - to install development dependencies. - - Dropped Python 2.6 and 3.3 Support - ++++++++++++++++++++++++++++++++++ - - ``natsort`` version 6.0.0 dropped support for Python 2.6 and Python 3.3. - Author ------ @@ -534,10 +548,11 @@ Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 Classifier: Topic :: Scientific/Engineering :: Information Analysis Classifier: Topic :: Utilities Classifier: Topic :: Text Processing Requires-Python: >=3.4 Description-Content-Type: text/x-rst -Provides-Extra: fast Provides-Extra: icu +Provides-Extra: fast diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/README.rst new/natsort-7.1.0/README.rst --- old/natsort-7.0.1/README.rst 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/README.rst 2020-11-21 02:38:43.000000000 +0100 @@ -101,6 +101,7 @@ -------------- - `Sorting Versions`_ +- `Sort Paths Like My File Browser (e.g. Windows Explorer on Windows)`_ - `Sorting by Real Numbers (i.e. Signed Floats)`_ - `Locale-Aware Sorting (or "Human Sorting")`_ - `Further Customizing Natsort`_ @@ -128,6 +129,30 @@ If you need to versions that use a more complicated scheme, please see `these examples <https://natsort.readthedocs.io/en/master/examples.html#rc-sorting>`_. +Sort Paths Like My File Browser (e.g. Windows Explorer on Windows) +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Prior to ``natsort`` version 7.1.0, it was a common request to be able to +sort paths like Windows Explorer. As of ``natsort`` 7.1.0, the function +``os_sorted`` has been added to provide users the ability to sort +in the order that their file browser might sort (e.g Windows Explorer on +Windows, Finder on MacOS, Dolphin/Nautilus/Thunar/etc. on Linux). + +.. code-block:: python + + import os + from natsort import os_sorted + print(os_sorted(os.listdir())) + # The directory sorted like your file browser might show + +Output will be different depending on the operating system you are on. + +For users **not** on Windows (e.g. MacOS/Linux) it is **strongly** recommended +to also install `PyICU <https://pypi.org/project/PyICU>`_, which will help +``natsort`` give results that match most file browsers. If this is not installed, +it will fall back on Python's built-in ``locale`` module and will give good +results for most input, but will give poor restuls for special characters. + Sorting by Real Numbers (i.e. Signed Floats) ++++++++++++++++++++++++++++++++++++++++++++ @@ -486,17 +511,6 @@ to be shown. Alternatively, you can just set the environment variable ``PYTHONWARNINGS`` to "default::DeprecationWarning" and then run your code. -Dropped Pipenv for Development -++++++++++++++++++++++++++++++ - -``natsort`` version 6.0.0 no longer uses `Pipenv <https://pipenv.readthedocs.io/en/latest/>`_ -to install development dependencies. - -Dropped Python 2.6 and 3.3 Support -++++++++++++++++++++++++++++++++++ - -``natsort`` version 6.0.0 dropped support for Python 2.6 and Python 3.3. - Author ------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/dev/generate_new_unicode_numbers.py new/natsort-7.1.0/dev/generate_new_unicode_numbers.py --- old/natsort-7.0.1/dev/generate_new_unicode_numbers.py 1970-01-01 01:00:00.000000000 +0100 +++ new/natsort-7.1.0/dev/generate_new_unicode_numbers.py 2020-11-21 02:38:43.000000000 +0100 @@ -0,0 +1,45 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +""" +Generate the numeric hex list of unicode numerals +""" +import os +import os.path +import sys +import unicodedata + +# This is intended to be called from project root. Enforce this. +this_file = os.path.abspath(__file__) +this_base = os.path.basename(this_file) +cwd = os.path.abspath(os.getcwd()) +desired_this_file = os.path.join(cwd, "dev", this_base) +if this_file != desired_this_file: + sys.exit(this_base + " must be called from project root") + +# We will write the new numeric hex collection to a natsort package file. +target = os.path.join(cwd, "natsort", "unicode_numeric_hex.py") +with open(target, "w") as fl: + print( + '''# -*- coding: utf-8 -*- +""" +Contains all possible non-ASCII unicode numbers. +""" + +# Rather than determine what unicode characters are numeric on the fly which +# would incur a startup runtime penalty, the hex values are hard-coded below. +numeric_hex = (''', + file=fl, + ) + + # Write out each individual hex value. + for i in range(0x110000): + try: + a = chr(i) + except ValueError: + break + if a in "0123456789": + continue + if unicodedata.numeric(a, None) is not None: + print(" 0x{:X},".format(i), file=fl) + + print(")", file=fl) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/docs/api.rst new/natsort-7.1.0/docs/api.rst --- old/natsort-7.0.1/docs/api.rst 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/docs/api.rst 2020-11-21 02:38:43.000000000 +0100 @@ -33,9 +33,24 @@ .. autofunction:: natsort_keygen +:func:`~natsort.os_sort_key` +++++++++++++++++++++++++++++ + +.. autofunction:: os_sort_key + +:func:`~natsort.os_sort_keygen` ++++++++++++++++++++++++++++++++ + +.. autofunction:: os_sort_keygen + Convenience Functions --------------------- +:func:`~natsort.os_sorted` ++++++++++++++++++++++++++++ + +.. autofunction:: os_sorted + :func:`~natsort.realsorted` +++++++++++++++++++++++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/docs/conf.py new/natsort-7.1.0/docs/conf.py --- old/natsort-7.0.1/docs/conf.py 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/docs/conf.py 2020-11-21 02:38:43.000000000 +0100 @@ -58,7 +58,7 @@ # built documents. # # The full version, including alpha/beta/rc tags. -release = '7.0.1' +release = '7.1.0' # The short X.Y version. version = '.'.join(release.split('.')[0:2]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/docs/examples.rst new/natsort-7.1.0/docs/examples.rst --- old/natsort-7.0.1/docs/examples.rst 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/docs/examples.rst 2020-11-21 02:38:43.000000000 +0100 @@ -401,10 +401,27 @@ Sorting a Pandas DataFrame -------------------------- -As of Pandas version 0.16.0, the sorting methods do not accept a ``key`` -argument, so you cannot simply pass :func:`natsort_keygen` to a Pandas -DataFrame and sort. This request has been made to the Pandas devs; see -`issue 3942 <https://github.com/pydata/pandas/issues/3942>`_ if you are interested. -If you need to sort a Pandas DataFrame, please check out +Starting from Pandas version 1.1.0, the +`sorting methods accept a "key" argument <https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.sort_values.html>`_, +so you can simply pass :func:`natsort_keygen` to the sorting methods and sort: + +.. code-block:: python + + import pandas as pd + from natsort import natsort_keygen + s = pd.Series(['2 ft 7 in', '1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '7 ft 6 in']) + s.sort_values(key=natsort_keygen()) + # 1 1 ft 5 in + # 0 2 ft 7 in + # 3 2 ft 11 in + # 4 7 ft 6 in + # 2 10 ft 2 in + # dtype: object + +Similarly, if you need to sort the index there is +`sort_index <https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.sort_index.html>`_ +of a DataFrame. + +If you are on an older version of Pandas, check out please check out `this answer on StackOverflow <https://stackoverflow.com/a/29582718/1399279>`_ -for ways to do this without the ``key`` argument to ``sort``. +for ways to do this without the ``key`` argument to ``sort_values``. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/docs/howitworks.rst new/natsort-7.1.0/docs/howitworks.rst --- old/natsort-7.0.1/docs/howitworks.rst 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/docs/howitworks.rst 2020-11-21 02:38:43.000000000 +0100 @@ -36,7 +36,7 @@ We as humans know that the above should be true, but why does Python think it is false? Here is how it is performing the comparison: -.. code-block:: +:: '2' <=> '2' ==> equal, so keep going ' ' <=> ' ' ==> equal, so keep going @@ -57,14 +57,14 @@ out-of-the-box, so the only hard part is actually making this string-to-list transformation and then Python will handle the rest. -.. code-block:: +:: '2 ft 7 in' ==> (2, ' ft ', 7, ' in') '2 ft 11 in' ==> (2, ' ft ', 11, ' in') When Python compares the two, it roughly follows the below logic: -.. code-block:: +:: 2 <=> 2 ==> equal, so keep going ' ft ' <=> ' ft ' ==> a string is a special type of sequence - evaluate each character individually @@ -395,8 +395,8 @@ ['Folder/file.tar.gz', 'Folder (1)/file.tar.gz', 'Folder (1)/file (1).tar.gz', 'Folder (10)/file.tar.gz'] This works because in addition to breaking the input by path separators, -the final filename component is separated from its extensions as well -[#f1]_. *Then*, each of these separated components is sent to the +the final filename component is separated from its extensions as well. +*Then*, each of these separated components is sent to the :mod:`natsort` algorithm, so the result is a tuple of tuples. Once that is done, we can see how comparisons can be done in the expected manner. @@ -456,7 +456,7 @@ or bytes, which is a no-no. #. ``natsort_key_with_poor_real_number_support('12 apples') < natsort_key_with_poor_real_number_support('apples')`` is the same as ``(12.0, ' apples') < ('apples',)``, and thus a number gets - compared to a string [#f2]_ which also is a no-no. + compared to a string [#f1]_ which also is a no-no. #. This one scores big on the astonishment scale, especially if one accidentally uses signed integers or real numbers when they mean to use unsigned integers. @@ -475,7 +475,7 @@ that the data is correct-by-construction, and this can be done by ensuring that the returned tuples *always* start with a string, and then alternate in a string-number-string-number-string pattern; this can be achieved by -adding an empty string wherever the pattern is not followed [#f3]_. This ends +adding an empty string wherever the pattern is not followed [#f2]_. This ends up working out pretty nicely because empty strings are always "less" than any non-empty string, and we typically want numbers to come before strings. @@ -705,7 +705,7 @@ Some believe that both the lowercase and uppercase versions should appear together (``['Apple', 'apple', 'Banana', 'banana', 'Corn', 'corn']``). -Some believe that both should be true ☹. Some people don't care at all [#f4]_. +Some believe that both should be true ☹. Some people don't care at all [#f3]_. Solving the first case (I call it *LOWERCASEFIRST*) is actually pretty easy... just call the :meth:`str.swapcase` method on the input. @@ -1042,7 +1042,7 @@ ... (?=[0-9]{{3}} # Three numbers must follow ... ([^0-9]|$) # But a non-number after that ... ) - ... '''.format(nodecimal=nodecimal, thou='.') # Thousands separator is '.' in German locale. + ... '''.format(nodecimal=nodecimal, thou=re.escape('.')) # Thousands separator is '.' in German locale. ... >>> re.sub(strip_thousands, '', 'Sir, €1.234,50 please.', flags=re.X) 'Sir, €1234,50 please.' @@ -1073,21 +1073,17 @@ .. rubric:: Footnotes .. [#f1] - To anyone looking through the actual code, you will note that I don't - actually use :mod:`pathlib` to split the paths... I wrote my own version - to avoid adding an external dependency of :mod:`pathlib` on Python < 3.4. -.. [#f2] *"But if you hadn't removed the leading empty string from re.split this wouldn't have happened!!"* I can hear you saying. Well, that's true. I don't have a *great* reason for having done that except that in an earlier non-optimal incarnation of the algorithm I needed to it, and it kind of stuck, and it made other parts of the code easier if the assumption that there were no empty strings was valid. -.. [#f3] +.. [#f2] I'm not going to show how this is implemented in this document, but if you are interested you can look at the code to :func:`sep_inserter` in `util.py`_. -.. [#f4] +.. [#f3] Handling each of these is straightforward, but coupled with the rapidly fracturing execution paths presented in :ref:`TL;DR 2 <tldr2>` one can imagine this will get out of hand quickly. If you take a look at diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/docs/requirements.in new/natsort-7.1.0/docs/requirements.in --- old/natsort-7.0.1/docs/requirements.in 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/docs/requirements.in 2020-11-21 02:38:43.000000000 +0100 @@ -1 +1 @@ -m2r +m2r @ git+https://github.com/crossnox/m2r@dev#egg=m2r diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/docs/requirements.txt new/natsort-7.1.0/docs/requirements.txt --- old/natsort-7.0.1/docs/requirements.txt 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/docs/requirements.txt 2020-11-21 02:38:43.000000000 +0100 @@ -4,6 +4,6 @@ # # pip-compile # -docutils==0.15.2 # via m2r -m2r==0.2.1 +docutils==0.16 # via m2r +git+https://github.com/crossnox/m2r@dev#egg=m2r # via -r requirements.in mistune==0.8.4 # via m2r diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/natsort/__init__.py new/natsort-7.1.0/natsort/__init__.py --- old/natsort-7.0.1/natsort/__init__.py 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/natsort/__init__.py 2020-11-21 02:38:43.000000000 +0100 @@ -14,11 +14,14 @@ ns, numeric_regex_chooser, order_by_index, + os_sort_key, + os_sort_keygen, + os_sorted, realsorted, ) from natsort.utils import chain_functions -__version__ = "7.0.1" +__version__ = "7.1.0" __all__ = [ "natsort_key", @@ -36,6 +39,9 @@ "ns", "chain_functions", "numeric_regex_chooser", + "os_sort_key", + "os_sort_keygen", + "os_sorted", ] # Add the ns keys to this namespace for convenience. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/natsort/natsort.py new/natsort-7.1.0/natsort/natsort.py --- old/natsort-7.0.1/natsort/natsort.py 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/natsort/natsort.py 2020-11-21 02:38:43.000000000 +0100 @@ -6,6 +6,7 @@ The majority of the "work" is defined in utils.py. """ +import platform from functools import partial from operator import itemgetter @@ -184,7 +185,7 @@ if alg & ns.PATH: string_func = utils.parse_path_factory(string_func) bytes_func = utils.parse_bytes_factory(alg) - num_func = utils.parse_number_factory(alg, sep, pre_sep) + num_func = utils.parse_number_or_none_factory(alg, sep, pre_sep) # Return the natsort key with the parsing path pre-chosen. return partial( @@ -245,6 +246,7 @@ realsorted : A wrapper for ``natsorted(seq, alg=ns.REAL)``. humansorted : A wrapper for ``natsorted(seq, alg=ns.LOCALE)``. index_natsorted : Returns the sorted indexes from `natsorted`. + os_sorted : Sort according to your operating system's rules. Examples -------- @@ -604,3 +606,154 @@ """ # Remove the leading and trailing parens return utils.regex_chooser(alg).pattern[1:-1] + + +def _split_apply(v, key=None): + if key is not None: + v = key(v) + return utils.path_splitter(str(v)) + + +# Choose the implementation based on the host OS +if platform.system() == "Windows": + + from ctypes import wintypes, windll + from functools import cmp_to_key + + _windows_sort_cmp = windll.Shlwapi.StrCmpLogicalW + _windows_sort_cmp.argtypes = [wintypes.LPWSTR, wintypes.LPWSTR] + _windows_sort_cmp.restype = wintypes.INT + _winsort_key = cmp_to_key(_windows_sort_cmp) + + def os_sort_keygen(key=None): + return lambda x: tuple(map(_winsort_key, _split_apply(x, key))) + + +else: + + # For UNIX-based platforms, ICU performs MUCH better than locale + # at replicating the file explorer's sort order. We will use + # ICU's ability to do basic natural sorting as it also better + # replicates than what natsort does by default. + # + # However, if the user does not have ICU installed then fall back + # on natsort's default handling for paths with locale turned on + # which will give good results in most cases (e.g. when there aren't + # a bunch of special characters). + try: + import icu + + except ImportError: + # No ICU installed + def os_sort_keygen(key=None): + return natsort_keygen( + key=key, alg=ns.LOCALE | ns.PATH | ns.IGNORECASE + ) + + else: + # ICU installed + def os_sort_keygen(key=None): + loc = natsort.compat.locale.get_icu_locale() + collator = icu.Collator.createInstance(loc) + collator.setAttribute( + icu.UCollAttribute.NUMERIC_COLLATION, icu.UCollAttributeValue.ON + ) + return lambda x: tuple(map(collator.getSortKey, _split_apply(x, key))) + + +os_sort_keygen.__doc__ = """ +Generate a sorting key to replicate your file browser's sort order + +See :func:`os_sorted` for description and caveats. + +Returns +------- +out : function + A function that parses input for OS path sorting that is + suitable for passing as the `key` argument to functions + such as `sorted`. + +See Also +-------- +os_sort_key +os_sorted + +Notes +----- +On Windows, this will implicitly coerce all inputs to str before +collating. + +""" + +os_sort_key = os_sort_keygen() +os_sort_key.__doc__ = """ +os_sort_key(val) +The default key to replicate your file browser's sort order + +This is the output of :func:`os_sort_keygen` with default values. + +See Also +-------- +os_sort_keygen + +""" + + +def os_sorted(seq, key=None, reverse=False): + """ + Sort elements in the same order as your operating system's file browser + + .. warning:: + + The resulting function will generate results that will be + differnt depending on your platform. This is intentional. + + On Windows, this will sort with the same order as Windows Explorer. + + On MacOS/Linux, you will get different results depending on whether + or not you have :mod:`pyicu` installed. + + - If you have :mod:`pyicu` installed, you will get results that are + the same as (or very close to) the same order as your operating + system's file browser. + - If you do not have :mod:`pyicu` installed, then this will give + the same results as if you used ``ns.LOCALE``, ``ns.PATH``, + and ``ns.IGNORECASE`` with :func:`natsorted`. If you do not have + special characters this will give correct results, but once + special characters are added you should lower your expectations. + + It is *strongly* reccommended to have :mod:`pyicu` installed on + MacOS/Linux if you want correct sort results. + + It does *not* take into account if a path is a directory or a file + when sorting. + + Parameters + ---------- + seq : iterable + The input to sort. Each element must be of type str. + + key : callable, optional + A key used to determine how to sort each element of the sequence. + It should accept a single argument and return a single value. + + reverse : {{True, False}}, optional + Return the list in reversed sorted order. The default is + `False`. + + Returns + ------- + out : list + The sorted input. + + See Also + -------- + natsorted + os_sort_keygen + + Notes + ----- + This will implicitly coerce all inputs to str before collating. + + """ + return sorted(seq, key=os_sort_keygen(key), reverse=reverse) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/natsort/ns_enum.py new/natsort-7.1.0/natsort/ns_enum.py --- old/natsort-7.0.1/natsort/ns_enum.py 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/natsort/ns_enum.py 2020-11-21 02:38:43.000000000 +0100 @@ -173,6 +173,7 @@ If an NaN shows up in the input, this instructs `natsort` to treat these as +Infinity and place them after all the other numbers. By default, an NaN be treated as -Infinity and be placed first. + Note that this ``None`` is treated like NaN internally. Notes ----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/natsort/unicode_numeric_hex.py new/natsort-7.1.0/natsort/unicode_numeric_hex.py --- old/natsort-7.0.1/natsort/unicode_numeric_hex.py 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/natsort/unicode_numeric_hex.py 2020-11-21 02:38:43.000000000 +0100 @@ -1150,6 +1150,13 @@ 0x10F52, 0x10F53, 0x10F54, + 0x10FC5, + 0x10FC6, + 0x10FC7, + 0x10FC8, + 0x10FC9, + 0x10FCA, + 0x10FCB, 0x11052, 0x11053, 0x11054, @@ -1311,6 +1318,16 @@ 0x118F0, 0x118F1, 0x118F2, + 0x11950, + 0x11951, + 0x11952, + 0x11953, + 0x11954, + 0x11955, + 0x11956, + 0x11957, + 0x11958, + 0x11959, 0x11C50, 0x11C51, 0x11C52, @@ -1815,6 +1832,16 @@ 0x1F10A, 0x1F10B, 0x1F10C, + 0x1FBF0, + 0x1FBF1, + 0x1FBF2, + 0x1FBF3, + 0x1FBF4, + 0x1FBF5, + 0x1FBF6, + 0x1FBF7, + 0x1FBF8, + 0x1FBF9, 0x20001, 0x20064, 0x200E2, @@ -1832,20 +1859,3 @@ 0x2626D, 0x2F890, ) - -# Some code that can be used to create the above list of hex numbers. -if __name__ == "__main__": - import unicodedata - - hex_chars = [] - for i in range(0x110000): - try: - a = chr(i) - except ValueError: - break - if a in "0123456789": - continue - if unicodedata.numeric(a, None) is not None: - hex_chars.append(i) - - print(", ".join(["0X{:X}".format(i) for i in hex_chars])) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/natsort/utils.py new/natsort-7.1.0/natsort/utils.py --- old/natsort-7.0.1/natsort/utils.py 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/natsort/utils.py 2020-11-21 02:38:43.000000000 +0100 @@ -262,9 +262,9 @@ return lambda x: (x,) -def parse_number_factory(alg, sep, pre_sep): +def parse_number_or_none_factory(alg, sep, pre_sep): """ - Create a function that will format a number into a tuple. + Create a function that will format a number (or None) into a tuple. Parameters ---------- @@ -295,7 +295,7 @@ def func(val, _nan_replace=nan_replace, _sep=sep): """Given a number, place it in a tuple with a leading null string.""" - return _sep, _nan_replace if val != val else val + return _sep, (_nan_replace if val != val or val is None else val) # Return the function, possibly wrapping in tuple if PATH is selected. if alg & ns.PATH and alg & ns.UNGROUPLETTERS and alg & ns.LOCALEALPHA: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/natsort.egg-info/PKG-INFO new/natsort-7.1.0/natsort.egg-info/PKG-INFO --- old/natsort-7.0.1/natsort.egg-info/PKG-INFO 2020-01-28 08:46:26.000000000 +0100 +++ new/natsort-7.1.0/natsort.egg-info/PKG-INFO 2020-11-21 02:39:03.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: natsort -Version: 7.0.1 +Version: 7.1.0 Summary: Simple yet flexible natural sorting in Python. Home-page: https://github.com/SethMMorton/natsort Author: Seth M. Morton @@ -109,6 +109,7 @@ -------------- - `Sorting Versions`_ + - `Sort Paths Like My File Browser (e.g. Windows Explorer on Windows)`_ - `Sorting by Real Numbers (i.e. Signed Floats)`_ - `Locale-Aware Sorting (or "Human Sorting")`_ - `Further Customizing Natsort`_ @@ -136,6 +137,30 @@ If you need to versions that use a more complicated scheme, please see `these examples <https://natsort.readthedocs.io/en/master/examples.html#rc-sorting>`_. + Sort Paths Like My File Browser (e.g. Windows Explorer on Windows) + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + Prior to ``natsort`` version 7.1.0, it was a common request to be able to + sort paths like Windows Explorer. As of ``natsort`` 7.1.0, the function + ``os_sorted`` has been added to provide users the ability to sort + in the order that their file browser might sort (e.g Windows Explorer on + Windows, Finder on MacOS, Dolphin/Nautilus/Thunar/etc. on Linux). + + .. code-block:: python + + import os + from natsort import os_sorted + print(os_sorted(os.listdir())) + # The directory sorted like your file browser might show + + Output will be different depending on the operating system you are on. + + For users **not** on Windows (e.g. MacOS/Linux) it is **strongly** recommended + to also install `PyICU <https://pypi.org/project/PyICU>`_, which will help + ``natsort`` give results that match most file browsers. If this is not installed, + it will fall back on Python's built-in ``locale`` module and will give good + results for most input, but will give poor restuls for special characters. + Sorting by Real Numbers (i.e. Signed Floats) ++++++++++++++++++++++++++++++++++++++++++++ @@ -494,17 +519,6 @@ to be shown. Alternatively, you can just set the environment variable ``PYTHONWARNINGS`` to "default::DeprecationWarning" and then run your code. - Dropped Pipenv for Development - ++++++++++++++++++++++++++++++ - - ``natsort`` version 6.0.0 no longer uses `Pipenv <https://pipenv.readthedocs.io/en/latest/>`_ - to install development dependencies. - - Dropped Python 2.6 and 3.3 Support - ++++++++++++++++++++++++++++++++++ - - ``natsort`` version 6.0.0 dropped support for Python 2.6 and Python 3.3. - Author ------ @@ -534,10 +548,11 @@ Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 Classifier: Topic :: Scientific/Engineering :: Information Analysis Classifier: Topic :: Utilities Classifier: Topic :: Text Processing Requires-Python: >=3.4 Description-Content-Type: text/x-rst -Provides-Extra: fast Provides-Extra: icu +Provides-Extra: fast diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/natsort.egg-info/SOURCES.txt new/natsort-7.1.0/natsort.egg-info/SOURCES.txt --- old/natsort-7.0.1/natsort.egg-info/SOURCES.txt 2020-01-28 08:46:26.000000000 +0100 +++ new/natsort-7.1.0/natsort.egg-info/SOURCES.txt 2020-11-21 02:39:03.000000000 +0100 @@ -9,6 +9,7 @@ dev/README.md dev/bump.py dev/clean.py +dev/generate_new_unicode_numbers.py dev/requirements.in dev/requirements.txt docs/api.rst @@ -50,6 +51,7 @@ tests/test_natsorted.py tests/test_natsorted_convenience.py tests/test_ns_enum.py +tests/test_os_sorted.py tests/test_parse_bytes_function.py tests/test_parse_number_function.py tests/test_parse_string_function.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/setup.cfg new/natsort-7.1.0/setup.cfg --- old/natsort-7.0.1/setup.cfg 2020-01-28 08:46:26.000000000 +0100 +++ new/natsort-7.1.0/setup.cfg 2020-11-21 02:39:03.000000000 +0100 @@ -1,5 +1,5 @@ [bumpversion] -current_version = 7.0.1 +current_version = 7.1.0 commit = True tag = True tag_name = {new_version} @@ -30,6 +30,7 @@ Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 Topic :: Scientific/Engineering :: Information Analysis Topic :: Utilities Topic :: Text Processing diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/setup.py new/natsort-7.1.0/setup.py --- old/natsort-7.0.1/setup.py 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/setup.py 2020-11-21 02:38:43.000000000 +0100 @@ -3,7 +3,7 @@ from setuptools import find_packages, setup setup( name='natsort', - version='7.0.1', + version='7.1.0', packages=find_packages(), entry_points={'console_scripts': ['natsort = natsort.__main__:main']}, python_requires=">=3.4", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/tests/test_os_sorted.py new/natsort-7.1.0/tests/test_os_sorted.py --- old/natsort-7.0.1/tests/test_os_sorted.py 1970-01-01 01:00:00.000000000 +0100 +++ new/natsort-7.1.0/tests/test_os_sorted.py 2020-11-21 02:38:43.000000000 +0100 @@ -0,0 +1,205 @@ +# -*- coding: utf-8 -*- +""" +Testing for the OS sorting +""" +import platform + +import natsort +import pytest + +try: + import icu # noqa: F401 +except ImportError: + has_icu = False +else: + has_icu = True + + +def test_os_sorted_compound(): + given = [ + "/p/Folder (10)/file.tar.gz", + "/p/Folder (1)/file (1).tar.gz", + "/p/Folder/file.x1.9.tar.gz", + "/p/Folder (2)/file.tar.gz", + "/p/Folder (1)/file.tar.gz", + "/p/Folder/file.x1.10.tar.gz", + ] + expected = [ + "/p/Folder/file.x1.9.tar.gz", + "/p/Folder/file.x1.10.tar.gz", + "/p/Folder (1)/file.tar.gz", + "/p/Folder (1)/file (1).tar.gz", + "/p/Folder (2)/file.tar.gz", + "/p/Folder (10)/file.tar.gz", + ] + result = natsort.os_sorted(given) + assert result == expected + + +def test_os_sorted_misc_no_fail(): + natsort.os_sorted([9, 4.3, None, float("nan")]) + + +def test_os_sorted_key(): + given = ["foo0", "foo2", "goo1"] + expected = ["foo0", "goo1", "foo2"] + result = natsort.os_sorted(given, key=lambda x: x.replace("g", "f")) + assert result == expected + + +# The following is a master list of things that might give trouble +# when sorting like the file explorer. +given_characters = [ + "11111", + "aaaaa", + "foo0", + "foo_0", + "foo1", + "foo2", + "foo4", + "foo10", + "Foo3", +] +given_special = [ + "!", + "#", + "$", + "%", + "&", + "'", + "(", + ")", + "+", + "+11111", + "+aaaaa", + ",", + "-", + ";", + "=", + "@", + "[", + "]", + "^", + "_", + "`", + "{", + "}", + "~", + "§", + "°", + "´", + "µ", + "€", +] + +# The expceted values change based on the environment +if platform.system() == "Windows": + given = given_characters + given_special + expected = [ + "'", + "-", + "!", + "#", + "$", + "%", + "&", + "(", + ")", + ",", + ";", + "@", + "[", + "]", + "^", + "_", + "`", + "{", + "}", + "~", + "´", + "€", + "+", + "+11111", + "+aaaaa", + "=", + "§", + "°", + "µ", + "11111", + "aaaaa", + "foo_0", + "foo0", + "foo1", + "foo2", + "Foo3", + "foo4", + "foo10", + ] + +elif has_icu: + given = given_characters + given_special + expected = [ + "_", + "-", + ",", + ";", + "!", + "'", + "(", + ")", + "[", + "]", + "{", + "}", + "§", + "@", + "&", + "#", + "%", + "`", + "´", + "^", + "°", + "+", + "+11111", + "+aaaaa", + "=", + "~", + "$", + "€", + "11111", + "aaaaa", + "foo_0", + "foo0", + "foo1", + "foo2", + "Foo3", + "foo4", + "foo10", + "µ", + ] +else: + # For non-ICU UNIX, the order is all over the place + # from platform to platform, distribution to distribution. + # It's not really possible to predict the order across all + # the different OS. To work around this, we will exclude + # the special characters from the sort. + given = given_characters + expected = [ + "11111", + "aaaaa", + "foo0", + "foo1", + "foo2", + "Foo3", + "foo4", + "foo10", + "foo_0", + ] + + +@pytest.mark.usefixtures("with_locale_en_us") +def test_os_sorted_corpus(): + result = natsort.os_sorted(given) + print(result) + assert result == expected diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/tests/test_parse_number_function.py new/natsort-7.1.0/tests/test_parse_number_function.py --- old/natsort-7.0.1/tests/test_parse_number_function.py 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/tests/test_parse_number_function.py 2020-11-21 02:38:43.000000000 +0100 @@ -5,7 +5,7 @@ from hypothesis import given from hypothesis.strategies import floats, integers from natsort.ns_enum import ns -from natsort.utils import parse_number_factory +from natsort.utils import parse_number_or_none_factory @pytest.mark.usefixtures("with_locale_en_us") @@ -20,7 +20,7 @@ ) @given(x=floats(allow_nan=False) | integers()) def test_parse_number_factory_makes_function_that_returns_tuple(x, alg, example_func): - parse_number_func = parse_number_factory(alg, "", "xx") + parse_number_func = parse_number_or_none_factory(alg, "", "xx") assert parse_number_func(x) == example_func(x) @@ -30,8 +30,10 @@ (ns.DEFAULT, 57, ("", 57)), (ns.DEFAULT, float("nan"), ("", float("-inf"))), # NaN transformed to -infinity (ns.NANLAST, float("nan"), ("", float("+inf"))), # NANLAST makes it +infinity + (ns.DEFAULT, None, ("", float("-inf"))), # None transformed to -infinity + (ns.NANLAST, None, ("", float("+inf"))), # NANLAST makes it +infinity ], ) -def test_parse_number_factory_treats_nan_special(alg, x, result): - parse_number_func = parse_number_factory(alg, "", "xx") +def test_parse_number_factory_treats_nan_and_none_special(alg, x, result): + parse_number_func = parse_number_or_none_factory(alg, "", "xx") assert parse_number_func(x) == result diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/tests/test_unicode_numbers.py new/natsort-7.1.0/tests/test_unicode_numbers.py --- old/natsort-7.0.1/tests/test_unicode_numbers.py 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/tests/test_unicode_numbers.py 2020-11-21 02:38:43.000000000 +0100 @@ -4,6 +4,7 @@ """ import unicodedata +import warnings from natsort.unicode_numbers import ( decimal_chars, @@ -34,11 +35,21 @@ def test_numeric_chars_contains_all_valid_unicode_numeric_and_digit_characters(): - set_numeric_hex = set(numeric_hex) set_numeric_chars = set(numeric_chars) set_digit_chars = set(digit_chars) set_decimal_chars = set(decimal_chars) - for i in range(0X110000): + + assert set_decimal_chars.isdisjoint(digits_no_decimals) + assert set_digit_chars.issuperset(digits_no_decimals) + + assert set_decimal_chars.isdisjoint(numeric_no_decimals) + assert set_numeric_chars.issuperset(numeric_no_decimals) + + +def test_missing_unicode_number_in_collection(): + ok = True + set_numeric_hex = set(numeric_hex) + for i in range(0x110000): try: a = chr(i) except ValueError: @@ -46,20 +57,18 @@ if a in "0123456789": continue if unicodedata.numeric(a, None) is not None: - assert i in set_numeric_hex - assert a in set_numeric_chars - if unicodedata.digit(a, None) is not None: - assert i in set_numeric_hex - assert a in set_digit_chars - if unicodedata.decimal(a, None) is not None: - assert i in set_numeric_hex - assert a in set_decimal_chars - - assert set_decimal_chars.isdisjoint(digits_no_decimals) - assert set_digit_chars.issuperset(digits_no_decimals) - - assert set_decimal_chars.isdisjoint(numeric_no_decimals) - assert set_numeric_chars.issuperset(numeric_no_decimals) + if i not in set_numeric_hex: + ok = False + if not ok: + warnings.warn( + """\ +Not all numeric unicode characters are represented in natsort/unicode_numeric_hex.py +This can be addressed by running dev/generate_new_unicode_numbers.py with the current \ +version of Python. +It would be much appreciated if you would submit a Pull Request to the natsort +repository (https://github.com/SethMMorton/natsort) with the resulting change. +""" + ) def test_combined_string_contains_all_characters_in_list(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/natsort-7.0.1/tox.ini new/natsort-7.1.0/tox.ini --- old/natsort-7.0.1/tox.ini 2020-01-28 08:46:08.000000000 +0100 +++ new/natsort-7.1.0/tox.ini 2020-11-21 02:38:43.000000000 +0100 @@ -5,7 +5,7 @@ [tox] envlist = - flake8, py35, py36, py37, py38 + flake8, py35, py36, py37, py38, py39 # Other valid evironments are: # docs # release @@ -52,7 +52,7 @@ # will already be installed on readthedocs. [testenv:docs] deps = - sphinx + sphinx < 3.3.0 sphinx_rtd_theme -r docs/requirements.txt commands = _______________________________________________ openSUSE Commits mailing list -- commit@lists.opensuse.org To unsubscribe, email commit-le...@lists.opensuse.org List Netiquette: https://en.opensuse.org/openSUSE:Mailing_list_netiquette List Archives: https://lists.opensuse.org/archives/list/commit@lists.opensuse.org