Skip to content

Simplify event loop handling and remove deprecated get_event_loop#340

Open
GDYendell wants to merge 1 commit intodocsfrom
simplify-event-loop
Open

Simplify event loop handling and remove deprecated get_event_loop#340
GDYendell wants to merge 1 commit intodocsfrom
simplify-event-loop

Conversation

@GDYendell
Copy link
Contributor

@GDYendell GDYendell commented Mar 13, 2026


In the process of writing documentation it became clear that

  • We are using get_event_loop, which is deprecated and planned for removal in 3.16
  • The event loop handling is over complicated

Summary:

  • Remove the loop parameter to FastCS and instead require clients to call serve from an async context with the loop they want it to run in
  • Don't pass a loop to transports and instead retrieve it via get_running_loop only where required (EpicaCA and Tango)
  • Use asyncio.create_task from an async context consistently in tests, rather than run_until_complete or ensure_future
  • Improve tango test handling of DeviceTestContext to fix resource warnings

Summary by CodeRabbit

  • Refactor
    • Simplified event loop configuration by removing explicit loop parameters from FastCS initialization and transport connection methods
    • Event loop management is now handled automatically by the framework
    • Updated async task handling patterns throughout the codebase for improved reliability

@GDYendell GDYendell requested review from coretl and shihab-dls March 13, 2026 16:47
@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1bb2bd87-41e4-43dd-955f-d7547bb82c07

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR removes explicit event loop parameters from the FastCS API and internal implementations, replacing manual loop passing with implicit asyncio patterns such as asyncio.get_running_loop() and asyncio.run(). The refactoring simplifies constructor signatures across all transport classes and the main control system.

Changes

Cohort / File(s) Summary
Core Control System
src/fastcs/control_system.py
Removed loop parameter from __init__, replaced explicit loop/task management with asyncio.run(), switched to asyncio.create_task(), enhanced task cancellation in _stop_scan_tasks(), and updated signal handler installation to use the current running loop.
Launch and CLI
src/fastcs/launch.py
Simplified FastCS instantiation by removing loop parameter from constructor call; removed asyncio import.
EPICS CA Transport
src/fastcs/transports/epics/ca/ioc.py, src/fastcs/transports/epics/ca/transport.py
EpicsCAIOC.run() signature changed to remove loop parameter and now uses asyncio.get_running_loop(); EpicsCATransport.connect() simplified to accept only controller_api and removed loop storage.
EPICS PVA Transport
src/fastcs/transports/epics/pva/transport.py
Removed loop parameter from EpicsPVATransport.connect() signature; simplified initialization to rely on implicit event loop handling.
GraphQL & REST Transports
src/fastcs/transports/graphql/transport.py, src/fastcs/transports/rest/transport.py
Both transport classes had loop parameter removed from connect() method signatures; simplified server initialization.
Tango Transport
src/fastcs/transports/tango/transport.py
Removed loop parameter from connect() and updated TangoDSR initialization to use asyncio.get_running_loop(); simplified serve() to directly await thread execution.
Transport Base
src/fastcs/transports/transport.py
Updated abstract connect() method to remove loop parameter and removed asyncio import; updated documentation to indicate implicit loop retrieval.
Test Updates
tests/benchmarking/controller.py, tests/test_control_system.py, tests/transports/epics/ca/test_initial_value.py, tests/transports/epics/pva/test_p4p.py, tests/transports/graphQL/test_graphql.py, tests/transports/rest/test_rest.py, tests/transports/tango/test_dsr.py
Updated all test instantiations to remove loop parameters; modernized async test patterns using @pytest.mark.asyncio and asyncio.create_task() instead of ensure_future; adjusted event loop lifecycle management in test setup and teardown.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR #332: Modifies src/fastcs/control_system.py to remove legacy build_controller_api path and transition to Controller.create_api_and_tasks, which may intersect with event loop initialization changes in this PR.

Suggested reviewers

  • shihab-dls

Poem

🐰 Loop, loop, whence did you go?
Event loops freed from chains below,
Asyncio patterns now take the lead,
Implicit flows are all we need!
Refactored paths run light and free,
The rabbit's work brings harmony! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main objective of the PR: simplifying event loop handling and removing deprecated get_event_loop usage. The changes across all modified files consistently reflect this primary goal.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch simplify-event-loop
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can generate a title for your PR based on the changes.

Add @coderabbitai placeholder anywhere in the title of your PR and CodeRabbit will replace it with a title based on the changes in the PR. You can change the placeholder by changing the reviews.auto_title_placeholder setting.

@GDYendell
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/fastcs/control_system.py`:
- Around line 54-55: The code currently installs SIGINT/SIGTERM handlers inside
serve(), which hijacks signal handling when serve() is called from an existing
async runtime; move the signal-handler installation out of serve() and into
run() so handlers are only installed when run() is explicitly invoked, and guard
the installation with a main-thread check (threading.current_thread() is
threading.main_thread()) and ensure the event loop supports add_signal_handler
before calling it (e.g., get the loop from asyncio.get_event_loop() and call
add_signal_handler only when permitted). Modify serve() to no longer call
add_signal_handler, and update run() to install the handlers conditionally for
SIGINT/SIGTERM using the same handler functions currently referenced in serve().
- Around line 164-167: The interactive_shell task created inside run()/wrapper
must be tracked and its exceptions handled: when creating the coroutine
(interactive_shell / asyncio.to_thread(partial(shell.mainloop, ...))) retain the
Task returned by asyncio.create_task and attach a done callback that
catches/logs exceptions and always calls stop_event.set() on error; also ensure
tasks spawned by run() are appended to a local registry so failures are visible
(e.g., add the created Task to a list in run() and on completion remove it), and
update wrapper to create the task via that helper so any raised exceptions won't
silently block await stop_event.wait().

In `@tests/transports/tango/test_dsr.py`:
- Around line 58-71: The current finally block's cleanup iterates
gc.get_objects() and closes any asyncio.AbstractEventLoop it finds, which is
unsafe; change the cleanup to only close loops created by this test/context by
recording references to event loops you create (e.g., the 'loop' variable and
any loops returned or owned by DeviceTestContext) or by snapshotting existing
loops before entering DeviceTestContext and closing only loops that appear
afterwards, and remove the gc.get_objects() iteration; ensure you still call
loop.run_until_complete(asyncio.sleep(0)), loop.close(),
asyncio.set_event_loop(None) and gc.collect() but limit any additional .close()
calls to the specific loop objects you tracked rather than all AbstractEventLoop
instances.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cc07bde6-a8ed-470a-a36d-83ba46c30250

📥 Commits

Reviewing files that changed from the base of the PR and between 6839f1d and b86c206.

📒 Files selected for processing (16)
  • src/fastcs/control_system.py
  • src/fastcs/launch.py
  • src/fastcs/transports/epics/ca/ioc.py
  • src/fastcs/transports/epics/ca/transport.py
  • src/fastcs/transports/epics/pva/transport.py
  • src/fastcs/transports/graphql/transport.py
  • src/fastcs/transports/rest/transport.py
  • src/fastcs/transports/tango/transport.py
  • src/fastcs/transports/transport.py
  • tests/benchmarking/controller.py
  • tests/test_control_system.py
  • tests/transports/epics/ca/test_initial_value.py
  • tests/transports/epics/pva/test_p4p.py
  • tests/transports/graphQL/test_graphql.py
  • tests/transports/rest/test_rest.py
  • tests/transports/tango/test_dsr.py
💤 Files with no reviewable changes (1)
  • tests/transports/epics/ca/test_initial_value.py

Copy link
Contributor

@coretl coretl left a comment

Choose a reason for hiding this comment

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

@ajgdls is now our resident Tango expert so I'll defer to him to review the tango bits. Everything else looks fine

@GDYendell GDYendell force-pushed the simplify-event-loop branch 2 times, most recently from 676fac5 to e8b2558 Compare March 16, 2026 12:47
@codecov
Copy link

codecov bot commented Mar 16, 2026

Codecov Report

❌ Patch coverage is 71.42857% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.98%. Comparing base (04d7d24) to head (d9d0620).

Files with missing lines Patch % Lines
src/fastcs/control_system.py 68.57% 11 Missing ⚠️
src/fastcs/transports/tango/transport.py 25.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             docs     #340      +/-   ##
==========================================
- Coverage   91.21%   90.98%   -0.23%     
==========================================
  Files          70       70              
  Lines        2594     2596       +2     
==========================================
- Hits         2366     2362       -4     
- Misses        228      234       +6     

☔ 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.

@GDYendell GDYendell force-pushed the simplify-event-loop branch from e8b2558 to 941e46b Compare March 16, 2026 12:51
@GDYendell GDYendell force-pushed the simplify-event-loop branch from 941e46b to d9d0620 Compare March 16, 2026 12:53
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