Skip to content

Implement async functions and generators (.NET)#2046

Merged
slozier merged 7 commits into
IronLanguages:mainfrom
BCSharp:net_async
Jun 11, 2026
Merged

Implement async functions and generators (.NET)#2046
slozier merged 7 commits into
IronLanguages:mainfrom
BCSharp:net_async

Conversation

@BCSharp

@BCSharp BCSharp commented May 28, 2026

Copy link
Copy Markdown
Member

This is an alternative implementation of PEP 492 from #2004 by @mikasoukhov (Python 3.5). It builds on top of async support in the DLR (IronLanguages/dlr#362), which is around native .NET async primitives: Task/Task<T>, IAsyncEnumerable<T>, IAsyncDisposable. As such, it fits better within a .NET ecosystem, e.g. IronPython engine embedded within a larger C# application. As a .NET async mechanism, it obeys the .NET scheduling and context following mechanisms. For Python behavioural compatibility, wrappers and bridges are provided.

It also implements PEP 525 (async generators), which are accepted in Python 3.6.

The previous async functionality in IronPython is still there, parallel to the new one, and which one is being used depends whether FEATURE_NET_ASYNC is being defined during compilation. If defined, the new functionality is being used, otherwise the previous functionality is being used. It is practical for testing and development, but perhaps some discrepancies will occur. I think in the end it is best to settle on just one mechanism, but I'd postpone this decision until the whole asyncio support is complete.

As it is now, FEATURE_NET_ASYNC is defined on .NET platforms, but undefined on .NET Framework 4.6.2.

@slozier slozier left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks! Apologies for the delay in reviewing this. Looks good to me!

I guess not having async generators in the other implementation is a bit of a blocker for a 3.6 release so this is good.



[LightThrowing]
public object athrow(object? type) => athrow(type, null, null);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just looking at future versions, it looks like the athrow argument is documented as value since 3.7 which seems reasonable to adopt? Also the other two overloads are deprecated in 3.12 (parhaps add a TODO comment?).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I do not see any changes in 3.7 on that. Do you have an URL to the documentation? When I run help(mygen.athrow) on 3.11.14, simply I get:

Help on built-in function athrow:

athrow(...) method of builtins.async_generator instance
    athrow(typ[,val[,tb]]) -> raise exception in generator.

Do you mean replacing the three overloads with one method and two default parameters? (This would relax the allowed calls to the method, e.g. type and traceback given, but null value — not in CPython)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Here's from the 3.7 docs: https://docs.python.org/3.7/reference/expressions.html#agen.athrow

I initially noticed it in the help from 3.12.10 which is my "main" version these days:

Help on built-in function athrow:

athrow(...) method of builtins.async_generator instance
    athrow(value)
    athrow(type[,value[,tb]])

    raise exception in generator.
    the (type, val, tb) signature is deprecated,
    and may be removed in a future version of Python.

My idea was to leave it as three overloads since that matches the CPython positional-only style, but changing the argument of the first overload to value... although I guess practically it doesn't really change anything beyond documentation.

@BCSharp BCSharp Jun 11, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I looked into CPython's source code and it seems that value, when passed as the first parameter, is simply the same as type (dual use, type or value), so there is indeed no change to the code, just documentation.



[LightThrowing]
public object @throw(object? type) => @throw(type, null, null);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same as AsyncGenerator, the argument is value in 3.7 and the other overloads are deprecated in 3.12.


#if FEATURE_NET_ASYNC

[PythonType("coroutine")]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since there's some shared code, perhaps move the class signature outside the feature branch instead or repeating it? Although don't feel strongly either way...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

OK.

self.assertEqual(type(g).__name__, 'async_generator')


def test_asend(self):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I guess a bunch of these tests are not .NET specific. Maybe we could put them in a class that's not skipped? I guess our run_coro is .NET only, but that's easily substituted with asyncio.run (new in 3.7)... Ran into this while testing the tests against CPython.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is a good point but I am hesitant to make this test runnable under CPython as yet. The test uses run_coro, which is .NET specific, and its purpose it to be a smoke test for the new asyncgen code. There are some changes in asyncio past 3.4, and we don't have 3.6 tests in the main branch.

Ideally, we would migrate the main development to 3.6 and follow that asyncio tests. In 3.4, I wanted to enable some existing asyncio tests (not this PR), as long as they are not CPython-specific and do not conflict with 3.6+. They should cover whatever test_asyncgen.py covers for .NET.

@BCSharp

BCSharp commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

Thanks! Apologies for the delay in reviewing this. Looks good to me!

I guess not having async generators in the other implementation is a bit of a blocker for a 3.6 release so this is good.

No problem, I, too, am less available for IronPython at the moment. My motivation with async was to get done whatever is blocking a switch to 3.6 as the main development branch. Can you add whatever you think is required to milestone 3.6-alpha?

@slozier

slozier commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

My motivation with async was to get done whatever is blocking a switch to 3.6 as the main development branch. Can you add whatever you think is required to milestone 3.6-alpha?

I was actually thinking of how I could go about completely dropping the 3.6 branch and having all versions run off the main branch. I've already (mostly) converged the two implementations on the C# side of things opting to backport most features. So that leaves the Python code which varies quite a bit. For our test suite, I think things are mostly the same so we might be able to get away with some version checks? As for the standard library, I was thinking of splitting it off into its own repo which would have multiple branches. The idea would be to "just" set the version number and check out the standard library branch you're interested in working on. Anyway, just something I've been thinking about, not sure if you have thoughts or opinions on the matter.

Back to your question, I too would like to move the project along to version 3.6. Looking at the existing milestone list, I think some of them are in a state that's good enough for an alpha. So my short list would actually be:

  • Insertion-order preserving dictionaries are a big one. I know it's technically a 3.7, but it was true and 3.6 and I expect a lot of code targetting 3.6 was dependant on this. There's also the related PEP 468.
  • "Complete" async syntax support. I think there's still some work to be done regarding comprehension (see Async comprehension #2023). Maybe not a big deal if some of it doesn't actually work, but syntax errors are problematic.
  • UTF-8 console

This is conditional on seeing if I run into real-world issues with some of the common pure-Python packages (and by common I mean the ones I actually use 🤣).

Will try and get the list updated...

@BCSharp

BCSharp commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

Will try and get the list updated...

Yes, please do. I will try to to the same on my end.

Form me, when I say "IronPython 3.6" I mainly mean IronPython with StdLib v3.6, because IronPython itself is a mix of features from various versions of Python anyway. I don't really mean "branch 3.6", I like developing on our main, so if you merge 3.6 to main, that's great. Having StdLib 3.4 and 3.6 side-by-side is an interesting idea, go for it if this is how you see it (and have the time :). I was simply thinking of one last release of 3.4 and then simply dropping StdLib 3.4 and replacing it by StdLib 3.6 (call it IronPython 3.6 alpha…).

For the order preserving dictionaries, you made a PR (#1908), which was a good approach and on which I commented, and then it stalled… Do you have comments on my comments, or how do you want to continue?

I can look into async comprehensions; I did some work on comprehensions in the past (though it was long time ago). My main concern with the whole async is having good test coverage without reinventing the wheel, i.e. letting the StdLib tests do the bulk of the work.

@slozier slozier merged commit 7697f70 into IronLanguages:main Jun 11, 2026
25 of 26 checks passed
@BCSharp BCSharp deleted the net_async branch June 11, 2026 16:44
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.

2 participants