diff options
author | Seth Morton <seth.m.morton@gmail.com> | 2021-10-27 22:34:55 -0700 |
---|---|---|
committer | Seth Morton <seth.m.morton@gmail.com> | 2021-10-27 22:39:36 -0700 |
commit | 90c68f35105361130c39c0fb4c4f62933304ae17 (patch) | |
tree | 4c98f6d7163ff7b7655d91c78c4c85fd07560eb7 | |
parent | 49aa573625df2abded05950afb22577356bdc8bf (diff) | |
download | natsort-90c68f35105361130c39c0fb4c4f62933304ae17.tar.gz |
natsort now passes mypy with --strict
-rw-r--r-- | natsort/__init__.py | 2 | ||||
-rw-r--r-- | natsort/__main__.py | 8 | ||||
-rw-r--r-- | natsort/compat/fake_fastnumbers.py | 4 | ||||
-rw-r--r-- | natsort/compat/fastnumbers.py | 2 | ||||
-rw-r--r-- | natsort/compat/locale.py | 10 | ||||
-rw-r--r-- | natsort/natsort.py | 83 | ||||
-rw-r--r-- | natsort/utils.py | 52 | ||||
-rw-r--r-- | tox.ini | 2 |
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 @@ -57,7 +57,7 @@ skip_install = true deps = mypy commands = - mypy natsort + mypy --strict natsort skip_install = true # Build documentation. |