Skip to content

Commit 25d7c6f

Browse files
committed
Typing fixes
- Do not use generic tuple for frozenlist - Fix various mypy errors - Use protocols instead of returning generic types
1 parent 57478df commit 25d7c6f

18 files changed

+287
-106
lines changed

expression/collections/asyncseq.py

+16-6
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,17 @@ def range(cls, stop: int) -> AsyncIterable[int]:
2626

2727
@overload
2828
@classmethod
29-
def range(cls, start: int, stop: int, step: Optional[int]) -> AsyncIterable[int]:
29+
def range(cls, start: int, stop: int) -> AsyncIterable[int]:
30+
...
31+
32+
@overload
33+
@classmethod
34+
def range(cls, start: int, stop: int, step: int) -> AsyncIterable[int]:
3035
...
3136

3237
@classmethod
33-
def range(cls, *args: Any) -> AsyncIterable[int]:
34-
return AsyncSeq(range(*args))
38+
def range(cls, *args: Any, **kw: Any) -> AsyncIterable[int]:
39+
return AsyncSeq(range(*args, **kw))
3540

3641
def __aiter__(self) -> AsyncIterator[TSource]:
3742
return self._ai.__aiter__()
@@ -63,12 +68,17 @@ async def range(stop: int) -> AsyncIterable[int]:
6368

6469

6570
@overload
66-
async def range(start: int, stop: int, step: Optional[int]) -> AsyncIterable[int]:
71+
async def range(start: int, stop: int) -> AsyncIterable[int]:
72+
...
73+
74+
75+
@overload
76+
async def range(start: int, stop: int, step: int) -> AsyncIterable[int]:
6777
...
6878

6979

70-
async def range(*args: Any) -> AsyncIterable[int]:
71-
for value in builtins.range(*args):
80+
async def range(*args: Any, **kw: Any) -> Any:
81+
for value in builtins.range(*args, **kw):
7282
yield value
7383

7484

expression/collections/frozenlist.py

+90-20
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,21 @@
2020

2121
import builtins
2222
import functools
23-
from typing import Any, Callable, Iterable, List, Optional, Tuple, TypeVar, cast, get_origin, overload
23+
from typing import (
24+
Any,
25+
Callable,
26+
Generic,
27+
Iterable,
28+
Iterator,
29+
List,
30+
Optional,
31+
Protocol,
32+
Tuple,
33+
TypeVar,
34+
cast,
35+
get_origin,
36+
overload,
37+
)
2438

2539
from expression.core import Case, Nothing, Option, Some, pipe
2640

@@ -37,7 +51,7 @@
3751
T6 = TypeVar("T6")
3852

3953

40-
class FrozenList(Tuple[TSource]):
54+
class FrozenList(Generic[TSource]):
4155
"""Immutable list type.
4256
4357
Is faster than `List` for prepending, but slower for
@@ -55,6 +69,11 @@ class FrozenList(Tuple[TSource]):
5569
>>> ys = empty.cons(1).cons(2).cons(3).cons(4).cons(5)
5670
"""
5771

72+
def __init__(self, value: Optional[Iterable[TSource]] = None) -> None:
73+
# Use composition instead of inheritance since generic tuples
74+
# are not suppored by mypy.
75+
self.value: Tuple[TSource, ...] = tuple(value) if value else tuple()
76+
5877
def match(self, pattern: Any) -> Any:
5978
case: Case[TSource] = Case(self)
6079
return case(pattern) if pattern else case
@@ -113,7 +132,7 @@ def pipe(self, *args: Any) -> Any:
113132
def append(self, other: "FrozenList[TSource]") -> "FrozenList[TSource]":
114133
"""Append frozen list to end of the frozen list."""
115134

116-
return FrozenList(self + other)
135+
return FrozenList(self.value + other.value)
117136

118137
def choose(self, chooser: Callable[[TSource], Option[TResult]]) -> "FrozenList[TResult]":
119138
"""Choose items from the list.
@@ -136,14 +155,14 @@ def mapper(x: TSource) -> FrozenList[TResult]:
136155
return self.collect(mapper)
137156

138157
def collect(self, mapping: Callable[[TSource], "FrozenList[TResult]"]) -> "FrozenList[TResult]":
139-
mapped = builtins.map(mapping, self)
158+
mapped = builtins.map(mapping, self.value)
140159
xs = (y for x in mapped for y in x)
141160
return FrozenList(xs)
142161

143162
def cons(self, element: TSource) -> "FrozenList[TSource]":
144163
"""Add element to front of List."""
145164

146-
return FrozenList((element,) + self) # NOTE: Faster than (element, *self)
165+
return FrozenList((element,) + self.value) # NOTE: Faster than (element, *self)
147166

148167
def filter(self, predicate: Callable[[TSource], bool]) -> "FrozenList[TSource]":
149168
"""Filter list.
@@ -158,7 +177,7 @@ def filter(self, predicate: Callable[[TSource], bool]) -> "FrozenList[TSource]":
158177
A list containing only the elements that satisfy the
159178
predicate.
160179
"""
161-
return FrozenList(builtins.filter(predicate, self))
180+
return FrozenList(builtins.filter(predicate, self.value))
162181

163182
def fold(self, folder: Callable[[TState, TSource], TState], state: TState) -> TState:
164183
"""Applies a function to each element of the collection,
@@ -263,7 +282,12 @@ def range(stop: int) -> "FrozenList[int]":
263282

264283
@overload
265284
@staticmethod
266-
def range(start: int, stop: int, step: Optional[int] = None) -> "FrozenList[int]":
285+
def range(start: int, stop: int) -> "FrozenList[int]":
286+
...
287+
288+
@overload
289+
@staticmethod
290+
def range(start: int, stop: int, step: int) -> "FrozenList[int]":
267291
...
268292

269293
@staticmethod
@@ -283,15 +307,15 @@ def skip(self, count: int) -> "FrozenList[TSource]":
283307
Returns:
284308
The list after removing the first N elements.
285309
"""
286-
return FrozenList(self[count:])
310+
return FrozenList(self.value[count:])
287311

288312
def skip_last(self, count: int) -> "FrozenList[TSource]":
289-
return FrozenList(self[:-count])
313+
return FrozenList(self.value[:-count])
290314

291315
def tail(self) -> "FrozenList[TSource]":
292316
"""Return tail of List."""
293317

294-
_, *tail = self
318+
_, *tail = self.value
295319
return FrozenList(tail)
296320

297321
def take(self, count: int) -> "FrozenList[TSource]":
@@ -303,7 +327,7 @@ def take(self, count: int) -> "FrozenList[TSource]":
303327
Returns:
304328
The result list.
305329
"""
306-
return FrozenList(self[:count])
330+
return FrozenList(self.value[:count])
307331

308332
def take_last(self, count: int) -> "FrozenList[TSource]":
309333
"""Returns a specified number of contiguous elements from the
@@ -315,13 +339,17 @@ def take_last(self, count: int) -> "FrozenList[TSource]":
315339
Returns:
316340
The result list.
317341
"""
318-
return FrozenList(self[-count:])
342+
return FrozenList(self.value[-count:])
319343

320344
def try_head(self) -> Option[TSource]:
321345
"""Returns the first element of the list, or None if the list is
322346
empty.
323347
"""
324-
return Some(self[0]) if self else Nothing
348+
if self.value:
349+
value = cast("TSource", self.value[0])
350+
return Some(value)
351+
352+
return Nothing
325353

326354
@staticmethod
327355
def unfold(generator: Callable[[TState], Option[Tuple[TSource, TState]]], state: TState) -> "FrozenList[TSource]":
@@ -352,7 +380,30 @@ def zip(self, other: "FrozenList[TResult]") -> "FrozenList[Tuple[TSource, TResul
352380
A single list containing pairs of matching elements from the
353381
input lists.
354382
"""
355-
return of_seq(builtins.zip(self, other))
383+
return of_seq(builtins.zip(self.value, other.value))
384+
385+
def __add__(self, other: "FrozenList[TSource]") -> "FrozenList[TSource]":
386+
return FrozenList(self.value + other.value)
387+
388+
@overload
389+
def __getitem__(self, key: slice) -> "FrozenList[TSource]":
390+
...
391+
392+
@overload
393+
def __getitem__(self, key: int) -> TSource:
394+
...
395+
396+
def __getitem__(self, key: Any) -> Any:
397+
return self.value[key]
398+
399+
def __iter__(self) -> Iterator[TSource]:
400+
return iter(self.value)
401+
402+
def __eq__(self, other: Any) -> bool:
403+
return self.value == other
404+
405+
def __len__(self) -> int:
406+
return len(self.value)
356407

357408
def __match__(self, pattern: Any) -> Iterable[List[TSource]]:
358409
if self == pattern:
@@ -374,6 +425,20 @@ def __repr__(self) -> str:
374425
return str(self)
375426

376427

428+
class ExitFn(Protocol):
429+
"""A partially applied exit function."""
430+
431+
def __call__(self, source: FrozenList[TSource]) -> TSource:
432+
...
433+
434+
435+
class FilterFn(Protocol):
436+
"""A partially applied filter function."""
437+
438+
def __call__(self, source: FrozenList[TSource]) -> FrozenList[TSource]:
439+
...
440+
441+
377442
def append(source: FrozenList[TSource]) -> Callable[[FrozenList[TSource]], FrozenList[TSource]]:
378443
def _append(other: FrozenList[TSource]) -> FrozenList[TSource]:
379444
return source.append(other)
@@ -513,7 +578,7 @@ def indexed(source: FrozenList[TSource]) -> FrozenList[Tuple[int, TSource]]:
513578
return source.indexed()
514579

515580

516-
def item(index: int) -> Callable[[FrozenList[TSource]], TSource]:
581+
def item(index: int) -> ExitFn:
517582
"""Indexes into the list. The first element has index 0.
518583
519584
Args:
@@ -575,7 +640,12 @@ def range(stop: int) -> FrozenList[int]:
575640

576641

577642
@overload
578-
def range(start: int, stop: int, step: Optional[int] = None) -> FrozenList[int]:
643+
def range(start: int, stop: int) -> FrozenList[int]:
644+
...
645+
646+
647+
@overload
648+
def range(start: int, stop: int, step: int) -> FrozenList[int]:
579649
...
580650

581651

@@ -587,7 +657,7 @@ def singleton(value: TSource) -> FrozenList[TSource]:
587657
return FrozenList((value,))
588658

589659

590-
def skip(count: int) -> Callable[[FrozenList[TSource]], FrozenList[TSource]]:
660+
def skip(count: int) -> FilterFn:
591661
"""Returns the list after removing the first N elements.
592662
593663
Args:
@@ -603,7 +673,7 @@ def _skip(source: FrozenList[TSource]) -> FrozenList[TSource]:
603673
return _skip
604674

605675

606-
def skip_last(count: int) -> Callable[[FrozenList[TSource]], FrozenList[TSource]]:
676+
def skip_last(count: int) -> FilterFn:
607677
"""Returns the list after removing the last N elements.
608678
609679
Args:
@@ -623,7 +693,7 @@ def tail(source: FrozenList[TSource]) -> FrozenList[TSource]:
623693
return source.tail()
624694

625695

626-
def take(count: int) -> Callable[[FrozenList[TSource]], FrozenList[TSource]]:
696+
def take(count: int) -> FilterFn:
627697
"""Returns the first N elements of the list.
628698
629699
Args:
@@ -639,7 +709,7 @@ def _take(source: FrozenList[TSource]) -> FrozenList[TSource]:
639709
return _take
640710

641711

642-
def take_last(count: int) -> Callable[[FrozenList[TSource]], FrozenList[TSource]]:
712+
def take_last(count: int) -> FilterFn:
643713
"""Returns a specified number of contiguous elements from the end of
644714
the list.
645715

expression/collections/map.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ def __len__(self) -> int:
260260
"""Return the number of bindings in the map."""
261261
return maptree.size(self._tree)
262262

263-
def __contains__(self, key: Key) -> bool:
263+
def __contains__(self, key: Any) -> bool:
264264
return self.contains_key(key)
265265

266266
def __eq__(self, other: Any) -> bool:
@@ -286,7 +286,8 @@ def __bool__(self) -> bool:
286286
def __str__(self) -> str:
287287
def to_str(item: Tuple[Key, Value]) -> str:
288288
key, value = item
289-
key = f'"{key}"' if isinstance(key, str) else key
289+
if isinstance(key, str):
290+
return f'("{key}", {value})'
290291
return f"({key}, {value})"
291292

292293
items = pipe(self.to_seq(), seq.map(to_str))

expression/collections/maptree.py

+6-12
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@
2424
"""
2525
import builtins
2626
from dataclasses import dataclass
27-
from typing import Any, Callable, Generic, Iterable, Iterator, Tuple, TypeVar, cast
27+
from typing import (Any, Callable, Generic, Iterable, Iterator, Tuple, TypeVar,
28+
cast)
2829

29-
from expression.core import Nothing, Option, Some, SupportsLessThan, failwith, pipe
30+
from expression.core import (Nothing, Option, Some, SupportsLessThan, failwith,
31+
pipe)
3032

3133
from . import frozenlist, seq
3234
from .frozenlist import FrozenList
@@ -47,10 +49,6 @@ class MapTreeLeaf(Generic[Key, Value]):
4749

4850
@dataclass
4951
class MapTreeNode(MapTreeLeaf[Key, Value]):
50-
# TODO: Remove key and value here when pylance bug fixed
51-
key: Key
52-
value: Value
53-
5452
left: MapTree[Key, Value]
5553
right: MapTree[Key, Value]
5654

@@ -467,12 +465,8 @@ def mk_from_iterator(acc: MapTree[Key, Value], e: Iterator[Tuple[Key, Value]]) -
467465

468466

469467
def of_seq(xs: Iterable[Tuple[Key, Value]]) -> MapTree[Key, Value]:
470-
if isinstance(xs, FrozenList):
471-
xs = cast(FrozenList[Tuple[Key, Value]], xs)
472-
return of_list(xs)
473-
else:
474-
ie = builtins.iter(xs)
475-
return mk_from_iterator(empty, ie)
468+
ie = builtins.iter(xs)
469+
return mk_from_iterator(empty, ie)
476470

477471

478472
# Imperative left-to-right iterators.

0 commit comments

Comments
 (0)