Fix ASYNC240: replace blocking filesystem calls with anyio#61616
Merged
jscheffl merged 10 commits intoapache:mainfrom Feb 8, 2026
Merged
Fix ASYNC240: replace blocking filesystem calls with anyio#61616jscheffl merged 10 commits intoapache:mainfrom
jscheffl merged 10 commits intoapache:mainfrom
Conversation
…th anyio Replace blocking os.path and pathlib.Path calls inside async functions with async-safe anyio.Path equivalents to prevent blocking the event loop during filesystem I/O. Re-enable the ASYNC240 ruff rule. closes apache#61418
anyio.Path.glob() returns AsyncIterator, not Awaitable. Use a sync helper function with anyio.to_thread.run_sync instead.
eladkal
reviewed
Feb 7, 2026
dheerajturaga
approved these changes
Feb 8, 2026
Member
dheerajturaga
left a comment
There was a problem hiding this comment.
The edge provider changes look good. The only observation is the test style inconsistency
The test_cli_test_with_deferrable_operator test was mocking os.path.isfile and os.path.getmtime, but these calls were replaced with anyio.Path in the FileTrigger. The stale mocks caused the trigger's while-True loop to never find the file, resulting in an infinite hang that timed out CI.
Contributor
Author
|
All feedback addressed. The only CI failure is a timeout on the GPG keys download step (infra issue, not related to the changes here). Happy to rebase if needed. |
jscheffl
approved these changes
Feb 8, 2026
Contributor
jscheffl
left a comment
There was a problem hiding this comment.
Cool! Thanks for the fix!
Contributor
|
Even better... wondered a moment why you can directly import anyio w/o changing any project dependencies until I realized that it was already installed as transitive dependency via httpx and starlette. So great that we picked this, even missed in the proposal to use anyio... |
jhgoebbert
pushed a commit
to jhgoebbert/airflow_Owen-CH-Leung
that referenced
this pull request
Feb 8, 2026
) * Fix ASYNC240: replace blocking filesystem calls in async functions with anyio Replace blocking os.path and pathlib.Path calls inside async functions with async-safe anyio.Path equivalents to prevent blocking the event loop during filesystem I/O. Re-enable the ASYNC240 ruff rule. closes apache#61418 * Fix mypy error: use sync helper for pathlib.glob in async context anyio.Path.glob() returns AsyncIterator, not Awaitable. Use a sync helper function with anyio.to_thread.run_sync instead. * Retrigger CI * Fix ruff format: add blank line before nested function definition * Remove newsfragment: internal change, not user-facing * Fix CLI test hang: update mocks to use anyio.Path instead of os.path The test_cli_test_with_deferrable_operator test was mocking os.path.isfile and os.path.getmtime, but these calls were replaced with anyio.Path in the FileTrigger. The stale mocks caused the trigger's while-True loop to never find the file, resulting in an infinite hang that timed out CI. * Cache anyio.Path instance in test to match production code style * Retrigger CI
81 tasks
Ratasa143
pushed a commit
to Ratasa143/airflow
that referenced
this pull request
Feb 15, 2026
) * Fix ASYNC240: replace blocking filesystem calls in async functions with anyio Replace blocking os.path and pathlib.Path calls inside async functions with async-safe anyio.Path equivalents to prevent blocking the event loop during filesystem I/O. Re-enable the ASYNC240 ruff rule. closes apache#61418 * Fix mypy error: use sync helper for pathlib.glob in async context anyio.Path.glob() returns AsyncIterator, not Awaitable. Use a sync helper function with anyio.to_thread.run_sync instead. * Retrigger CI * Fix ruff format: add blank line before nested function definition * Remove newsfragment: internal change, not user-facing * Fix CLI test hang: update mocks to use anyio.Path instead of os.path The test_cli_test_with_deferrable_operator test was mocking os.path.isfile and os.path.getmtime, but these calls were replaced with anyio.Path in the FileTrigger. The stale mocks caused the trigger's while-True loop to never find the file, resulting in an infinite hang that timed out CI. * Cache anyio.Path instance in test to match production code style * Retrigger CI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replace blocking
os.pathandpathlib.Pathcalls inside async functions with async-safeanyio.Pathequivalents, fixing all ASYNC240 lint violations and re-enabling the rule.providers/standard/triggers/file.py: Replaceos.path.isfile(),os.path.getmtime(),os.walk(),os.remove()withanyio.Pathandanyio.to_thread.run_sync()providers/edge3/cli/worker.py: Replacepathlib.exists(),pathlib.stat()withanyio.Pathdevel-common/sphinx_exts/pagefind_search/builder.py: Replacepathlib.glob(),pathlib.is_file(),pathlib.read_text()withanyio.Pathanyio.Pathpyproject.toml: RemoveASYNC240from the ruff ignore listcloses #61418
Related: #61417
Decision context
anyiowas chosen as the async filesystem library per discussion on the issue (approved by @jscheffl). It is already a transitive dependency of the project via httpx, sqlalchemy async, etc.Test plan
ruff check --select ASYNC240passes with zero violationsproviders/standard/tests/unit/standard/triggers/test_file.pyproviders/edge3/tests/unit/edge3/cli/test_worker.pyairflow-core/tests/unit/api_fastapi/auth/test_tokens.py