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

Move read_text to executor #5688

Merged
merged 7 commits into from
Mar 1, 2025
Merged

Move read_text to executor #5688

merged 7 commits into from
Mar 1, 2025

Conversation

mdegat01
Copy link
Contributor

@mdegat01 mdegat01 commented Feb 28, 2025

Proposed change

Begin moving calls to read_text to executor. This PR was intended to cover all calls to read_text but then I realized our sentry method capture_exception was doing a call to read_text. That required a lot of changes to move all calls to that to the executor so I stopped there and will continue in a follow-up PR.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (which adds functionality to the supervisor)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

  • This PR fixes or closes issue: fixes #
  • This PR is related to issue:
  • Link to documentation pull request:
  • Link to cli pull request:
  • Link to client library pull request:

Checklist

  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • The code has been formatted using Ruff (ruff format supervisor tests)
  • Tests have been added to verify that the new code works.

If API endpoints or add-on configuration are added/changed:

Summary by CodeRabbit

  • Refactor
    • Transitioned numerous operations—including error handling, file access, and system queries—to an asynchronous model for improved responsiveness and smoother operations.
  • Tests
    • Updated testing routines to accommodate asynchronous behavior, ensuring reliable validations.

@mdegat01 mdegat01 requested a review from agners February 28, 2025 02:37
@mdegat01 mdegat01 added the refactor A code change that neither fixes a bug nor adds a feature label Feb 28, 2025
@agners agners marked this pull request as ready for review February 28, 2025 09:22
Copy link
Contributor

coderabbitai bot commented Feb 28, 2025

Warning

Rate limit exceeded

@agners has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 25 minutes and 34 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between b47b65f and 670bddd.

📒 Files selected for processing (47)
  • supervisor/__main__.py (1 hunks)
  • supervisor/addons/addon.py (2 hunks)
  • supervisor/addons/manager.py (3 hunks)
  • supervisor/addons/model.py (1 hunks)
  • supervisor/api/__init__.py (2 hunks)
  • supervisor/api/addons.py (1 hunks)
  • supervisor/api/host.py (1 hunks)
  • supervisor/api/store.py (8 hunks)
  • supervisor/backups/manager.py (3 hunks)
  • supervisor/bootstrap.py (2 hunks)
  • supervisor/core.py (4 hunks)
  • supervisor/dbus/network/__init__.py (2 hunks)
  • supervisor/docker/addon.py (3 hunks)
  • supervisor/docker/interface.py (3 hunks)
  • supervisor/hardware/disk.py (2 hunks)
  • supervisor/homeassistant/core.py (5 hunks)
  • supervisor/host/info.py (1 hunks)
  • supervisor/jobs/__init__.py (1 hunks)
  • supervisor/jobs/decorator.py (3 hunks)
  • supervisor/misc/filter.py (1 hunks)
  • supervisor/misc/tasks.py (3 hunks)
  • supervisor/mounts/manager.py (2 hunks)
  • supervisor/mounts/mount.py (3 hunks)
  • supervisor/os/data_disk.py (3 hunks)
  • supervisor/os/manager.py (2 hunks)
  • supervisor/plugins/audio.py (2 hunks)
  • supervisor/plugins/base.py (2 hunks)
  • supervisor/plugins/cli.py (2 hunks)
  • supervisor/plugins/dns.py (2 hunks)
  • supervisor/plugins/manager.py (4 hunks)
  • supervisor/plugins/multicast.py (2 hunks)
  • supervisor/plugins/observer.py (2 hunks)
  • supervisor/resolution/check.py (2 hunks)
  • supervisor/resolution/checks/dns_server.py (2 hunks)
  • supervisor/resolution/checks/dns_server_ipv6.py (2 hunks)
  • supervisor/resolution/checks/free_space.py (2 hunks)
  • supervisor/resolution/evaluate.py (2 hunks)
  • supervisor/resolution/fixup.py (2 hunks)
  • supervisor/resolution/notify.py (1 hunks)
  • supervisor/supervisor.py (2 hunks)
  • supervisor/utils/dbus.py (2 hunks)
  • supervisor/utils/log_format.py (1 hunks)
  • supervisor/utils/sentry.py (2 hunks)
  • tests/api/test_supervisor.py (1 hunks)
  • tests/homeassistant/test_home_assistant_watchdog.py (1 hunks)
  • tests/host/test_info.py (1 hunks)
  • tests/utils/test_log_format.py (0 hunks)
📝 Walkthrough

Walkthrough

The pull request refactors numerous parts of the codebase to align with an asynchronous execution model. Most changes involve replacing direct, synchronous calls to error capturing (using capture_exception) with non-blocking, asynchronous invocations via executor routines. In addition, several functions and properties—such as methods returning file contents and disk space information—have been converted into async methods. Minor adjustments also include parameter additions and improvements in docstrings to clarify executor requirements, alongside corresponding changes in the test suite to support these async modifications.

Changes

File(s) Change Summary
supervisor/addons/addon.py, supervisor/addons/manager.py, supervisor/api/__init__.py, supervisor/backups/manager.py, supervisor/core.py, supervisor/dbus/network/__init__.py, supervisor/docker/addon.py, supervisor/docker/interface.py, supervisor/homeassistant/core.py, supervisor/jobs/decorator.py, supervisor/misc/tasks.py, supervisor/mounts/manager.py, supervisor/mounts/mount.py, supervisor/os/data_disk.py, supervisor/os/manager.py, supervisor/plugins/{audio.py, base.py, cli.py, dns.py, manager.py, multicast.py, observer.py}, supervisor/resolution/{check.py, checks/dns_server.py, checks/dns_server_ipv6.py, evaluate.py, fixup.py}, supervisor/supervisor.py, supervisor/utils/dbus.py, supervisor/resolution/notify.py, supervisor/jobs/__init__.py Async Error Handling: Replaced direct calls to capture_exception with asynchronous invocations using sys_run_in_executor (or asyncio.get_running_loop().run_in_executor). This shift ensures error reporting does not block the event loop.
supervisor/__main__.py, supervisor/addons/model.py, supervisor/api/{addons.py, host.py, middleware/security.py, store.py}, supervisor/bootstrap.py, supervisor/host/info.py, supervisor/resolution/checks/free_space.py, supervisor/utils/log_format.py Async Method Conversion: Converted several functions and properties to async methods and adjusted invocations accordingly (e.g., awaiting long_description(), disk space retrieval, and supervisor debugger).
tests/{homeassistant/test_home_assistant_watchdog.py, host/test_info.py, utils/test_log_format.py} Test Updates: Modified test functions to be asynchronous and adjusted timing (e.g., sleep duration) to support the updated async API calls.
supervisor/api/store.py, supervisor/hardware/disk.py, supervisor/utils/sentry.py Minor Signature & Docstring Updates: Added a binary mode parameter for file reading, updated docstrings to specify executor usage, and refined function declarations to reflect async context where needed.

Sequence Diagram(s)

sequenceDiagram
    participant Component as Calling Component
    participant Func as Async Function
    participant Executor as Executor Context
    participant Sentry as capture_exception

    Component->>Func: Invoke async method
    Func->>Executor: await sys_run_in_executor(capture_exception, error)
    Executor->>Sentry: Execute capture_exception(error)
    Sentry-->>Executor: Exception logged
    Executor-->>Func: Return control asynchronously
    Func-->>Component: Complete async error handling
Loading

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@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: 2

🧹 Nitpick comments (7)
supervisor/jobs/__init__.py (1)

193-196: Change in error handling approach

The direct call to capture_exception() has been removed, and instead the _LOGGER.critical is being passed as a parameter to RuntimeError. This changes how exceptions are logged and processed in this error path.

This approach appears to be part of a broader strategy to move potentially blocking operations to executors, though in this specific case it's taking a different approach by using the logger directly.

Ensure that the error is still being properly captured elsewhere in the exception handling flow, possibly by a global exception handler that processes the logger parameter.

supervisor/utils/log_format.py (1)

16-16: Typographical nitpick in docstring.
Consider replacing “formated” with “formatted” in the docstring to maintain clarity.

supervisor/utils/dbus.py (1)

127-129: Broad exception handling.
Catching all exceptions here is likely intentional for a final fallback. If possible, consider narrowing the exception scope or adding specialized error handling for better maintainability.

supervisor/jobs/decorator.py (1)

376-376: Updated to use async free space method.

The code now properly awaits free_space() as it has been changed from a synchronous property to an asynchronous method.

However, there's a potential issue in the error message on line 382. It still references coresys.sys_host.info.free_space as if it were a property:

-                f"'{method_name}' blocked from execution, not enough free space ({coresys.sys_host.info.free_space}GB) left on the device"
+                f"'{method_name}' blocked from execution, not enough free space ({await coresys.sys_host.info.free_space()}GB) left on the device"
supervisor/api/store.py (3)

188-193: Consider concurrency limiting with large numbers of add-ons.
Using asyncio.gather for generating addon information concurrently will improve performance. However, if the store grows significantly, this could create too many tasks at once. Consider concurrency-limiting solutions if the store can hold hundreds of add-ons.


204-209: Reapply concurrency-limiting consideration here.
Similar to the previous comment about store_info, generating an extensive add-ons list concurrently may require a concurrency limit if the add-on count grows.


287-289: Reading text is appropriate if documentation is purely text.
If the documentation includes non-text content, consider binary mode or a separate function that can distinguish file types.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 151d4bd and 1eaa22e.

📒 Files selected for processing (55)
  • supervisor/__main__.py (1 hunks)
  • supervisor/addons/addon.py (1 hunks)
  • supervisor/addons/manager.py (2 hunks)
  • supervisor/addons/model.py (1 hunks)
  • supervisor/api/__init__.py (1 hunks)
  • supervisor/api/addons.py (1 hunks)
  • supervisor/api/host.py (1 hunks)
  • supervisor/api/middleware/security.py (1 hunks)
  • supervisor/api/store.py (11 hunks)
  • supervisor/api/utils.py (5 hunks)
  • supervisor/backups/manager.py (2 hunks)
  • supervisor/bootstrap.py (2 hunks)
  • supervisor/core.py (3 hunks)
  • supervisor/dbus/network/__init__.py (2 hunks)
  • supervisor/docker/addon.py (2 hunks)
  • supervisor/docker/interface.py (2 hunks)
  • supervisor/hardware/disk.py (2 hunks)
  • supervisor/homeassistant/core.py (4 hunks)
  • supervisor/host/apparmor.py (1 hunks)
  • supervisor/host/info.py (1 hunks)
  • supervisor/jobs/__init__.py (1 hunks)
  • supervisor/jobs/decorator.py (2 hunks)
  • supervisor/misc/filter.py (1 hunks)
  • supervisor/misc/tasks.py (2 hunks)
  • supervisor/mounts/manager.py (1 hunks)
  • supervisor/mounts/mount.py (4 hunks)
  • supervisor/os/data_disk.py (2 hunks)
  • supervisor/os/manager.py (2 hunks)
  • supervisor/plugins/audio.py (1 hunks)
  • supervisor/plugins/base.py (1 hunks)
  • supervisor/plugins/cli.py (1 hunks)
  • supervisor/plugins/dns.py (1 hunks)
  • supervisor/plugins/manager.py (3 hunks)
  • supervisor/plugins/multicast.py (1 hunks)
  • supervisor/plugins/observer.py (1 hunks)
  • supervisor/resolution/check.py (1 hunks)
  • supervisor/resolution/checks/dns_server.py (1 hunks)
  • supervisor/resolution/checks/dns_server_ipv6.py (1 hunks)
  • supervisor/resolution/checks/free_space.py (2 hunks)
  • supervisor/resolution/evaluate.py (1 hunks)
  • supervisor/resolution/fixup.py (1 hunks)
  • supervisor/resolution/notify.py (1 hunks)
  • supervisor/store/data.py (1 hunks)
  • supervisor/store/repository.py (2 hunks)
  • supervisor/supervisor.py (1 hunks)
  • supervisor/utils/apparmor.py (2 hunks)
  • supervisor/utils/common.py (2 hunks)
  • supervisor/utils/dbus.py (1 hunks)
  • supervisor/utils/log_format.py (2 hunks)
  • supervisor/utils/sentry.py (1 hunks)
  • supervisor/utils/yaml.py (2 hunks)
  • tests/homeassistant/test_home_assistant_watchdog.py (1 hunks)
  • tests/host/test_info.py (1 hunks)
  • tests/mounts/test_manager.py (2 hunks)
  • tests/utils/test_log_format.py (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • supervisor/utils/yaml.py
  • supervisor/utils/apparmor.py
  • supervisor/utils/common.py
🧰 Additional context used
📓 Path-based instructions (6)
`*/**(html|markdown|md)`: - For instructional content in doc...

*/**(html|markdown|md): - For instructional content in documentation, use a direct and authoritative tone. Avoid expressions of politeness such as 'may' or 'please', and ensure the goal of the instruction is fronted.

  • Apply the Microsoft Style Guide to ensure documentation maintains clarity and conciseness.
  • In step-by-step instructions, front the location phrase in the instructional sentence.
  • In step-by-step instructions, front the 'goal' in the instructional sentence.
  • In step-by-step instructions, if in doubt what to front, front the 'goal' before the location phrase in the instructional sentence.
  • do not hyphenate terms like 'top-right' or 'bottom-left' with 'corner'
  • supervisor/supervisor.py
  • supervisor/core.py
  • supervisor/bootstrap.py
  • supervisor/__main__.py
`*/**(html|markdown|md)`: - Use bold to mark UI strings. - I...

*/**(html|markdown|md): - Use bold to mark UI strings.

  • If "" are used to mark UI strings, replace them by bold.
  • supervisor/supervisor.py
  • supervisor/core.py
  • supervisor/bootstrap.py
  • supervisor/__main__.py
`*/**(html|markdown|md)`: - Be brief in your replies and don...

*/**(html|markdown|md): - Be brief in your replies and don't add fluff like "thank you for..." and "Please let me know if"

  • supervisor/supervisor.py
  • supervisor/core.py
  • supervisor/bootstrap.py
  • supervisor/__main__.py
`*/**(html|markdown|md)`: - Use sentence-style capitalizatio...

*/**(html|markdown|md): - Use sentence-style capitalization also in headings.

  • supervisor/supervisor.py
  • supervisor/core.py
  • supervisor/bootstrap.py
  • supervisor/__main__.py
`*/**(html|markdown|md)`: do not comment on HTML used for ic...

*/**(html|markdown|md): do not comment on HTML used for icons

  • supervisor/supervisor.py
  • supervisor/core.py
  • supervisor/bootstrap.py
  • supervisor/__main__.py
`*/**(html|markdown|md)`: Avoid flagging inline HTML for emb...

*/**(html|markdown|md): Avoid flagging inline HTML for embedding videos in future reviews for this repository.

  • supervisor/supervisor.py
  • supervisor/core.py
  • supervisor/bootstrap.py
  • supervisor/__main__.py
🔇 Additional comments (93)
tests/host/test_info.py (1)

8-12: Properly updated test to use async/await pattern.

The test has been correctly modified to match the updated asynchronous API of the free_space method. The function is now declared as async and properly awaits the result.

supervisor/plugins/cli.py (1)

117-117: Properly moved error capturing to executor.

The change correctly moves the capture_exception call to run in an executor, preventing blocking behavior in the main asynchronous event loop during error handling.

supervisor/resolution/fixup.py (1)

60-60: Properly moved error capturing to executor.

The change correctly moves the capture_exception call to run in an executor, preventing blocking behavior in the main asynchronous event loop during error handling.

supervisor/mounts/manager.py (1)

180-180: Properly moved error capturing to executor.

The change correctly moves the capture_exception call to run in an executor, preventing blocking behavior in the main asynchronous event loop during error handling.

supervisor/resolution/checks/dns_server_ipv6.py (1)

50-50: Good asynchronous pattern implementation

The change moves the capture_exception call to run in an executor, which prevents blocking the main event loop when capturing exceptions. This is a good practice for potentially I/O-bound operations.

supervisor/plugins/dns.py (1)

413-413: Appropriate use of executor for error capture

Moving capture_exception to run in an executor is a good practice. This prevents the main event loop from being blocked during error processing, especially if the error capture involves I/O operations.

supervisor/resolution/checks/dns_server.py (1)

45-45: Well-implemented asynchronous error handling

The modification correctly moves the exception capture to an executor, consistent with the pattern applied in other files. This ensures non-blocking error handling.

supervisor/store/data.py (1)

77-80: Improved docstring clarity

The added docstring note explicitly states that this function must be run in an executor, which is valuable information for developers. This helps prevent potential blocking of the event loop if the function is called incorrectly.

supervisor/plugins/observer.py (1)

124-124: Good change - error handling moved to executor

Moving the capture_exception call to run in an executor is a good improvement that helps maintain the asynchronous nature of the code, preventing potential blocking operations during error handling.

supervisor/misc/filter.py (1)

83-85: Good change - disk free space retrieval updated

This change looks good. Replacing the direct property access with an explicit method call to get disk free space fits with the PR's goal of moving operations like this to controlled contexts.

supervisor/supervisor.py (1)

222-222: Good change - error capture moved to executor

Moving the Sentry error capture to run in an executor is a beneficial change. It prevents blocking the main event loop during exception handling in the update process.

supervisor/plugins/audio.py (1)

166-166: Good change - error handling moved to executor

Moving the capture_exception call to run in an executor is consistent with the pattern applied to other similar methods throughout the codebase. This improves the async behavior of the repair method.

tests/homeassistant/test_home_assistant_watchdog.py (1)

144-144: Increased sleep duration for test stability.

The sleep duration has been increased from 0 to 0.1 seconds, which likely helps avoid race conditions when testing asynchronous behavior, especially now that more operations are running in executors.

supervisor/__main__.py (1)

57-57: Properly awaiting the now-asynchronous supervisor_debugger function.

This change correctly awaits the supervisor_debugger function which has been converted to an async function, maintaining the proper execution flow.

supervisor/misc/tasks.py (2)

227-227: Moved capture_exception to run in executor.

The error capturing operation has been properly moved to run in an executor to prevent blocking the main event loop during I/O operations.


344-344: Moved capture_exception to run in executor.

Similar to the other instance, this change properly moves the error capturing to run in an executor, consistently applying the pattern throughout the codebase.

supervisor/api/__init__.py (1)

415-415: Moved capture_exception to run in executor.

The error capturing operation has been correctly moved to run in an executor, consistent with the pattern applied in other files and aligning with the PR's objective.

supervisor/resolution/check.py (1)

69-69: Moved exception capture to executor

The capture_exception call has been moved to run in an executor via self.sys_run_in_executor. This is consistent with the PR objective of relocating potentially blocking I/O operations to executors.

This change will prevent the exception capture process (which might involve network requests or file I/O) from blocking the main event loop, improving overall responsiveness of the application.

supervisor/plugins/multicast.py (1)

112-112: Moved exception capture to executor

The capture_exception call has been moved to run in an executor via self.sys_run_in_executor. This is consistent with the PR objective of relocating potentially blocking I/O operations to executors.

This change will prevent the exception capture process from blocking the main event loop, improving overall responsiveness of the application.

supervisor/resolution/notify.py (1)

39-39: Changed property access to async method call

The direct property access self.sys_host.info.free_space has been changed to an asynchronous method call await self.sys_host.info.free_space(). This indicates that retrieving free space information now happens asynchronously.

This change is aligned with the PR's objective of moving potentially blocking operations (like disk space checks) to asynchronous execution. Since this is now an async call, it's correctly awaited within the f-string interpolation.

supervisor/utils/log_format.py (2)

3-3: Import looks good.
No issues with introducing asyncio here.


24-24: Asynchronous exception capture is well-implemented.
Running capture_exception in an executor helps avoid blocking the event loop.

supervisor/homeassistant/core.py (4)

163-163: Async exception capture is consistent.
Using self.sys_run_in_executor for exception capture prevents blocking the event loop.


195-195: Good practice for capturing unexpected exceptions.
Continuing the installation in a loop while capturing any errors asynchronously is a sound approach.


560-560: Resilient error handling in watchdog.
Restart logic remains non-blocking by offloading Sentry capture to a separate thread.


572-572: Consistent error capture pattern.
Maintaining a uniform approach to exception handling across the watchdog restart improves supportability.

supervisor/core.py (4)

172-172: Good improvement to non-blocking error handling

Moving the Sentry exception capture to run in an executor is a good practice that prevents blocking the async event loop during error processing.


189-189: Consistent async pattern for error handling

Moving error capture to an executor maintains consistent error handling approach throughout the system.


246-247: Correct async error capturing

Good change to prevent blocking during HomeAssistant error processing.


251-251: Appropriate executor usage

Correctly moves error capturing to an executor, consistent with other error handling changes.

supervisor/os/data_disk.py (2)

340-341: Proper async error handling

Moving exception capture to an executor prevents blocking the event loop when errors occur during device formatting operations.


357-358: Consistent async pattern

This change aligns with the project's move toward more consistent asynchronous error handling patterns.

supervisor/addons/addon.py (1)

1533-1533: Improved error handling in watchdog restart

Moving the exception capture to an executor in the addon restart process aligns with the project's shift toward non-blocking error handling.

supervisor/backups/manager.py (2)

528-529: Enhanced backup process error handling

Moving exception capturing to an executor improves the responsiveness of the system during backup error scenarios.


721-722: Consistent async error handling during restore

This change ensures restore operations don't block the event loop when capturing errors, consistent with other parts of the system.

supervisor/resolution/evaluate.py (1)

69-69: Good conversion to asynchronous error handling.

The change to run capture_exception in an executor is a good improvement to avoid blocking the main event loop during error handling.

tests/mounts/test_manager.py (1)

6-6: Improving test stability with order-insensitive comparison.

Using unorderable_list_difference makes the test more resilient to order changes in how mount operations are processed. This is a good practice when the order of operations doesn't matter, only that all expected operations are performed.

Also applies to: 115-154

supervisor/plugins/base.py (1)

132-132: Good conversion to asynchronous error handling.

The change to run capture_exception in an executor is a good improvement to avoid blocking the main event loop during error handling.

supervisor/host/apparmor.py (1)

74-76: Good conversion to asynchronous profile validation.

Moving the AppArmor profile validation to run in an executor is a good improvement to avoid blocking the main event loop during potentially IO-intensive validation operations.

supervisor/api/addons.py (1)

215-215: Property access changed to async method call.

The change from accessing addon.long_description as a property to awaiting it as an async method (await addon.long_description()) is correct since this is moving file I/O operations to an executor.

supervisor/resolution/checks/free_space.py (2)

26-26: Property access changed to async method call.

The modification to await self.sys_host.info.free_space() is aligned with the PR objective to move disk operations to executor.


48-48: Property access changed to async method call.

Similar to the previous change, awaiting the free space check is the correct approach.

supervisor/addons/manager.py (2)

173-173: Exception capture moved to executor.

Moving the Sentry capture_exception to an executor through sys_run_in_executor is a good approach since it prevents blocking the main event loop during error handling.


391-391: Exception capture moved to executor.

Consistent with the earlier change, this correctly moves the exception capture to an executor.

supervisor/api/middleware/security.py (1)

211-213: Error response function is now awaited.

The change to await api_return_error is correct as the function has been made asynchronous as part of this PR.

supervisor/docker/interface.py (2)

281-281: Changed error handling to use async executor pattern.

This change moves the Sentry exception capture to run in an executor, ensuring error reporting doesn't block the main event loop.


397-397: Consistent async error handling implementation.

Moving the exception capture to an executor keeps error reporting non-blocking and aligns with the pattern used throughout the codebase.

supervisor/utils/sentry.py (2)

46-49: Added important executor usage documentation.

The updated docstring correctly clarifies that this function must be called within an executor context, which matches the implementation changes made throughout the codebase.


57-60: Added critical executor requirement to docstring.

This documentation update is essential since all calls to this function are being migrated to run within executors throughout the codebase.

supervisor/plugins/manager.py (3)

83-83: Updated exception handling to run in executor.

This change ensures that Sentry exception capture runs in a separate executor context rather than potentially blocking the main event loop.


116-116: Consistent use of asynchronous error handling.

Moving the exception capture to an executor maintains consistency with the pattern applied throughout the codebase for non-blocking error handling.


134-134: Improved error handling in shutdown sequence.

Converting exception capture to run in an executor enhances shutdown reliability by preventing the main event loop from being blocked during error reporting.

supervisor/docker/addon.py (2)

609-609: Updated DNS error handling to use async pattern.

This change makes the error reporting non-blocking by moving the Sentry capture to an executor, following the same pattern used throughout the codebase.


790-790: Consistent async error handling during container stop.

Moving the exception capture to an executor during DNS host deletion ensures error reporting doesn't block the container stop process.

supervisor/addons/model.py (1)

637-650:

❓ Verification inconclusive

Good asynchronous refactoring of long_description method.

The transformation from a synchronous property to an asynchronous method is a well-structured change. Moving file I/O operations to run in an executor prevents blocking the event loop during potentially slow disk operations.

Make sure all callers of this method have been updated to use await when accessing this method, as it has changed from a property to an async method.


🏁 Script executed:

#!/bin/bash
# Find all instances where long_description is accessed to ensure proper awaiting
rg "\.long_description[^(]" --type py

Length of output: 39


Asynchronous refactoring approved, but please manually verify usage.
The refactoring converting long_description from a synchronous property to an asynchronous method is well done—offloading file I/O to an executor avoids blocking the event loop. However, since our automated search for callers not using await (via the provided rg command) did not return any output, it's unclear if all usages have been updated correctly. Please confirm that every invocation uses await, for example:

  • await instance.long_description() instead of instance.long_description
supervisor/dbus/network/__init__.py (3)

3-3: Added required asyncio import.

Required for the asynchronous error handling below.


227-229: Good practice: Moving error capture to executor.

Moving capture_exception to run in an executor prevents blocking the event loop during error handling operations.


235-237: Consistent error handling approach.

This change matches the error handling pattern applied above, ensuring consistent non-blocking error handling throughout the method.

supervisor/api/host.py (1)

101-104: Updated to use async disk space methods.

The code has been properly updated to await the disk space information methods, which have been changed from synchronous properties to asynchronous methods elsewhere in the codebase.

This change ensures disk I/O operations don't block the event loop, improving system responsiveness.

supervisor/jobs/decorator.py (1)

316-316: Good practice: Moving error capture to executor.

Moving the capture_exception call to run in an executor prevents blocking the event loop during error handling operations.

supervisor/bootstrap.py (1)

299-304: Converted supervisor_debugger to async function.

The function has been updated to be asynchronous and now imports the debugpy module using an executor. This change aligns with the PR objective of moving potentially blocking operations off the main thread.

supervisor/os/manager.py (2)

219-228: File I/O operations moved to an executor.

The file operations (open, write, close) have been properly moved to run in an executor to prevent blocking the main thread. Good job adding the try/finally block to ensure the file is always closed, even if an exception occurs during writing.


388-388: Exception handling moved to an executor.

The capture_exception call is now executed in an executor, preventing potential blocking during error handling. This is consistent with the PR's objective of moving synchronous calls to executors.

supervisor/api/utils.py (4)

131-143: Made api_return_error function asynchronous.

The function has been properly converted to async, and the format_message call is now correctly awaited. This change is necessary since format_message has been made asynchronous as part of the effort to move text reading operations to executors.


66-70: Updated calls to await the now asynchronous api_return_error function.

All calls to api_return_error in the exception handling blocks are now properly awaited, which is necessary since the function has been made asynchronous.


79-79: Added await to api_return_error call.

This call has been properly updated to await the now asynchronous function.


110-119: Updated api_return_error calls to use await.

All calls within the error handling blocks have been properly updated to use await for the now asynchronous api_return_error function.

tests/utils/test_log_format.py (3)

6-12: Updated test to be asynchronous.

The test function has been properly converted to async and now awaits the format_message call, which is necessary since the function being tested has been made asynchronous.


15-21: Converted test to async pattern.

This test function has been correctly updated to use async/await syntax to match the changes in the format_message function.


24-27: Updated exception test to use async pattern.

The exception test has been properly converted to be asynchronous and now awaits the format_message call.

supervisor/api/store.py (7)

5-5: No concerns on new import.
This import is necessary for the new _read_static_file helper and does not present any issues.


112-115: Asynchronous conversion looks good.
Switching _generate_addon_information to an async function is consistent with the rest of the changes.


159-159: Asynchronous property retrieval is appropriate.
Awaiting addon.long_description() aligns with the new asynchronous flow.


241-241: No issues with awaiting add-on information.
This call is consistent with the new async flow and ensures extended information is retrieved.


250-250: Reading binary file in text mode.
This call expects bytes but _read_static_file currently uses "r". Please see the previously suggested fix on lines 72-79.


259-259: Reading binary file in text mode.
This call expects bytes but _read_static_file currently uses "r". Refer to the fix proposed in _read_static_file.


273-273: Reading text is fine, but verify.
addons_addon_changelog expects a string, aligning better with "r" mode. If the add-on changelog is purely text, there is no issue. Otherwise, consider a binary read.

supervisor/store/repository.py (2)

77-80: Docstring clarification is helpful.
Indicating that validate must be run in an executor clarifies the synchronous nature of the I/O involved.


110-110: Running validation in executor is consistent.
Invoking validate within sys_run_in_executor aligns with the updated docstring requirements and ensures non-blocking behavior.

supervisor/hardware/disk.py (4)

52-55: Docstring improvement recognized.
Explicitly stating that get_disk_total_space must run in an executor clarifies when to perform blocking I/O.


60-63: Executor requirement is clear.
Similarly, clarifying get_disk_used_space usage in an executor helps avoid blocking the main thread.


68-71: No problems with added docstring mention.
The get_disk_free_space method follows the same pattern of requiring executor usage.


125-128: Documentation clarity is beneficial.
Stating that get_disk_life_time requires executor usage will help avoid performance bottlenecks in asynchronous code.

supervisor/mounts/mount.py (5)

211-211: Good async pattern for error handling.

Moving the error capture operation to an executor prevents blocking the main event loop when sending error reports, which aligns with the PR objective.


224-224: Good async pattern for error handling.

Similar to the previous change, this moves error capturing to an executor to prevent blocking the main event loop.


282-299: Good refactoring and async pattern implementation.

This change improves code organization by extracting the folder validation logic into a standalone function that's executed in a background thread. The error messages are clear and descriptive, and the implementation properly handles various edge cases.


496-505: Good refactoring and async pattern for credential handling.

The credential writing logic has been appropriately refactored into a dedicated function and executed in a background thread, which prevents blocking the main event loop during file I/O operations.


511-511: Good async pattern for file operations.

Moving the file deletion operation to an executor prevents blocking the main event loop, consistent with the pattern established in the rest of this file.

supervisor/host/info.py (5)

106-109: Good addition of virtualization property.

This property provides useful information about the virtualization environment, which can be valuable for diagnostics and system understanding.


110-115: Good conversion to async method for disk operations.

Converting this property to an async method that runs disk space calculation in a background thread prevents blocking the main event loop during I/O operations.


117-122: Good conversion to async method for disk operations.

Similar to the previous change, this properly moves potentially blocking disk operations to a background thread.


124-129: Good conversion to async method for disk operations.

Consistent with the pattern, this change properly handles disk operations asynchronously.


131-136: Good conversion to async method for disk operations.

This change follows the established pattern of moving potentially blocking disk operations to a background thread, improving overall system responsiveness.

@mdegat01 mdegat01 force-pushed the read-text-to-executor branch from 1eaa22e to d3a0cf4 Compare February 28, 2025 15:24
@@ -1530,7 +1530,7 @@ async def _restart_after_problem(self, state: ContainerState):
except AddonsError as err:
attempts = attempts + 1
_LOGGER.error("Watchdog restart of addon %s failed!", self.name)
capture_exception(err)
await self.sys_run_in_executor(capture_exception, err)
Copy link
Member

@agners agners Feb 28, 2025

Choose a reason for hiding this comment

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

I was actually wondering that, is capture_exception really blocking I/O? I guess it is, but it nowhere really says, from what I can tell? 🤔

In any case, I am a bit worried that we don't get some very fatal issues anymore with this e.g. what if the error leads to a segfault right afterwards? In that case the Sentry event might not make it out.

Now I am not sure, maybe that is a bit scripted, and given that people who opt-in to sentry might actually block unnecessarily in their production system, we should probably really do this in an executor.

But I'd like to have this centralized. Can you create a async_capture_exception which calls the executor? This would allow us to "opt-out" (call synchronously), e.g. for a dev instance or something.

Copy link
Contributor Author

@mdegat01 mdegat01 Feb 28, 2025

Choose a reason for hiding this comment

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

I was actually wondering that, is capture_exception really blocking I/O? I guess it is, but it nowhere really says, from what I can tell? 🤔

It is. Though not necessarily because of Sentry. I also am not sure if the library i blocking I/O or not. But it doesn't seem to support async and makes HTTP calls so I think it is?

But the reason I know its I/O is because of us. We add read the remaining free space from the disk every time and attach that info as metadata in the call here:

"disk_free_space": coresys.host.info.free_space,

That was how it ended up in this PR. I found the read_text call in the free_space property and backtracked to where it was used.

Copy link
Contributor

@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: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 40314b5 and c2f4f54.

📒 Files selected for processing (31)
  • supervisor/addons/addon.py (2 hunks)
  • supervisor/addons/manager.py (3 hunks)
  • supervisor/api/__init__.py (2 hunks)
  • supervisor/backups/manager.py (3 hunks)
  • supervisor/core.py (4 hunks)
  • supervisor/dbus/network/__init__.py (2 hunks)
  • supervisor/docker/addon.py (3 hunks)
  • supervisor/docker/interface.py (3 hunks)
  • supervisor/homeassistant/core.py (5 hunks)
  • supervisor/jobs/decorator.py (3 hunks)
  • supervisor/misc/tasks.py (3 hunks)
  • supervisor/mounts/manager.py (2 hunks)
  • supervisor/mounts/mount.py (3 hunks)
  • supervisor/os/data_disk.py (3 hunks)
  • supervisor/os/manager.py (2 hunks)
  • supervisor/plugins/audio.py (2 hunks)
  • supervisor/plugins/base.py (2 hunks)
  • supervisor/plugins/cli.py (2 hunks)
  • supervisor/plugins/dns.py (2 hunks)
  • supervisor/plugins/manager.py (4 hunks)
  • supervisor/plugins/multicast.py (2 hunks)
  • supervisor/plugins/observer.py (2 hunks)
  • supervisor/resolution/check.py (2 hunks)
  • supervisor/resolution/checks/dns_server.py (2 hunks)
  • supervisor/resolution/checks/dns_server_ipv6.py (2 hunks)
  • supervisor/resolution/evaluate.py (2 hunks)
  • supervisor/resolution/fixup.py (2 hunks)
  • supervisor/supervisor.py (2 hunks)
  • supervisor/utils/dbus.py (2 hunks)
  • supervisor/utils/log_format.py (2 hunks)
  • supervisor/utils/sentry.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (30)
  • supervisor/os/manager.py
  • supervisor/plugins/cli.py
  • supervisor/core.py
  • supervisor/plugins/audio.py
  • supervisor/misc/tasks.py
  • supervisor/resolution/checks/dns_server.py
  • supervisor/resolution/evaluate.py
  • supervisor/plugins/observer.py
  • supervisor/plugins/multicast.py
  • supervisor/resolution/checks/dns_server_ipv6.py
  • supervisor/api/init.py
  • supervisor/utils/dbus.py
  • supervisor/backups/manager.py
  • supervisor/plugins/manager.py
  • supervisor/dbus/network/init.py
  • supervisor/addons/manager.py
  • supervisor/plugins/base.py
  • supervisor/resolution/check.py
  • supervisor/addons/addon.py
  • supervisor/docker/interface.py
  • supervisor/resolution/fixup.py
  • supervisor/utils/log_format.py
  • supervisor/homeassistant/core.py
  • supervisor/plugins/dns.py
  • supervisor/jobs/decorator.py
  • supervisor/os/data_disk.py
  • supervisor/mounts/mount.py
  • supervisor/mounts/manager.py
  • supervisor/docker/addon.py
  • supervisor/supervisor.py
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Build armv7 supervisor
  • GitHub Check: Build armhf supervisor
  • GitHub Check: Build aarch64 supervisor
  • GitHub Check: Run tests Python 3.13.2
🔇 Additional comments (4)
supervisor/utils/sentry.py (4)

3-3: No concerns on this new import.


50-53: Docstring update is consistent with the transition to an executor-based call.


60-67: Asynchronous event capture logic looks good.


71-74: Docstring clarification aligns with executor usage.

@mdegat01 mdegat01 force-pushed the read-text-to-executor branch from b1e4440 to aa2f482 Compare February 28, 2025 19:35
Copy link
Contributor

@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: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa2f482 and b47b65f.

📒 Files selected for processing (1)
  • tests/api/test_supervisor.py (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Run tests Python 3.13.2
  • GitHub Check: Build armv7 supervisor
  • GitHub Check: Build armhf supervisor
  • GitHub Check: Build aarch64 supervisor

@mdegat01 mdegat01 force-pushed the read-text-to-executor branch from ed3f50f to baca69f Compare February 28, 2025 20:52
The error handling got introduced in #2052, however, #2100 essentially
makes sure there will never be a byte object passed to this function.
And even if, the Sentry aiohttp plug-in will properly catch such an
exception.
Copy link
Member

@agners agners left a comment

Choose a reason for hiding this comment

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

LGTM!

@agners agners merged commit 86133f8 into main Mar 1, 2025
20 checks passed
@agners agners deleted the read-text-to-executor branch March 1, 2025 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla-signed refactor A code change that neither fixes a bug nor adds a feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants