Codebase list natsort / fresh-snapshots/main
New upstream snapshot. Debian Janitor 1 year, 4 months ago
40 changed file(s) with 1465 addition(s) and 691 deletion(s). Raw diff Collapse all Expand all
+0
-16
.coveragerc less more
0 [report]
1 # Regexes for lines to exclude from consideration
2 exclude_lines =
3 # Have to re-enable the standard pragma
4 pragma: no cover
5
6 # Don't complain if tests don't hit defensive assertion code:
7 raise AssertionError
8 raise NotImplementedError
9 raise$
10
11 # Don't complain if non-runnable code isn't run:
12 if 0:
13 if __name__ == .__main__.:
14
15 ignore_errors = True
+0
-23
.github/ISSUE_TEMPLATE/bug_report.md less more
0 ---
1 name: Bug report
2 about: Report unexpected behavior, a crash, or incorrect results
3
4 ---
5
6 **Describe the bug**
7 A clear and concise description of what the bug is.
8
9 **Expected behavior**
10 A clear and concise description of what you expected to happen.
11
12 **Environment (please complete the following information):**
13 - Python Version: [e.g. 3.6]
14 - OS [e.g. Windows, Fedora]
15 - If the bug involves `LOCALE` or `humansorted`:
16 - Is `PyICU` installed?
17 - Do you have a locale set? If so, to what?
18
19 **To Reproduce**
20 Include a Minimum, Complete, Verifiable Example. If there is a traceback (or error message), **please** include the *entire* traceback (or error message), even if you think it is too big.
21
22 See https://stackoverflow.com/help/mcve for an explanation.
+0
-14
.github/ISSUE_TEMPLATE/feature_request.md less more
0 ---
1 name: Feature request
2 about: Suggest or request an enhancement
3
4 ---
5
6 **Describe the feature or enhancement**
7 Be as descriptive and precise as possible.
8
9 **Provide a concrete example of how the feature or enhancement will improve `natsort`**
10 Code examples are an excellent way to show how this feature or enhancement will help. To make your case stronger, show the current workaround due to the lack of the feature. What is the return-on-investment for including the feature or enhancement?
11
12 **Would you be willing to submit a Pull Request for this feature?**
13 Extra help is *always* welcome.
+0
-7
.github/ISSUE_TEMPLATE/question.md less more
0 ---
1 name: Question
2 about: Inquiry about natsort
3
4 ---
5
6 - [ ] I have read the [`natsort` documentation](https://natsort.readthedocs.io/en/master/) and the [README](https://github.com/SethMMorton/natsort#natsort), and my question is still not answered
+0
-86
.github/workflows/code-quality.yml less more
0 name: Code Quality
1
2 # Only run on branches (e.g. not tags)
3 on:
4 push:
5 branches:
6 - "*"
7 pull_request:
8 branches:
9 - "*"
10
11 jobs:
12 formatting:
13 name: Formatting
14 runs-on: ubuntu-latest
15 steps:
16 - name: Checkout code
17 uses: actions/checkout@v2
18
19 - name: Set up Python
20 uses: actions/setup-python@v2
21 with:
22 python-version: '3.6'
23
24 - name: Install black
25 run: pip install black
26
27 - name: Run black
28 run: black --quiet --check --diff .
29
30 static-analysis:
31 name: Static Analysis
32 runs-on: ubuntu-latest
33 steps:
34 - name: Checkout code
35 uses: actions/checkout@v2
36
37 - name: Set up Python
38 uses: actions/setup-python@v2
39 with:
40 python-version: '3.6'
41
42 - name: Install Flake8
43 run: pip install flake8 flake8-import-order flake8-bugbear pep8-naming
44
45 - name: Run Flake8
46 run: flake8
47
48 type-checking:
49 name: Type Checking
50 runs-on: ubuntu-latest
51 steps:
52 - name: Checkout code
53 uses: actions/checkout@v2
54
55 - name: Set up Python
56 uses: actions/setup-python@v2
57 with:
58 python-version: '3.6'
59
60 - name: Install MyPy
61 run: pip install mypy hypothesis pytest pytest-mock fastnumbers
62
63 - name: Run MyPy
64 run: mypy --strict natsort tests
65
66 package-validation:
67 name: Package Validation
68 runs-on: ubuntu-latest
69 steps:
70 - name: Checkout code
71 uses: actions/checkout@v2
72
73 - name: Set up Python
74 uses: actions/setup-python@v2
75 with:
76 python-version: '3.6'
77
78 - name: Install Validators
79 run: pip install twine check-manifest
80
81 - name: Run Validation
82 run: |
83 check-manifest --ignore ".github*,*.md,.coveragerc"
84 python setup.py sdist
85 twine check dist/*
+0
-31
.github/workflows/deploy.yml less more
0 name: Deploy
1
2 # Only run on tagged commits
3 on:
4 push:
5 tags:
6 - "*"
7
8 jobs:
9 deploy:
10 name: Deploy
11 runs-on: ubuntu-latest
12 steps:
13 - name: Checkout code
14 uses: actions/checkout@v2
15
16 - name: Set up Python
17 uses: actions/setup-python@v2
18 with:
19 python-version: 3.9
20
21 - name: Build Source Distribution and Wheel
22 run: |
23 pip install wheel
24 python setup.py sdist --format=gztar bdist_wheel
25
26 - name: Publish to PyPI
27 uses: pypa/gh-action-pypi-publish@master
28 with:
29 user: __token__
30 password: ${{ secrets.pypi_token_password }}
+0
-61
.github/workflows/tests.yml less more
0 name: Tests
1
2 # Only run on branches (e.g. not tags)
3 on:
4 push:
5 branches:
6 - "*"
7 pull_request:
8 branches:
9 - "*"
10
11 jobs:
12 tests:
13 name: Tests
14 runs-on: ${{ matrix.os }}
15 strategy:
16 matrix:
17 python-version: [3.6, 3.7, 3.8, 3.9, "3.10"]
18 os: [ubuntu-latest]
19 extras: [false]
20 include:
21 - {python-version: 3.9, os: windows-latest, extras: false}
22 - {python-version: 3.9, os: macos-latest, extras: false}
23 - {python-version: 3.9, os: ubuntu-latest, extras: true}
24
25 steps:
26 - name: Checkout code
27 uses: actions/checkout@v2
28
29 - name: Set up Python ${{ matrix.python-version }}
30 uses: actions/setup-python@v2
31 with:
32 python-version: ${{ matrix.python-version }}
33
34 - name: Install Locales
35 if: matrix.os == 'ubuntu-latest'
36 run: |
37 sudo apt-get update
38 sudo apt-get install language-pack-de language-pack-en language-pack-cs
39
40 - name: Install ICU
41 if: matrix.extras
42 run: sudo apt-get install libicu-dev
43
44 - name: Install Dependencies
45 run: |
46 python -m pip install --upgrade pip
47 python -m pip install tox tox-gh-actions codecov
48
49 - name: Set Extras Environment
50 if: matrix.extras
51 run: echo WITH_EXTRAS=fast,icu >> $GITHUB_ENV
52
53 - name: Run Tests
54 run: tox
55
56 - name: Generate Coverage Report
57 run: coverage xml
58
59 - name: Upload to CodeCov
60 uses: codecov/codecov-action@v1
+0
-44
.gitignore less more
0 *.py[co]
1
2 # Packages
3 *.egg
4 *.eggs
5 *.egg-info
6 dist
7 build
8 eggs
9 parts
10 bin
11 var
12 sdist
13 develop-eggs
14 .installed.cfg
15 .python-version
16
17 # We are using MANIFEST.in instead
18 MANIFEST
19
20 # Installer logs
21 pip-log.txt
22
23 # Unit test / coverage reports
24 .hypothesis
25 .coverage
26 .tox
27 .cache
28 .pytest_cache
29 .pytest
30 .envrc
31 .venv
32
33 #Translations
34 *.mo
35
36 #Mr Developer
37 .mr.developer.cfg
38
39 # PyCharm
40 .idea
41
42 # VSCode
43 .vscode
00 Unreleased
11 ---
22
3 ### Removed
4
5 - Support for EOL Python 3.6
6
7 [8.2.0] - 2022-09-01
8 ---
9
10 ### Changed
11 - Auto-coerce `pathlib.Path` objects to `str` since it is the least astonishing
12 behavior ([@Gilthans](https://github.com/Gilthans), issues #152, #153)
13 - Reduce strictness of type hints to avoid over-constraining client code
14 (issues #154, #155)
15
16 [8.1.0] - 2022-01-30
17 ---
18
19 ### Changed
20 - When using `ns.PATH`, only split off a maximum of two suffixes from
21 a file name (issues #145, #146).
22
323 [8.0.2] - 2021-12-14
424 ---
525
626 ### Fixed
7 - Bug where sorting paths fail if one of the paths is '.'.
27 - Bug where sorting paths fail if one of the paths is '.' (issues #142, #143)
828
929 [8.0.1] - 2021-12-10
1030 ---
1131
1232 ### Fixed
1333 - Compose unicode characters when using locale to ensure sorting is correct
14 across all locales.
34 across all locales (issues #140, #141)
1535
1636 [8.0.0] - 2021-11-03
1737 ---
5171
5272 ### Changed
5373 - MacOS unit tests run on native Python
54 - Treate `None` like `NaN` internally to avoid `TypeError` (issue #117)
74 - Treat `None` like `NaN` internally to avoid `TypeError` (issue #117)
5575 - No longer fail tests every time a new Python version is released (issue #122)
5676
5777 ### Fixed
7595 - Release checklist in `RELEASING.md` ([@hugovk](https://github.com/hugovk), issue #106)
7696
7797 ### Changed
78 - Updated auxillary shell scripts to be written in python, and added
98 - Updated auxiliary shell scripts to be written in python, and added
7999 ability to call these from `tox`
80100 - Improved Travis-CI experience
81101 - Update testing dependency versions
280300 because the new factory function paradigm eliminates most `if` branches
281301 during execution). For the most cases, the code is 30-40% faster than version 4.0.4.
282302 If using `ns.LOCALE` or `humansorted`, the code is 1100% faster than version 4.0.4
283 - Improved clarity of documentaion with regards to locale-aware sorting
303 - Improved clarity of documentation with regards to locale-aware sorting
284304
285305 ### Deprecated
286306 - `ns.TYPESAFE` option as it is now always on (due to a new
426446 - `reverse` option to `natsorted` & co. to make it's API more
427447 similar to the builtin 'sorted'
428448 - More unit tests
429 - Auxillary test code that helps in profiling and stress-testing
449 - Auxiliary test code that helps in profiling and stress-testing
430450 - Support for coveralls.io
431451
432452 ### Changed
573593 ---
574594
575595 ### Added
576 - Tests into the natsort.py file iteself
596 - Tests into the natsort.py file itself
577597
578598 ### Changed
579599 - Reorganized directory structure
589609 - Sorting algorithm to support floats (including exponentials) and basic version number support
590610
591611 <!---Comparison links-->
612 [8.2.0]: https://github.com/SethMMorton/natsort/compare/8.1.0...8.2.0
613 [8.1.0]: https://github.com/SethMMorton/natsort/compare/8.0.2...8.1.0
592614 [8.0.2]: https://github.com/SethMMorton/natsort/compare/8.0.1...8.0.2
593615 [8.0.1]: https://github.com/SethMMorton/natsort/compare/8.0.0...8.0.1
594616 [8.0.0]: https://github.com/SethMMorton/natsort/compare/7.2.0...8.0.0
+0
-46
CODE_OF_CONDUCT.md less more
0 # Contributor Covenant Code of Conduct
1
2 ## Our Pledge
3
4 In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
5
6 ## Our Standards
7
8 Examples of behavior that contributes to creating a positive environment include:
9
10 * Using welcoming and inclusive language
11 * Being respectful of differing viewpoints and experiences
12 * Gracefully accepting constructive criticism
13 * Focusing on what is best for the community
14 * Showing empathy towards other community members
15
16 Examples of unacceptable behavior by participants include:
17
18 * The use of sexualized language or imagery and unwelcome sexual attention or advances
19 * Trolling, insulting/derogatory comments, and personal or political attacks
20 * Public or private harassment
21 * Publishing others' private information, such as a physical or electronic address, without explicit permission
22 * Other conduct which could reasonably be considered inappropriate in a professional setting
23
24 ## Our Responsibilities
25
26 Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
27
28 Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
29
30 ## Scope
31
32 This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
33
34 ## Enforcement
35
36 Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at drtuba78@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
37
38 Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
39
40 ## Attribution
41
42 This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html][version]
43
44 [homepage]: https://www.contributor-covenant.org/
45 [version]: https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+0
-44
CONTRIBUTING.md less more
0 # Contributing
1
2 If you have an idea for how to improve `natsort`, please contribute! It can
3 be as simple as a bug fix or documentation update, or as complicated as a more
4 robust algorithm. Contributions that change the public API of
5 `natsort` will have to ensure that the library does not become
6 less usable after the contribution and is backwards-compatible (unless there is
7 a good reason not to be).
8
9 I do not have strong opinions on how one should contribute, so
10 I have copy/pasted some text verbatim from the
11 [Contributor's Guide](http://docs.python-requests.org/en/latest/dev/contributing/) section of
12 [Kenneth Reitz's](http://docs.python-requests.org/en/latest/dev/contributing/)
13 excellent [requests](https://github.com/kennethreitz/requests) library in
14 lieu of coming up with my own.
15
16 > ### Steps for Submitting Code
17
18 > When contributing code, you'll want to follow this checklist:
19
20 > - Fork the repository on GitHub.
21 > - Run the tests to confirm they all pass on your system.
22 If they don't, you'll need to investigate why they fail.
23 If you're unable to diagnose this yourself,
24 raise it as a bug report.
25 > - Write tests that demonstrate your bug or feature. Ensure that they fail.
26 > - Make your change.
27 > - Run the entire test suite again, confirming that all tests pass including the
28 ones you just added.
29 > - Send a GitHub Pull Request to the main repository's master branch.
30 GitHub Pull Requests are the expected method of code collaboration on this project.
31
32 > ### Documentation Contributions
33 > Documentation improvements are always welcome! The documentation files live in the
34 docs/ directory of the codebase. They're written in
35 [reStructuredText](http://docutils.sourceforge.net/rst.html), and use
36 [Sphinx](http://sphinx-doc.org/index.html)
37 to generate the full suite of documentation.
38
39 > When contributing documentation, please do your best to follow the style of the
40 documentation files. This means a soft-limit of 79 characters wide in your text
41 files and a semi-formal, yet friendly and approachable, prose style.
42
43 > When presenting Python code, use single-quoted strings ('hello' instead of "hello").
11 include CHANGELOG.md
22 include tox.ini
33 include RELEASING.md
4 recursive-include mypy_stubs *.pyi
45 graft dev
56 graft docs
67 graft natsort
0 Metadata-Version: 2.1
1 Name: natsort
2 Version: 8.2.0
3 Summary: Simple yet flexible natural sorting in Python.
4 Home-page: https://github.com/SethMMorton/natsort
5 Author: Seth M. Morton
6 Author-email: drtuba78@gmail.com
7 License: MIT
8 Classifier: Development Status :: 5 - Production/Stable
9 Classifier: Intended Audience :: Developers
10 Classifier: Intended Audience :: Science/Research
11 Classifier: Intended Audience :: System Administrators
12 Classifier: Intended Audience :: Information Technology
13 Classifier: Intended Audience :: Financial and Insurance Industry
14 Classifier: Operating System :: OS Independent
15 Classifier: License :: OSI Approved :: MIT License
16 Classifier: Natural Language :: English
17 Classifier: Programming Language :: Python
18 Classifier: Programming Language :: Python :: 3
19 Classifier: Programming Language :: Python :: 3.7
20 Classifier: Programming Language :: Python :: 3.8
21 Classifier: Programming Language :: Python :: 3.9
22 Classifier: Programming Language :: Python :: 3.10
23 Classifier: Topic :: Scientific/Engineering :: Information Analysis
24 Classifier: Topic :: Utilities
25 Classifier: Topic :: Text Processing
26 Requires-Python: >=3.7
27 Description-Content-Type: text/x-rst
28 Provides-Extra: fast
29 Provides-Extra: icu
30 License-File: LICENSE
31
32 natsort
33 =======
34
35 .. image:: https://img.shields.io/pypi/v/natsort.svg
36 :target: https://pypi.org/project/natsort/
37
38 .. image:: https://img.shields.io/pypi/pyversions/natsort.svg
39 :target: https://pypi.org/project/natsort/
40
41 .. image:: https://img.shields.io/pypi/l/natsort.svg
42 :target: https://github.com/SethMMorton/natsort/blob/master/LICENSE
43
44 .. image:: https://github.com/SethMMorton/natsort/workflows/Tests/badge.svg
45 :target: https://github.com/SethMMorton/natsort/actions
46
47 .. image:: https://codecov.io/gh/SethMMorton/natsort/branch/master/graph/badge.svg
48 :target: https://codecov.io/gh/SethMMorton/natsort
49
50 Simple yet flexible natural sorting in Python.
51
52 - Source Code: https://github.com/SethMMorton/natsort
53 - Downloads: https://pypi.org/project/natsort/
54 - Documentation: https://natsort.readthedocs.io/
55
56 - `Examples and Recipes <https://natsort.readthedocs.io/en/master/examples.html>`_
57 - `How Does Natsort Work? <https://natsort.readthedocs.io/en/master/howitworks.html>`_
58 - `API <https://natsort.readthedocs.io/en/master/api.html>`_
59
60 - `Quick Description`_
61 - `Quick Examples`_
62 - `FAQ`_
63 - `Requirements`_
64 - `Optional Dependencies`_
65 - `Installation`_
66 - `How to Run Tests`_
67 - `How to Build Documentation`_
68 - `Dropped Deprecated APIs`_
69 - `History`_
70
71 **NOTE**: Please see the `Dropped Deprecated APIs`_ section for changes.
72
73 Quick Description
74 -----------------
75
76 When you try to sort a list of strings that contain numbers, the normal python
77 sort algorithm sorts lexicographically, so you might not get the results that
78 you expect:
79
80 .. code-block:: pycon
81
82 >>> a = ['2 ft 7 in', '1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '7 ft 6 in']
83 >>> sorted(a)
84 ['1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '2 ft 7 in', '7 ft 6 in']
85
86 Notice that it has the order ('1', '10', '2') - this is because the list is
87 being sorted in lexicographical order, which sorts numbers like you would
88 letters (i.e. 'b', 'ba', 'c').
89
90 ``natsort`` provides a function ``natsorted`` that helps sort lists
91 "naturally" ("naturally" is rather ill-defined, but in general it means
92 sorting based on meaning and not computer code point).
93 Using ``natsorted`` is simple:
94
95 .. code-block:: pycon
96
97 >>> from natsort import natsorted
98 >>> a = ['2 ft 7 in', '1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '7 ft 6 in']
99 >>> natsorted(a)
100 ['1 ft 5 in', '2 ft 7 in', '2 ft 11 in', '7 ft 6 in', '10 ft 2 in']
101
102 ``natsorted`` identifies numbers anywhere in a string and sorts them
103 naturally. Below are some other things you can do with ``natsort``
104 (also see the `examples <https://natsort.readthedocs.io/en/master/examples.html>`_
105 for a quick start guide, or the
106 `api <https://natsort.readthedocs.io/en/master/api.html>`_ for complete details).
107
108 **Note**: ``natsorted`` is designed to be a drop-in replacement for the
109 built-in ``sorted`` function. Like ``sorted``, ``natsorted``
110 `does not sort in-place`. To sort a list and assign the output to the same
111 variable, you must explicitly assign the output to a variable:
112
113 .. code-block:: pycon
114
115 >>> a = ['2 ft 7 in', '1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '7 ft 6 in']
116 >>> natsorted(a)
117 ['1 ft 5 in', '2 ft 7 in', '2 ft 11 in', '7 ft 6 in', '10 ft 2 in']
118 >>> print(a) # 'a' was not sorted; "natsorted" simply returned a sorted list
119 ['2 ft 7 in', '1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '7 ft 6 in']
120 >>> a = natsorted(a) # Now 'a' will be sorted because the sorted list was assigned to 'a'
121 >>> print(a)
122 ['1 ft 5 in', '2 ft 7 in', '2 ft 11 in', '7 ft 6 in', '10 ft 2 in']
123
124 Please see `Generating a Reusable Sorting Key and Sorting In-Place`_ for
125 an alternate way to sort in-place naturally.
126
127 Quick Examples
128 --------------
129
130 - `Sorting Versions`_
131 - `Sort Paths Like My File Browser (e.g. Windows Explorer on Windows)`_
132 - `Sorting by Real Numbers (i.e. Signed Floats)`_
133 - `Locale-Aware Sorting (or "Human Sorting")`_
134 - `Further Customizing Natsort`_
135 - `Sorting Mixed Types`_
136 - `Handling Bytes`_
137 - `Generating a Reusable Sorting Key and Sorting In-Place`_
138 - `Other Useful Things`_
139
140 Sorting Versions
141 ++++++++++++++++
142
143 ``natsort`` does not actually *comprehend* version numbers.
144 It just so happens that the most common versioning schemes are designed to
145 work with standard natural sorting techniques; these schemes include
146 ``MAJOR.MINOR``, ``MAJOR.MINOR.PATCH``, ``YEAR.MONTH.DAY``. If your data
147 conforms to a scheme like this, then it will work out-of-the-box with
148 ``natsorted`` (as of ``natsort`` version >= 4.0.0):
149
150 .. code-block:: pycon
151
152 >>> a = ['version-1.9', 'version-2.0', 'version-1.11', 'version-1.10']
153 >>> natsorted(a)
154 ['version-1.9', 'version-1.10', 'version-1.11', 'version-2.0']
155
156 If you need to versions that use a more complicated scheme, please see
157 `these examples <https://natsort.readthedocs.io/en/master/examples.html#rc-sorting>`_.
158
159 Sort Paths Like My File Browser (e.g. Windows Explorer on Windows)
160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
161
162 Prior to ``natsort`` version 7.1.0, it was a common request to be able to
163 sort paths like Windows Explorer. As of ``natsort`` 7.1.0, the function
164 ``os_sorted`` has been added to provide users the ability to sort
165 in the order that their file browser might sort (e.g Windows Explorer on
166 Windows, Finder on MacOS, Dolphin/Nautilus/Thunar/etc. on Linux).
167
168 .. code-block:: python
169
170 import os
171 from natsort import os_sorted
172 print(os_sorted(os.listdir()))
173 # The directory sorted like your file browser might show
174
175 Output will be different depending on the operating system you are on.
176
177 For users **not** on Windows (e.g. MacOS/Linux) it is **strongly** recommended
178 to also install `PyICU <https://pypi.org/project/PyICU>`_, which will help
179 ``natsort`` give results that match most file browsers. If this is not installed,
180 it will fall back on Python's built-in ``locale`` module and will give good
181 results for most input, but will give poor results for special characters.
182
183 Sorting by Real Numbers (i.e. Signed Floats)
184 ++++++++++++++++++++++++++++++++++++++++++++
185
186 This is useful in scientific data analysis (and was
187 the default behavior of ``natsorted`` for ``natsort``
188 version < 4.0.0). Use the ``realsorted`` function:
189
190 .. code-block:: pycon
191
192 >>> from natsort import realsorted, ns
193 >>> # Note that when interpreting as signed floats, the below numbers are
194 >>> # +5.10, -3.00, +5.30, +2.00
195 >>> a = ['position5.10.data', 'position-3.data', 'position5.3.data', 'position2.data']
196 >>> natsorted(a)
197 ['position2.data', 'position5.3.data', 'position5.10.data', 'position-3.data']
198 >>> natsorted(a, alg=ns.REAL)
199 ['position-3.data', 'position2.data', 'position5.10.data', 'position5.3.data']
200 >>> realsorted(a) # shortcut for natsorted with alg=ns.REAL
201 ['position-3.data', 'position2.data', 'position5.10.data', 'position5.3.data']
202
203 Locale-Aware Sorting (or "Human Sorting")
204 +++++++++++++++++++++++++++++++++++++++++
205
206 This is where the non-numeric characters are also ordered based on their
207 meaning, not on their ordinal value, and a locale-dependent thousands
208 separator and decimal separator is accounted for in the number.
209 This can be achieved with the ``humansorted`` function:
210
211 .. code-block:: pycon
212
213 >>> a = ['Apple', 'apple15', 'Banana', 'apple14,689', 'banana']
214 >>> natsorted(a)
215 ['Apple', 'Banana', 'apple14,689', 'apple15', 'banana']
216 >>> import locale
217 >>> locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
218 'en_US.UTF-8'
219 >>> natsorted(a, alg=ns.LOCALE)
220 ['apple15', 'apple14,689', 'Apple', 'banana', 'Banana']
221 >>> from natsort import humansorted
222 >>> humansorted(a) # shortcut for natsorted with alg=ns.LOCALE
223 ['apple15', 'apple14,689', 'Apple', 'banana', 'Banana']
224
225 You may find you need to explicitly set the locale to get this to work
226 (as shown in the example).
227 Please see `locale issues <https://natsort.readthedocs.io/en/master/locale_issues.html>`_ and the
228 `Optional Dependencies`_ section below before using the ``humansorted`` function.
229
230 Further Customizing Natsort
231 +++++++++++++++++++++++++++
232
233 If you need to combine multiple algorithm modifiers (such as ``ns.REAL``,
234 ``ns.LOCALE``, and ``ns.IGNORECASE``), you can combine the options using the
235 bitwise OR operator (``|``). For example,
236
237 .. code-block:: pycon
238
239 >>> a = ['Apple', 'apple15', 'Banana', 'apple14,689', 'banana']
240 >>> natsorted(a, alg=ns.REAL | ns.LOCALE | ns.IGNORECASE)
241 ['Apple', 'apple15', 'apple14,689', 'Banana', 'banana']
242 >>> # The ns enum provides long and short forms for each option.
243 >>> ns.LOCALE == ns.L
244 True
245 >>> # You can also customize the convenience functions, too.
246 >>> natsorted(a, alg=ns.REAL | ns.LOCALE | ns.IGNORECASE) == realsorted(a, alg=ns.L | ns.IC)
247 True
248 >>> natsorted(a, alg=ns.REAL | ns.LOCALE | ns.IGNORECASE) == humansorted(a, alg=ns.R | ns.IC)
249 True
250
251 All of the available customizations can be found in the documentation for
252 `the ns enum <https://natsort.readthedocs.io/en/master/api.html#natsort.ns>`_.
253
254 You can also add your own custom transformation functions with the ``key``
255 argument. These can be used with ``alg`` if you wish.
256
257 .. code-block:: pycon
258
259 >>> a = ['apple2.50', '2.3apple']
260 >>> natsorted(a, key=lambda x: x.replace('apple', ''), alg=ns.REAL)
261 ['2.3apple', 'apple2.50']
262
263 Sorting Mixed Types
264 +++++++++++++++++++
265
266 You can mix and match ``int``, ``float``, and ``str`` (or ``unicode``) types
267 when you sort:
268
269 .. code-block:: pycon
270
271 >>> a = ['4.5', 6, 2.0, '5', 'a']
272 >>> natsorted(a)
273 [2.0, '4.5', '5', 6, 'a']
274 >>> # sorted(a) would raise an "unorderable types" TypeError
275
276 Handling Bytes
277 ++++++++++++++
278
279 ``natsort`` does not officially support the `bytes` type, but
280 convenience functions are provided that help you decode to `str` first:
281
282 .. code-block:: pycon
283
284 >>> from natsort import as_utf8
285 >>> a = [b'a', 14.0, 'b']
286 >>> # natsorted(a) would raise a TypeError (bytes() < str())
287 >>> natsorted(a, key=as_utf8) == [14.0, b'a', 'b']
288 True
289 >>> a = [b'a56', b'a5', b'a6', b'a40']
290 >>> # natsorted(a) would return the same results as sorted(a)
291 >>> natsorted(a, key=as_utf8) == [b'a5', b'a6', b'a40', b'a56']
292 True
293
294 Generating a Reusable Sorting Key and Sorting In-Place
295 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
296
297 Under the hood, ``natsorted`` works by generating a custom sorting
298 key using ``natsort_keygen`` and then passes that to the built-in
299 ``sorted``. You can use the ``natsort_keygen`` function yourself to
300 generate a custom sorting key to sort in-place using the ``list.sort``
301 method.
302
303 .. code-block:: pycon
304
305 >>> from natsort import natsort_keygen
306 >>> natsort_key = natsort_keygen()
307 >>> a = ['2 ft 7 in', '1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '7 ft 6 in']
308 >>> natsorted(a) == sorted(a, key=natsort_key)
309 True
310 >>> a.sort(key=natsort_key)
311 >>> a
312 ['1 ft 5 in', '2 ft 7 in', '2 ft 11 in', '7 ft 6 in', '10 ft 2 in']
313
314 All of the algorithm customizations mentioned in the
315 `Further Customizing Natsort`_ section can also be applied to
316 ``natsort_keygen`` through the *alg* keyword option.
317
318 Other Useful Things
319 +++++++++++++++++++
320
321 - recursively descend into lists of lists
322 - automatic unicode normalization of input data
323 - `controlling the case-sensitivity <https://natsort.readthedocs.io/en/master/examples.html#case-sort>`_
324 - `sorting file paths correctly <https://natsort.readthedocs.io/en/master/examples.html#path-sort>`_
325 - `allow custom sorting keys <https://natsort.readthedocs.io/en/master/examples.html#custom-sort>`_
326 - `accounting for units <https://natsort.readthedocs.io/en/master/examples.html#accounting-for-units-when-sorting>`_
327
328 FAQ
329 ---
330
331 How do I debug ``natsort.natsorted()``?
332 The best way to debug ``natsorted()`` is to generate a key using ``natsort_keygen()``
333 with the same options being passed to ``natsorted``. One can take a look at
334 exactly what is being done with their input using this key - it is highly
335 recommended
336 to `look at this issue describing how to debug <https://github.com/SethMMorton/natsort/issues/13#issuecomment-50422375>`_
337 for *how* to debug, and also to review the
338 `How Does Natsort Work? <https://natsort.readthedocs.io/en/master/howitworks.html>`_
339 page for *why* ``natsort`` is doing that to your data.
340
341 If you are trying to sort custom classes and running into trouble, please
342 take a look at https://github.com/SethMMorton/natsort/issues/60. In short,
343 custom classes are not likely to be sorted correctly if one relies
344 on the behavior of ``__lt__`` and the other rich comparison operators in
345 their custom class - it is better to use a ``key`` function with
346 ``natsort``, or use the ``natsort`` key as part of your rich comparison
347 operator definition.
348
349 ``natsort`` gave me results I didn't expect, and it's a terrible library!
350 Did you try to debug using the above advice? If so, and you still cannot figure out
351 the error, then please `file an issue <https://github.com/SethMMorton/natsort/issues/new>`_.
352
353 How *does* ``natsort`` work?
354 If you don't want to read `How Does Natsort Work? <https://natsort.readthedocs.io/en/master/howitworks.html>`_,
355 here is a quick primer.
356
357 ``natsort`` provides a `key function <https://docs.python.org/3/howto/sorting.html#key-functions>`_
358 that can be passed to `list.sort() <https://docs.python.org/3/library/stdtypes.html#list.sort>`_
359 or `sorted() <https://docs.python.org/3/library/functions.html#sorted>`_ in order to
360 modify the default sorting behavior. This key is generated on-demand with
361 the key generator ``natsort.natsort_keygen()``. ``natsort.natsorted()``
362 is essentially a wrapper for the following code:
363
364 .. code-block:: pycon
365
366 >>> from natsort import natsort_keygen
367 >>> natsort_key = natsort_keygen()
368 >>> sorted(['1', '10', '2'], key=natsort_key)
369 ['1', '2', '10']
370
371 Users can further customize ``natsort`` sorting behavior with the ``key``
372 and/or ``alg`` options (see details in the `Further Customizing Natsort`_
373 section).
374
375 The key generated by ``natsort_keygen`` *always* returns a ``tuple``. It
376 does so in the following way (*some details omitted for clarity*):
377
378 1. Assume the input is a string, and attempt to split it into numbers and
379 non-numbers using regular expressions. Numbers are then converted into
380 either ``int`` or ``float``.
381 2. If the above fails because the input is not a string, assume the input
382 is some other sequence (e.g. ``list`` or ``tuple``), and recursively
383 apply the key to each element of the sequence.
384 3. If the above fails because the input is not iterable, assume the input
385 is an ``int`` or ``float``, and just return the input in a ``tuple``.
386
387 Because a ``tuple`` is always returned, a ``TypeError`` should not be common
388 unless one tries to do something odd like sort an ``int`` against a ``list``.
389
390 Shell script
391 ------------
392
393 ``natsort`` comes with a shell script called ``natsort``, or can also be called
394 from the command line with ``python -m natsort``.
395
396 Requirements
397 ------------
398
399 ``natsort`` requires Python 3.7 or greater.
400
401 Optional Dependencies
402 ---------------------
403
404 fastnumbers
405 +++++++++++
406
407 The most efficient sorting can occur if you install the
408 `fastnumbers <https://pypi.org/project/fastnumbers>`_ package
409 (version >=2.0.0); it helps with the string to number conversions.
410 ``natsort`` will still run (efficiently) without the package, but if you need
411 to squeeze out that extra juice it is recommended you include this as a
412 dependency. ``natsort`` will not require (or check) that
413 `fastnumbers <https://pypi.org/project/fastnumbers>`_ is installed
414 at installation.
415
416 PyICU
417 +++++
418
419 It is recommended that you install `PyICU <https://pypi.org/project/PyICU>`_
420 if you wish to sort in a locale-dependent manner, see
421 https://natsort.readthedocs.io/en/master/locale_issues.html for an explanation why.
422
423 Installation
424 ------------
425
426 Use ``pip``!
427
428 .. code-block:: console
429
430 $ pip install natsort
431
432 If you want to install the `Optional Dependencies`_, you can use the
433 `"extras" notation <https://packaging.python.org/tutorials/installing-packages/#installing-setuptools-extras>`_
434 at installation time to install those dependencies as well - use ``fast`` for
435 `fastnumbers <https://pypi.org/project/fastnumbers>`_ and ``icu`` for
436 `PyICU <https://pypi.org/project/PyICU>`_.
437
438 .. code-block:: console
439
440 # Install both optional dependencies.
441 $ pip install natsort[fast,icu]
442 # Install just fastnumbers
443 $ pip install natsort[fast]
444
445 How to Run Tests
446 ----------------
447
448 Please note that ``natsort`` is NOT set-up to support ``python setup.py test``.
449
450 The recommended way to run tests is with `tox <https://tox.readthedocs.io/en/latest/>`_.
451 After installing ``tox``, running tests is as simple as executing the following
452 in the ``natsort`` directory:
453
454 .. code-block:: console
455
456 $ tox
457
458 ``tox`` will create virtual a virtual environment for your tests and install
459 all the needed testing requirements for you. You can specify a particular
460 python version with the ``-e`` flag, e.g. ``tox -e py36``. Static analysis
461 is done with ``tox -e flake8``. You can see all available testing environments
462 with ``tox --listenvs``.
463
464 How to Build Documentation
465 --------------------------
466
467 If you want to build the documentation for ``natsort``, it is recommended to
468 use ``tox``:
469
470 .. code-block:: console
471
472 $ tox -e docs
473
474 This will place the documentation in ``build/sphinx/html``.
475
476 Dropped Deprecated APIs
477 -----------------------
478
479 In ``natsort`` version 6.0.0, the following APIs and functions were removed
480
481 - ``number_type`` keyword argument (deprecated since 3.4.0)
482 - ``signed`` keyword argument (deprecated since 3.4.0)
483 - ``exp`` keyword argument (deprecated since 3.4.0)
484 - ``as_path`` keyword argument (deprecated since 3.4.0)
485 - ``py3_safe`` keyword argument (deprecated since 3.4.0)
486 - ``ns.TYPESAFE`` (deprecated since version 5.0.0)
487 - ``ns.DIGIT`` (deprecated since version 5.0.0)
488 - ``ns.VERSION`` (deprecated since version 5.0.0)
489 - ``versorted()`` (discouraged since version 4.0.0,
490 officially deprecated since version 5.5.0)
491 - ``index_versorted()`` (discouraged since version 4.0.0,
492 officially deprecated since version 5.5.0)
493
494 In general, if you want to determine if you are using deprecated APIs you
495 can run your code with the following flag
496
497 .. code-block:: console
498
499 $ python -Wdefault::DeprecationWarning my-code.py
500
501 By default ``DeprecationWarnings`` are not shown, but this will cause them
502 to be shown. Alternatively, you can just set the environment variable
503 ``PYTHONWARNINGS`` to "default::DeprecationWarning" and then run your code.
504
505 Author
506 ------
507
508 Seth M. Morton
509
510 History
511 -------
512
513 Please visit the changelog
514 `on GitHub <https://github.com/SethMMorton/natsort/blob/master/CHANGELOG.md>`_ or
515 `in the documentation <https://natsort.readthedocs.io/en/master/changelog.html>`_.
3333 - `Installation`_
3434 - `How to Run Tests`_
3535 - `How to Build Documentation`_
36 - `Deprecation Schedule`_
36 - `Dropped Deprecated APIs`_
3737 - `History`_
3838
39 **NOTE**: Please see the `Deprecation Schedule`_ section for changes in
40 ``natsort`` version 7.0.0.
39 **NOTE**: Please see the `Dropped Deprecated APIs`_ section for changes.
4140
4241 Quick Description
4342 -----------------
102101 - `Locale-Aware Sorting (or "Human Sorting")`_
103102 - `Further Customizing Natsort`_
104103 - `Sorting Mixed Types`_
105 - `Handling Bytes on Python 3`_
104 - `Handling Bytes`_
106105 - `Generating a Reusable Sorting Key and Sorting In-Place`_
107106 - `Other Useful Things`_
108107
240239 >>> a = ['4.5', 6, 2.0, '5', 'a']
241240 >>> natsorted(a)
242241 [2.0, '4.5', '5', 6, 'a']
243 >>> # On Python 2, sorted(a) would return [2.0, 6, '4.5', '5', 'a']
244 >>> # On Python 3, sorted(a) would raise an "unorderable types" TypeError
245
246 Handling Bytes on Python 3
247 ++++++++++++++++++++++++++
248
249 ``natsort`` does not officially support the `bytes` type on Python 3, but
242 >>> # sorted(a) would raise an "unorderable types" TypeError
243
244 Handling Bytes
245 ++++++++++++++
246
247 ``natsort`` does not officially support the `bytes` type, but
250248 convenience functions are provided that help you decode to `str` first:
251249
252250 .. code-block:: pycon
253251
254252 >>> from natsort import as_utf8
255253 >>> a = [b'a', 14.0, 'b']
256 >>> # On Python 2, natsorted(a) would would work as expected.
257 >>> # On Python 3, natsorted(a) would raise a TypeError (bytes() < str())
254 >>> # natsorted(a) would raise a TypeError (bytes() < str())
258255 >>> natsorted(a, key=as_utf8) == [14.0, b'a', 'b']
259256 True
260257 >>> a = [b'a56', b'a5', b'a6', b'a40']
261 >>> # On Python 2, natsorted(a) would would work as expected.
262 >>> # On Python 3, natsorted(a) would return the same results as sorted(a)
258 >>> # natsorted(a) would return the same results as sorted(a)
263259 >>> natsorted(a, key=as_utf8) == [b'a5', b'a6', b'a40', b'a56']
264260 True
265261
368364 Requirements
369365 ------------
370366
371 ``natsort`` requires Python 3.6 or greater.
367 ``natsort`` requires Python 3.7 or greater.
372368
373369 Optional Dependencies
374370 ---------------------
445441
446442 This will place the documentation in ``build/sphinx/html``.
447443
448 Deprecation Schedule
449 --------------------
450
451 Dropped Python 3.4 and Python 3.5 Support
452 +++++++++++++++++++++++++++++++++++++++++
453
454 ``natsort`` version 8.0.0 dropped support for Python < 3.6.
455
456 Dropped Python 2.7 Support
457 ++++++++++++++++++++++++++
458
459 ``natsort`` version 7.0.0 dropped support for Python 2.7.
460
461 The version 6.X branch will remain as a "long term support" branch where bug
462 fixes are applied so that users who cannot update from Python 2.7 will not be
463 forced to use a buggy ``natsort`` version (bug fixes will need to be requested;
464 by default only the 7.X branch will be updated).
465 New features would not be added to version 6.X, only bug fixes.
466
467444 Dropped Deprecated APIs
468 +++++++++++++++++++++++
445 -----------------------
469446
470447 In ``natsort`` version 6.0.0, the following APIs and functions were removed
471448
0 natsort (8.2.0+git20221218.1.837a387-1) UNRELEASED; urgency=low
1
2 * New upstream snapshot.
3
4 -- Debian Janitor <janitor@jelmer.uk> Mon, 19 Dec 2022 06:11:09 -0000
5
06 natsort (8.0.2-2) unstable; urgency=medium
17
28 [ Debian Janitor ]
8282
8383 .. _bytes_help:
8484
85 Help With Bytes On Python 3
86 +++++++++++++++++++++++++++
85 Help With Bytes
86 +++++++++++++++
8787
8888 The official stance of :mod:`natsort` is to not support `bytes` for
8989 sorting; there is just too much that can go wrong when trying to automate
120120 Help With Type Hinting
121121 ++++++++++++++++++++++
122122
123 If you need to explictly specify the types that natsort accepts or returns
123 If you need to explicitly specify the types that natsort accepts or returns
124124 in your code, the following types have been exposed for your convenience.
125125
126126 +--------------------------------+----------------------------------------------------------------------------------------+
5858 # built documents.
5959 #
6060 # The full version, including alpha/beta/rc tags.
61 release = "8.0.2"
61 release = "8.2.0"
6262 # The short X.Y version.
6363 version = ".".join(release.split(".")[0:2])
6464
157157 >>> natsorted(a, alg=ns.IGNORECASE)
158158 ['Apple', 'apple', 'Banana', 'banana', 'corn', 'Corn']
159159
160 Note thats since Python's sorting is stable, the order of equivalent
160 Note that's since Python's sorting is stable, the order of equivalent
161161 elements after lowering the case is the same order they appear in the
162162 original list.
163163
348348 >>> natsorted(a, reverse=True)
349349 ['a10', 'a9', 'a4', 'a2', 'a1']
350350
351 Sorting Bytes on Python 3
352 -------------------------
353
354 Python 3 is rather strict about comparing strings and bytes, and this
351 Sorting Bytes
352 -------------
353
354 Python is rather strict about comparing strings and bytes, and this
355355 can make it difficult to deal with collections of both. Because of the
356356 challenge of guessing which encoding should be used to decode a bytes
357357 array to a string, :mod:`natsort` does *not* try to guess and automatically
367367
368368 >>> from natsort import as_ascii
369369 >>> a = [b'a', 14.0, 'b']
370 >>> # On Python 2, natsorted(a) would would work as expected.
371 >>> # On Python 3, natsorted(a) would raise a TypeError (bytes() < str())
370 >>> # natsorted(a) would raise a TypeError (bytes() < str())
372371 >>> natsorted(a, key=as_ascii) == [14.0, b'a', 'b']
373372 True
374373
412412 >>> a > b
413413 True
414414
415 Comparing Different Types on Python 3
416 +++++++++++++++++++++++++++++++++++++
415 .. note::
416
417 The actual :meth:`decompose_path_into_components`-equivalent function in
418 :mod:`natsort` actually has a few more heuristics than shown here so that
419 it is not over-zealous in what it defines as a path suffix, but this has
420 been omitted in this how-to for clarity.
421
422 Comparing Different Types
423 +++++++++++++++++++++++++
417424
418425 `The second major special case I encountered was sorting of different types`_.
419 If you are on Python 2 (i.e. legacy Python), this mostly doesn't matter *too*
426 On Python 2 (i.e. legacy Python), this mostly didnt't matter *too*
420427 much since it uses an arbitrary heuristic to allow traditionally un-comparable
421428 types to be compared (such as comparing ``'a'`` to ``1``). However, on Python 3
422429 (i.e. Python) it simply won't let you perform such nonsense, raising a
661668 #. :mod:`locale` is a thin wrapper over your operating system's *locale*
662669 library, so if *that* is broken (like it is on BSD and OSX) then
663670 :mod:`locale` is broken in Python.
664 #. Because of a bug in legacy Python (i.e. Python 2), there is no uniform
671 #. Because of a bug in legacy Python (i.e. Python 2), there was no uniform
665672 way to use the :mod:`locale` sorting functionality between legacy Python
666 and Python 3.
673 and Python (luckily this is no longer an issue now that Python 2 is EOL).
667674 #. People have differing opinions of how capitalization should affect word
668675 order.
669676 #. There is no built-in way to handle locale-dependent thousands separators
714721 >>> sorted(a, key=lambda x: x.swapcase())
715722 ['apple', 'banana', 'corn', 'Apple', 'Banana', 'Corn']
716723
717 The last (i call it *IGNORECASE*) should be super easy, right?
718 Simply call :meth:`str.lowercase` on the input. This will work but may
719 not always give the correct answer on non-latin character sets. It's
720 a good thing that in Python 3.3
721 :meth:`str.casefold` was introduced, which does a better job of removing
722 all case information from unicode characters in
723 non-latin alphabets.
724
725 .. code-block:: pycon
726
727 >>> def remove_case(x):
728 ... try:
729 ... return x.casefold()
730 ... except AttributeError: # Legacy Python backwards compatibility
731 ... return x.lowercase()
732 ...
733 >>> sorted(a, key=remove_case)
724 The last (i call it *IGNORECASE*) is pretty easy.
725 Simply call :meth:`str.casefold` on the input (it's like :meth:`std.lowercase`
726 but does a better job on non-latin character sets).
727
728 .. code-block:: pycon
729
730 >>> sorted(a, key=lambda x: x.casefold())
734731 ['Apple', 'apple', 'Banana', 'banana', 'corn', 'Corn']
735732
736733 The middle case (I call it *GROUPLETTERS*) is less straightforward.
741738
742739 >>> import itertools
743740 >>> def groupletters(x):
744 ... return ''.join(itertools.chain.from_iterable((remove_case(y), y) for y in x))
741 ... return ''.join(itertools.chain.from_iterable((y.casefold(), y) for y in x))
745742 ...
746743 >>> groupletters('Apple')
747744 'aAppppllee'
903900 be applied as part of the :func:`coerce_to_int`/:func:`coerce_to_float`
904901 functions in a manner similar to :func:`groupletters`.
905902
906 As you might have guessed, there is a small problem.
907 It turns out the there is a bug in the legacy Python implementation of
908 :func:`locale.strxfrm` that causes it to outright fail for :func:`unicode`
909 input (https://bugs.python.org/issue2481). :func:`locale.strcoll` works,
910 but is intended for use with ``cmp``, which does not exist in current Python
911 implementations. Luckily, the :func:`functools.cmp_to_key` function
912 makes :func:`locale.strcoll` behave like :func:`locale.strxfrm`.
903 Unicode Support With Local
904 ++++++++++++++++++++++++++
905
906 Remember how in the `Basic Unicode Support`_ section I mentioned that we
907 use the "decompressed" Unicode normalization form (e.g. NFD) on all inputs
908 to ensure the order is as expected?
909
910 If you have been following along so far, you probably expect that it is not
911 that easy. You would be correct.
912
913 It turns out that some locales (but not all) expect the input to be in
914 "compressed form" (e.g. NFC) or the ordering is not as you might expect.
915 `Check out this issue for a real-world example`_. Here's a relevant
916 snippet of code
917
918 .. code-block:: pycon
919
920 In [1]: import locale, unicodedata
921
922 In [2]: a = ['Aš', 'Cheb', 'Česko', 'Cibulov', 'Znojmo', 'Žilina']
923
924 In [3]: locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
925 Out[3]: 'en_US.UTF-8'
926
927 In [4]: sorted(a, key=locale.strxfrm)
928 Out[4]: ['Aš', 'Česko', 'Cheb', 'Cibulov', 'Žilina', 'Znojmo']
929
930 In [5]: sorted(a, key=lambda x: locale.strxfrm(unicodedata.normalize("NFD", x)))
931 Out[5]: ['Aš', 'Česko', 'Cheb', 'Cibulov', 'Žilina', 'Znojmo']
932
933 In [6]: sorted(a, key=lambda x: locale.strxfrm(unicodedata.normalize("NFC", x)))
934 Out[6]: ['Aš', 'Česko', 'Cheb', 'Cibulov', 'Žilina', 'Znojmo']
935
936 In [7]: locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8')
937 Out[7]: 'de_DE.UTF-8'
938
939 In [8]: sorted(a, key=locale.strxfrm)
940 Out[8]: ['Aš', 'Česko', 'Cheb', 'Cibulov', 'Žilina', 'Znojmo']
941
942 In [9]: sorted(a, key=lambda x: locale.strxfrm(unicodedata.normalize("NFD", x)))
943 Out[9]: ['Aš', 'Česko', 'Cheb', 'Cibulov', 'Žilina', 'Znojmo']
944
945 In [10]: sorted(a, key=lambda x: locale.strxfrm(unicodedata.normalize("NFC", x)))
946 Out[10]: ['Aš', 'Česko', 'Cheb', 'Cibulov', 'Žilina', 'Znojmo']
947
948 In [11]: locale.setlocale(locale.LC_ALL, 'cs_CZ.UTF-8')
949 Out[11]: 'cs_CZ.UTF-8'
950
951 In [12]: sorted(a, key=locale.strxfrm)
952 Out[12]: ['Aš', 'Cibulov', 'Česko', 'Cheb', 'Znojmo', 'Žilina']
953
954 In [13]: sorted(a, key=lambda x: locale.strxfrm(unicodedata.normalize("NFD", x)))
955 Out[13]: ['Aš', 'Česko', 'Cibulov', 'Cheb', 'Žilina', 'Znojmo']
956
957 In [14]: sorted(a, key=lambda x: locale.strxfrm(unicodedata.normalize("NFC", x)))
958 Out[14]: ['Aš', 'Cibulov', 'Česko', 'Cheb', 'Znojmo', 'Žilina']
959
960 Two out of three locales sort the same data in the same order no matter how the unicode
961 input was normalized, but Czech seems to care how the input is formatted!
962
963 So, everthing mentioned in `Basic Unicode Support`_ is conditional on whether
964 or not the user wants to use the :mod:`locale` library or not. If not, then
965 "NFD" normalization is used. If they do, "NFC" normalization is used.
913966
914967 Handling Broken Locale On OSX
915968 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11201173 .. _really good: https://hypothesis.readthedocs.io/en/latest/
11211174 .. _testing strategy: https://docs.pytest.org/en/latest/
11221175 .. _check out some official Unicode documentation: https://unicode.org/reports/tr15/
1176 .. _Check out this issue for a real-world example: https://github.com/SethMMorton/natsort/issues/140
2222
2323 When :func:`~natsort.natsort_keygen` is called it returns a key function that
2424 hard-codes the provided settings. This means that the key returned when
25 ``ns.LOCALE`` is used contains the settings specifed by the locale
25 ``ns.LOCALE`` is used contains the settings specified by the locale
2626 *loaded at the time the key is generated*. If you change the locale,
2727 you should regenerate the key to account for the new locale.
2828
0 from typing import overload
1
2 @overload
3 def Locale() -> str: ...
4 @overload
5 def Locale(x: str) -> str: ...
6
7 class UCollAttribute:
8 NUMERIC_COLLATION: int
9
10 class UCollAttributeValue:
11 ON: int
12
13 class DecimalFormatSymbols:
14 kGroupingSeparatorSymbol: int
15 kDecimalSeparatorSymbol: int
16 def __init__(self, locale: str) -> None: ...
17 def getSymbol(self, symbol: int) -> str: ...
18
19 class Collator:
20 @classmethod
21 def createInstance(cls, locale: str) -> Collator: ...
22 def getSortKey(self, source: str) -> bytes: ...
23 def setAttribute(self, attr: int, value: int) -> None: ...
2222 from natsort.ns_enum import NSType, ns
2323 from natsort.utils import KeyType, NatsortInType, NatsortOutType, chain_functions
2424
25 __version__ = "8.0.2"
25 __version__ = "8.2.0"
2626
2727 __all__ = [
2828 "natsort_key",
3737
3838 # If using icu, get the locale from the current global locale,
3939 def get_icu_locale() -> str:
40 try:
41 return cast(str, icu.Locale(".".join(getlocale())))
42 except TypeError: # pragma: no cover
43 return cast(str, icu.Locale())
40 language_code, encoding = getlocale()
41 if language_code is None or encoding is None: # pragma: no cover
42 return icu.Locale()
43 return icu.Locale(f"{language_code}.{encoding}")
4444
4545 def get_strxfrm() -> TrxfmFunc:
46 return cast(TrxfmFunc, icu.Collator.createInstance(get_icu_locale()).getSortKey)
46 return icu.Collator.createInstance(get_icu_locale()).getSortKey
4747
4848 def get_thousands_sep() -> str:
4949 sep = icu.DecimalFormatSymbols.kGroupingSeparatorSymbol
50 return cast(str, icu.DecimalFormatSymbols(get_icu_locale()).getSymbol(sep))
50 return icu.DecimalFormatSymbols(get_icu_locale()).getSymbol(sep)
5151
5252 def get_decimal_point() -> str:
5353 sep = icu.DecimalFormatSymbols.kDecimalSeparatorSymbol
54 return cast(str, icu.DecimalFormatSymbols(get_icu_locale()).getSymbol(sep))
54 return icu.DecimalFormatSymbols(get_icu_locale()).getSymbol(sep)
5555
5656 except ImportError:
5757 import locale
7474 # characters are incorrectly blank. Here is a lookup table of the
7575 # corrections I am aware of.
7676 if dumb_sort():
77 try:
78 loc = ".".join(locale.getlocale())
79 except TypeError: # No locale loaded, default to ','
77 language_code, encoding = locale.getlocale()
78 if language_code is None or encoding is None:
79 # No locale loaded, default to ','
8080 return ","
81 loc = f"{language_code}.{encoding}"
8182 return {
8283 "de_DE.ISO8859-15": ".",
8384 "es_ES.ISO8859-1": ".",
1717 Optional,
1818 Sequence,
1919 Tuple,
20 Union,
20 TypeVar,
2121 cast,
22 overload,
2322 )
2423
2524 import natsort.compat.locale
2625 from natsort import utils
2726 from natsort.ns_enum import NSType, NS_DUMB, ns
28 from natsort.utils import (
29 KeyType,
30 MaybeKeyType,
31 NatsortInType,
32 NatsortOutType,
33 StrBytesNum,
34 StrBytesPathNum,
35 )
27 from natsort.utils import NatsortInType, NatsortOutType
3628
3729 # Common input and output types
38 Iter_ns = Iterable[NatsortInType]
39 Iter_any = Iterable[Any]
40 List_ns = List[NatsortInType]
41 List_any = List[Any]
42 List_int = List[int]
30 T = TypeVar("T")
31 NatsortInTypeT = TypeVar("NatsortInTypeT", bound=NatsortInType)
4332
4433 # The type that natsort_key returns
4534 NatsortKeyType = Callable[[NatsortInType], NatsortOutType]
4635
4736 # Types for os_sorted
48 OSSortInType = Iterable[Optional[StrBytesPathNum]]
49 OSSortOutType = Tuple[Union[StrBytesNum, Tuple[StrBytesNum, ...]], ...]
50 OSSortKeyType = Callable[[Optional[StrBytesPathNum]], OSSortOutType]
51 Iter_path = Iterable[Optional[StrBytesPathNum]]
52 List_path = List[StrBytesPathNum]
53
54
55 def decoder(encoding: str) -> Callable[[NatsortInType], NatsortInType]:
37 OSSortKeyType = Callable[[NatsortInType], NatsortOutType]
38
39
40 def decoder(encoding: str) -> Callable[[Any], Any]:
5641 """
5742 Return a function that can be used to decode bytes to unicode.
5843
9378 return partial(utils.do_decoding, encoding=encoding)
9479
9580
96 def as_ascii(s: NatsortInType) -> NatsortInType:
81 def as_ascii(s: Any) -> Any:
9782 """
9883 Function to decode an input with the ASCII codec, or return as-is.
9984
116101 return utils.do_decoding(s, "ascii")
117102
118103
119 def as_utf8(s: NatsortInType) -> NatsortInType:
104 def as_utf8(s: Any) -> Any:
120105 """
121106 Function to decode an input with the UTF-8 codec, or return as-is.
122107
140125
141126
142127 def natsort_keygen(
143 key: MaybeKeyType = None, alg: NSType = ns.DEFAULT
144 ) -> NatsortKeyType:
128 key: Optional[Callable[[Any], NatsortInType]] = None, alg: NSType = ns.DEFAULT
129 ) -> Callable[[Any], NatsortOutType]:
145130 """
146131 Generate a key to sort strings and numbers naturally.
147132
251236 """
252237
253238
254 @overload
255239 def natsorted(
256 seq: Iter_ns, key: None = None, reverse: bool = False, alg: NSType = ns.DEFAULT
257 ) -> List_ns:
258 ...
259
260
261 @overload
262 def natsorted(
263 seq: Iter_any, key: KeyType, reverse: bool = False, alg: NSType = ns.DEFAULT
264 ) -> List_any:
265 ...
266
267
268 def natsorted(
269 seq: Iter_any,
270 key: MaybeKeyType = None,
240 seq: Iterable[T],
241 key: Optional[Callable[[T], NatsortInType]] = None,
271242 reverse: bool = False,
272243 alg: NSType = ns.DEFAULT,
273 ) -> List_any:
244 ) -> List[T]:
274245 """
275246 Sorts an iterable naturally.
276247
318289 return sorted(seq, reverse=reverse, key=natsort_keygen(key, alg))
319290
320291
321 @overload
322292 def humansorted(
323 seq: Iter_ns, key: None = None, reverse: bool = False, alg: NSType = ns.DEFAULT
324 ) -> List_ns:
325 ...
326
327
328 @overload
329 def humansorted(
330 seq: Iter_any, key: KeyType, reverse: bool = False, alg: NSType = ns.DEFAULT
331 ) -> List_any:
332 ...
333
334
335 def humansorted(
336 seq: Iter_any,
337 key: MaybeKeyType = None,
293 seq: Iterable[T],
294 key: Optional[Callable[[T], NatsortInType]] = None,
338295 reverse: bool = False,
339296 alg: NSType = ns.DEFAULT,
340 ) -> List_any:
297 ) -> List[T]:
341298 """
342299 Convenience function to properly sort non-numeric characters.
343300
389346 return natsorted(seq, key, reverse, alg | ns.LOCALE)
390347
391348
392 @overload
393349 def realsorted(
394 seq: Iter_ns, key: None = None, reverse: bool = False, alg: NSType = ns.DEFAULT
395 ) -> List_ns:
396 ...
397
398
399 @overload
400 def realsorted(
401 seq: Iter_any, key: KeyType, reverse: bool = False, alg: NSType = ns.DEFAULT
402 ) -> List_any:
403 ...
404
405
406 def realsorted(
407 seq: Iter_any,
408 key: MaybeKeyType = None,
350 seq: Iterable[T],
351 key: Optional[Callable[[T], NatsortInType]] = None,
409352 reverse: bool = False,
410353 alg: NSType = ns.DEFAULT,
411 ) -> List_any:
354 ) -> List[T]:
412355 """
413356 Convenience function to properly sort signed floats.
414357
461404 return natsorted(seq, key, reverse, alg | ns.REAL)
462405
463406
464 @overload
465407 def index_natsorted(
466 seq: Iter_ns, key: None = None, reverse: bool = False, alg: NSType = ns.DEFAULT
467 ) -> List_int:
468 ...
469
470
471 @overload
472 def index_natsorted(
473 seq: Iter_any, key: KeyType, reverse: bool = False, alg: NSType = ns.DEFAULT
474 ) -> List_int:
475 ...
476
477
478 def index_natsorted(
479 seq: Iter_any,
480 key: MaybeKeyType = None,
408 seq: Iterable[T],
409 key: Optional[Callable[[T], NatsortInType]] = None,
481410 reverse: bool = False,
482411 alg: NSType = ns.DEFAULT,
483 ) -> List_int:
412 ) -> List[int]:
484413 """
485414 Determine the list of the indexes used to sort the input sequence.
486415
536465 ['baz', 'foo', 'bar']
537466
538467 """
539 newkey: KeyType
468 newkey: Callable[[Tuple[int, T]], NatsortInType]
540469 if key is None:
541470 newkey = itemgetter(1)
542471 else:
543472
544 def newkey(x: Any) -> NatsortInType:
545 return cast(KeyType, key)(itemgetter(1)(x))
473 def newkey(x: Tuple[int, T]) -> NatsortInType:
474 return cast(Callable[[T], NatsortInType], key)(itemgetter(1)(x))
546475
547476 # Pair the index and sequence together, then sort by element
548477 index_seq_pair = [(x, y) for x, y in enumerate(seq)]
550479 return [x for x, _ in index_seq_pair]
551480
552481
553 @overload
554482 def index_humansorted(
555 seq: Iter_ns, key: None = None, reverse: bool = False, alg: NSType = ns.DEFAULT
556 ) -> List_int:
557 ...
558
559
560 @overload
561 def index_humansorted(
562 seq: Iter_any, key: KeyType, reverse: bool = False, alg: NSType = ns.DEFAULT
563 ) -> List_int:
564 ...
565
566
567 def index_humansorted(
568 seq: Iter_any,
569 key: MaybeKeyType = None,
483 seq: Iterable[T],
484 key: Optional[Callable[[T], NatsortInType]] = None,
570485 reverse: bool = False,
571486 alg: NSType = ns.DEFAULT,
572 ) -> List_int:
487 ) -> List[int]:
573488 """
574489 This is a wrapper around ``index_natsorted(seq, alg=ns.LOCALE)``.
575490
618533 return index_natsorted(seq, key, reverse, alg | ns.LOCALE)
619534
620535
621 @overload
622536 def index_realsorted(
623 seq: Iter_ns, key: None = None, reverse: bool = False, alg: NSType = ns.DEFAULT
624 ) -> List_int:
625 ...
626
627
628 @overload
629 def index_realsorted(
630 seq: Iter_any, key: KeyType, reverse: bool = False, alg: NSType = ns.DEFAULT
631 ) -> List_int:
632 ...
633
634
635 def index_realsorted(
636 seq: Iter_any,
637 key: MaybeKeyType = None,
537 seq: Iterable[T],
538 key: Optional[Callable[[T], NatsortInType]] = None,
638539 reverse: bool = False,
639540 alg: NSType = ns.DEFAULT,
640 ) -> List_int:
541 ) -> List[int]:
641542 """
642543 This is a wrapper around ``index_natsorted(seq, alg=ns.REAL)``.
643544
682583 return index_natsorted(seq, key, reverse, alg | ns.REAL)
683584
684585
685 # noinspection PyShadowingBuiltins,PyUnresolvedReferences
686586 def order_by_index(
687587 seq: Sequence[Any], index: Iterable[int], iter: bool = False
688 ) -> Iter_any:
588 ) -> Iterable[Any]:
689589 """
690590 Order a given sequence by an index sequence.
691591
763663 return utils.regex_chooser(alg).pattern[1:-1]
764664
765665
766 def _split_apply(v: Any, key: MaybeKeyType = None) -> Iterator[str]:
666 def _split_apply(
667 v: Any, key: Optional[Callable[[T], NatsortInType]] = None
668 ) -> Iterator[str]:
767669 if key is not None:
768670 v = key(v)
769671 return utils.path_splitter(str(v))
780682 _windows_sort_cmp.restype = wintypes.INT
781683 _winsort_key = cmp_to_key(_windows_sort_cmp)
782684
783 def os_sort_keygen(key: MaybeKeyType = None) -> OSSortKeyType:
685 def os_sort_keygen(
686 key: Optional[Callable[[Any], NatsortInType]] = None
687 ) -> Callable[[Any], NatsortOutType]:
784688 return cast(
785 OSSortKeyType, lambda x: tuple(map(_winsort_key, _split_apply(x, key)))
689 Callable[[Any], NatsortOutType],
690 lambda x: tuple(map(_winsort_key, _split_apply(x, key))),
786691 )
787692
788693 else:
801706
802707 except ImportError:
803708 # No ICU installed
804 def os_sort_keygen(key: MaybeKeyType = None) -> OSSortKeyType:
805 return cast(
806 OSSortKeyType,
807 natsort_keygen(key=key, alg=ns.LOCALE | ns.PATH | ns.IGNORECASE),
808 )
709 def os_sort_keygen(
710 key: Optional[Callable[[Any], NatsortInType]] = None
711 ) -> Callable[[Any], NatsortOutType]:
712 return natsort_keygen(key=key, alg=ns.LOCALE | ns.PATH | ns.IGNORECASE)
809713
810714 else:
811715 # ICU installed
812 def os_sort_keygen(key: MaybeKeyType = None) -> OSSortKeyType:
716 def os_sort_keygen(
717 key: Optional[Callable[[Any], NatsortInType]] = None
718 ) -> Callable[[Any], NatsortOutType]:
813719 loc = natsort.compat.locale.get_icu_locale()
814720 collator = icu.Collator.createInstance(loc)
815721 collator.setAttribute(
856762 """
857763
858764
859 @overload
860 def os_sorted(seq: Iter_path, key: None = None, reverse: bool = False) -> List_path:
861 ...
862
863
864 @overload
865 def os_sorted(seq: Iter_any, key: KeyType, reverse: bool = False) -> List_any:
866 ...
867
868
869765 def os_sorted(
870 seq: Iter_any, key: MaybeKeyType = None, reverse: bool = False
871 ) -> List_any:
766 seq: Iterable[T],
767 key: Optional[Callable[[T], NatsortInType]] = None,
768 reverse: bool = False,
769 ) -> List[T]:
872770 """
873771 Sort elements in the same order as your operating system's file browser
874772
875773 .. warning::
876774
877775 The resulting function will generate results that will be
878 differnt depending on your platform. This is intentional.
776 different depending on your platform. This is intentional.
879777
880778 On Windows, this will sort with the same order as Windows Explorer.
881779
891789 special characters this will give correct results, but once
892790 special characters are added you should lower your expectations.
893791
894 It is *strongly* reccommended to have :mod:`pyicu` installed on
792 It is *strongly* recommended to have :mod:`pyicu` installed on
895793 MacOS/Linux if you want correct sort results.
896794
897795 It does *not* take into account if a path is a directory or a file
2323 # The digit characters are a subset of the numerals.
2424 digit_chars = [a for a in numeric_chars if unicodedata.digit(a, None) is not None]
2525
26 # The decimal characters are a subset of the numberals
26 # The decimal characters are a subset of the numerals
2727 # (probably of the digits, but let's be safe).
2828 decimal_chars = [a for a in numeric_chars if unicodedata.decimal(a, None) is not None]
2929
5252 Match,
5353 Optional,
5454 Pattern,
55 TYPE_CHECKING,
5556 Tuple,
5657 Union,
5758 cast,
6970 from natsort.ns_enum import NSType, NS_DUMB, ns
7071 from natsort.unicode_numbers import digits_no_decimals, numeric_no_decimals
7172
73 if TYPE_CHECKING:
74 from typing_extensions import Protocol
75 else:
76 Protocol = object
77
7278 #
7379 # Pre-define a slew of aggregate types which makes the type hinting below easier
7480 #
81
82
83 class SupportsDunderLT(Protocol):
84 def __lt__(self, __other: Any) -> bool:
85 ...
86
87
88 class SupportsDunderGT(Protocol):
89 def __gt__(self, __other: Any) -> bool:
90 ...
91
92
93 Sortable = Union[SupportsDunderLT, SupportsDunderGT]
94
7595 StrToStr = Callable[[str], str]
7696 AnyCall = Callable[[Any], Any]
7797
82102 BytesTransformer = Callable[[bytes], BytesTransform]
83103
84104 # For the number transform factory
85 NumType = Union[float, int]
86 MaybeNumType = Optional[NumType]
87 NumTuple = Tuple[StrOrBytes, NumType]
88 NestedNumTuple = Tuple[NumTuple]
89 StrNumTuple = Tuple[Tuple[str], NumTuple]
90 NestedStrNumTuple = Tuple[StrNumTuple]
91 MaybeNumTransform = Union[NumTuple, NestedNumTuple, StrNumTuple, NestedStrNumTuple]
92 MaybeNumTransformer = Callable[[MaybeNumType], MaybeNumTransform]
105 BasicTuple = Tuple[Any, ...]
106 NestedAnyTuple = Tuple[BasicTuple, ...]
107 AnyTuple = Union[BasicTuple, NestedAnyTuple]
108 NumTransform = AnyTuple
109 NumTransformer = Callable[[Any], NumTransform]
93110
94111 # For the string component transform factory
95112 StrBytesNum = Union[str, bytes, float, int]
96113 StrTransformer = Callable[[str], StrBytesNum]
97114
98115 # For the final data transform factory
99 TwoBlankTuple = Tuple[Tuple[()], Tuple[()]]
100 TupleOfAny = Tuple[Any, ...]
101 TupleOfStrAnyPair = Tuple[Tuple[str], TupleOfAny]
102 FinalTransform = Union[TwoBlankTuple, TupleOfAny, TupleOfStrAnyPair]
116 FinalTransform = AnyTuple
103117 FinalTransformer = Callable[[Iterable[Any], str], FinalTransform]
118
119 PathArg = Union[str, PurePath]
120 MatchFn = Callable[[str], Optional[Match]]
104121
105122 # For the string parsing factory
106123 StrSplitter = Callable[[str], Iterable[str]]
107 StrParser = Callable[[str], FinalTransform]
108
109 # For the path splitter
110 PathArg = Union[str, PurePath]
111 MatchFn = Callable[[str], Optional[Match]]
124 StrParser = Callable[[PathArg], FinalTransform]
112125
113126 # For the path parsing factory
114127 PathSplitter = Callable[[PathArg], Tuple[FinalTransform, ...]]
115128
116129 # For the natsort key
117 StrBytesPathNum = Union[str, bytes, float, int, PurePath]
118 NatsortInType = Union[
119 Optional[StrBytesPathNum], Iterable[Union[Optional[StrBytesPathNum], Iterable[Any]]]
120 ]
121 NatsortOutType = Tuple[
122 Union[StrBytesNum, Tuple[Union[StrBytesNum, Tuple[Any, ...]], ...]], ...
123 ]
130 NatsortInType = Optional[Sortable]
131 NatsortOutType = Tuple[Sortable, ...]
124132 KeyType = Callable[[Any], NatsortInType]
125133 MaybeKeyType = Optional[KeyType]
126134
259267 key: None,
260268 string_func: Union[StrParser, PathSplitter],
261269 bytes_func: BytesTransformer,
262 num_func: MaybeNumTransformer,
270 num_func: NumTransformer,
263271 ) -> NatsortOutType:
264272 ...
265273
270278 key: KeyType,
271279 string_func: Union[StrParser, PathSplitter],
272280 bytes_func: BytesTransformer,
273 num_func: MaybeNumTransformer,
281 num_func: NumTransformer,
274282 ) -> NatsortOutType:
275283 ...
276284
280288 key: MaybeKeyType,
281289 string_func: Union[StrParser, PathSplitter],
282290 bytes_func: BytesTransformer,
283 num_func: MaybeNumTransformer,
291 num_func: NumTransformer,
284292 ) -> NatsortOutType:
285293 """
286294 Key to sort strings and numbers naturally.
347355
348356 # If that failed, it must be a number.
349357 except TypeError:
350 return num_func(cast(NumType, val))
358 return num_func(val)
351359
352360
353361 def parse_bytes_factory(alg: NSType) -> BytesTransformer:
385393
386394 def parse_number_or_none_factory(
387395 alg: NSType, sep: StrOrBytes, pre_sep: str
388 ) -> MaybeNumTransformer:
396 ) -> NumTransformer:
389397 """
390398 Create a function that will format a number (or None) into a tuple.
391399
417425 nan_replace = float("+inf") if alg & ns.NANLAST else float("-inf")
418426
419427 def func(
420 val: MaybeNumType, _nan_replace: float = nan_replace, _sep: StrOrBytes = sep
421 ) -> NumTuple:
428 val: Any, _nan_replace: float = nan_replace, _sep: StrOrBytes = sep
429 ) -> BasicTuple:
422430 """Given a number, place it in a tuple with a leading null string."""
423431 return _sep, (_nan_replace if val != val or val is None else val)
424432
492500 normalize_input = _normalize_input_factory(alg)
493501 compose_input = _compose_input_factory(alg) if alg & ns.LOCALEALPHA else _no_op
494502
495 def func(x: str) -> FinalTransform:
503 def func(x: PathArg) -> FinalTransform:
504 if isinstance(x, PurePath):
505 # While paths are technically not strings, it is natural for them
506 # to be treated the same.
507 x = str(x)
496508 # Apply string input transformation function and return to x.
497509 # Original function is usually a no-op, but some algorithms require it
498510 # to also be the transformation function.
724736 """
725737 if alg & ns.UNGROUPLETTERS and alg & ns.LOCALEALPHA:
726738 swap = alg & NS_DUMB and alg & ns.LOWERCASEFIRST
727 transform = cast(StrToStr, methodcaller("swapcase")) if swap else _no_op
739 transform = cast(StrToStr, methodcaller("swapcase") if swap else _no_op)
728740
729741 def func(
730742 split_val: Iterable[NatsortInType],
830842
831843
832844 @overload
833 def do_decoding(s: NatsortInType, encoding: str) -> NatsortInType:
845 def do_decoding(s: Any, encoding: str) -> Any:
834846 ...
835847
836848
837 def do_decoding(s: NatsortInType, encoding: str) -> NatsortInType:
849 def do_decoding(s: Any, encoding: str) -> Any:
838850 """
839851 Helper to decode a *bytes* object, or return the object as-is.
840852
892904 path_parts = []
893905 base = str(s)
894906
895 # Now, split off the file extensions until we reach a decimal number at
896 # the beginning of the suffix or there are no more extensions.
897 suffixes = PurePath(base).suffixes
898 try:
899 digit_index = next(i for i, x in enumerate(reversed(suffixes)) if _d_match(x))
900 except StopIteration:
901 pass
902 else:
903 digit_index = len(suffixes) - digit_index
904 suffixes = suffixes[digit_index:]
905
907 # Now, split off the file extensions until
908 # - we reach a decimal number at the beginning of the suffix
909 # - more than two suffixes have been seen
910 # - a suffix is more than five characters (including leading ".")
911 # - there are no more extensions
912 suffixes = []
913 for i, suffix in enumerate(reversed(PurePath(base).suffixes)):
914 if _d_match(suffix) or i > 1 or len(suffix) > 5:
915 break
916 suffixes.append(suffix)
917 suffixes.reverse()
918
919 # Remove the suffixes from the base component
906920 base = base.replace("".join(suffixes), "")
907 return filter(None, ichain(path_parts, [base], suffixes))
921 base_component = [base] if base else []
922
923 # Join all path comonents in an iterator
924 return filter(None, ichain(path_parts, base_component, suffixes))
0 Metadata-Version: 2.1
1 Name: natsort
2 Version: 8.2.0
3 Summary: Simple yet flexible natural sorting in Python.
4 Home-page: https://github.com/SethMMorton/natsort
5 Author: Seth M. Morton
6 Author-email: drtuba78@gmail.com
7 License: MIT
8 Classifier: Development Status :: 5 - Production/Stable
9 Classifier: Intended Audience :: Developers
10 Classifier: Intended Audience :: Science/Research
11 Classifier: Intended Audience :: System Administrators
12 Classifier: Intended Audience :: Information Technology
13 Classifier: Intended Audience :: Financial and Insurance Industry
14 Classifier: Operating System :: OS Independent
15 Classifier: License :: OSI Approved :: MIT License
16 Classifier: Natural Language :: English
17 Classifier: Programming Language :: Python
18 Classifier: Programming Language :: Python :: 3
19 Classifier: Programming Language :: Python :: 3.7
20 Classifier: Programming Language :: Python :: 3.8
21 Classifier: Programming Language :: Python :: 3.9
22 Classifier: Programming Language :: Python :: 3.10
23 Classifier: Topic :: Scientific/Engineering :: Information Analysis
24 Classifier: Topic :: Utilities
25 Classifier: Topic :: Text Processing
26 Requires-Python: >=3.7
27 Description-Content-Type: text/x-rst
28 Provides-Extra: fast
29 Provides-Extra: icu
30 License-File: LICENSE
31
32 natsort
33 =======
34
35 .. image:: https://img.shields.io/pypi/v/natsort.svg
36 :target: https://pypi.org/project/natsort/
37
38 .. image:: https://img.shields.io/pypi/pyversions/natsort.svg
39 :target: https://pypi.org/project/natsort/
40
41 .. image:: https://img.shields.io/pypi/l/natsort.svg
42 :target: https://github.com/SethMMorton/natsort/blob/master/LICENSE
43
44 .. image:: https://github.com/SethMMorton/natsort/workflows/Tests/badge.svg
45 :target: https://github.com/SethMMorton/natsort/actions
46
47 .. image:: https://codecov.io/gh/SethMMorton/natsort/branch/master/graph/badge.svg
48 :target: https://codecov.io/gh/SethMMorton/natsort
49
50 Simple yet flexible natural sorting in Python.
51
52 - Source Code: https://github.com/SethMMorton/natsort
53 - Downloads: https://pypi.org/project/natsort/
54 - Documentation: https://natsort.readthedocs.io/
55
56 - `Examples and Recipes <https://natsort.readthedocs.io/en/master/examples.html>`_
57 - `How Does Natsort Work? <https://natsort.readthedocs.io/en/master/howitworks.html>`_
58 - `API <https://natsort.readthedocs.io/en/master/api.html>`_
59
60 - `Quick Description`_
61 - `Quick Examples`_
62 - `FAQ`_
63 - `Requirements`_
64 - `Optional Dependencies`_
65 - `Installation`_
66 - `How to Run Tests`_
67 - `How to Build Documentation`_
68 - `Dropped Deprecated APIs`_
69 - `History`_
70
71 **NOTE**: Please see the `Dropped Deprecated APIs`_ section for changes.
72
73 Quick Description
74 -----------------
75
76 When you try to sort a list of strings that contain numbers, the normal python
77 sort algorithm sorts lexicographically, so you might not get the results that
78 you expect:
79
80 .. code-block:: pycon
81
82 >>> a = ['2 ft 7 in', '1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '7 ft 6 in']
83 >>> sorted(a)
84 ['1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '2 ft 7 in', '7 ft 6 in']
85
86 Notice that it has the order ('1', '10', '2') - this is because the list is
87 being sorted in lexicographical order, which sorts numbers like you would
88 letters (i.e. 'b', 'ba', 'c').
89
90 ``natsort`` provides a function ``natsorted`` that helps sort lists
91 "naturally" ("naturally" is rather ill-defined, but in general it means
92 sorting based on meaning and not computer code point).
93 Using ``natsorted`` is simple:
94
95 .. code-block:: pycon
96
97 >>> from natsort import natsorted
98 >>> a = ['2 ft 7 in', '1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '7 ft 6 in']
99 >>> natsorted(a)
100 ['1 ft 5 in', '2 ft 7 in', '2 ft 11 in', '7 ft 6 in', '10 ft 2 in']
101
102 ``natsorted`` identifies numbers anywhere in a string and sorts them
103 naturally. Below are some other things you can do with ``natsort``
104 (also see the `examples <https://natsort.readthedocs.io/en/master/examples.html>`_
105 for a quick start guide, or the
106 `api <https://natsort.readthedocs.io/en/master/api.html>`_ for complete details).
107
108 **Note**: ``natsorted`` is designed to be a drop-in replacement for the
109 built-in ``sorted`` function. Like ``sorted``, ``natsorted``
110 `does not sort in-place`. To sort a list and assign the output to the same
111 variable, you must explicitly assign the output to a variable:
112
113 .. code-block:: pycon
114
115 >>> a = ['2 ft 7 in', '1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '7 ft 6 in']
116 >>> natsorted(a)
117 ['1 ft 5 in', '2 ft 7 in', '2 ft 11 in', '7 ft 6 in', '10 ft 2 in']
118 >>> print(a) # 'a' was not sorted; "natsorted" simply returned a sorted list
119 ['2 ft 7 in', '1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '7 ft 6 in']
120 >>> a = natsorted(a) # Now 'a' will be sorted because the sorted list was assigned to 'a'
121 >>> print(a)
122 ['1 ft 5 in', '2 ft 7 in', '2 ft 11 in', '7 ft 6 in', '10 ft 2 in']
123
124 Please see `Generating a Reusable Sorting Key and Sorting In-Place`_ for
125 an alternate way to sort in-place naturally.
126
127 Quick Examples
128 --------------
129
130 - `Sorting Versions`_
131 - `Sort Paths Like My File Browser (e.g. Windows Explorer on Windows)`_
132 - `Sorting by Real Numbers (i.e. Signed Floats)`_
133 - `Locale-Aware Sorting (or "Human Sorting")`_
134 - `Further Customizing Natsort`_
135 - `Sorting Mixed Types`_
136 - `Handling Bytes`_
137 - `Generating a Reusable Sorting Key and Sorting In-Place`_
138 - `Other Useful Things`_
139
140 Sorting Versions
141 ++++++++++++++++
142
143 ``natsort`` does not actually *comprehend* version numbers.
144 It just so happens that the most common versioning schemes are designed to
145 work with standard natural sorting techniques; these schemes include
146 ``MAJOR.MINOR``, ``MAJOR.MINOR.PATCH``, ``YEAR.MONTH.DAY``. If your data
147 conforms to a scheme like this, then it will work out-of-the-box with
148 ``natsorted`` (as of ``natsort`` version >= 4.0.0):
149
150 .. code-block:: pycon
151
152 >>> a = ['version-1.9', 'version-2.0', 'version-1.11', 'version-1.10']
153 >>> natsorted(a)
154 ['version-1.9', 'version-1.10', 'version-1.11', 'version-2.0']
155
156 If you need to versions that use a more complicated scheme, please see
157 `these examples <https://natsort.readthedocs.io/en/master/examples.html#rc-sorting>`_.
158
159 Sort Paths Like My File Browser (e.g. Windows Explorer on Windows)
160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
161
162 Prior to ``natsort`` version 7.1.0, it was a common request to be able to
163 sort paths like Windows Explorer. As of ``natsort`` 7.1.0, the function
164 ``os_sorted`` has been added to provide users the ability to sort
165 in the order that their file browser might sort (e.g Windows Explorer on
166 Windows, Finder on MacOS, Dolphin/Nautilus/Thunar/etc. on Linux).
167
168 .. code-block:: python
169
170 import os
171 from natsort import os_sorted
172 print(os_sorted(os.listdir()))
173 # The directory sorted like your file browser might show
174
175 Output will be different depending on the operating system you are on.
176
177 For users **not** on Windows (e.g. MacOS/Linux) it is **strongly** recommended
178 to also install `PyICU <https://pypi.org/project/PyICU>`_, which will help
179 ``natsort`` give results that match most file browsers. If this is not installed,
180 it will fall back on Python's built-in ``locale`` module and will give good
181 results for most input, but will give poor results for special characters.
182
183 Sorting by Real Numbers (i.e. Signed Floats)
184 ++++++++++++++++++++++++++++++++++++++++++++
185
186 This is useful in scientific data analysis (and was
187 the default behavior of ``natsorted`` for ``natsort``
188 version < 4.0.0). Use the ``realsorted`` function:
189
190 .. code-block:: pycon
191
192 >>> from natsort import realsorted, ns
193 >>> # Note that when interpreting as signed floats, the below numbers are
194 >>> # +5.10, -3.00, +5.30, +2.00
195 >>> a = ['position5.10.data', 'position-3.data', 'position5.3.data', 'position2.data']
196 >>> natsorted(a)
197 ['position2.data', 'position5.3.data', 'position5.10.data', 'position-3.data']
198 >>> natsorted(a, alg=ns.REAL)
199 ['position-3.data', 'position2.data', 'position5.10.data', 'position5.3.data']
200 >>> realsorted(a) # shortcut for natsorted with alg=ns.REAL
201 ['position-3.data', 'position2.data', 'position5.10.data', 'position5.3.data']
202
203 Locale-Aware Sorting (or "Human Sorting")
204 +++++++++++++++++++++++++++++++++++++++++
205
206 This is where the non-numeric characters are also ordered based on their
207 meaning, not on their ordinal value, and a locale-dependent thousands
208 separator and decimal separator is accounted for in the number.
209 This can be achieved with the ``humansorted`` function:
210
211 .. code-block:: pycon
212
213 >>> a = ['Apple', 'apple15', 'Banana', 'apple14,689', 'banana']
214 >>> natsorted(a)
215 ['Apple', 'Banana', 'apple14,689', 'apple15', 'banana']
216 >>> import locale
217 >>> locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
218 'en_US.UTF-8'
219 >>> natsorted(a, alg=ns.LOCALE)
220 ['apple15', 'apple14,689', 'Apple', 'banana', 'Banana']
221 >>> from natsort import humansorted
222 >>> humansorted(a) # shortcut for natsorted with alg=ns.LOCALE
223 ['apple15', 'apple14,689', 'Apple', 'banana', 'Banana']
224
225 You may find you need to explicitly set the locale to get this to work
226 (as shown in the example).
227 Please see `locale issues <https://natsort.readthedocs.io/en/master/locale_issues.html>`_ and the
228 `Optional Dependencies`_ section below before using the ``humansorted`` function.
229
230 Further Customizing Natsort
231 +++++++++++++++++++++++++++
232
233 If you need to combine multiple algorithm modifiers (such as ``ns.REAL``,
234 ``ns.LOCALE``, and ``ns.IGNORECASE``), you can combine the options using the
235 bitwise OR operator (``|``). For example,
236
237 .. code-block:: pycon
238
239 >>> a = ['Apple', 'apple15', 'Banana', 'apple14,689', 'banana']
240 >>> natsorted(a, alg=ns.REAL | ns.LOCALE | ns.IGNORECASE)
241 ['Apple', 'apple15', 'apple14,689', 'Banana', 'banana']
242 >>> # The ns enum provides long and short forms for each option.
243 >>> ns.LOCALE == ns.L
244 True
245 >>> # You can also customize the convenience functions, too.
246 >>> natsorted(a, alg=ns.REAL | ns.LOCALE | ns.IGNORECASE) == realsorted(a, alg=ns.L | ns.IC)
247 True
248 >>> natsorted(a, alg=ns.REAL | ns.LOCALE | ns.IGNORECASE) == humansorted(a, alg=ns.R | ns.IC)
249 True
250
251 All of the available customizations can be found in the documentation for
252 `the ns enum <https://natsort.readthedocs.io/en/master/api.html#natsort.ns>`_.
253
254 You can also add your own custom transformation functions with the ``key``
255 argument. These can be used with ``alg`` if you wish.
256
257 .. code-block:: pycon
258
259 >>> a = ['apple2.50', '2.3apple']
260 >>> natsorted(a, key=lambda x: x.replace('apple', ''), alg=ns.REAL)
261 ['2.3apple', 'apple2.50']
262
263 Sorting Mixed Types
264 +++++++++++++++++++
265
266 You can mix and match ``int``, ``float``, and ``str`` (or ``unicode``) types
267 when you sort:
268
269 .. code-block:: pycon
270
271 >>> a = ['4.5', 6, 2.0, '5', 'a']
272 >>> natsorted(a)
273 [2.0, '4.5', '5', 6, 'a']
274 >>> # sorted(a) would raise an "unorderable types" TypeError
275
276 Handling Bytes
277 ++++++++++++++
278
279 ``natsort`` does not officially support the `bytes` type, but
280 convenience functions are provided that help you decode to `str` first:
281
282 .. code-block:: pycon
283
284 >>> from natsort import as_utf8
285 >>> a = [b'a', 14.0, 'b']
286 >>> # natsorted(a) would raise a TypeError (bytes() < str())
287 >>> natsorted(a, key=as_utf8) == [14.0, b'a', 'b']
288 True
289 >>> a = [b'a56', b'a5', b'a6', b'a40']
290 >>> # natsorted(a) would return the same results as sorted(a)
291 >>> natsorted(a, key=as_utf8) == [b'a5', b'a6', b'a40', b'a56']
292 True
293
294 Generating a Reusable Sorting Key and Sorting In-Place
295 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
296
297 Under the hood, ``natsorted`` works by generating a custom sorting
298 key using ``natsort_keygen`` and then passes that to the built-in
299 ``sorted``. You can use the ``natsort_keygen`` function yourself to
300 generate a custom sorting key to sort in-place using the ``list.sort``
301 method.
302
303 .. code-block:: pycon
304
305 >>> from natsort import natsort_keygen
306 >>> natsort_key = natsort_keygen()
307 >>> a = ['2 ft 7 in', '1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '7 ft 6 in']
308 >>> natsorted(a) == sorted(a, key=natsort_key)
309 True
310 >>> a.sort(key=natsort_key)
311 >>> a
312 ['1 ft 5 in', '2 ft 7 in', '2 ft 11 in', '7 ft 6 in', '10 ft 2 in']
313
314 All of the algorithm customizations mentioned in the
315 `Further Customizing Natsort`_ section can also be applied to
316 ``natsort_keygen`` through the *alg* keyword option.
317
318 Other Useful Things
319 +++++++++++++++++++
320
321 - recursively descend into lists of lists
322 - automatic unicode normalization of input data
323 - `controlling the case-sensitivity <https://natsort.readthedocs.io/en/master/examples.html#case-sort>`_
324 - `sorting file paths correctly <https://natsort.readthedocs.io/en/master/examples.html#path-sort>`_
325 - `allow custom sorting keys <https://natsort.readthedocs.io/en/master/examples.html#custom-sort>`_
326 - `accounting for units <https://natsort.readthedocs.io/en/master/examples.html#accounting-for-units-when-sorting>`_
327
328 FAQ
329 ---
330
331 How do I debug ``natsort.natsorted()``?
332 The best way to debug ``natsorted()`` is to generate a key using ``natsort_keygen()``
333 with the same options being passed to ``natsorted``. One can take a look at
334 exactly what is being done with their input using this key - it is highly
335 recommended
336 to `look at this issue describing how to debug <https://github.com/SethMMorton/natsort/issues/13#issuecomment-50422375>`_
337 for *how* to debug, and also to review the
338 `How Does Natsort Work? <https://natsort.readthedocs.io/en/master/howitworks.html>`_
339 page for *why* ``natsort`` is doing that to your data.
340
341 If you are trying to sort custom classes and running into trouble, please
342 take a look at https://github.com/SethMMorton/natsort/issues/60. In short,
343 custom classes are not likely to be sorted correctly if one relies
344 on the behavior of ``__lt__`` and the other rich comparison operators in
345 their custom class - it is better to use a ``key`` function with
346 ``natsort``, or use the ``natsort`` key as part of your rich comparison
347 operator definition.
348
349 ``natsort`` gave me results I didn't expect, and it's a terrible library!
350 Did you try to debug using the above advice? If so, and you still cannot figure out
351 the error, then please `file an issue <https://github.com/SethMMorton/natsort/issues/new>`_.
352
353 How *does* ``natsort`` work?
354 If you don't want to read `How Does Natsort Work? <https://natsort.readthedocs.io/en/master/howitworks.html>`_,
355 here is a quick primer.
356
357 ``natsort`` provides a `key function <https://docs.python.org/3/howto/sorting.html#key-functions>`_
358 that can be passed to `list.sort() <https://docs.python.org/3/library/stdtypes.html#list.sort>`_
359 or `sorted() <https://docs.python.org/3/library/functions.html#sorted>`_ in order to
360 modify the default sorting behavior. This key is generated on-demand with
361 the key generator ``natsort.natsort_keygen()``. ``natsort.natsorted()``
362 is essentially a wrapper for the following code:
363
364 .. code-block:: pycon
365
366 >>> from natsort import natsort_keygen
367 >>> natsort_key = natsort_keygen()
368 >>> sorted(['1', '10', '2'], key=natsort_key)
369 ['1', '2', '10']
370
371 Users can further customize ``natsort`` sorting behavior with the ``key``
372 and/or ``alg`` options (see details in the `Further Customizing Natsort`_
373 section).
374
375 The key generated by ``natsort_keygen`` *always* returns a ``tuple``. It
376 does so in the following way (*some details omitted for clarity*):
377
378 1. Assume the input is a string, and attempt to split it into numbers and
379 non-numbers using regular expressions. Numbers are then converted into
380 either ``int`` or ``float``.
381 2. If the above fails because the input is not a string, assume the input
382 is some other sequence (e.g. ``list`` or ``tuple``), and recursively
383 apply the key to each element of the sequence.
384 3. If the above fails because the input is not iterable, assume the input
385 is an ``int`` or ``float``, and just return the input in a ``tuple``.
386
387 Because a ``tuple`` is always returned, a ``TypeError`` should not be common
388 unless one tries to do something odd like sort an ``int`` against a ``list``.
389
390 Shell script
391 ------------
392
393 ``natsort`` comes with a shell script called ``natsort``, or can also be called
394 from the command line with ``python -m natsort``.
395
396 Requirements
397 ------------
398
399 ``natsort`` requires Python 3.7 or greater.
400
401 Optional Dependencies
402 ---------------------
403
404 fastnumbers
405 +++++++++++
406
407 The most efficient sorting can occur if you install the
408 `fastnumbers <https://pypi.org/project/fastnumbers>`_ package
409 (version >=2.0.0); it helps with the string to number conversions.
410 ``natsort`` will still run (efficiently) without the package, but if you need
411 to squeeze out that extra juice it is recommended you include this as a
412 dependency. ``natsort`` will not require (or check) that
413 `fastnumbers <https://pypi.org/project/fastnumbers>`_ is installed
414 at installation.
415
416 PyICU
417 +++++
418
419 It is recommended that you install `PyICU <https://pypi.org/project/PyICU>`_
420 if you wish to sort in a locale-dependent manner, see
421 https://natsort.readthedocs.io/en/master/locale_issues.html for an explanation why.
422
423 Installation
424 ------------
425
426 Use ``pip``!
427
428 .. code-block:: console
429
430 $ pip install natsort
431
432 If you want to install the `Optional Dependencies`_, you can use the
433 `"extras" notation <https://packaging.python.org/tutorials/installing-packages/#installing-setuptools-extras>`_
434 at installation time to install those dependencies as well - use ``fast`` for
435 `fastnumbers <https://pypi.org/project/fastnumbers>`_ and ``icu`` for
436 `PyICU <https://pypi.org/project/PyICU>`_.
437
438 .. code-block:: console
439
440 # Install both optional dependencies.
441 $ pip install natsort[fast,icu]
442 # Install just fastnumbers
443 $ pip install natsort[fast]
444
445 How to Run Tests
446 ----------------
447
448 Please note that ``natsort`` is NOT set-up to support ``python setup.py test``.
449
450 The recommended way to run tests is with `tox <https://tox.readthedocs.io/en/latest/>`_.
451 After installing ``tox``, running tests is as simple as executing the following
452 in the ``natsort`` directory:
453
454 .. code-block:: console
455
456 $ tox
457
458 ``tox`` will create virtual a virtual environment for your tests and install
459 all the needed testing requirements for you. You can specify a particular
460 python version with the ``-e`` flag, e.g. ``tox -e py36``. Static analysis
461 is done with ``tox -e flake8``. You can see all available testing environments
462 with ``tox --listenvs``.
463
464 How to Build Documentation
465 --------------------------
466
467 If you want to build the documentation for ``natsort``, it is recommended to
468 use ``tox``:
469
470 .. code-block:: console
471
472 $ tox -e docs
473
474 This will place the documentation in ``build/sphinx/html``.
475
476 Dropped Deprecated APIs
477 -----------------------
478
479 In ``natsort`` version 6.0.0, the following APIs and functions were removed
480
481 - ``number_type`` keyword argument (deprecated since 3.4.0)
482 - ``signed`` keyword argument (deprecated since 3.4.0)
483 - ``exp`` keyword argument (deprecated since 3.4.0)
484 - ``as_path`` keyword argument (deprecated since 3.4.0)
485 - ``py3_safe`` keyword argument (deprecated since 3.4.0)
486 - ``ns.TYPESAFE`` (deprecated since version 5.0.0)
487 - ``ns.DIGIT`` (deprecated since version 5.0.0)
488 - ``ns.VERSION`` (deprecated since version 5.0.0)
489 - ``versorted()`` (discouraged since version 4.0.0,
490 officially deprecated since version 5.5.0)
491 - ``index_versorted()`` (discouraged since version 4.0.0,
492 officially deprecated since version 5.5.0)
493
494 In general, if you want to determine if you are using deprecated APIs you
495 can run your code with the following flag
496
497 .. code-block:: console
498
499 $ python -Wdefault::DeprecationWarning my-code.py
500
501 By default ``DeprecationWarnings`` are not shown, but this will cause them
502 to be shown. Alternatively, you can just set the environment variable
503 ``PYTHONWARNINGS`` to "default::DeprecationWarning" and then run your code.
504
505 Author
506 ------
507
508 Seth M. Morton
509
510 History
511 -------
512
513 Please visit the changelog
514 `on GitHub <https://github.com/SethMMorton/natsort/blob/master/CHANGELOG.md>`_ or
515 `in the documentation <https://natsort.readthedocs.io/en/master/changelog.html>`_.
0 CHANGELOG.md
1 LICENSE
2 MANIFEST.in
3 README.rst
4 RELEASING.md
5 setup.cfg
6 setup.py
7 tox.ini
8 dev/README.md
9 dev/bump.py
10 dev/clean.py
11 dev/generate_new_unicode_numbers.py
12 docs/api.rst
13 docs/changelog.rst
14 docs/conf.py
15 docs/examples.rst
16 docs/howitworks.rst
17 docs/index.rst
18 docs/locale_issues.rst
19 docs/requirements.in
20 docs/requirements.txt
21 docs/shell.rst
22 docs/special_cases_everywhere.jpg
23 mypy_stubs/icu.pyi
24 natsort/__init__.py
25 natsort/__main__.py
26 natsort/natsort.py
27 natsort/ns_enum.py
28 natsort/py.typed
29 natsort/unicode_numbers.py
30 natsort/unicode_numeric_hex.py
31 natsort/utils.py
32 natsort.egg-info/PKG-INFO
33 natsort.egg-info/SOURCES.txt
34 natsort.egg-info/dependency_links.txt
35 natsort.egg-info/entry_points.txt
36 natsort.egg-info/not-zip-safe
37 natsort.egg-info/requires.txt
38 natsort.egg-info/top_level.txt
39 natsort/compat/__init__.py
40 natsort/compat/fake_fastnumbers.py
41 natsort/compat/fastnumbers.py
42 natsort/compat/locale.py
43 tests/conftest.py
44 tests/profile_natsorted.py
45 tests/test_fake_fastnumbers.py
46 tests/test_final_data_transform_factory.py
47 tests/test_input_string_transform_factory.py
48 tests/test_main.py
49 tests/test_natsort_key.py
50 tests/test_natsort_keygen.py
51 tests/test_natsorted.py
52 tests/test_natsorted_convenience.py
53 tests/test_ns_enum.py
54 tests/test_os_sorted.py
55 tests/test_parse_bytes_function.py
56 tests/test_parse_number_function.py
57 tests/test_parse_string_function.py
58 tests/test_regex.py
59 tests/test_string_component_transform_factory.py
60 tests/test_unicode_numbers.py
61 tests/test_utils.py
0 [console_scripts]
1 natsort = natsort.__main__:main
0
1 [fast]
2 fastnumbers>=2.0.0
3
4 [icu]
5 PyICU>=1.0.0
00 [bumpversion]
1 current_version = 8.0.2
1 current_version = 8.2.0
22 commit = True
33 tag = True
44 tag_name = {new_version}
2424 Natural Language :: English
2525 Programming Language :: Python
2626 Programming Language :: Python :: 3
27 Programming Language :: Python :: 3.6
2827 Programming Language :: Python :: 3.7
2928 Programming Language :: Python :: 3.8
3029 Programming Language :: Python :: 3.9
6362 .venv
6463
6564 [mypy]
65 mypy_path = mypy_stubs
6666
67 [mypy-icu]
68 ignore_missing_imports = True
67 [egg_info]
68 tag_build =
69 tag_date = 0
70
33
44 setup(
55 name="natsort",
6 version="8.0.2",
6 version="8.2.0",
77 packages=find_packages(),
88 entry_points={"console_scripts": ["natsort = natsort.__main__:main"]},
9 python_requires=">=3.6",
9 python_requires=">=3.7",
1010 extras_require={"fast": ["fastnumbers >= 2.0.0"], "icu": ["PyICU >= 1.0.0"]},
1111 package_data={"": ["py.typed"]},
1212 zip_safe=False,
44 """
55
66 from operator import itemgetter
7 from pathlib import PurePosixPath
78 from typing import List, Tuple, Union
89
910 import pytest
8182 given = ["1.9.9a", "1.11", "1.9.9b", "1.11.4", "1.10.1"]
8283 expected = ["1.9.9a", "1.9.9b", "1.10.1", "1.11", "1.11.4"]
8384 assert natsorted(given) == expected
85
86
87 def test_natsorted_can_sorts_paths_same_as_strings() -> None:
88 paths = [
89 PurePosixPath("a/1/something"),
90 PurePosixPath("a/2/something"),
91 PurePosixPath("a/10/something"),
92 ]
93 assert [str(p) for p in natsorted(paths)] == natsorted([str(p) for p in paths])
8494
8595
8696 @pytest.mark.parametrize(
178188 # You can sort paths and numbers, not that you'd want to
179189 given: List[Union[str, int]] = ["/Folder (9)/file.exe", 43]
180190 expected: List[Union[str, int]] = [43, "/Folder (9)/file.exe"]
191 assert natsorted(given, alg=ns.PATH) == expected
192
193
194 def test_natsorted_path_extensions_heuristic() -> None:
195 # https://github.com/SethMMorton/natsort/issues/145
196 given = [
197 "Try.Me.Bug - 09 - One.Two.Three.[text].mkv",
198 "Try.Me.Bug - 07 - One.Two.5.[text].mkv",
199 "Try.Me.Bug - 08 - One.Two.Three[text].mkv",
200 ]
201 expected = [
202 "Try.Me.Bug - 07 - One.Two.5.[text].mkv",
203 "Try.Me.Bug - 08 - One.Two.Three[text].mkv",
204 "Try.Me.Bug - 09 - One.Two.Three.[text].mkv",
205 ]
181206 assert natsorted(given, alg=ns.PATH) == expected
182207
183208
22 Testing for the OS sorting
33 """
44 import platform
5 from typing import cast
65
76 import natsort
87 import pytest
4342 def test_os_sorted_key() -> None:
4443 given = ["foo0", "foo2", "goo1"]
4544 expected = ["foo0", "goo1", "foo2"]
46 result = natsort.os_sorted(given, key=lambda x: cast(str, x).replace("g", "f"))
45 result = natsort.os_sorted(given, key=lambda x: x.replace("g", "f"))
4746 assert result == expected
4847
4948
66 from hypothesis import given
77 from hypothesis.strategies import floats, integers
88 from natsort.ns_enum import NSType, ns
9 from natsort.utils import MaybeNumTransformer, parse_number_or_none_factory
9 from natsort.utils import NumTransformer, parse_number_or_none_factory
1010
1111
1212 @pytest.mark.usefixtures("with_locale_en_us")
2121 )
2222 @given(x=floats(allow_nan=False) | integers())
2323 def test_parse_number_factory_makes_function_that_returns_tuple(
24 x: Union[float, int], alg: NSType, example_func: MaybeNumTransformer
24 x: Union[float, int], alg: NSType, example_func: NumTransformer
2525 ) -> None:
2626 parse_number_func = parse_number_or_none_factory(alg, "", "xx")
2727 assert parse_number_func(x) == example_func(x)
55 import string
66 from itertools import chain
77 from operator import neg as op_neg
8 from typing import List, Pattern, Union
8 from typing import List, Pattern, Tuple, Union
99
1010 import pytest
1111 from hypothesis import given
154154 assert tuple(utils.path_splitter(z)) == tuple(pathlib.Path(z).parts)
155155
156156
157 def test_path_splitter_splits_path_string_by_sep_and_removes_extension_example() -> None:
158 given = "/this/is/a/path/file.x1.10.tar.gz"
159 expected = (os.sep, "this", "is", "a", "path", "file.x1.10", ".tar", ".gz")
157 @pytest.mark.parametrize(
158 "given, expected",
159 [
160 (
161 "/this/is/a/path/file.x1.10.tar.gz",
162 (os.sep, "this", "is", "a", "path", "file.x1.10", ".tar", ".gz"),
163 ),
164 (
165 "/this/is/a/path/file.x1.10.tar",
166 (os.sep, "this", "is", "a", "path", "file.x1.10", ".tar"),
167 ),
168 (
169 "/this/is/a/path/file.x1.threethousand.tar",
170 (os.sep, "this", "is", "a", "path", "file.x1.threethousand", ".tar"),
171 ),
172 ],
173 )
174 def test_path_splitter_splits_path_string_by_sep_and_removes_extension_example(
175 given: str, expected: Tuple[str, ...]
176 ) -> None:
160177 assert tuple(utils.path_splitter(given)) == tuple(expected)
161178
162179
44
55 [tox]
66 envlist =
7 flake8, mypy, py36, py37, py38, py39, py310
8 # Other valid evironments are:
7 flake8, mypy, py37, py38, py39, py310
8 # Other valid environments are:
99 # docs
1010 # release
1111 # clean
5959 pytest
6060 pytest-mock
6161 fastnumbers
62 typing_extensions
6263 commands =
6364 mypy --strict natsort tests
6465 skip_install = true
103104 # Get GitHub actions to run the correct tox environment
104105 [gh-actions]
105106 python =
106 3.5: py35
107 3.6: py36
108107 3.7: py37
109108 3.8: py38
110109 3.9: py39