Skip to content

feat: switch to async - #2

Open
xbcsmith wants to merge 1 commit into
mainfrom
pr-xbcsmith-async
Open

feat: switch to async#2
xbcsmith wants to merge 1 commit into
mainfrom
pr-xbcsmith-async

Conversation

@xbcsmith

Copy link
Copy Markdown
Owner

No description provided.

@xbcsmith
xbcsmith requested a review from CopilotOctober 10, 2025 14:15

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull Request Overview

This PR switches the EPR client from synchronous to asynchronous operations. The main purpose is to modernize the HTTP client implementation by replacing urllib3 with httpx and making all client operations async-compatible, which is especially beneficial for MCP servers and other async contexts.

Key changes:

  • Replace urllib3 with httpx for HTTP client functionality
  • Convert Client class to use async/await pattern with context manager support
  • Update GraphQLQuery model to use dict instead of string for variables
  • Modify search and create functions to be async and use asyncio.run in CLI

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
src/epr/client.pyComplete async refactor with httpx integration and context manager support
src/epr/models.pyUpdate GraphQLQuery variables type from string to dict
src/epr/create.pyConvert to async function with proper client usage
src/epr/search.pyConvert to async function with proper client usage
src/epr/main.pyAdd asyncio.run calls and fix field handling
pyproject.tomlReplace urllib3 dependency with httpx
tests/unit/test_errors.pyUpdate mock import and fix fake exception handling
tests/unit/test_models.pyAdd comprehensive tests for updated models
tests/unit/test_async_client.pyNew comprehensive async client tests
tests/unit/test_async_search_create.pyNew tests for async search/create functions
tests/unit/test_async_main.pyNew tests for async main functionality
tests/test_smoke.pyAdd async functionality smoke tests
examples/async_client_example.pyNew example demonstrating async client usage
docs/README.mdUpdate documentation with async examples
README.mdMinor path update for virtual environment

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment threadsrc/epr/main.py
Comment on lines +52 to 53
return []
if "," in value:

CopilotAIOct 10, 2025

Copy link

Choose a reason for hiding this comment

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

The variable fields is declared but removed in the first condition. This creates inconsistent behavior - the function should either consistently return a list or handle the None case differently to maintain the original variable pattern.

Suggested change
return []
if","invalue:
fields= []
elif","invalue:

Copilot uses AI. Check for mistakes.
tb = FakeTraceback([frame], [1])
exc = FakeException("foo").with_traceback(tb)
return FakeException, exc, tb
return FakeException, exc, exc._fake_traceback

CopilotAIOct 10, 2025

Copy link

Choose a reason for hiding this comment

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

Direct access to private attribute _fake_traceback breaks encapsulation. Consider adding a public property or method to access the fake traceback.

Copilot uses AI. Check for mistakes.
Comment on lines +261 to +274
def run_async_test(coro):
"""Helper to run async test methods."""

def wrapper(self):
return asyncio.run(coro(self))

return wrapper


# Apply the async wrapper to all async test methods
for attr_name in dir(AsyncSearchCreateTestCase):
attr = getattr(AsyncSearchCreateTestCase, attr_name)
if callable(attr) and asyncio.iscoroutinefunction(attr) and attr_name.startswith("test_"):
setattr(AsyncSearchCreateTestCase, attr_name, run_async_test(attr))

CopilotAIOct 10, 2025

Copy link

Choose a reason for hiding this comment

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

The dynamic method wrapping approach is complex and could be error-prone. Consider using unittest.IsolatedAsyncioTestCase (available in Python 3.8+) which provides native async test support, or use pytest with pytest-asyncio for cleaner async test handling.

Copilot uses AI. Check for mistakes.
Comment on lines +258 to +271
def run_async_test(coro):
"""Helper to run async test methods."""

def wrapper(self):
return asyncio.run(coro(self))

return wrapper


# Apply the async wrapper to all async test methods
for attr_name in dir(AsyncClientTestCase):
attr = getattr(AsyncClientTestCase, attr_name)
if callable(attr) and asyncio.iscoroutinefunction(attr) and attr_name.startswith("test_"):
setattr(AsyncClientTestCase, attr_name, run_async_test(attr))

CopilotAIOct 10, 2025

Copy link

Choose a reason for hiding this comment

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

The dynamic method wrapping approach is complex and could be error-prone. Consider using unittest.IsolatedAsyncioTestCase (available in Python 3.8+) which provides native async test support, or use pytest with pytest-asyncio for cleaner async test handling.

Copilot uses AI. Check for mistakes.
Sign up for freeto 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

@xbcsmith