flpit 0.1.1.dev2__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flpit-0.1.1.dev2 → flpit-0.1.2}/PKG-INFO +10 -6
- {flpit-0.1.1.dev2 → flpit-0.1.2}/README.md +9 -5
- {flpit-0.1.1.dev2 → flpit-0.1.2}/pyproject.toml +1 -1
- {flpit-0.1.1.dev2 → flpit-0.1.2}/pyproject.toml.orig +5 -1
- {flpit-0.1.1.dev2 → flpit-0.1.2}/src/flp/__init__.py +2 -5
- {flpit-0.1.1.dev2 → flpit-0.1.2}/src/flp/core/linq.py +171 -63
- {flpit-0.1.1.dev2 → flpit-0.1.2}/src/flp/core/__init__.py +0 -0
- {flpit-0.1.1.dev2 → flpit-0.1.2}/src/flp/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: flpit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Fluent LINQ for Python — flip your iterables
|
|
5
5
|
License: MIT
|
|
6
6
|
Classifier: Development Status :: 3 - Alpha
|
|
@@ -30,16 +30,17 @@ Description-Content-Type: text/markdown
|
|
|
30
30
|
## ⚙️ Installation
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
uv add
|
|
33
|
+
uv add flpit
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
## 💡 Quick Start
|
|
37
37
|
|
|
38
38
|
```python
|
|
39
|
-
|
|
39
|
+
import flp
|
|
40
|
+
from flp import FlpIt, FlpList
|
|
40
41
|
|
|
41
|
-
#
|
|
42
|
-
data =
|
|
42
|
+
# Deferred iterable via shorthand
|
|
43
|
+
data: FlpIt[int] = flp.it([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
|
43
44
|
|
|
44
45
|
# Deferred / lazy pipeline
|
|
45
46
|
query: FlpIt[int] = (
|
|
@@ -48,8 +49,11 @@ query: FlpIt[int] = (
|
|
|
48
49
|
.select(lambda x: x * 10)
|
|
49
50
|
)
|
|
50
51
|
|
|
51
|
-
#
|
|
52
|
+
# Materialize query results explicitly
|
|
52
53
|
result: FlpList[int] = query.to_list() # [20, 40, 60, 80, 100]
|
|
54
|
+
|
|
55
|
+
# Or start directly with an eager container
|
|
56
|
+
eager_list: FlpList[int] = flp.lst([1, 2, 3, 4])
|
|
53
57
|
```
|
|
54
58
|
|
|
55
59
|
## 📜 License
|
|
@@ -14,16 +14,17 @@
|
|
|
14
14
|
## ⚙️ Installation
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
uv add
|
|
17
|
+
uv add flpit
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
## 💡 Quick Start
|
|
21
21
|
|
|
22
22
|
```python
|
|
23
|
-
|
|
23
|
+
import flp
|
|
24
|
+
from flp import FlpIt, FlpList
|
|
24
25
|
|
|
25
|
-
#
|
|
26
|
-
data =
|
|
26
|
+
# Deferred iterable via shorthand
|
|
27
|
+
data: FlpIt[int] = flp.it([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
|
27
28
|
|
|
28
29
|
# Deferred / lazy pipeline
|
|
29
30
|
query: FlpIt[int] = (
|
|
@@ -32,8 +33,11 @@ query: FlpIt[int] = (
|
|
|
32
33
|
.select(lambda x: x * 10)
|
|
33
34
|
)
|
|
34
35
|
|
|
35
|
-
#
|
|
36
|
+
# Materialize query results explicitly
|
|
36
37
|
result: FlpList[int] = query.to_list() # [20, 40, 60, 80, 100]
|
|
38
|
+
|
|
39
|
+
# Or start directly with an eager container
|
|
40
|
+
eager_list: FlpList[int] = flp.lst([1, 2, 3, 4])
|
|
37
41
|
```
|
|
38
42
|
|
|
39
43
|
## 📜 License
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "flpit"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "Fluent LINQ for Python — flip your iterables"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -37,3 +37,7 @@ module-root = "src"
|
|
|
37
37
|
include = ["src"]
|
|
38
38
|
exclude = ["**/__pycache__", "tests"]
|
|
39
39
|
typeCheckingMode = "basic"
|
|
40
|
+
|
|
41
|
+
#[tool.pyrefly]
|
|
42
|
+
#extra-paths = ["src", "tests"]
|
|
43
|
+
#python-interpreter-path = ".venv/bin/python" # or .venv/Scripts/python.exe on Windows
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Fluent LINQ for Python — flip your iterables.
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
|
+
from importlib.metadata import version
|
|
6
|
+
__version__ = version("flpit")
|
|
5
7
|
|
|
6
8
|
import builtins
|
|
7
9
|
from typing import Iterable as _Iterable, TypeVar
|
|
@@ -27,9 +29,6 @@ def repeat(element: TItem, count: int) -> FlpIt[TItem]:
|
|
|
27
29
|
"""Generates a lazy sequence that contains one repeated value."""
|
|
28
30
|
return FlpIt(element for _ in builtins.range(count))
|
|
29
31
|
|
|
30
|
-
Iterable = it
|
|
31
|
-
List = lst
|
|
32
|
-
|
|
33
32
|
__all__ = [
|
|
34
33
|
"FlpIt",
|
|
35
34
|
"OrderedIt",
|
|
@@ -37,8 +36,6 @@ __all__ = [
|
|
|
37
36
|
"FlpList",
|
|
38
37
|
"it",
|
|
39
38
|
"lst",
|
|
40
|
-
"Iterable",
|
|
41
|
-
"List",
|
|
42
39
|
"range",
|
|
43
40
|
"repeat",
|
|
44
41
|
]
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import cast
|
|
4
|
+
|
|
3
5
|
import builtins
|
|
4
6
|
from collections import UserList
|
|
5
7
|
from functools import wraps
|
|
@@ -28,12 +30,25 @@ TOther = TypeVar("TOther")
|
|
|
28
30
|
TAccumulate = TypeVar("TAccumulate")
|
|
29
31
|
|
|
30
32
|
_SENTINEL = object()
|
|
33
|
+
_MISSING = object()
|
|
31
34
|
|
|
32
35
|
|
|
33
36
|
class EmptySequenceError(ValueError):
|
|
34
37
|
def __init__(self, message="Sequence contains no elements"):
|
|
35
38
|
super().__init__(message)
|
|
36
39
|
|
|
40
|
+
class NoMatchError(ValueError):
|
|
41
|
+
def __init__(self, message="Sequence contains no matching elements"):
|
|
42
|
+
super().__init__(message)
|
|
43
|
+
|
|
44
|
+
class MultipleMatchesError(ValueError):
|
|
45
|
+
def __init__(self, message="Sequence contains more than one matching element"):
|
|
46
|
+
super().__init__(message)
|
|
47
|
+
|
|
48
|
+
class MultipleElementsError(ValueError):
|
|
49
|
+
def __init__(self, message="Sequence contains more than one element"):
|
|
50
|
+
super().__init__(message)
|
|
51
|
+
|
|
37
52
|
|
|
38
53
|
def _guard_empty(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
39
54
|
"""Catches Python's native empty-sequence ValueError and re-raises LINQ-compliant error."""
|
|
@@ -96,6 +111,49 @@ class FlpIt(Iterable[TItem], Generic[TItem]):
|
|
|
96
111
|
|
|
97
112
|
return FlpIt(_FactoryIterable(_generator))
|
|
98
113
|
|
|
114
|
+
@overload
|
|
115
|
+
def any(self) -> bool: ...
|
|
116
|
+
|
|
117
|
+
@overload
|
|
118
|
+
def any(self, predicate: Callable[[TItem], bool]) -> bool: ...
|
|
119
|
+
|
|
120
|
+
def any(
|
|
121
|
+
self,
|
|
122
|
+
predicate: Optional[Callable[[TItem], bool]] = None,
|
|
123
|
+
) -> bool:
|
|
124
|
+
"""
|
|
125
|
+
Determines whether the sequence contains any elements,
|
|
126
|
+
or whether any element satisfies the predicate.
|
|
127
|
+
|
|
128
|
+
Mirrors Enumerable.Any:
|
|
129
|
+
Any()
|
|
130
|
+
Any(predicate)
|
|
131
|
+
|
|
132
|
+
Evaluation stops as soon as the result is known.
|
|
133
|
+
"""
|
|
134
|
+
if predicate is not None:
|
|
135
|
+
return builtins.any(map(predicate, self))
|
|
136
|
+
|
|
137
|
+
iterator = iter(self)
|
|
138
|
+
try:
|
|
139
|
+
next(iterator)
|
|
140
|
+
except StopIteration:
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def all(self, predicate: Callable[[TItem], bool]) -> bool:
|
|
147
|
+
"""
|
|
148
|
+
Determines whether all elements satisfy the predicate.
|
|
149
|
+
|
|
150
|
+
Mirrors Enumerable.All.
|
|
151
|
+
|
|
152
|
+
Returns True for an empty sequence.
|
|
153
|
+
Evaluation stops at the first element that fails the predicate.
|
|
154
|
+
"""
|
|
155
|
+
return builtins.all(map(predicate, self))
|
|
156
|
+
|
|
99
157
|
def concat(self, second: Iterable[TItem]) -> "FlpIt[TItem]":
|
|
100
158
|
def _generator() -> Iterator[TItem]:
|
|
101
159
|
yield from self
|
|
@@ -275,29 +333,7 @@ class FlpIt(Iterable[TItem], Generic[TItem]):
|
|
|
275
333
|
|
|
276
334
|
return FlpIt(_FactoryIterable(_generator))
|
|
277
335
|
|
|
278
|
-
|
|
279
|
-
# self,
|
|
280
|
-
# inner: Iterable[TOther],
|
|
281
|
-
# outer_key_selector: Callable[[TItem], TKey],
|
|
282
|
-
# inner_key_selector: Callable[[TOther], TKey],
|
|
283
|
-
# result_selector: Callable[[TItem, TOther], TResult],
|
|
284
|
-
# ) -> FlpIt[TResult]:
|
|
285
|
-
# """Correlates elements of two sequences based on matching keys (Hash Join)."""
|
|
286
|
-
# def _generator() -> Iterator[TResult]:
|
|
287
|
-
# lookup: dict[TKey, List[TOther]] = {}
|
|
288
|
-
# for inner_item in inner:
|
|
289
|
-
# key = inner_key_selector(inner_item)
|
|
290
|
-
# lookup.setdefault(key, []).append(inner_item)
|
|
291
|
-
#
|
|
292
|
-
# for outer_item in self:
|
|
293
|
-
# key = outer_key_selector(outer_item)
|
|
294
|
-
# if key in lookup:
|
|
295
|
-
# for inner_item in lookup[key]:
|
|
296
|
-
# yield result_selector(outer_item, inner_item)
|
|
297
|
-
#
|
|
298
|
-
# return FlpIt(_FactoryIterable(_generator))
|
|
299
|
-
|
|
300
|
-
# --- Immediate Execution (Materialization & Aggregation) ---
|
|
336
|
+
# --- Immediate Execution (Materialization & Aggregation) ---
|
|
301
337
|
|
|
302
338
|
@overload
|
|
303
339
|
def aggregate(self, func: Callable[[TItem, TItem], TItem]) -> TItem: ...
|
|
@@ -327,33 +363,61 @@ class FlpIt(Iterable[TItem], Generic[TItem]):
|
|
|
327
363
|
accumulator = func(accumulator, item)
|
|
328
364
|
return accumulator
|
|
329
365
|
|
|
366
|
+
@overload
|
|
367
|
+
def last(self) -> TItem: ...
|
|
368
|
+
|
|
369
|
+
@overload
|
|
370
|
+
def last(self, predicate: Callable[[TItem], bool]) -> TItem: ...
|
|
371
|
+
|
|
372
|
+
def last(
|
|
373
|
+
self,
|
|
374
|
+
predicate: Optional[Callable[[TItem], bool]] = None,
|
|
375
|
+
) -> TItem:
|
|
376
|
+
"""
|
|
377
|
+
Returns the last element of the sequence, or the last element
|
|
378
|
+
satisfying the predicate.
|
|
379
|
+
|
|
380
|
+
Mirrors Enumerable.Last:
|
|
381
|
+
Last()
|
|
382
|
+
Last(predicate)
|
|
383
|
+
|
|
384
|
+
Raises EmptySequenceError if the sequence is empty or if no
|
|
385
|
+
element satisfies the predicate.
|
|
386
|
+
"""
|
|
387
|
+
last_item: TItem | object = _SENTINEL
|
|
388
|
+
|
|
389
|
+
for item in self:
|
|
390
|
+
if predicate is None or predicate(item):
|
|
391
|
+
last_item = item
|
|
392
|
+
|
|
393
|
+
if last_item is _SENTINEL:
|
|
394
|
+
raise EmptySequenceError() if predicate is None else NoMatchError()
|
|
395
|
+
|
|
396
|
+
return cast(TItem, last_item)
|
|
397
|
+
|
|
398
|
+
def _extreme(self, func, **kwargs) -> TItem:
|
|
399
|
+
result = func(self, default=_MISSING, **kwargs)
|
|
400
|
+
|
|
401
|
+
if result is _MISSING:
|
|
402
|
+
raise EmptySequenceError()
|
|
403
|
+
|
|
404
|
+
return cast(TItem, result)
|
|
405
|
+
|
|
330
406
|
def min(self) -> TItem:
|
|
331
407
|
"""Returns the minimum value in a sequence."""
|
|
332
|
-
|
|
333
|
-
return builtins.min(self) # type: ignore[type-var]
|
|
334
|
-
except ValueError:
|
|
335
|
-
raise EmptySequenceError()
|
|
408
|
+
return self._extreme(builtins.min)
|
|
336
409
|
|
|
337
410
|
def min_by(self, key_selector: Callable[[TItem], Any]) -> TItem:
|
|
338
411
|
"""Returns the value in a sequence that has the minimum key value."""
|
|
339
|
-
|
|
340
|
-
return builtins.min(self, key=key_selector)
|
|
341
|
-
except ValueError:
|
|
342
|
-
raise EmptySequenceError()
|
|
412
|
+
return self._extreme(builtins.min, key=key_selector)
|
|
343
413
|
|
|
344
414
|
def max(self) -> TItem:
|
|
345
415
|
"""Returns the maximum value in a sequence."""
|
|
346
|
-
|
|
347
|
-
return builtins.max(self) # type: ignore[type-var]
|
|
348
|
-
except ValueError:
|
|
349
|
-
raise EmptySequenceError()
|
|
416
|
+
return self._extreme(builtins.max)
|
|
350
417
|
|
|
351
418
|
def max_by(self, key_selector: Callable[[TItem], Any]) -> TItem:
|
|
352
419
|
"""Returns the value in a sequence that has the maximum key value."""
|
|
353
|
-
|
|
354
|
-
return builtins.max(self, key=key_selector)
|
|
355
|
-
except ValueError:
|
|
356
|
-
raise EmptySequenceError()
|
|
420
|
+
return self._extreme(builtins.max, key=key_selector)
|
|
357
421
|
|
|
358
422
|
def average(
|
|
359
423
|
self, selector: Optional[Callable[[TItem], Union[int, float]]] = None
|
|
@@ -394,7 +458,7 @@ class FlpIt(Iterable[TItem], Generic[TItem]):
|
|
|
394
458
|
|
|
395
459
|
def count(self, predicate: Optional[Callable[[TItem], bool]] = None) -> int:
|
|
396
460
|
"""Counts elements in the sequence matching an optional predicate."""
|
|
397
|
-
query = self.where(predicate) if predicate else self
|
|
461
|
+
query = self.where(predicate) if predicate is not None else self
|
|
398
462
|
return builtins.sum(1 for _ in query)
|
|
399
463
|
|
|
400
464
|
def element_at(self, index: int) -> TItem:
|
|
@@ -408,10 +472,10 @@ class FlpIt(Iterable[TItem], Generic[TItem]):
|
|
|
408
472
|
|
|
409
473
|
def first(self, predicate: Optional[Callable[[TItem], bool]] = None) -> TItem:
|
|
410
474
|
"""Returns the first element matching a predicate, or raises ValueError."""
|
|
411
|
-
query = self.where(predicate) if predicate else self
|
|
475
|
+
query = self.where(predicate) if predicate is not None else self
|
|
412
476
|
for item in query:
|
|
413
477
|
return item
|
|
414
|
-
raise
|
|
478
|
+
raise EmptySequenceError() if predicate is None else NoMatchError()
|
|
415
479
|
|
|
416
480
|
def first_or_default(
|
|
417
481
|
self, default: TResult, predicate: Optional[Callable[[TItem], bool]] = None
|
|
@@ -419,24 +483,30 @@ class FlpIt(Iterable[TItem], Generic[TItem]):
|
|
|
419
483
|
"""Returns the first element matching a predicate, or a default value."""
|
|
420
484
|
try:
|
|
421
485
|
return self.first(predicate)
|
|
422
|
-
except
|
|
486
|
+
except (EmptySequenceError, NoMatchError):
|
|
423
487
|
return default
|
|
424
488
|
|
|
425
|
-
def single(self, predicate: Optional[Callable[[TItem], bool]] =
|
|
489
|
+
def single(self, predicate: Optional[Callable[[TItem], bool]] = _SENTINEL) -> TItem:
|
|
426
490
|
"""Returns the single, unique element matching a predicate."""
|
|
427
|
-
|
|
491
|
+
if predicate is None:
|
|
492
|
+
raise TypeError("Predicate cannot be None")
|
|
493
|
+
|
|
494
|
+
has_predicate = predicate is not _SENTINEL
|
|
495
|
+
query = self.where(predicate) if has_predicate else self
|
|
428
496
|
it = iter(query)
|
|
497
|
+
|
|
429
498
|
try:
|
|
430
499
|
first_val = next(it)
|
|
431
500
|
except StopIteration:
|
|
432
|
-
raise
|
|
501
|
+
raise NoMatchError() if has_predicate else EmptySequenceError()
|
|
433
502
|
|
|
434
503
|
try:
|
|
435
504
|
next(it)
|
|
436
505
|
except StopIteration:
|
|
437
506
|
return first_val
|
|
438
507
|
|
|
439
|
-
raise
|
|
508
|
+
raise MultipleMatchesError() if has_predicate else MultipleElementsError()
|
|
509
|
+
|
|
440
510
|
|
|
441
511
|
def to_list(self) -> FlpList[TItem]:
|
|
442
512
|
"""Explicitly materializes the query into a FlpList."""
|
|
@@ -640,9 +710,26 @@ class FlpList(UserList[TItem], Sequence[TItem], Generic[TItem]):
|
|
|
640
710
|
self.data.append(first_item)
|
|
641
711
|
self.data.extend(it)
|
|
642
712
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
713
|
+
@overload
|
|
714
|
+
def any(self) -> bool: ...
|
|
715
|
+
|
|
716
|
+
@overload
|
|
717
|
+
def any(self, predicate: Callable[[TItem], bool]) -> bool: ...
|
|
718
|
+
|
|
719
|
+
def any(
|
|
720
|
+
self,
|
|
721
|
+
predicate: Optional[Callable[[TItem], bool]] = None,
|
|
722
|
+
) -> bool:
|
|
723
|
+
if predicate is None:
|
|
724
|
+
return len(self.data) > 0
|
|
725
|
+
|
|
726
|
+
return any(predicate(item) for item in self.data)
|
|
727
|
+
|
|
728
|
+
def all(
|
|
729
|
+
self,
|
|
730
|
+
predicate: Callable[[TItem], bool],
|
|
731
|
+
) -> bool:
|
|
732
|
+
return all(predicate(item) for item in self.data)
|
|
646
733
|
|
|
647
734
|
def append_linq(self, element: TItem) -> FlpIt[TItem]:
|
|
648
735
|
"""Appends an element to the sequence lazily, returning a FlpIt without mutating this list."""
|
|
@@ -719,17 +806,6 @@ class FlpList(UserList[TItem], Sequence[TItem], Generic[TItem]):
|
|
|
719
806
|
) -> FlpIt[Grouping[TKey, TItem]]:
|
|
720
807
|
return FlpIt(self.data).group_by(key_selector)
|
|
721
808
|
|
|
722
|
-
# def join(
|
|
723
|
-
# self,
|
|
724
|
-
# inner: Iterable[TOther],
|
|
725
|
-
# outer_key_selector: Callable[[TItem], TKey],
|
|
726
|
-
# inner_key_selector: Callable[[TOther], TKey],
|
|
727
|
-
# result_selector: Callable[[TItem, TOther], TResult],
|
|
728
|
-
# ) -> FlpIt[TResult]:
|
|
729
|
-
# return FlpIt(self.data).join(
|
|
730
|
-
# inner, outer_key_selector, inner_key_selector, result_selector
|
|
731
|
-
# )
|
|
732
|
-
|
|
733
809
|
@overload
|
|
734
810
|
def aggregate(self, func: Callable[[TItem, TItem], TItem]) -> TItem: ...
|
|
735
811
|
|
|
@@ -810,14 +886,46 @@ class FlpList(UserList[TItem], Sequence[TItem], Generic[TItem]):
|
|
|
810
886
|
def first(self, predicate: Optional[Callable[[TItem], bool]] = None) -> TItem:
|
|
811
887
|
return FlpIt(self.data).first(predicate)
|
|
812
888
|
|
|
889
|
+
@overload
|
|
890
|
+
def last(self) -> TItem: ...
|
|
891
|
+
|
|
892
|
+
@overload
|
|
893
|
+
def last(self, predicate: Callable[[TItem], bool]) -> TItem: ...
|
|
894
|
+
|
|
895
|
+
def last(
|
|
896
|
+
self,
|
|
897
|
+
predicate: Optional[Callable[[TItem], bool]] = None,
|
|
898
|
+
) -> TItem:
|
|
899
|
+
"""
|
|
900
|
+
Returns the last element of the sequence, or the last element
|
|
901
|
+
satisfying the predicate.
|
|
902
|
+
|
|
903
|
+
Mirrors Enumerable.Last:
|
|
904
|
+
Last()
|
|
905
|
+
Last(predicate)
|
|
906
|
+
|
|
907
|
+
Raises EmptySequenceError if the sequence is empty or if no
|
|
908
|
+
element satisfies the predicate.
|
|
909
|
+
"""
|
|
910
|
+
if predicate is None:
|
|
911
|
+
if not self.data:
|
|
912
|
+
raise EmptySequenceError()
|
|
913
|
+
return self.data[-1]
|
|
914
|
+
|
|
915
|
+
for item in reversed(self.data):
|
|
916
|
+
if predicate(item):
|
|
917
|
+
return item
|
|
918
|
+
|
|
919
|
+
raise NoMatchError()
|
|
920
|
+
|
|
813
921
|
def first_or_default(
|
|
814
922
|
self, default: TResult, predicate: Optional[Callable[[TItem], bool]] = None
|
|
815
923
|
) -> Union[TItem, TResult]:
|
|
816
924
|
return FlpIt(self.data).first_or_default(default, predicate)
|
|
817
925
|
|
|
818
|
-
def single(self, predicate: Optional[Callable[[TItem], bool]] =
|
|
926
|
+
def single(self, predicate: Optional[Callable[[TItem], bool]] = _SENTINEL) -> TItem:
|
|
819
927
|
return FlpIt(self.data).single(predicate)
|
|
820
928
|
|
|
821
929
|
def to_list(self) -> FlpList[TItem]:
|
|
822
930
|
"""Explicitly returns a shallow copy instance to isolate mutations."""
|
|
823
|
-
return FlpList(self.data.copy())
|
|
931
|
+
return FlpList(self.data.copy())
|
|
File without changes
|
|
File without changes
|