Conversation
|
wow, I loved this Async support!!! great job @drish |
|
@bukinoshita @felipevolpone Is there any update on this? Are there any outstanding tasks that any of us could potentially help with to get this PR unblocked and merged? |
@ojh i had to re-prioritize some work and this ended up having to wait a bit. I'll be working on this back again this coming week, should be very close to wrap up. |
|
nice! i was thinking about this and now i am happy because this pull request. thanks |
There was a problem hiding this comment.
10 issues found across 23 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="resend/contacts/_contacts.py">
<violation number="1" location="resend/contacts/_contacts.py:379">
P2: Custom agent: **API Key Permission Check SDK Methods**
New async contact paths now call the global /contacts endpoints. Please confirm that production API keys have the required permissions for global contact operations to avoid permission-related failures after deployment (API Key Permission Check SDK Methods rule).</violation>
</file>
<file name="resend/emails/_attachments.py">
<violation number="1" location="resend/emails/_attachments.py:133">
P2: AsyncRequest can be undefined when async extras aren’t installed, causing a NameError on get_async. Import it inside the method (or raise a clear ImportError) to fail fast with a clear message.</violation>
</file>
<file name="tests/contacts_segments_async_test.py">
<violation number="1" location="tests/contacts_segments_async_test.py:8">
P2: Async test methods on a `unittest.TestCase` base are not awaited, so these tests won’t run and will report false positives. Use `IsolatedAsyncioTestCase` (or a pytest async marker) for async tests.</violation>
</file>
<file name="tests/contact_properties_async_test.py">
<violation number="1" location="tests/contact_properties_async_test.py:8">
P2: These async tests inherit from `unittest.TestCase` via `ResendBaseTest`, so the `async def` test methods are never awaited by the unittest runner. The assertions inside the new tests won’t execute. Use an async-capable base like `unittest.IsolatedAsyncioTestCase` or convert these to pytest-style async tests.</violation>
</file>
<file name="resend/webhooks/_webhooks.py">
<violation number="1" location="resend/webhooks/_webhooks.py:370">
P2: Async methods call `AsyncRequest` even when the optional async dependency isn’t installed, which will raise `NameError` at runtime. Add an explicit guard (and/or define `AsyncRequest = None` on import failure) to raise a clear ImportError before trying to use it.</violation>
</file>
<file name="tests/templates_async_test.py">
<violation number="1" location="tests/templates_async_test.py:8">
P2: Async test methods are defined on a `unittest.TestCase` subclass, which doesn’t await async tests. These tests will be skipped or treated as coroutines rather than executed. Use an async-aware base class (e.g., `unittest.IsolatedAsyncioTestCase`) or mark the tests for an async test runner (e.g., pytest with `@pytest.mark.asyncio`) and adjust the base class accordingly.</violation>
</file>
<file name="resend/contact_properties/_contact_properties.py">
<violation number="1" location="resend/contact_properties/_contact_properties.py:284">
P2: Guard async methods against missing `resend[async]` so callers get a clear ImportError instead of a NameError when AsyncRequest isn’t available.</violation>
</file>
<file name="resend/contacts/_topics.py">
<violation number="1" location="resend/contacts/_topics.py:217">
P2: AsyncRequest is optional but the ImportError is swallowed, leaving AsyncRequest undefined. Calling list_async/update_async without the async extra will raise a NameError instead of a clear ImportError. Add an explicit guard or error when the async dependency is missing.</violation>
</file>
<file name="resend/topics/_topics.py">
<violation number="1" location="resend/topics/_topics.py:251">
P2: `AsyncRequest` is silently ignored on ImportError, so calling any `*_async` method without the async extra raises `NameError` instead of a clear dependency error. Add a guard or explicit ImportError for missing async extras.</violation>
</file>
<file name="tests/webhooks_async_test.py">
<violation number="1" location="tests/webhooks_async_test.py:8">
P2: Async tests in a unittest.TestCase subclass won’t be awaited, so these new async tests won’t actually run (they’ll return coroutine objects and be treated as passing). Use an async-capable base (e.g., IsolatedAsyncioTestCase) or pytest-asyncio markers instead.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
26 issues found across 50 files
Confidence score: 3/5
- There is a concrete runtime risk in multiple async code paths (for example
resend/emails/_emails.py,resend/emails/_batch.py, andresend/domains/_domains.py): when the async extra is not installed,AsyncRequestcan be undefined and_asynccalls may fail withNameErrorinstead of a clear guidance error. - Several new async examples (
examples/async/batch_email_send_async.py,examples/async/audiences_async.py,examples/async/contacts_async.py,examples/async/domains_async.py) depend on specific API-key scopes, so production usage can fail with permission errors if keys are not provisioned correctly. - Given the medium-high severity and strong confidence on user-facing failure modes, this carries some regression risk, though fixes appear straightforward (explicit async dependency guards and clearer permission prerequisites).
- Pay close attention to
resend/emails/_emails.py,resend/emails/_batch.py,resend/domains/_domains.py,resend/emails/_attachments.py,resend/topics/_topics.py,resend/contact_properties/_contact_properties.py- async methods should fail gracefully with a clear ImportError when async support is missing.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="examples/async/batch_email_send_async.py">
<violation number="1" location="examples/async/batch_email_send_async.py:8">
P3: Use os.environ.get(...) (and optionally assign it) so missing RESEND_API_KEY triggers the intended error instead of a KeyError.</violation>
<violation number="2" location="examples/async/batch_email_send_async.py:33">
P1: Custom agent: **API Key Permission Check SDK Methods**
The new async batch send example uses Resend’s Batch.send_async. Confirm that production API keys have the required permissions for batch email sending before rollout to avoid permission failures. (Rule: API Key Permission Check SDK Methods)</violation>
</file>
<file name="examples/async/audiences_async.py">
<violation number="1" location="examples/async/audiences_async.py:6">
P2: Use os.environ.get(...) for the check so missing variables raise the intended EnvironmentError instead of a KeyError.</violation>
<violation number="2" location="examples/async/audiences_async.py:16">
P1: Custom agent: **API Key Permission Check SDK Methods**
Add a reminder that API keys must have segment management permissions for these new async operations to avoid permission failures.</violation>
</file>
<file name="examples/async/contacts_async.py">
<violation number="1" location="examples/async/contacts_async.py:24">
P1: Custom agent: **API Key Permission Check SDK Methods**
Confirm that the production API keys have the required Contacts/Audiences permissions for these new async operations to avoid permission-related failures after deployment.</violation>
</file>
<file name="examples/async/domains_async.py">
<violation number="1" location="examples/async/domains_async.py:18">
P1: Custom agent: **API Key Permission Check SDK Methods**
These newly added async domain-management calls introduce new provider SDK operations; confirm production API keys have the required domain-management permissions to avoid permission failures after deployment (Rule: API Key Permission Check SDK Methods).</violation>
</file>
<file name="examples/async/receiving_email_async.py">
<violation number="1" location="examples/async/receiving_email_async.py:7">
P3: Use `os.environ.get` (or `os.getenv`) so the missing API key triggers the intended EnvironmentError instead of a KeyError.</violation>
</file>
<file name="resend/contacts/_contacts.py">
<violation number="1" location="resend/contacts/_contacts.py:381">
P2: Guard async methods when `resend[async]` is missing so they raise a clear ImportError instead of `NameError`.</violation>
</file>
<file name="resend/emails/_emails.py">
<violation number="1" location="resend/emails/_emails.py:387">
P2: If the async extra isn’t installed, `AsyncRequest` is undefined and calling this method raises a `NameError`. Add an explicit guard to raise a helpful ImportError when async support is missing.</violation>
</file>
<file name="resend/emails/_batch.py">
<violation number="1" location="resend/emails/_batch.py:122">
P2: Guard the async path when the optional AsyncRequest import is unavailable; otherwise send_async will crash with NameError when the async extra isn’t installed.</violation>
</file>
<file name="resend/webhooks/_webhooks.py">
<violation number="1" location="resend/webhooks/_webhooks.py:370">
P2: Guard async methods when the async extra isn’t installed; otherwise calling create_async/get_async raises NameError because AsyncRequest is undefined.</violation>
</file>
<file name="examples/async/api_keys_async.py">
<violation number="1" location="examples/async/api_keys_async.py:7">
P3: Use os.getenv (or os.environ.get) to avoid a KeyError when RESEND_API_KEY is missing so the intended EnvironmentError is raised.</violation>
</file>
<file name="resend/segments/_segments.py">
<violation number="1" location="resend/segments/_segments.py:14">
P2: Swallowing the ImportError leaves `AsyncRequest` undefined, so calling any async method without the async extra will crash with a `NameError`. Raise a clear error (or guard in the async methods) so missing extras fail predictably.</violation>
</file>
<file name="tests/contacts_async_test.py">
<violation number="1" location="tests/contacts_async_test.py:68">
P2: This test will pass even if `update_async` doesn’t raise because there’s no failure path when no exception occurs. Use `pytest.raises` to make the expectation explicit.</violation>
</file>
<file name="resend/contacts/segments/_contact_segments.py">
<violation number="1" location="resend/contacts/segments/_contact_segments.py:268">
P2: Guard the async methods when AsyncRequest isn’t available. Right now a missing async extra will surface as a NameError instead of a clear install error.</violation>
</file>
<file name="resend/domains/_domains.py">
<violation number="1" location="resend/domains/_domains.py:270">
P2: Guard async methods with a clear ImportError when `AsyncRequest` isn't available; otherwise calling these methods without `resend[async]` raises a `NameError`.</violation>
</file>
<file name="resend/emails/_attachments.py">
<violation number="1" location="resend/emails/_attachments.py:133">
P2: Calling the async methods will raise `NameError` when the optional async dependency is not installed because `AsyncRequest` is never defined after the import `pass`. Add a guard or explicit error so users get a clear ImportError instead of a runtime NameError.</violation>
</file>
<file name="examples/async/broadcasts_async.py">
<violation number="1" location="examples/async/broadcasts_async.py:8">
P2: Use `os.environ.get` (or `os.getenv`) so missing keys trigger the intended `EnvironmentError` instead of a `KeyError`.</violation>
</file>
<file name="resend/topics/_topics.py">
<violation number="1" location="resend/topics/_topics.py:251">
P2: `AsyncRequest` can be undefined when the async extra isn’t installed, so calling this async method will raise `NameError`. Add an explicit guard/ImportError so users get a clear message when async support isn’t available.</violation>
</file>
<file name="resend/api_keys/_api_keys.py">
<violation number="1" location="resend/api_keys/_api_keys.py:167">
P2: Guard async methods when AsyncRequest isn’t available so callers get a clear ImportError instead of a NameError.</violation>
</file>
<file name="resend/contact_properties/_contact_properties.py">
<violation number="1" location="resend/contact_properties/_contact_properties.py:13">
P2: Swallowing the ImportError leaves `AsyncRequest` undefined, so any `_async` call crashes with a NameError/TypeError. Define a sentinel and raise a clear ImportError when async methods are used without the extra installed.</violation>
</file>
<file name="resend/emails/_receiving.py">
<violation number="1" location="resend/emails/_receiving.py:173">
P2: Guard async methods when AsyncRequest is unavailable to avoid a NameError and raise a clear ImportError for missing async extras.</violation>
</file>
<file name="examples/async/simple_email_async.py">
<violation number="1" location="examples/async/simple_email_async.py:6">
P2: Avoid direct os.environ indexing here; it throws KeyError when RESEND_API_KEY is unset, so your custom error is never raised.</violation>
</file>
<file name="resend/contacts/_topics.py">
<violation number="1" location="resend/contacts/_topics.py:217">
P2: Guard async calls with a clear ImportError when the async extra isn’t installed; otherwise calling list_async will raise NameError.</violation>
</file>
<file name="resend/broadcasts/_broadcasts.py">
<violation number="1" location="resend/broadcasts/_broadcasts.py:394">
P2: Guard async methods when AsyncRequest isn’t available. With the current silent ImportError pass, calling a *_async method without the async extra raises a NameError instead of a clear install hint.</violation>
</file>
<file name="resend/templates/_templates.py">
<violation number="1" location="resend/templates/_templates.py:362">
P2: Guard async methods when the async extra isn’t installed. As written, calling this method without `resend[async]` raises NameError because AsyncRequest isn’t defined.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| else: | ||
| path = "/contacts" | ||
|
|
||
| resp = await AsyncRequest[Contacts.CreateContactResponse]( |
There was a problem hiding this comment.
P2: Guard async methods when resend[async] is missing so they raise a clear ImportError instead of NameError.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At resend/contacts/_contacts.py, line 381:
<comment>Guard async methods when `resend[async]` is missing so they raise a clear ImportError instead of `NameError`.</comment>
<file context>
@@ -344,3 +355,161 @@ def remove(
+ else:
+ path = "/contacts"
+
+ resp = await AsyncRequest[Contacts.CreateContactResponse](
+ path=path, params=cast(Dict[Any, Any], params), verb="post"
+ ).perform_with_content()
</file context>
| CreateResponse: The new broadcast object response | ||
| """ | ||
| path = "/broadcasts" | ||
| resp = await AsyncRequest[Broadcasts.CreateResponse]( |
There was a problem hiding this comment.
P2: Guard async methods when AsyncRequest isn’t available. With the current silent ImportError pass, calling a *_async method without the async extra raises a NameError instead of a clear install hint.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At resend/broadcasts/_broadcasts.py, line 394:
<comment>Guard async methods when AsyncRequest isn’t available. With the current silent ImportError pass, calling a *_async method without the async extra raises a NameError instead of a clear install hint.</comment>
<file context>
@@ -371,3 +377,113 @@ def remove(cls, id: str) -> RemoveResponse:
+ CreateResponse: The new broadcast object response
+ """
+ path = "/broadcasts"
+ resp = await AsyncRequest[Broadcasts.CreateResponse](
+ path=path, params=cast(Dict[Any, Any], params), verb="post"
+ ).perform_with_content()
</file context>
| CreateResponse: The created template response with ID and object type. | ||
| """ | ||
| path = "/templates" | ||
| resp = await AsyncRequest[Templates.CreateResponse]( |
There was a problem hiding this comment.
P2: Guard async methods when the async extra isn’t installed. As written, calling this method without resend[async] raises NameError because AsyncRequest isn’t defined.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At resend/templates/_templates.py, line 362:
<comment>Guard async methods when the async extra isn’t installed. As written, calling this method without `resend[async]` raises NameError because AsyncRequest isn’t defined.</comment>
<file context>
@@ -341,3 +347,119 @@ def remove(cls, template_id: str) -> RemoveResponse:
+ CreateResponse: The created template response with ID and object type.
+ """
+ path = "/templates"
+ resp = await AsyncRequest[Templates.CreateResponse](
+ path=path, params=cast(Dict[Any, Any], params), verb="post"
+ ).perform_with_content()
</file context>
| import resend | ||
| from resend import EmailsReceiving | ||
|
|
||
| if not os.environ["RESEND_API_KEY"]: |
There was a problem hiding this comment.
P3: Use os.environ.get (or os.getenv) so the missing API key triggers the intended EnvironmentError instead of a KeyError.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/async/receiving_email_async.py, line 7:
<comment>Use `os.environ.get` (or `os.getenv`) so the missing API key triggers the intended EnvironmentError instead of a KeyError.</comment>
<file context>
@@ -0,0 +1,163 @@
+import resend
+from resend import EmailsReceiving
+
+if not os.environ["RESEND_API_KEY"]:
+ raise EnvironmentError("RESEND_API_KEY is missing")
+
</file context>
|
|
||
| import resend | ||
|
|
||
| if not os.environ["RESEND_API_KEY"]: |
There was a problem hiding this comment.
P3: Use os.getenv (or os.environ.get) to avoid a KeyError when RESEND_API_KEY is missing so the intended EnvironmentError is raised.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/async/api_keys_async.py, line 7:
<comment>Use os.getenv (or os.environ.get) to avoid a KeyError when RESEND_API_KEY is missing so the intended EnvironmentError is raised.</comment>
<file context>
@@ -0,0 +1,34 @@
+
+import resend
+
+if not os.environ["RESEND_API_KEY"]:
+ raise EnvironmentError("RESEND_API_KEY is missing")
+
</file context>
Introduces async support using the httpx library.
SDK async version
Users will need to install the async version of the sdk (extra-requries) with:
pip install resend[async]Set the default_http_client:
_async):