|
2 | 2 |
|
3 | 3 | import re
|
4 | 4 | import traceback
|
5 |
| -from typing import Any, Callable, Dict, Iterator, Optional, Type |
| 5 | +from typing import Any, Callable, Dict, Iterable, Iterator, Optional, Type |
6 | 6 |
|
7 | 7 |
|
8 | 8 | def is_iterable(obj):
|
@@ -52,46 +52,63 @@ def method(*args, **kwargs):
|
52 | 52 |
|
53 | 53 |
|
54 | 54 | class Pipe(metaclass=PipeMeta):
|
55 |
| - functions: Dict[str, Type[Function]] = {} |
| 55 | + _functions: Dict[str, Type[Function]] = {} |
56 | 56 |
|
57 | 57 | def __init__(
|
58 | 58 | self,
|
59 | 59 | *iterable,
|
60 |
| - function: Optional[Callable] = identity, |
61 |
| - handler: Optional[Callable] = log_traceback_and_continue, |
| 60 | + function: Callable = identity, |
| 61 | + handler: Callable = log_traceback_and_continue, |
62 | 62 | ):
|
63 | 63 | if len(iterable) == 1 and is_iterable(iterable[0]):
|
64 | 64 | iterable = iterable[0]
|
65 |
| - self.iterable = iterable |
66 |
| - self.function = function |
67 |
| - self.handler = handler |
| 65 | + self._iterable: Iterable = iterable |
| 66 | + self._function = function |
| 67 | + self._handler = handler |
68 | 68 |
|
69 | 69 | def __iter__(self):
|
70 |
| - for item in self.function(iter(self.iterable)): |
| 70 | + for item in self._function(iter(self._iterable)): |
71 | 71 | try:
|
72 | 72 | yield item
|
73 | 73 | except Exception as e:
|
74 |
| - self.handler(e) |
| 74 | + self._handler(e) |
75 | 75 |
|
76 |
| - def close(self): |
77 |
| - # Necessary to use `yield from` on a Pipe object |
78 |
| - pass |
79 |
| - |
80 |
| - def list(self): |
81 |
| - return list(self) |
| 76 | + def _new_pipe( |
| 77 | + self, |
| 78 | + iterable: Optional[Iterable] = None, |
| 79 | + function: Optional[Callable] = None, |
| 80 | + functions: Optional[Dict[str, Type[Function]]] = None, |
| 81 | + handler: Optional[Callable] = None, |
| 82 | + ): |
| 83 | + # This is for composition intead of init so that init can be customized |
| 84 | + pipe = self.__class__() |
| 85 | + pipe._iterable = iterable or self._iterable |
| 86 | + pipe._function = function or self._function |
| 87 | + pipe._functions = functions or self._functions |
| 88 | + pipe._handler = handler or self._handler |
| 89 | + return pipe |
82 | 90 |
|
83 | 91 | def __getattr__(self, name):
|
84 | 92 | def method(*args, **kwargs):
|
85 |
| - cls = self.__class__ |
86 |
| - function = self.functions[name](*args, **kwargs) |
87 |
| - return cls(self.iterable, function=compose(self.function, function)) |
| 93 | + return self._new_pipe( |
| 94 | + function=compose(self._function, self._functions[name](*args, **kwargs)) |
| 95 | + ) |
88 | 96 |
|
89 | 97 | return method
|
90 | 98 |
|
91 | 99 | def __call__(self, *iterable):
|
92 |
| - return self.__class__(*iterable, function=self.function) |
| 100 | + if len(iterable) == 1 and is_iterable(iterable[0]): |
| 101 | + iterable = iterable[0] |
| 102 | + return self._new_pipe(iterable=iterable) |
| 103 | + |
| 104 | + def close(self): |
| 105 | + # Necessary to use `yield from` on a Pipe object |
| 106 | + pass |
| 107 | + |
| 108 | + def list(self): |
| 109 | + return list(self) |
93 | 110 |
|
94 | 111 | @classmethod
|
95 | 112 | def add_fn(cls, fn: Type[Function], name: Optional[str] = None):
|
96 | 113 | fn_name = camelcase_to_snakecase(fn.__name__) if name is None else name
|
97 |
| - cls.functions[fn_name] = fn |
| 114 | + cls._functions[fn_name] = fn |
0 commit comments