Skip to content

Conversation

Copy link

Copilot AI commented Feb 9, 2026

Plan for Adding Async Function Extensions

  • Create src/extype/builtin_extensions/coroutine_ext.py with then and catch extension methods for coroutine objects
  • Add the coroutine extension module to extend_all.py to auto-import
  • Add coroutine_ext.py to meson.build for package inclusion
  • Create comprehensive tests in tests/test_builtin_extensions.py for async extensions
  • Add pytest-asyncio dependency to pyproject.toml
  • Update README.md to document the new async function extensions
  • Run full test suite to validate all functionality (53 tests passing)
  • Fix linting issues with flake8
  • Address code review feedback (use iscoroutine instead of iscoroutinefunction)
  • Run security scanning with codeql_checker (no vulnerabilities found)
  • Fix CI test failures by installing pytest-asyncio in all workflow files
  • Fix Python 3.7+ compatibility by using Union and Type from typing

Summary

Successfully implemented async function extensions for coroutines with:

  • then(fn): Maps awaitable results through sync or async functions
  • catch(fn, exception=Exception): Handles exceptions with sync or async error handlers
  • All 53 tests passing locally
  • CI workflows updated to install pytest-asyncio
  • Python 3.7+ compatibility using Union and Type instead of PEP 604 syntax
  • No security vulnerabilities
  • Full documentation in README.md
Original prompt

This section details on the original issue you should resolve

<issue_title>Add async function extensions</issue_title>
<issue_description>We can (optionally) extend coroutine objects with the following extensions:

def then(Awaitable[T], Callable[[T], Awaitable[U] | U]) -> Awaitable[U]

def catch(Awaitable[T], Callable[[E], Awaitable[U] | U], *, exception: type[E] = Excpetion) -> Awaitable[T | U]

The then function basically maps the result of the first awaitable via an optionally async function. If the function is async, it is awaited in the context of the wrapped awaitable.

The catch function catches an exception of the given type and the passed function is called with the caught exception.

If no exception was raised inside the wrapped awaitable, the function will not be called.

The passed function can optionally return a value to be returned in case of an error.

The passed function can be either sync or async. If it's async, it is awaited in the context of the wrapped awaitable.

Implementation Suggestion

First, create 2 wrapping functions:

from inspect import iscoroutinefunction as is_async

async def then(awaitable, fn):
  result = fn(await awaitable)
  if is_async(fn):
    return await result
  return result

async def catch(awaitable, fn, *, exception=Exception):
  try:
    return await awaitable
  except exception as e:
    result = fn(e)
    if is_async(fn):
      return await result
    return result

Then, the extension methods will call these respectively, passing self as the awaitable and the other arguments as is.

A PR implementing this should also consider updating README.md to include the added built-in extension.</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 3 commits February 9, 2026 20:12
Co-authored-by: neriyaco <42520786+neriyaco@users.noreply.github.com>
Co-authored-by: neriyaco <42520786+neriyaco@users.noreply.github.com>
Co-authored-by: neriyaco <42520786+neriyaco@users.noreply.github.com>
Copilot AI changed the title [WIP] Add async function extensions for coroutine objects Add async function extensions for coroutine objects Feb 9, 2026
Copilot AI requested a review from neriyaco February 9, 2026 20:17
@neriyaco neriyaco requested a review from binyamin555 February 9, 2026 20:18
Copy link
Member

@binyamin555 binyamin555 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@binyamin555 binyamin555 marked this pull request as ready for review February 10, 2026 10:26
@binyamin555
Copy link
Member

@copilot tests are failing

Co-authored-by: binyamin555 <42520501+binyamin555@users.noreply.github.com>
Copy link
Author

Copilot AI commented Feb 10, 2026

@copilot tests are failing

Fixed in b545dd1. The CI workflows weren't installing pytest-asyncio. Updated all three workflow files (Linux, Windows, macOS) to install it alongside typing-extensions.

Copy link
Member

@binyamin555 binyamin555 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR breaks support for older versions of Python (due to typing).

Please make sure you're using only types from typing and typing_extensions, including the usage of Type and Union.

@binyamin555
Copy link
Member

@copilot

Co-authored-by: binyamin555 <42520501+binyamin555@users.noreply.github.com>
Copy link
Author

Copilot AI commented Feb 10, 2026

This PR breaks support for older versions of Python (due to typing).

Please make sure you're using only types from typing and typing_extensions, including the usage of Type and Union....

Fixed in 2e969bd. Replaced all Python 3.10+ union syntax (|) with Union[] from typing and replaced lowercase type with Type from typing for Python 3.7+ compatibility.

@binyamin555
Copy link
Member

@neriyaco @elazarcoh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add async function extensions

4 participants