Skip to content

Asyncify request_transport context manager#7209

Merged
khsrali merged 3 commits into
aiidateam:mainfrom
khsrali:asyncified-transportQ
Feb 17, 2026
Merged

Asyncify request_transport context manager#7209
khsrali merged 3 commits into
aiidateam:mainfrom
khsrali:asyncified-transportQ

Conversation

@khsrali
Copy link
Copy Markdown
Collaborator

@khsrali khsrali commented Feb 9, 2026

This avoids two nested loops in transportQ

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.64%. Comparing base (7536ec2) to head (93c81b0).
⚠️ Report is 117 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7209      +/-   ##
==========================================
+ Coverage   79.63%   79.64%   +0.02%     
==========================================
  Files         565      565              
  Lines       43710    43762      +52     
==========================================
+ Hits        34802    34851      +49     
- Misses       8908     8911       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@khsrali khsrali force-pushed the asyncified-transportQ branch from 971e821 to 5b3d4d4 Compare February 9, 2026 10:31
@khsrali khsrali requested a review from danielhollas February 9, 2026 15:03
Comment thread src/aiida/transports/transport.py Outdated
self._enters += 1
else:
self.open()
self._enters += 1
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Isn't this logic simpler?

            if self.is_open:
                self.open()
            self._enters += 1

Right now you have a logic that considers self._enters aand self.is_open but also assumes that it works the same to work properly.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Okay this is just taken over from somewhere else in transport. Then we do not touch this logic in this PR. Maybe in a subsequent PR but its not that important

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This caught my attention as well, would be good to clean up.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks @danielhollas
ok, the logic which was used in three places, is now moved into a set of helper functions.

Comment thread src/aiida/engine/transports.py Outdated
Comment on lines +103 to +109
empty_ctx = contextvars.Context()
open_callback_handle = empty_ctx.run(self._loop.create_task, do_open())
# Note: after making do_open async, we use create_task instead of call_later.
# Passing `context` to create_task is only supported in Python 3.11+, so we use
# empty_ctx.run() to ensure the task inherits an empty context.
# Once the minimum supported Python version is 3.11, this can be simplified to:
# open_callback_handle = self._loop.create_task(do_open(), context=contextvars.Context())
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@agoscinski regarding your question about call_later:
call_later is only for synchronous callbacks, not coroutines. Since do_open was made async, call_later can't be used with it.

If you think the comment here is verbose, I could reduce to something like

Suggested change
empty_ctx = contextvars.Context()
open_callback_handle = empty_ctx.run(self._loop.create_task, do_open())
# Note: after making do_open async, we use create_task instead of call_later.
# Passing `context` to create_task is only supported in Python 3.11+, so we use
# empty_ctx.run() to ensure the task inherits an empty context.
# Once the minimum supported Python version is 3.11, this can be simplified to:
# open_callback_handle = self._loop.create_task(do_open(), context=contextvars.Context())
empty_ctx = contextvars.Context()
open_callback_handle = empty_ctx.run(self._loop.create_task, do_open())
# self._loop.create_task supports passing a context but only after Python 3.11+

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I reduced the comment

@khsrali khsrali requested a review from agoscinski February 13, 2026 09:54
@danielhollas
Copy link
Copy Markdown
Collaborator

@khsrali would you mind expanding the PR description to explain what this PR is solving? It's very hard to tell just by reading the code, without knowing the details of the current Transport implementation. (Is there an issue open for this?)

@khsrali
Copy link
Copy Markdown
Collaborator Author

khsrali commented Feb 13, 2026

Hi @danielhollas
Thanks for looking into this.

This PR, is pre-requisite (and also general improvement!) for the upcoming #7206 (-> get rids of nest_asyncio in favour of python grrenlets)

This PR removes two nested coroutine loops (calling on open() and close() that they internally call on loop.run_untill_complete) inside request_transport context manager.

The request_transport is always used in an async context in our codebase, there's no reason for it to be sync.

Copy link
Copy Markdown
Collaborator

@danielhollas danielhollas left a comment

Choose a reason for hiding this comment

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

Thanks for the context @khsrali

I don't have capacity to do careful review but looks good on brief look.

Comment thread src/aiida/transports/transport.py Outdated
self._enters += 1
else:
self.open()
self._enters += 1
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This caught my attention as well, would be good to clean up.

@khsrali khsrali force-pushed the asyncified-transportQ branch from f3967ac to 88bf124 Compare February 13, 2026 13:51
Copy link
Copy Markdown
Collaborator

@agoscinski agoscinski left a comment

Choose a reason for hiding this comment

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

Just one comment. You decide if you want to change it back.

Manages the ``_enters`` reference counter. If the transport is already open
(e.g. opened externally), an extra count is added so the final exit won't close it.
"""
need_open = False
Copy link
Copy Markdown
Collaborator

@agoscinski agoscinski Feb 16, 2026

Choose a reason for hiding this comment

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

I am not entirely sure about decoupling it. Its usage can be now

self._track_enter()
await something # another self._track_enter() happens that will also want to open it
self.open()

Yes it is internal, but we developers make also mistakes, and you dont save many lines of code by reusing this function.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

yeah, vaild point.
I don't know what's the best practise here. Also repeating this code three times, is subject to mistake. 🤔

Copy link
Copy Markdown
Collaborator Author

@khsrali khsrali Feb 17, 2026

Choose a reason for hiding this comment

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

Ok @agoscinski, I added a clear docstring that how _track_enter should be used.
I also added asyncio.lock for any potential race opening condition --which I believe there's no double opening in engine. But better to be safe than sorry.

@khsrali khsrali merged commit 41acc86 into aiidateam:main Feb 17, 2026
16 checks passed
@khsrali khsrali deleted the asyncified-transportQ branch February 17, 2026 10:09
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.

3 participants