summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSeth Morton <seth.m.morton@gmail.com>2021-10-27 22:34:55 -0700
committerSeth Morton <seth.m.morton@gmail.com>2021-10-27 22:39:36 -0700
commit90c68f35105361130c39c0fb4c4f62933304ae17 (patch)
tree4c98f6d7163ff7b7655d91c78c4c85fd07560eb7
parent49aa573625df2abded05950afb22577356bdc8bf (diff)
downloadnatsort-90c68f35105361130c39c0fb4c4f62933304ae17.tar.gz
natsort now passes mypy with --strict
-rw-r--r--natsort/__init__.py2
-rw-r--r--natsort/__main__.py8
-rw-r--r--natsort/compat/fake_fastnumbers.py4
-rw-r--r--natsort/compat/fastnumbers.py2
-rw-r--r--natsort/compat/locale.py10
-rw-r--r--natsort/natsort.py83
-rw-r--r--natsort/utils.py52
-rw-r--r--tox.ini2
8 files changed, 97 insertions, 66 deletions
diff --git a/natsort/__init__.py b/natsort/__init__.py
index 266d75e..d09da73 100644
--- a/natsort/__init__.py
+++ b/natsort/__init__.py
@@ -14,7 +14,6 @@ from natsort.natsort import (
natsort_key,
natsort_keygen,
natsorted,
- ns,
numeric_regex_chooser,
order_by_index,
os_sort_key,
@@ -22,6 +21,7 @@ from natsort.natsort import (
os_sorted,
realsorted,
)
+from natsort.ns_enum import ns
from natsort.utils import KeyType, NatsortInType, NatsortOutType, chain_functions
__version__ = "7.1.1"
diff --git a/natsort/__main__.py b/natsort/__main__.py
index d4289eb..a91e08a 100644
--- a/natsort/__main__.py
+++ b/natsort/__main__.py
@@ -216,7 +216,11 @@ def check_filters(filters: NumPairIter) -> Optional[List[NumPair]]:
def keep_entry_range(
- entry: str, lows: NumIter, highs: NumIter, converter: NumConverter, regex: Pattern
+ entry: str,
+ lows: NumIter,
+ highs: NumIter,
+ converter: NumConverter,
+ regex: Pattern[str],
) -> bool:
"""
Check if an entry falls into a desired range.
@@ -250,7 +254,7 @@ def keep_entry_range(
def keep_entry_value(
- entry: str, values: NumIter, converter: NumConverter, regex: Pattern
+ entry: str, values: NumIter, converter: NumConverter, regex: Pattern[str]
) -> bool:
"""
Check if an entry does not match a given value.
diff --git a/natsort/compat/fake_fastnumbers.py b/natsort/compat/fake_fastnumbers.py
index 59b5a7c..5d44605 100644
--- a/natsort/compat/fake_fastnumbers.py
+++ b/natsort/compat/fake_fastnumbers.py
@@ -4,7 +4,7 @@ This module is intended to replicate some of the functionality
from the fastnumbers module in the event that module is not installed.
"""
import unicodedata
-from typing import Callable, FrozenSet, Union
+from typing import Callable, FrozenSet, Optional, Union
from natsort.unicode_numbers import decimal_chars
@@ -39,7 +39,7 @@ StrOrInt = Union[str, int]
def fast_float(
x: str,
key: Callable[[str], StrOrFloat] = lambda x: x,
- nan: StrOrFloat = None,
+ nan: Optional[StrOrFloat] = None,
_uni: Callable[[str, StrOrFloat], StrOrFloat] = unicodedata.numeric,
_nan_inf: FrozenSet[str] = NAN_INF,
_first_char: FrozenSet[str] = POTENTIAL_FIRST_CHAR,
diff --git a/natsort/compat/fastnumbers.py b/natsort/compat/fastnumbers.py
index ba94938..3b10fc8 100644
--- a/natsort/compat/fastnumbers.py
+++ b/natsort/compat/fastnumbers.py
@@ -5,6 +5,8 @@ having to worry if it is actually installed.
"""
import re
+__all__ = ["fast_float", "fast_int"]
+
def is_supported_fastnumbers(fastnumbers_version: str) -> bool:
match = re.match(
diff --git a/natsort/compat/locale.py b/natsort/compat/locale.py
index 1115339..9af5e7a 100644
--- a/natsort/compat/locale.py
+++ b/natsort/compat/locale.py
@@ -39,20 +39,20 @@ try: # noqa: C901
# If using icu, get the locale from the current global locale,
def get_icu_locale() -> str:
try:
- return icu.Locale(".".join(getlocale()))
+ return cast(str, icu.Locale(".".join(getlocale())))
except TypeError: # pragma: no cover
- return icu.Locale()
+ return cast(str, icu.Locale())
def get_strxfrm() -> TrxfmFunc:
- return icu.Collator.createInstance(get_icu_locale()).getSortKey
+ return cast(TrxfmFunc, icu.Collator.createInstance(get_icu_locale()).getSortKey)
def get_thousands_sep() -> str:
sep = icu.DecimalFormatSymbols.kGroupingSeparatorSymbol
- return icu.DecimalFormatSymbols(get_icu_locale()).getSymbol(sep)
+ return cast(str, icu.DecimalFormatSymbols(get_icu_locale()).getSymbol(sep))
def get_decimal_point() -> str:
sep = icu.DecimalFormatSymbols.kDecimalSeparatorSymbol
- return icu.DecimalFormatSymbols(get_icu_locale()).getSymbol(sep)
+ return cast(str, icu.DecimalFormatSymbols(get_icu_locale()).getSymbol(sep))
except ImportError:
diff --git a/natsort/natsort.py b/natsort/natsort.py
index 1dcf9ce..c5136dd 100644
--- a/natsort/natsort.py
+++ b/natsort/natsort.py
@@ -18,7 +18,6 @@ from typing import (
Optional,
Sequence,
Tuple,
- TypeVar,
Union,
cast,
overload,
@@ -28,25 +27,19 @@ import natsort.compat.locale
from natsort import utils
from natsort.compat.locale import StrOrBytes
from natsort.ns_enum import NS_DUMB, NS_t, ns
-from natsort.utils import KeyType, NatsortInType, NatsortOutType, PathArg
-
-# Some generic types
-T_ns = TypeVar("T_ns", bound=NatsortInType)
-T_path = TypeVar("T_path", bound=PathArg)
-T_any = TypeVar("T_any")
+from natsort.utils import KeyType, MaybeKeyType, NatsortInType, NatsortOutType, PathArg
# Keys that natsort accepts
-MaybeKeyType = Optional[KeyType]
PathKeyType = Callable[[Any_t], PathArg]
MaybePathKeyType = Optional[PathKeyType]
# Common input and output types
-Iter_ns = Iterable[T_ns]
-Iter_any = Iterable[T_any]
-Iter_path = Iterable[T_path]
-List_ns = List[T_ns]
-List_any = List[T_any]
-List_path = List[T_path]
+Iter_ns = Iterable[NatsortInType]
+Iter_any = Iterable[Any_t]
+Iter_path = Iterable[PathArg]
+List_ns = List[NatsortInType]
+List_any = List[Any_t]
+List_path = List[PathArg]
List_int = List[int]
# The type that natsort_key returns
@@ -144,7 +137,7 @@ def as_utf8(s: NatsortInType) -> NatsortInType:
return utils.do_decoding(s, "utf-8")
-def natsort_keygen(key=MaybeKeyType, alg: NS_t = ns.DEFAULT) -> NatsortKeyType:
+def natsort_keygen(key: MaybeKeyType = None, alg: NS_t = ns.DEFAULT) -> NatsortKeyType:
"""
Generate a key to sort strings and numbers naturally.
@@ -260,17 +253,17 @@ def natsorted(seq: Iter_ns) -> List_ns:
@overload
-def natsorted(seq: Iter_ns, reverse: bool) -> List_ns:
+def natsorted(seq: Iter_ns, *, reverse: bool) -> List_ns:
...
@overload
-def natsorted(seq: Iter_ns, alg: NS_t) -> List_ns:
+def natsorted(seq: Iter_ns, *, alg: NS_t) -> List_ns:
...
@overload
-def natsorted(seq: Iter_ns, reverse: bool, alg: NS_t) -> List_ns:
+def natsorted(seq: Iter_ns, *, reverse: bool, alg: NS_t) -> List_ns:
...
@@ -285,7 +278,7 @@ def natsorted(seq: Iter_ns, key: None, reverse: bool) -> List_ns:
@overload
-def natsorted(seq: Iter_ns, key: None, alg: NS_t) -> List_ns:
+def natsorted(seq: Iter_ns, key: None, *, alg: NS_t) -> List_ns:
...
@@ -305,7 +298,7 @@ def natsorted(seq: Iter_any, key: KeyType, reverse: bool) -> List_any:
@overload
-def natsorted(seq: Iter_any, key: KeyType, alg: NS_t) -> List_any:
+def natsorted(seq: Iter_any, key: KeyType, *, alg: NS_t) -> List_any:
...
@@ -314,7 +307,12 @@ def natsorted(seq: Iter_any, key: KeyType, reverse: bool, alg: NS_t) -> List_any
...
-def natsorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
+def natsorted(
+ seq: Iter_any,
+ key: MaybeKeyType = None,
+ reverse: bool = False,
+ alg: NS_t = ns.DEFAULT,
+) -> List_any:
"""
Sorts an iterable naturally.
@@ -359,12 +357,14 @@ def natsorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
['num2', 'num3', 'num5']
"""
- key = natsort_keygen(key, alg)
- return sorted(seq, reverse=reverse, key=key)
+ return sorted(seq, reverse=reverse, key=natsort_keygen(key, alg))
def humansorted(
- seq: Iter_any, key=MaybeKeyType, reverse: bool = False, alg: NS_t = ns.DEFAULT
+ seq: Iter_any,
+ key: MaybeKeyType = None,
+ reverse: bool = False,
+ alg: NS_t = ns.DEFAULT,
) -> List_any:
"""
Convenience function to properly sort non-numeric characters.
@@ -481,17 +481,17 @@ def index_natsorted(seq: Iter_ns) -> List_int:
@overload
-def index_natsorted(seq: Iter_ns, reverse: bool) -> List_int:
+def index_natsorted(seq: Iter_ns, *, reverse: bool) -> List_int:
...
@overload
-def index_natsorted(seq: Iter_ns, alg: NS_t) -> List_int:
+def index_natsorted(seq: Iter_ns, *, alg: NS_t) -> List_int:
...
@overload
-def index_natsorted(seq: Iter_ns, reverse: bool, alg: NS_t) -> List_int:
+def index_natsorted(seq: Iter_ns, *, reverse: bool, alg: NS_t) -> List_int:
...
@@ -506,7 +506,7 @@ def index_natsorted(seq: Iter_ns, key: None, reverse: bool) -> List_int:
@overload
-def index_natsorted(seq: Iter_ns, key: None, alg: NS_t) -> List_int:
+def index_natsorted(seq: Iter_ns, key: None, *, alg: NS_t) -> List_int:
...
@@ -526,7 +526,7 @@ def index_natsorted(seq: Iter_any, key: KeyType, reverse: bool) -> List_int:
@overload
-def index_natsorted(seq: Iter_any, key: KeyType, alg: NS_t) -> List_int:
+def index_natsorted(seq: Iter_any, key: KeyType, *, alg: NS_t) -> List_int:
...
@@ -535,7 +535,12 @@ def index_natsorted(seq: Iter_any, key: KeyType, reverse: bool, alg: NS_t) -> Li
...
-def index_natsorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
+def index_natsorted(
+ seq: Iter_any,
+ key: MaybeKeyType = None,
+ reverse: bool = False,
+ alg: NS_t = ns.DEFAULT,
+) -> List_int:
"""
Determine the list of the indexes used to sort the input sequence.
@@ -592,12 +597,12 @@ def index_natsorted(seq, key=None, reverse=False, alg=ns.DEFAULT):
"""
newkey: KeyType
- if key is None:
+ if key is not None:
newkey = itemgetter(1)
else:
- def newkey(x):
- return key(itemgetter(1)(x))
+ def newkey(x: Any_t) -> NatsortInType:
+ return cast(KeyType, 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)]
@@ -711,7 +716,7 @@ def index_realsorted(
# noinspection PyShadowingBuiltins,PyUnresolvedReferences
def order_by_index(
- seq: Sequence[T_any], index: Iterable[int], iter: bool = False
+ seq: Sequence[Any_t], index: Iterable[int], iter: bool = False
) -> Iter_any:
"""
Order a given sequence by an index sequence.
@@ -830,7 +835,9 @@ else:
except ImportError:
# No ICU installed
def os_sort_keygen(key: MaybePathKeyType = None) -> OSSortKeyType:
- return natsort_keygen(key=key, alg=ns.LOCALE | ns.PATH | ns.IGNORECASE)
+ return natsort_keygen(
+ key=cast(MaybeKeyType, key), alg=ns.LOCALE | ns.PATH | ns.IGNORECASE
+ )
else:
# ICU installed
@@ -887,7 +894,7 @@ def os_sorted(seq: Iter_path) -> List_path:
@overload
-def os_sorted(seq: Iter_path, reverse: bool) -> List_path:
+def os_sorted(seq: Iter_path, *, reverse: bool) -> List_path:
...
@@ -911,7 +918,9 @@ def os_sorted(seq: Iter_any, key: PathKeyType, reverse: bool) -> List_any:
...
-def os_sorted(seq, key=None, reverse=False):
+def os_sorted(
+ seq: Iter_any, key: MaybePathKeyType = None, reverse: bool = False
+) -> List_any:
"""
Sort elements in the same order as your operating system's file browser
diff --git a/natsort/utils.py b/natsort/utils.py
index 2d15e39..27791d7 100644
--- a/natsort/utils.py
+++ b/natsort/utils.py
@@ -115,9 +115,18 @@ MatchFn = Callable[[str], Optional[Match]]
PathSplitter = Callable[[PathArg], Tuple[FinalTransform, ...]]
# For the natsort key
-NatsortInType = Union[StrBytesNum, Iterable[Union[StrBytesNum, Iterable]]]
-NatsortOutType = Tuple[Union[StrBytesNum, Tuple[Union[StrBytesNum, tuple], ...]], ...]
+NatsortIterType = Iterable[Union[StrBytesNum, Iterable[Any_t]]]
+NatsortInType = Union[StrBytesNum, NatsortIterType]
+NatsortOutElement = Union[
+ FinalTransform,
+ Tuple[FinalTransform],
+ StrBytesNum,
+ MaybeNumTransform,
+ BytesTransform,
+]
+NatsortOutType = Union[NatsortOutElement, Tuple[Union[NatsortOutElement, tuple], ...]]
KeyType = Callable[[Any_t], NatsortInType]
+MaybeKeyType = Optional[KeyType]
class NumericalRegularExpressions:
@@ -139,42 +148,42 @@ class NumericalRegularExpressions:
float_num: str = r"(?:\d+\.?\d*|\.\d+)"
@classmethod
- def _construct_regex(cls, fmt: str) -> Pattern:
+ def _construct_regex(cls, fmt: str) -> Pattern[str]:
"""Given a format string, construct the regex with class attributes."""
return re.compile(fmt.format(**vars(cls)), flags=re.U)
@classmethod
- def int_sign(cls) -> Pattern:
+ def int_sign(cls) -> Pattern[str]:
"""Regular expression to match a signed int."""
return cls._construct_regex(r"([-+]?\d+|[{digits}])")
@classmethod
- def int_nosign(cls) -> Pattern:
+ def int_nosign(cls) -> Pattern[str]:
"""Regular expression to match an unsigned int."""
return cls._construct_regex(r"(\d+|[{digits}])")
@classmethod
- def float_sign_exp(cls) -> Pattern:
+ def float_sign_exp(cls) -> Pattern[str]:
"""Regular expression to match a signed float with exponent."""
return cls._construct_regex(r"([-+]?{float_num}{exp}|[{numeric}])")
@classmethod
- def float_nosign_exp(cls) -> Pattern:
+ def float_nosign_exp(cls) -> Pattern[str]:
"""Regular expression to match an unsigned float with exponent."""
return cls._construct_regex(r"({float_num}{exp}|[{numeric}])")
@classmethod
- def float_sign_noexp(cls) -> Pattern:
+ def float_sign_noexp(cls) -> Pattern[str]:
"""Regular expression to match a signed float without exponent."""
return cls._construct_regex(r"([-+]?{float_num}|[{numeric}])")
@classmethod
- def float_nosign_noexp(cls) -> Pattern:
+ def float_nosign_noexp(cls) -> Pattern[str]:
"""Regular expression to match an unsigned float without exponent."""
return cls._construct_regex(r"({float_num}|[{numeric}])")
-def regex_chooser(alg: NS_t) -> Pattern:
+def regex_chooser(alg: NS_t) -> Pattern[str]:
"""
Select an appropriate regex for the type of number of interest.
@@ -251,7 +260,13 @@ def natsort_key(
...
-def natsort_key(val, key, string_func, bytes_func, num_func):
+def natsort_key(
+ val: Union[NatsortInType, Any_t],
+ key: MaybeKeyType,
+ string_func: Union[StrParser, PathSplitter],
+ bytes_func: BytesTransformer,
+ num_func: MaybeNumTransformer,
+) -> NatsortOutType:
"""
Key to sort strings and numbers naturally.
@@ -300,23 +315,24 @@ def natsort_key(val, key, string_func, bytes_func, num_func):
# Assume the input are strings, which is the most common case
try:
- return string_func(val)
+ return string_func(cast(str, val))
except (TypeError, AttributeError):
# If bytes type, use the bytes_func
if type(val) in (bytes,):
- return bytes_func(val)
+ return bytes_func(cast(bytes, val))
# Otherwise, assume it is an iterable that must be parsed recursively.
# Do not apply the key recursively.
try:
return tuple(
- natsort_key(x, None, string_func, bytes_func, num_func) for x in val
+ natsort_key(x, None, string_func, bytes_func, num_func)
+ for x in cast(NatsortIterType, val)
)
# If that failed, it must be a number.
except TypeError:
- return num_func(val)
+ return num_func(cast(NumType, val))
def parse_bytes_factory(alg: NS_t) -> BytesTransformer:
@@ -502,7 +518,7 @@ def parse_path_factory(str_split: StrParser) -> PathSplitter:
return lambda x: tuple(map(str_split, path_splitter(x)))
-def sep_inserter(iterator: Iterator, sep: StrOrBytes) -> Iterator:
+def sep_inserter(iterator: Iterator[Any_t], sep: StrOrBytes) -> Iterator[Any_t]:
"""
Insert '' between numbers in an iterator.
@@ -801,7 +817,7 @@ def do_decoding(s: NatsortInType, encoding: str) -> NatsortInType:
...
-def do_decoding(s, encoding):
+def do_decoding(s: NatsortInType, encoding: str) -> NatsortInType:
"""
Helper to decode a *bytes* object, or return the object as-is.
@@ -819,7 +835,7 @@ def do_decoding(s, encoding):
"""
try:
- return s.decode(encoding)
+ return cast(bytes, s).decode(encoding)
except (AttributeError, TypeError):
return s
diff --git a/tox.ini b/tox.ini
index 9db8e4e..fb2c58f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -57,7 +57,7 @@ skip_install = true
deps =
mypy
commands =
- mypy natsort
+ mypy --strict natsort
skip_install = true
# Build documentation.