Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make start_soon always start soon - support eager tasks #851

Closed
wants to merge 12 commits into from

Conversation

graingert
Copy link
Collaborator

@graingert graingert commented Jan 1, 2025

Changes

Fixes #.

Checklist

If this is a user-facing code change, like a bugfix or a new feature, please ensure that
you've fulfilled the following conditions (where applicable):

  • You've added tests (in tests/) added which would fail without your patch
  • You've updated the documentation (in docs/, in case of behavior changes or new
    features)
  • You've added a new changelog entry (in docs/versionhistory.rst).

If this is a trivial change, like a typo fix or a code reformatting, then you can ignore
these instructions.

Updating the changelog

If there are no entries after the last release, use **UNRELEASED** as the version.
If, say, your patch fixes issue #123, the entry should look like this:

- Fix big bad boo-boo in task groups
  (`#123 <https://github.com/agronholm/anyio/issues/123>`_; PR by @yourgithubaccount)

If there's no issue linked, just link to your pull request instead by updating the
changelog after you've created the PR.

@graingert
Copy link
Collaborator Author

does this need documenting as a new feature?

@graingert graingert marked this pull request as ready for review January 1, 2025 08:41
@graingert graingert requested a review from agronholm January 1, 2025 08:41
@agronholm
Copy link
Owner

I weep at the complexity. Is all this absolutely necessary?

@graingert
Copy link
Collaborator Author

graingert commented Jan 1, 2025

Hopefully we get a loop.create_task(..., eager_start=False) in 3.14 and we can ditch the complexity

This took a few rounds to squash down as much complexity as possible. It used to be much worse - so I'm biased to be somewhat happy with it

@graingert
Copy link
Collaborator Author

This probably needs a test with a fresh instance of create_eager_task_factory so it goes through the eagerness measurement process

tests/test_taskgroups.py Outdated Show resolved Hide resolved
Comment on lines +1793 to +1807
def create_eager_task_factory(
custom_task_constructor: Callable[..., Any],
) -> Callable[..., Any]:
def factory(
loop: Any,
coro: Any,
*,
name: str | None = None,
context: contextvars.Context | None = None,
) -> Any:
return custom_task_constructor(
coro, loop=loop, name=name, context=context, eager_start=True
)

return factory
Copy link
Owner

Choose a reason for hiding this comment

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

Just what is the point of this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is to make a task factory that's eager, but not the same as the built in eager task factory, so it can test the slow path that measures task factories for eagerness

Comment on lines +1810 to +1821
pytest.param(
(
"asyncio",
{
"debug": True,
"loop_factory": task_factory_loop_factory_factory(
asyncio.create_eager_task_factory(asyncio.Task)
),
},
),
id="asyncio+stdlib-custom-eager",
),
Copy link
Owner

Choose a reason for hiding this comment

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

Isn't this covered by the other asyncio_params already?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No this is different because it uses a totally custom eager task factory that takes the slow path in is_eager

@graingert
Copy link
Collaborator Author

I pushed a simplification that gets rid of the two counts of pending_eager_tasks, and collects any errors from the eager task spawner

Comment on lines +952 to +954
if getattr(tf, "__code__", object()) is _TF_CODE:
_TF_IS_EAGER_MAP[tf] = True
return True
Copy link
Owner

Choose a reason for hiding this comment

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

Why is it necessary to special case asyncio.eager_task_factory?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

To avoid having to create a task and measure it in the default case

pass


if sys.version_info >= (3, 12):
Copy link
Owner

Choose a reason for hiding this comment

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

Wouldn't it be possible to create an eager task factory on earlier Python versions?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's very difficult to get current_task to work without the 3.12 swap_task code

nonlocal ran
ran = True

tf(_ShimEventLoop(), corofn()).cancel()
Copy link
Owner

Choose a reason for hiding this comment

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

What do we need the shim event loop for really?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

To avoid creating a task that gets destroyed when pending

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think I could refactor this to use a task in the TaskGroup

@agronholm
Copy link
Owner

Closing in favor of #853.

@agronholm agronholm closed this Jan 3, 2025
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