ProvenPin Install

Natural sorting with natsort for filenames, versions and paths: working versions (tested 2026-10-16)

Sort filenames and version strings the way humans expect with natsort, using IGNORECASE and PATH flags, parallel-list ordering and the FLOAT algorithm for decimals.

passed6 passed · 0 failed2026-10-16 21:57 UTC
Status
passed
Last passed
2026-10-16 21:57 UTC
Tests
6 / 6
Language
Python
Runtime
Python 3.13.16
pinned versions2
natsort@8.4.0pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 38972656245

The code

Exactly the files that passed. Your agent can pull them with get_recipe("natsort-natural-sorting") (install), or see them on GitHub.

requirements.txt
natsort==8.4.0
pytest==9.1.1
sorting.py
from natsort import (
    index_natsorted,
    natsort_keygen,
    natsorted,
    ns,
    order_by_index,
)


def sort_filenames(names):
    return natsorted(names)


def sort_versions(versions):
    return natsorted(versions)


def sort_case_insensitive(values):
    return natsorted(values, alg=ns.IGNORECASE)


def sort_paths(paths):
    return natsorted(paths, alg=ns.PATH)


def sort_rows(filenames, sizes):
    order = index_natsorted(filenames)
    return [filenames[i] for i in order], order_by_index(sizes, order)


def sort_decimal_versions(versions):
    key = natsort_keygen(alg=ns.FLOAT)
    return sorted(versions, key=key)
tests/test_sorting.py
from natsort import natsorted

from sorting import (
    sort_case_insensitive,
    sort_decimal_versions,
    sort_filenames,
    sort_paths,
    sort_rows,
    sort_versions,
)


def test_filenames_use_natural_order():
    names = ["file10", "file2", "file1", "file20"]
    assert sorted(names) == ["file1", "file10", "file2", "file20"]
    assert sort_filenames(names) == ["file1", "file2", "file10", "file20"]


def test_versions_are_compared_componentwise():
    versions = ["1.9.0", "1.10.0", "1.2.3", "1.10.1"]
    assert sort_versions(versions) == ["1.2.3", "1.9.0", "1.10.0", "1.10.1"]


def test_case_insensitive_needs_the_ignorecase_flag():
    values = ["Apple", "apple", "Banana", "banana"]
    assert natsorted(values) == ["Apple", "Banana", "apple", "banana"]
    assert sort_case_insensitive(values) == ["Apple", "apple", "Banana", "banana"]


def test_path_flag_keeps_directories_together():
    paths = ["a10/y", "a/10/y", "a/2/x"]
    assert natsorted(paths) == ["a10/y", "a/2/x", "a/10/y"]
    assert sort_paths(paths) == ["a/2/x", "a/10/y", "a10/y"]


def test_parallel_lists_follow_the_same_order():
    filenames = ["img12.png", "img1.png", "img2.png"]
    sizes = ["large", "small", "medium"]
    ordered_files, ordered_sizes = sort_rows(filenames, sizes)
    assert ordered_files == ["img1.png", "img2.png", "img12.png"]
    assert ordered_sizes == ["small", "medium", "large"]


def test_float_algorithm_reads_decimals_as_numbers():
    versions = ["1.5", "1.10", "1.9"]
    assert sort_versions(versions) == ["1.5", "1.9", "1.10"]
    assert sort_decimal_versions(versions) == ["1.10", "1.5", "1.9"]
pytest.ini
[pytest]
pythonpath = .

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.