Skip to content

fix: replace blocking requests.post with aiohttp in GovernanceEthereum#1721

Merged
openminddev merged 3 commits intoOpenMind:mainfrom
0xbyt4:fix/ethereum-governance-blocking
Jan 20, 2026
Merged

fix: replace blocking requests.post with aiohttp in GovernanceEthereum#1721
openminddev merged 3 commits intoOpenMind:mainfrom
0xbyt4:fix/ethereum-governance-blocking

Conversation

@0xbyt4
Copy link
Copy Markdown
Contributor

@0xbyt4 0xbyt4 commented Jan 19, 2026

Summary

Fix critical event loop blocking bug in GovernanceEthereum input plugin. The load_rules_from_blockchain() method was using synchronous requests.post() which blocked the entire async event loop for up to 10 seconds during blockchain RPC calls.

Problem

The original implementation had two critical issues:

  1. Blocking HTTP Call: Used requests.post() (synchronous) instead of aiohttp (async)
  2. Blocking in __init__: Called the blocking method during initialization
# OLD CODE (BLOCKING)
def load_rules_from_blockchain(self):
    response = requests.post(self.rpc_url, json=payload, timeout=10)  # BLOCKS EVENT LOOP
    ...

def __init__(self, config):
    self.universal_rule = self.load_rules_from_blockchain()  # Called in sync __init__!

Impact: During initialization and every poll, the entire event loop would freeze for up to 10+ seconds, preventing all other coroutines (camera, ASR, GPS, etc.) from running.

Solution

  • Replace requests.post with aiohttp.ClientSession for non-blocking HTTP
  • Make load_rules_from_blockchain() an async method
  • Defer blockchain loading from __init__ to first _poll() call
  • Add specific exception handling for aiohttp.ClientError and asyncio.TimeoutError
# NEW CODE (NON-BLOCKING)
async def load_rules_from_blockchain(self) -> Optional[str]:
    async with aiohttp.ClientSession() as session:
        async with session.post(
            self.rpc_url,
            json=payload,
            timeout=aiohttp.ClientTimeout(total=10),
        ) as response:
            result = await response.json()
            ...

def __init__(self, config):
    self.universal_rule = None  # Deferred to async context

Changes

File Change
src/inputs/plugins/ethereum_governance.py Replace requests with aiohttp, make method async
tests/inputs/base/test_governance_ethereum.py Update tests with async mocks, add comprehensive coverage

Non-Blocking Verification

Added tests that run concurrent tasks during HTTP calls to verify the event loop is not blocked:

@pytest.mark.asyncio
async def test_load_rules_is_non_blocking():
    # Runs a ticker task that counts ticks during HTTP call
    # If blocking: 0 ticks | If non-blocking: 3+ ticks
    await asyncio.gather(governance_call(), concurrent_ticker())
    assert ticks_during_call >= 3  # Proves non-blocking

Test Coverage

  • 24 tests - All passing
  • 100% code coverage
Category Tests
Blockchain Loading success, failure, empty result, missing key
Exception Handling ClientError, TimeoutError, generic Exception
Non-Blocking load_rules, _poll concurrent verification
Decode Response valid, invalid, too short, control chars, no 0x prefix
Message Processing raw_to_text, buffer management, formatted output

Related PR

This PR supersedes #1361 which only added tests without the blocking fix. This PR includes:

  • The critical blocking bug fix
  • More comprehensive tests (24 vs 17)
  • Non-blocking verification tests
  • Proper aiohttp mocks instead of requests mocks

Test Plan

  • uv run ruff check - Passed
  • uv run black --check - Passed
  • uv run isort --check-only - Passed
  • uv run pyright - 0 errors
  • uv run pytest tests/inputs/base/test_governance_ethereum.py -v - 24 passed
  • uv run pre-commit run --files - All passed

The load_rules_from_blockchain() method was using blocking requests.post()
which blocked the async event loop, preventing other coroutines from running.

Changes:
- Replace requests.post with aiohttp async HTTP client
- Make load_rules_from_blockchain() async method
- Move blockchain loading from __init__ to first _poll() call
- Add specific exception handling for ClientError and TimeoutError
- Add POLL_INTERVAL type hint for float compatibility
- Update tests with async mocks and 100% coverage
- Add edge case tests for short hex, control chars, missing result key
@0xbyt4 0xbyt4 requested review from a team as code owners January 19, 2026 19:08
@github-actions github-actions Bot added robotics Robotics code changes python Python code tests Test files labels Jan 19, 2026
openminddev and others added 2 commits January 19, 2026 21:52
Removed redundant comments and improved type annotations in the GovernanceEthereum class. Updated tests to add descriptive docstrings, fixed POLL_INTERVAL type assertion, and enhanced test clarity for edge cases and error handling.
@openminddev openminddev merged commit d5ed1a1 into OpenMind:main Jan 20, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Python code robotics Robotics code changes tests Test files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants