flpit 0.2.1.dev6__tar.gz → 0.2.1.dev7__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: flpit
3
- Version: 0.2.1.dev6
3
+ Version: 0.2.1.dev7
4
4
  Summary: Fluent LINQ for Python — flip your iterables
5
5
  Project-URL: Homepage, https://github.com/riemar/flpit
6
6
  Project-URL: Repository, https://github.com/riemar/flpit
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "flpit"
3
- version = "0.2.1.dev6"
3
+ version = "0.2.1.dev7"
4
4
  description = "Fluent LINQ for Python — flip your iterables"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -34,6 +34,7 @@ dev = [
34
34
  "pyright>=1.1.414",
35
35
  "pytest-benchmark>=5.3.0",
36
36
  "mypy>=2.4.0",
37
+ "httpx>=0.28.1",
37
38
  ]
38
39
 
39
40
 
@@ -599,7 +599,7 @@ class Task(Generic[T]):
599
599
  Accepts Task instances and raw awaitables (including coroutine objects)
600
600
  positionally or in one iterable. Raw awaitables are scheduled
601
601
  automatically. The winning task is returned even if it failed or was
602
- cancelled; inspect/await that task to observe
602
+ canceled; inspect/await that task to observe
603
603
  its outcome. Losing tasks continue running. An empty input is invalid.
604
604
  If multiple tasks are already complete at observation time, input order
605
605
  breaks the tie deterministically.
@@ -612,6 +612,22 @@ class Task(Generic[T]):
612
612
  cls.run(_when_any_coroutine, normalized, name=name, token=token),
613
613
  )
614
614
 
615
+ @staticmethod
616
+ async def collect(*tasks: Task[Any] | list[Task[Any]]):
617
+ """
618
+ Yields tasks as they complete. Accepts either a list or loose positional tasks.
619
+ """
620
+ # FIX: If it's a list or tuple, extract and cast the FIRST element to a mutable list
621
+ if len(tasks) == 1 and isinstance(tasks[0], (list, tuple)):
622
+ pending = list(tasks[0]) # Converts the inner container into a fresh list
623
+ else:
624
+ pending = list(tasks) # Converts the *tasks tuple into a fresh list
625
+
626
+ while pending:
627
+ finished_task = await Task.when_any(pending)
628
+ pending.remove(finished_task) # Safe to call, because pending is ALWAYS a list!
629
+ yield finished_task
630
+
615
631
  @classmethod
616
632
  def delay(
617
633
  cls,
@@ -1574,4 +1590,21 @@ def task(async_function: Callable[..., Any]) -> Callable[..., Task[Any]]:
1574
1590
  def wrapper(*args: Any, **kwargs: Any) -> Task[Any]:
1575
1591
  return Task.run(async_function, *args, **kwargs)
1576
1592
 
1577
- return wrapper
1593
+ return wrapper
1594
+
1595
+
1596
+ def coro_task(async_function: Callable[..., Any]) -> Callable[..., Task[Any]]:
1597
+ """
1598
+ | Wraps an async coroutine function to be compatible with Task.run(coro)
1599
+
1600
+ Use with caution as this transfers execution control to an isolated asyncio loop runner.
1601
+ """
1602
+ import asyncio
1603
+ @functools.wraps(async_function)
1604
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
1605
+ with asyncio.Runner() as runner:
1606
+ # Call the function with its arguments to generate the Coroutine object instance
1607
+ coro = async_function(*args, **kwargs)
1608
+ return runner.run(coro)
1609
+
1610
+ return task(wrapper)
File without changes
File without changes
File without changes
File without changes
File without changes