From 9483cf0531ca4861b10e4269bd2aa2c48e326209 Mon Sep 17 00:00:00 2001 From: Morgana Date: Thu, 29 Jan 2026 16:58:18 -0600 Subject: [PATCH] Unittests and stream apply --- .gitignore | 3 ++- src/morgslib/morgslib.py | 16 +++++++++++++--- src/morgslib/msl_tests.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 src/morgslib/msl_tests.py diff --git a/.gitignore b/.gitignore index 7773828..567e06a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -dist/ \ No newline at end of file +dist/ +__pycache__/ \ No newline at end of file diff --git a/src/morgslib/morgslib.py b/src/morgslib/morgslib.py index c5aebd4..f5b727e 100644 --- a/src/morgslib/morgslib.py +++ b/src/morgslib/morgslib.py @@ -24,14 +24,16 @@ Tn = TypeVar('Tn') class Stream(Generic[T]): iter: Iterator[T] + singleton: bool = False - def __init__(self, base: Iterable[T]|Iterator[T]) -> None: + def __init__(self, base: Iterable[T]|Iterator[T]|T) -> None: if isinstance(base, Iterator): self.iter = base elif isinstance(base, Iterable): self.iter = iter(base) else: - raise TypeError("Stream base must be an Iterable or Iterator!") # pyright: ignore[reportUnreachable] + self.iter = iter((base, )) + singleton = True def __iter__(self) -> Self: return self @@ -48,14 +50,22 @@ class Stream(Generic[T]): def first(self, fxn: Callable[[T], bool]) -> T | None: return next(filter(fxn, self.iter), None) - def groupby(self, fxn: Callable[[T], Tn]) -> Stream[tuple[Tn, list[T]]]: + def group(self, fxn: Callable[[T], Tn]) -> Stream[tuple[Tn, list[T]]]: out: defaultdict[Tn, list[T]] = defaultdict(list) for item in self.iter: out[fxn(item)].append(item) return Stream(out.items()) + def apply(self, fxn: Callable[[Iterator[T]], Iterable[Tn]|Tn]) -> Stream[Tn]: + return Stream(fxn(self.iter)) + + def apply_single(self, fxn: Callable[[T], Iterable[Tn]|Tn]) -> Stream[Tn]: + return Stream(fxn(next(self.iter))) + def to(self, fxn: Callable) -> Any: return fxn(self.iter) + def to_single(self, fxn: Callable) -> Any: + return fxn(next(self.iter)) def print(self, sep=","): print(*self.iter, sep=sep) \ No newline at end of file diff --git a/src/morgslib/msl_tests.py b/src/morgslib/msl_tests.py new file mode 100644 index 0000000..f4cedeb --- /dev/null +++ b/src/morgslib/msl_tests.py @@ -0,0 +1,32 @@ +import unittest + +from morgslib import Stream + +class TestStringMethods(unittest.TestCase): + + def test_stream_iterable(self): + self.assertIsInstance(Stream([1, 2, 3]), Stream) + def test_stream_iterator(self): + self.assertIsInstance(Stream(iter([1, 2, 3])), Stream) + def test_stream_singleton(self): + self.assertIsInstance(Stream(17), Stream) + + def test_map(self): + self.assertEqual(Stream([1, 2, 3, 4, 5]).map(lambda x: x*x).to(list), [1, 4, 9, 16, 25]) + def test_filter(self): + self.assertEqual(Stream([1, 2, 3, 4, 5]).filter(lambda x: x%2 == 0).to(list), [2, 4]) + + def test_first(self): + self.assertEqual(Stream([1, 2, 3, 4, 5]).first(lambda x: x>3), 4) + def test_group(self): + self.assertEqual(Stream([1, 2, 3, 4, 5]).group(lambda x: x%3).to(dict), {0: [3], 1: [1, 4], 2: [2, 5]}) + + def test_apply_single(self): + self.assertEqual(Stream([1, 2, 3, 4, 5]).apply(sum).to_single(int), 15) + def test_apply_multi(self): + self.assertEqual(Stream(["a", "b", "c"]).apply("".join).to(list), ["a", "b", "c"]) + def test_singleton_methods(self): + self.assertEqual(Stream(5).apply_single(range).to(list), [0, 1, 2, 3, 4]) + +if __name__ == '__main__': + unittest.main()