Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
704 changes: 704 additions & 0 deletions documentation.md

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion pyenumerable/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Implementation of .NET's IEnumerable interface in python W/ support for generics.
""" # noqa: E501


from pyenumerable.constructors import pp_enumerable
from pyenumerable.implementations import PurePythonEnumerable
from pyenumerable.protocol import Enumerable
Expand Down
3 changes: 1 addition & 2 deletions pyenumerable/constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@


def pp_enumerable[TSource](
*items: TSource,
from_iterable: Iterable[Iterable[TSource]] | None = None
*items: TSource, from_iterable: Iterable[Iterable[TSource]] | None = None
) -> Enumerable[TSource]:
return PurePythonEnumerable(*items, from_iterable=from_iterable)
8 changes: 5 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Implementation of .NET's [IEnumerable](https://learn.microsoft.com/en-us/dotnet/
### v1.0.x
- [x] Design protocols for each operation set
- [x] Design & Implement `Enumerable` constructor(s) for PP implementation
- [x] Add pure python implementation of `Enumerable` (assuming inputs aren't guaranteed to be `Hashable` or immutable & maintaining order)
- [x] Add pure python implementation of `Enumerable` (assuming inputs aren't guaranteed to be `Hashable` or immutable; preserving order)
- [x] Any
- [x] All
- [x] Aggregate
Expand Down Expand Up @@ -39,7 +39,9 @@ Implementation of .NET's [IEnumerable](https://learn.microsoft.com/en-us/dotnet/
- [x] Max
- [x] remove `Comparable` bind from type variables
- [x] Publish on pypi
- [ ] Add external wrapper constructor
- [x] Add external wrapper constructor
- [x] Add technical documentation pure python implementation
- [ ] Implement `__str__` & `__repr__` for
### v1.1.x
- [ ] Improve test code quality
- [ ] Add hashed pure python implementation of `Enumerable` (assuming inputs are guaranteed to be `Hashable` & immutable & not maintaining order)
- [ ] Add hashed pure python implementation of `Enumerable` (assuming inputs are guaranteed to be `Hashable` & immutable; not preserving order)
16 changes: 13 additions & 3 deletions test/unit/pure_python/enumerable/chunk_/test_chunk_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,19 @@

class TestChunkMethod:
def test_chunk(self) -> None:
obj = PurePythonEnumerable(*range(length := 7))
obj = PurePythonEnumerable(
one := 1,
two := 2,
three := 3,
four := 4,
five := 5,
six := 6,
seven := 7,
)

res = obj.chunk(size := 3)

assert len(res) == ceil(length / size)
assert len(res[-1].source) == 1
assert len(res) == ceil(len(obj.source) / size)
assert res[0].source == (one, two, three)
assert res[1].source == (four, five, six)
assert res[2].source == (seven,)
4 changes: 2 additions & 2 deletions test/unit/pure_python/enumerable/concat/test_concat_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ def test_concat(self) -> None:
*(second_items := (-9, -3, -8, -1, 8, -9)),
)

final = first.concat(second)
res = first.concat(second)

assert final.source == first_items + second_items
assert res.source == first_items + second_items
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def test_without_comparer(self) -> None:
from_iterable=(items := tuple(range(7)), [-i for i in items]),
)

res = obj.distinct_by(lambda i: i**2)
res = obj.distinct_by(lambda i: abs(i))

assert res.source == items

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ def test_group_by(self) -> None:
p.y == second_group_key
for p in res.source[second_group_index].source
)
assert res.source[first_group_index].key == first_group_key
assert res.source[second_group_index].key == second_group_key
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ def test_group_join(self) -> None:

res = first_object.group_join(
second_object,
lambda parent: parent.age,
lambda child: child.parent.age
lambda parent: parent.name,
lambda child: child.parent.name
if child.parent is not None
else None,
lambda parent, children: (parent.age, children.source),
lambda parent, children: (parent, children.source),
)

assert res.source == (
(first_parent.age, (first_child, second_child)),
(second_parent.age, (third_child, fourth_child, fifth_child)),
(first_parent, (first_child, second_child)),
(second_parent, (third_child, fourth_child, fifth_child)),
)

def test_overlap_remove(self) -> None:
Expand Down
58 changes: 20 additions & 38 deletions test/unit/pure_python/enumerable/join/test_join_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ def test_without_outcaster(self) -> None:
second_group_key = 3

first_object = PurePythonEnumerable(
fgfo := Point(0, first_group_key),
sgfo := Point(0, second_group_key),
sgso := Point(1, second_group_key),
first_group_first_outer := Point(0, first_group_key),
second_group_first_outer := Point(0, second_group_key),
second_group_second_outer := Point(1, second_group_key),
)
second_object = PurePythonEnumerable(
fgfi := Point(first_group_key, 0),
fgsi := Point(first_group_key, 1),
sgfi := Point(second_group_key, 0),
first_group_first_inner := Point(first_group_key, 0),
first_group_second_inner := Point(first_group_key, 1),
second_group_first_inner := Point(second_group_key, 0),
)

res = first_object.join(
Expand All @@ -26,38 +26,29 @@ def test_without_outcaster(self) -> None:
)

assert res.source == (
(fgfo, fgfi), # (first group first outer, first group first inner)
(
fgfo,
fgsi,
), # (first group first outer, first group second inner)
(
sgfo,
sgfi,
), # (second group first outer, second group first inner)
(
sgso,
sgfi,
), # (second group second outer, second group first inner)
(first_group_first_outer, first_group_first_inner),
(first_group_first_outer, first_group_second_inner),
(second_group_first_outer, second_group_first_inner),
(second_group_second_outer, second_group_first_inner),
)

def test_with_outcaster(self) -> None:
first_group_key = 1
second_group_key = 3

first_object = PurePythonEnumerable(
fgfo := Point(0, first_group_key),
first_group_first_outer := Point(0, first_group_key),
Point(0, 2),
Point(1, 2),
sgfo := Point(0, second_group_key),
sgso := Point(1, second_group_key),
second_group_first_outer := Point(0, second_group_key),
second_group_second_outer := Point(1, second_group_key),
)
second_object = PurePythonEnumerable(
fgfi := Point(first_group_key, 0),
fgsi := Point(first_group_key, 1),
first_group_first_inner := Point(first_group_key, 0),
first_group_second_inner := Point(first_group_key, 1),
Point(4, 0),
Point(4, 1),
sgfi := Point(second_group_key, 0),
second_group_first_inner := Point(second_group_key, 0),
)

res = first_object.join(
Expand All @@ -68,17 +59,8 @@ def test_with_outcaster(self) -> None:
)

assert res.source == (
(fgfo, fgfi), # (first group first outer, first group first inner)
(
fgfo,
fgsi,
), # (first group first outer, first group second inner)
(
sgfo,
sgfi,
), # (second group first outer, second group first inner)
(
sgso,
sgfi,
), # (second group second outer, second group first inner)
(first_group_first_outer, first_group_first_inner),
(first_group_first_outer, first_group_second_inner),
(second_group_first_outer, second_group_first_inner),
(second_group_second_outer, second_group_first_inner),
)
15 changes: 5 additions & 10 deletions test/unit/pure_python/enumerable/of_type/test_of_type_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@


class TestOfTypeMethod:
def test_capturing_functionality(self) -> None:
obj = PurePythonEnumerable(None, "first", *(items := (1, 2)))
def test_of_type(self) -> None:
obj = PurePythonEnumerable(
zero := 0, None, "one", two := 2, three := 3
)

res = obj.of_type(int)

assert len(res.source) == len(items)

def test_filtering_functionality(self) -> None:
obj = PurePythonEnumerable(None, "first", two := 2, three := 3)

res = obj.of_type(int)

assert set(res.source) == {two, three}
assert res.source == (zero, two, three)
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,7 @@


class TestSequenceEqualMethod:
def test_pass_without_comparer(self) -> None:
first_object = PurePythonEnumerable(*range(stop := 7))
second_object = PurePythonEnumerable(*range(stop))

res = first_object.sequence_equal(second_object)

assert res is True

def test_fail_without_comparer(self) -> None:
first_object = PurePythonEnumerable(*range(7))
second_object = PurePythonEnumerable(*range(1, 8))

res = first_object.sequence_equal(second_object)

assert res is False

def test_pass_with_comparer(self) -> None:
def test_pass(self) -> None:
first_object = PurePythonEnumerable(
Point(0, 1), Point(1, 3), Point(2, 4)
)
Expand All @@ -35,7 +19,7 @@ def test_pass_with_comparer(self) -> None:

assert res is True

def test_fail_with_comparer(self) -> None:
def test_fail(self) -> None:
first_object = PurePythonEnumerable(
Point(0, 1), Point(1, 3), Point(2, 4)
)
Expand Down
14 changes: 7 additions & 7 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.