summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSeth Morton <seth.m.morton@gmail.com>2022-09-01 14:35:38 -0700
committerGitHub <noreply@github.com>2022-09-01 14:35:38 -0700
commit5c52a8888c2cb0c3f08192e2684851e0253b4437 (patch)
tree6551115c28d117e509719aa3091f61068c4740a5
parente5d2e4507728e53d1867ac87e169fca1d251d8cf (diff)
parent24b6b81661c95c7ff9da17aea88a98c4bf05fdfd (diff)
downloadnatsort-5c52a8888c2cb0c3f08192e2684851e0253b4437.tar.gz
Merge pull request #155 from SethMMorton/make-typing-less-strict
Make typing less strict
-rw-r--r--CHANGELOG.md6
-rw-r--r--MANIFEST.in1
-rw-r--r--mypy_stubs/icu.pyi24
-rw-r--r--natsort/compat/locale.py10
-rw-r--r--natsort/natsort.py204
-rw-r--r--natsort/utils.py68
-rw-r--r--setup.cfg4
-rw-r--r--tests/test_os_sorted.py3
-rw-r--r--tests/test_parse_number_function.py4
-rw-r--r--tox.ini1
10 files changed, 130 insertions, 195 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d83289d..e326486 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,12 @@
Unreleased
---
+### Changed
+- Auto-coerce `pathlib.Path` objects to `str` since it is the least astonishing
+ behavior ([@Gilthans](https://github.com/Gilthans), issues #152, #153)
+- Reduce strictness of type hints to avoid over-constraining client code
+ (issues #154, #155)
+
[8.1.0] - 2022-01-30
---
diff --git a/MANIFEST.in b/MANIFEST.in
index a4e0ee4..27d7981 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -2,6 +2,7 @@ include LICENSE
include CHANGELOG.md
include tox.ini
include RELEASING.md
+recursive-include mypy_stubs *.pyi
graft dev
graft docs
graft natsort
diff --git a/mypy_stubs/icu.pyi b/mypy_stubs/icu.pyi
new file mode 100644
index 0000000..f7ac204
--- /dev/null
+++ b/mypy_stubs/icu.pyi
@@ -0,0 +1,24 @@
+from typing import overload
+
+@overload
+def Locale() -> str: ...
+@overload
+def Locale(x: str) -> str: ...
+
+class UCollAttribute:
+ NUMERIC_COLLATION: int
+
+class UCollAttributeValue:
+ ON: int
+
+class DecimalFormatSymbols:
+ kGroupingSeparatorSymbol: int
+ kDecimalSeparatorSymbol: int
+ def __init__(self, locale: str) -> None: ...
+ def getSymbol(self, symbol: int) -> str: ...
+
+class Collator:
+ @classmethod
+ def createInstance(cls, locale: str) -> Collator: ...
+ def getSortKey(self, source: str) -> bytes: ...
+ def setAttribute(self, attr: int, value: int) -> None: ...
diff --git a/natsort/compat/locale.py b/natsort/compat/locale.py
index 53080c3..d802194 100644
--- a/natsort/compat/locale.py
+++ b/natsort/compat/locale.py
@@ -40,19 +40,19 @@ try: # noqa: C901
def get_icu_locale() -> str:
language_code, encoding = getlocale()
if language_code is None or encoding is None: # pragma: no cover
- return cast(str, icu.Locale())
- return cast(str, icu.Locale(f"{language_code}.{encoding}"))
+ return icu.Locale()
+ return icu.Locale(f"{language_code}.{encoding}")
def get_strxfrm() -> TrxfmFunc:
- return cast(TrxfmFunc, icu.Collator.createInstance(get_icu_locale()).getSortKey)
+ return icu.Collator.createInstance(get_icu_locale()).getSortKey
def get_thousands_sep() -> str:
sep = icu.DecimalFormatSymbols.kGroupingSeparatorSymbol
- return cast(str, icu.DecimalFormatSymbols(get_icu_locale()).getSymbol(sep))
+ return icu.DecimalFormatSymbols(get_icu_locale()).getSymbol(sep)
def get_decimal_point() -> str:
sep = icu.DecimalFormatSymbols.kDecimalSeparatorSymbol
- return cast(str, icu.DecimalFormatSymbols(get_icu_locale()).getSymbol(sep))
+ return icu.DecimalFormatSymbols(get_icu_locale()).getSymbol(sep)
except ImportError:
import locale
diff --git a/natsort/natsort.py b/natsort/natsort.py
index c0eec58..2038f3e 100644
--- a/natsort/natsort.py
+++ b/natsort/natsort.py
@@ -18,42 +18,27 @@ from typing import (
Optional,
Sequence,
Tuple,
- Union,
+ TypeVar,
cast,
- overload,
)
import natsort.compat.locale
from natsort import utils
from natsort.ns_enum import NSType, NS_DUMB, ns
-from natsort.utils import (
- KeyType,
- MaybeKeyType,
- NatsortInType,
- NatsortOutType,
- StrBytesNum,
- StrBytesPathNum,
-)
+from natsort.utils import NatsortInType, NatsortOutType
# Common input and output types
-Iter_ns = Iterable[NatsortInType]
-Iter_any = Iterable[Any]
-List_ns = List[NatsortInType]
-List_any = List[Any]
-List_int = List[int]
+T = TypeVar("T")
+NatsortInTypeT = TypeVar("NatsortInTypeT", bound=NatsortInType)
# The type that natsort_key returns
NatsortKeyType = Callable[[NatsortInType], NatsortOutType]
# Types for os_sorted
-OSSortInType = Iterable[Optional[StrBytesPathNum]]
-OSSortOutType = Tuple[Union[StrBytesNum, Tuple[StrBytesNum, ...]], ...]
-OSSortKeyType = Callable[[Optional[StrBytesPathNum]], OSSortOutType]
-Iter_path = Iterable[Optional[StrBytesPathNum]]
-List_path = List[StrBytesPathNum]
+OSSortKeyType = Callable[[NatsortInType], NatsortOutType]
-def decoder(encoding: str) -> Callable[[NatsortInType], NatsortInType]:
+def decoder(encoding: str) -> Callable[[Any], Any]:
"""
Return a function that can be used to decode bytes to unicode.
@@ -94,7 +79,7 @@ def decoder(encoding: str) -> Callable[[NatsortInType], NatsortInType]:
return partial(utils.do_decoding, encoding=encoding)
-def as_ascii(s: NatsortInType) -> NatsortInType:
+def as_ascii(s: Any) -> Any:
"""
Function to decode an input with the ASCII codec, or return as-is.
@@ -117,7 +102,7 @@ def as_ascii(s: NatsortInType) -> NatsortInType:
return utils.do_decoding(s, "ascii")
-def as_utf8(s: NatsortInType) -> NatsortInType:
+def as_utf8(s: Any) -> Any:
"""
Function to decode an input with the UTF-8 codec, or return as-is.
@@ -141,8 +126,8 @@ def as_utf8(s: NatsortInType) -> NatsortInType:
def natsort_keygen(
- key: MaybeKeyType = None, alg: NSType = ns.DEFAULT
-) -> NatsortKeyType:
+ key: Optional[Callable[[Any], NatsortInType]] = None, alg: NSType = ns.DEFAULT
+) -> Callable[[Any], NatsortOutType]:
"""
Generate a key to sort strings and numbers naturally.
@@ -252,26 +237,12 @@ natsort_keygen
"""
-@overload
-def natsorted(
- seq: Iter_ns, key: None = None, reverse: bool = False, alg: NSType = ns.DEFAULT
-) -> List_ns:
- ...
-
-
-@overload
def natsorted(
- seq: Iter_any, key: KeyType, reverse: bool = False, alg: NSType = ns.DEFAULT
-) -> List_any:
- ...
-
-
-def natsorted(
- seq: Iter_any,
- key: MaybeKeyType = None,
+ seq: Iterable[T],
+ key: Optional[Callable[[T], NatsortInType]] = None,
reverse: bool = False,
alg: NSType = ns.DEFAULT,
-) -> List_any:
+) -> List[T]:
"""
Sorts an iterable naturally.
@@ -319,26 +290,12 @@ def natsorted(
return sorted(seq, reverse=reverse, key=natsort_keygen(key, alg))
-@overload
-def humansorted(
- seq: Iter_ns, key: None = None, reverse: bool = False, alg: NSType = ns.DEFAULT
-) -> List_ns:
- ...
-
-
-@overload
-def humansorted(
- seq: Iter_any, key: KeyType, reverse: bool = False, alg: NSType = ns.DEFAULT
-) -> List_any:
- ...
-
-
def humansorted(
- seq: Iter_any,
- key: MaybeKeyType = None,
+ seq: Iterable[T],
+ key: Optional[Callable[[T], NatsortInType]] = None,
reverse: bool = False,
alg: NSType = ns.DEFAULT,
-) -> List_any:
+) -> List[T]:
"""
Convenience function to properly sort non-numeric characters.
@@ -390,26 +347,12 @@ def humansorted(
return natsorted(seq, key, reverse, alg | ns.LOCALE)
-@overload
-def realsorted(
- seq: Iter_ns, key: None = None, reverse: bool = False, alg: NSType = ns.DEFAULT
-) -> List_ns:
- ...
-
-
-@overload
def realsorted(
- seq: Iter_any, key: KeyType, reverse: bool = False, alg: NSType = ns.DEFAULT
-) -> List_any:
- ...
-
-
-def realsorted(
- seq: Iter_any,
- key: MaybeKeyType = None,
+ seq: Iterable[T],
+ key: Optional[Callable[[T], NatsortInType]] = None,
reverse: bool = False,
alg: NSType = ns.DEFAULT,
-) -> List_any:
+) -> List[T]:
"""
Convenience function to properly sort signed floats.
@@ -462,26 +405,12 @@ def realsorted(
return natsorted(seq, key, reverse, alg | ns.REAL)
-@overload
-def index_natsorted(
- seq: Iter_ns, key: None = None, reverse: bool = False, alg: NSType = ns.DEFAULT
-) -> List_int:
- ...
-
-
-@overload
def index_natsorted(
- seq: Iter_any, key: KeyType, reverse: bool = False, alg: NSType = ns.DEFAULT
-) -> List_int:
- ...
-
-
-def index_natsorted(
- seq: Iter_any,
- key: MaybeKeyType = None,
+ seq: Iterable[T],
+ key: Optional[Callable[[T], NatsortInType]] = None,
reverse: bool = False,
alg: NSType = ns.DEFAULT,
-) -> List_int:
+) -> List[int]:
"""
Determine the list of the indexes used to sort the input sequence.
@@ -537,13 +466,13 @@ def index_natsorted(
['baz', 'foo', 'bar']
"""
- newkey: KeyType
+ newkey: Callable[[Tuple[int, T]], NatsortInType]
if key is None:
newkey = itemgetter(1)
else:
- def newkey(x: Any) -> NatsortInType:
- return cast(KeyType, key)(itemgetter(1)(x))
+ def newkey(x: Tuple[int, T]) -> NatsortInType:
+ return cast(Callable[[T], NatsortInType], key)(itemgetter(1)(x))
# Pair the index and sequence together, then sort by element
index_seq_pair = [(x, y) for x, y in enumerate(seq)]
@@ -551,26 +480,12 @@ def index_natsorted(
return [x for x, _ in index_seq_pair]
-@overload
-def index_humansorted(
- seq: Iter_ns, key: None = None, reverse: bool = False, alg: NSType = ns.DEFAULT
-) -> List_int:
- ...
-
-
-@overload
def index_humansorted(
- seq: Iter_any, key: KeyType, reverse: bool = False, alg: NSType = ns.DEFAULT
-) -> List_int:
- ...
-
-
-def index_humansorted(
- seq: Iter_any,
- key: MaybeKeyType = None,
+ seq: Iterable[T],
+ key: Optional[Callable[[T], NatsortInType]] = None,
reverse: bool = False,
alg: NSType = ns.DEFAULT,
-) -> List_int:
+) -> List[int]:
"""
This is a wrapper around ``index_natsorted(seq, alg=ns.LOCALE)``.
@@ -619,26 +534,12 @@ def index_humansorted(
return index_natsorted(seq, key, reverse, alg | ns.LOCALE)
-@overload
def index_realsorted(
- seq: Iter_ns, key: None = None, reverse: bool = False, alg: NSType = ns.DEFAULT
-) -> List_int:
- ...
-
-
-@overload
-def index_realsorted(
- seq: Iter_any, key: KeyType, reverse: bool = False, alg: NSType = ns.DEFAULT
-) -> List_int:
- ...
-
-
-def index_realsorted(
- seq: Iter_any,
- key: MaybeKeyType = None,
+ seq: Iterable[T],
+ key: Optional[Callable[[T], NatsortInType]] = None,
reverse: bool = False,
alg: NSType = ns.DEFAULT,
-) -> List_int:
+) -> List[int]:
"""
This is a wrapper around ``index_natsorted(seq, alg=ns.REAL)``.
@@ -683,10 +584,9 @@ def index_realsorted(
return index_natsorted(seq, key, reverse, alg | ns.REAL)
-# noinspection PyShadowingBuiltins,PyUnresolvedReferences
def order_by_index(
seq: Sequence[Any], index: Iterable[int], iter: bool = False
-) -> Iter_any:
+) -> Iterable[Any]:
"""
Order a given sequence by an index sequence.
@@ -764,7 +664,9 @@ def numeric_regex_chooser(alg: NSType) -> str:
return utils.regex_chooser(alg).pattern[1:-1]
-def _split_apply(v: Any, key: MaybeKeyType = None) -> Iterator[str]:
+def _split_apply(
+ v: Any, key: Optional[Callable[[T], NatsortInType]] = None
+) -> Iterator[str]:
if key is not None:
v = key(v)
return utils.path_splitter(str(v))
@@ -781,9 +683,12 @@ if platform.system() == "Windows":
_windows_sort_cmp.restype = wintypes.INT
_winsort_key = cmp_to_key(_windows_sort_cmp)
- def os_sort_keygen(key: MaybeKeyType = None) -> OSSortKeyType:
+ def os_sort_keygen(
+ key: Optional[Callable[[Any], NatsortInType]] = None
+ ) -> Callable[[Any], NatsortOutType]:
return cast(
- OSSortKeyType, lambda x: tuple(map(_winsort_key, _split_apply(x, key)))
+ Callable[[Any], NatsortOutType],
+ lambda x: tuple(map(_winsort_key, _split_apply(x, key))),
)
else:
@@ -802,15 +707,16 @@ else:
except ImportError:
# No ICU installed
- def os_sort_keygen(key: MaybeKeyType = None) -> OSSortKeyType:
- return cast(
- OSSortKeyType,
- natsort_keygen(key=key, alg=ns.LOCALE | ns.PATH | ns.IGNORECASE),
- )
+ def os_sort_keygen(
+ key: Optional[Callable[[Any], NatsortInType]] = None
+ ) -> Callable[[Any], NatsortOutType]:
+ return natsort_keygen(key=key, alg=ns.LOCALE | ns.PATH | ns.IGNORECASE)
else:
# ICU installed
- def os_sort_keygen(key: MaybeKeyType = None) -> OSSortKeyType:
+ def os_sort_keygen(
+ key: Optional[Callable[[Any], NatsortInType]] = None
+ ) -> Callable[[Any], NatsortOutType]:
loc = natsort.compat.locale.get_icu_locale()
collator = icu.Collator.createInstance(loc)
collator.setAttribute(
@@ -857,19 +763,11 @@ os_sort_keygen
"""
-@overload
-def os_sorted(seq: Iter_path, key: None = None, reverse: bool = False) -> List_path:
- ...
-
-
-@overload
-def os_sorted(seq: Iter_any, key: KeyType, reverse: bool = False) -> List_any:
- ...
-
-
def os_sorted(
- seq: Iter_any, key: MaybeKeyType = None, reverse: bool = False
-) -> List_any:
+ seq: Iterable[T],
+ key: Optional[Callable[[T], NatsortInType]] = None,
+ reverse: bool = False,
+) -> List[T]:
"""
Sort elements in the same order as your operating system's file browser
diff --git a/natsort/utils.py b/natsort/utils.py
index 2bceb85..1e38bec 100644
--- a/natsort/utils.py
+++ b/natsort/utils.py
@@ -53,6 +53,7 @@ from typing import (
Match,
Optional,
Pattern,
+ TYPE_CHECKING,
Tuple,
Union,
cast,
@@ -70,9 +71,28 @@ from natsort.compat.locale import (
from natsort.ns_enum import NSType, NS_DUMB, ns
from natsort.unicode_numbers import digits_no_decimals, numeric_no_decimals
+if TYPE_CHECKING:
+ from typing_extensions import Protocol
+else:
+ Protocol = object
+
#
# Pre-define a slew of aggregate types which makes the type hinting below easier
#
+
+
+class SupportsDunderLT(Protocol):
+ def __lt__(self, __other: Any) -> bool:
+ ...
+
+
+class SupportsDunderGT(Protocol):
+ def __gt__(self, __other: Any) -> bool:
+ ...
+
+
+Sortable = Union[SupportsDunderLT, SupportsDunderGT]
+
StrToStr = Callable[[str], str]
AnyCall = Callable[[Any], Any]
@@ -83,27 +103,20 @@ BytesTransform = Union[BytesTuple, NestedBytesTuple]
BytesTransformer = Callable[[bytes], BytesTransform]
# For the number transform factory
-NumType = Union[float, int]
-MaybeNumType = Optional[NumType]
-NumTuple = Tuple[StrOrBytes, NumType]
-NestedNumTuple = Tuple[NumTuple]
-StrNumTuple = Tuple[Tuple[str], NumTuple]
-NestedStrNumTuple = Tuple[StrNumTuple]
-MaybeNumTransform = Union[NumTuple, NestedNumTuple, StrNumTuple, NestedStrNumTuple]
-MaybeNumTransformer = Callable[[MaybeNumType], MaybeNumTransform]
+BasicTuple = Tuple[Any, ...]
+NestedAnyTuple = Tuple[BasicTuple, ...]
+AnyTuple = Union[BasicTuple, NestedAnyTuple]
+NumTransform = AnyTuple
+NumTransformer = Callable[[Any], NumTransform]
# For the string component transform factory
StrBytesNum = Union[str, bytes, float, int]
StrTransformer = Callable[[str], StrBytesNum]
# For the final data transform factory
-TwoBlankTuple = Tuple[Tuple[()], Tuple[()]]
-TupleOfAny = Tuple[Any, ...]
-TupleOfStrAnyPair = Tuple[Tuple[str], TupleOfAny]
-FinalTransform = Union[TwoBlankTuple, TupleOfAny, TupleOfStrAnyPair]
+FinalTransform = AnyTuple
FinalTransformer = Callable[[Iterable[Any], str], FinalTransform]
-# For the path splitter
PathArg = Union[str, PurePath]
MatchFn = Callable[[str], Optional[Match]]
@@ -115,13 +128,8 @@ StrParser = Callable[[PathArg], FinalTransform]
PathSplitter = Callable[[PathArg], Tuple[FinalTransform, ...]]
# For the natsort key
-StrBytesPathNum = Union[str, bytes, float, int, PurePath]
-NatsortInType = Union[
- Optional[StrBytesPathNum], Iterable[Union[Optional[StrBytesPathNum], Iterable[Any]]]
-]
-NatsortOutType = Tuple[
- Union[StrBytesNum, Tuple[Union[StrBytesNum, Tuple[Any, ...]], ...]], ...
-]
+NatsortInType = Optional[Sortable]
+NatsortOutType = Tuple[Sortable, ...]
KeyType = Callable[[Any], NatsortInType]
MaybeKeyType = Optional[KeyType]
@@ -260,7 +268,7 @@ def natsort_key(
key: None,
string_func: Union[StrParser, PathSplitter],
bytes_func: BytesTransformer,
- num_func: MaybeNumTransformer,
+ num_func: NumTransformer,
) -> NatsortOutType:
...
@@ -271,7 +279,7 @@ def natsort_key(
key: KeyType,
string_func: Union[StrParser, PathSplitter],
bytes_func: BytesTransformer,
- num_func: MaybeNumTransformer,
+ num_func: NumTransformer,
) -> NatsortOutType:
...
@@ -281,7 +289,7 @@ def natsort_key(
key: MaybeKeyType,
string_func: Union[StrParser, PathSplitter],
bytes_func: BytesTransformer,
- num_func: MaybeNumTransformer,
+ num_func: NumTransformer,
) -> NatsortOutType:
"""
Key to sort strings and numbers naturally.
@@ -348,7 +356,7 @@ def natsort_key(
# If that failed, it must be a number.
except TypeError:
- return num_func(cast(NumType, val))
+ return num_func(val)
def parse_bytes_factory(alg: NSType) -> BytesTransformer:
@@ -386,7 +394,7 @@ def parse_bytes_factory(alg: NSType) -> BytesTransformer:
def parse_number_or_none_factory(
alg: NSType, sep: StrOrBytes, pre_sep: str
-) -> MaybeNumTransformer:
+) -> NumTransformer:
"""
Create a function that will format a number (or None) into a tuple.
@@ -418,8 +426,8 @@ def parse_number_or_none_factory(
nan_replace = float("+inf") if alg & ns.NANLAST else float("-inf")
def func(
- val: MaybeNumType, _nan_replace: float = nan_replace, _sep: StrOrBytes = sep
- ) -> NumTuple:
+ val: Any, _nan_replace: float = nan_replace, _sep: StrOrBytes = sep
+ ) -> BasicTuple:
"""Given a number, place it in a tuple with a leading null string."""
return _sep, (_nan_replace if val != val or val is None else val)
@@ -729,7 +737,7 @@ def final_data_transform_factory(
"""
if alg & ns.UNGROUPLETTERS and alg & ns.LOCALEALPHA:
swap = alg & NS_DUMB and alg & ns.LOWERCASEFIRST
- transform = cast(StrToStr, methodcaller("swapcase")) if swap else _no_op
+ transform = cast(StrToStr, methodcaller("swapcase") if swap else _no_op)
def func(
split_val: Iterable[NatsortInType],
@@ -835,11 +843,11 @@ def do_decoding(s: bytes, encoding: str) -> str:
@overload
-def do_decoding(s: NatsortInType, encoding: str) -> NatsortInType:
+def do_decoding(s: Any, encoding: str) -> Any:
...
-def do_decoding(s: NatsortInType, encoding: str) -> NatsortInType:
+def do_decoding(s: Any, encoding: str) -> Any:
"""
Helper to decode a *bytes* object, or return the object as-is.
diff --git a/setup.cfg b/setup.cfg
index 6fd9d1d..a99791c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -64,6 +64,4 @@ exclude =
.venv
[mypy]
-
-[mypy-icu]
-ignore_missing_imports = True
+mypy_path = mypy_stubs
diff --git a/tests/test_os_sorted.py b/tests/test_os_sorted.py
index d0ecc79..f714437 100644
--- a/tests/test_os_sorted.py
+++ b/tests/test_os_sorted.py
@@ -3,7 +3,6 @@
Testing for the OS sorting
"""
import platform
-from typing import cast
import natsort
import pytest
@@ -44,7 +43,7 @@ def test_os_sorted_misc_no_fail() -> None:
def test_os_sorted_key() -> None:
given = ["foo0", "foo2", "goo1"]
expected = ["foo0", "goo1", "foo2"]
- result = natsort.os_sorted(given, key=lambda x: cast(str, x).replace("g", "f"))
+ result = natsort.os_sorted(given, key=lambda x: x.replace("g", "f"))
assert result == expected
diff --git a/tests/test_parse_number_function.py b/tests/test_parse_number_function.py
index e5f417d..85d6b96 100644
--- a/tests/test_parse_number_function.py
+++ b/tests/test_parse_number_function.py
@@ -7,7 +7,7 @@ import pytest
from hypothesis import given
from hypothesis.strategies import floats, integers
from natsort.ns_enum import NSType, ns
-from natsort.utils import MaybeNumTransformer, parse_number_or_none_factory
+from natsort.utils import NumTransformer, parse_number_or_none_factory
@pytest.mark.usefixtures("with_locale_en_us")
@@ -22,7 +22,7 @@ from natsort.utils import MaybeNumTransformer, parse_number_or_none_factory
)
@given(x=floats(allow_nan=False) | integers())
def test_parse_number_factory_makes_function_that_returns_tuple(
- x: Union[float, int], alg: NSType, example_func: MaybeNumTransformer
+ x: Union[float, int], alg: NSType, example_func: NumTransformer
) -> None:
parse_number_func = parse_number_or_none_factory(alg, "", "xx")
assert parse_number_func(x) == example_func(x)
diff --git a/tox.ini b/tox.ini
index 74a7066..19ab53d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -60,6 +60,7 @@ deps =
pytest
pytest-mock
fastnumbers
+ typing_extensions
commands =
mypy --strict natsort tests
skip_install = true