refactor(api): type SMTP mail payload with TypedDict#35174
refactor(api): type SMTP mail payload with TypedDict#35174YB0y wants to merge 3 commits intolanggenius:mainfrom
Conversation
Pyrefly Diffbase → PR--- /tmp/pyrefly_base.txt 2026-04-14 12:34:22.765817474 +0000
+++ /tmp/pyrefly_pr.txt 2026-04-14 12:34:12.243727180 +0000
@@ -5890,6 +5890,16 @@
--> tests/unit_tests/libs/test_rate_limiter.py:42:22
ERROR Argument `dict[@_, @_]` is not assignable to parameter `headers` with type `Message` in function `python_http_client.exceptions.HTTPError.__init__` [bad-argument-type]
--> tests/unit_tests/libs/test_sendgrid_client.py:38:99
+ERROR Argument `dict[Unknown, Unknown]` is not assignable to parameter `mail` with type `SMTPMessageDict` in function `libs.smtp.SMTPClient.send` [bad-argument-type]
+ --> tests/unit_tests/libs/test_smtp_client.py:16:17
+ERROR Argument `dict[Unknown, Unknown]` is not assignable to parameter `mail` with type `SMTPMessageDict` in function `libs.smtp.SMTPClient.send` [bad-argument-type]
+ --> tests/unit_tests/libs/test_smtp_client.py:35:17
+ERROR Argument `dict[Unknown, Unknown]` is not assignable to parameter `mail` with type `SMTPMessageDict` in function `libs.smtp.SMTPClient.send` [bad-argument-type]
+ --> tests/unit_tests/libs/test_smtp_client.py:62:21
+ERROR Argument `dict[Unknown, Unknown]` is not assignable to parameter `mail` with type `SMTPMessageDict` in function `libs.smtp.SMTPClient.send` [bad-argument-type]
+ --> tests/unit_tests/libs/test_smtp_client.py:74:21
+ERROR Argument `dict[Unknown, Unknown]` is not assignable to parameter `mail` with type `SMTPMessageDict` in function `libs.smtp.SMTPClient.send` [bad-argument-type]
+ --> tests/unit_tests/libs/test_smtp_client.py:95:21
ERROR Argument `None` is not assignable to parameter `duration_str` with type `str` in function `libs.time_parser.parse_time_duration` [bad-argument-type]
--> tests/unit_tests/libs/test_time_parser.py:54:38
ERROR Argument `MockRequest` is not assignable to parameter `request` with type `Request` in function `libs.token.extract_access_token` [bad-argument-type]
|
830dbbf to
a7a7dec
Compare
Pyrefly Diffbase → PR--- /tmp/pyrefly_base.txt 2026-04-14 12:45:51.907828339 +0000
+++ /tmp/pyrefly_pr.txt 2026-04-14 12:45:41.194917008 +0000
@@ -5890,6 +5890,16 @@
--> tests/unit_tests/libs/test_rate_limiter.py:42:22
ERROR Argument `dict[@_, @_]` is not assignable to parameter `headers` with type `Message` in function `python_http_client.exceptions.HTTPError.__init__` [bad-argument-type]
--> tests/unit_tests/libs/test_sendgrid_client.py:38:99
+ERROR Argument `dict[Unknown, Unknown]` is not assignable to parameter `mail` with type `SMTPMessageDict` in function `libs.smtp.SMTPClient.send` [bad-argument-type]
+ --> tests/unit_tests/libs/test_smtp_client.py:16:17
+ERROR Argument `dict[Unknown, Unknown]` is not assignable to parameter `mail` with type `SMTPMessageDict` in function `libs.smtp.SMTPClient.send` [bad-argument-type]
+ --> tests/unit_tests/libs/test_smtp_client.py:35:17
+ERROR Argument `dict[Unknown, Unknown]` is not assignable to parameter `mail` with type `SMTPMessageDict` in function `libs.smtp.SMTPClient.send` [bad-argument-type]
+ --> tests/unit_tests/libs/test_smtp_client.py:62:21
+ERROR Argument `dict[Unknown, Unknown]` is not assignable to parameter `mail` with type `SMTPMessageDict` in function `libs.smtp.SMTPClient.send` [bad-argument-type]
+ --> tests/unit_tests/libs/test_smtp_client.py:74:21
+ERROR Argument `dict[Unknown, Unknown]` is not assignable to parameter `mail` with type `SMTPMessageDict` in function `libs.smtp.SMTPClient.send` [bad-argument-type]
+ --> tests/unit_tests/libs/test_smtp_client.py:95:21
ERROR Argument `None` is not assignable to parameter `duration_str` with type `str` in function `libs.time_parser.parse_time_duration` [bad-argument-type]
--> tests/unit_tests/libs/test_time_parser.py:54:38
ERROR Argument `MockRequest` is not assignable to parameter `request` with type `Request` in function `libs.token.extract_access_token` [bad-argument-type]
|
There was a problem hiding this comment.
Pull request overview
Refactors the SMTP email sending API to use a TypedDict so the expected payload shape (to, subject, html) is explicit to type checkers and readers.
Changes:
- Added
SMTPMessageDictTypedDictto represent the SMTP mail payload. - Updated
SMTPClient.send()to acceptSMTPMessageDictand returnNone. - Updated
Mail.send()to pass an SMTP-specific payload shape when the underlying client isSMTPClient.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
api/libs/smtp.py |
Introduces SMTPMessageDict and narrows SMTPClient.send()’s parameter type to this TypedDict. |
api/extensions/ext_mail.py |
Imports the new TypedDict and routes SMTP sends through an SMTP-specific payload shape. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| from configs import dify_config | ||
| from dify_app import DifyApp | ||
| from libs.smtp import SMTPClient, SMTPMessageDict |
There was a problem hiding this comment.
SMTPClient/SMTPMessageDict are now imported at module import time, but SMTPClient is also imported lazily inside init_app() for the SMTP case. This is redundant and also removes the previous lazy-load behavior for non-SMTP mail types. Consider keeping only one import strategy (e.g., keep the lazy import and remove the top-level import, or remove the inner import if you want eager import).
| SMTPMessageDict( | ||
| to=to, | ||
| subject=subject, | ||
| html=html, | ||
| ) |
There was a problem hiding this comment.
Using SMTPMessageDict(...) as a runtime constructor is unnecessary here (TypedDict is primarily for static typing) and can be confusing to readers. Prefer constructing a normal dict literal and annotating/casting it to SMTPMessageDict before passing it to SMTPClient.send().
| self._client.send( | ||
| SMTPMessageDict( | ||
| to=to, | ||
| subject=subject, | ||
| html=html, | ||
| ) | ||
| ) | ||
| return | ||
|
|
||
| self._client.send( |
There was a problem hiding this comment.
The new SMTP-specific branch (isinstance(self._client, SMTPClient)) changes the runtime call path and payload shape passed to the client, but the existing Mail extension tests only exercise the generic MagicMock client path. Add a unit test that sets _client to an actual SMTPClient (mocking out smtplib) and asserts Mail.send() calls SMTPClient.send() with the expected 3-key payload and returns early.
| self._client.send( | |
| SMTPMessageDict( | |
| to=to, | |
| subject=subject, | |
| html=html, | |
| ) | |
| ) | |
| return | |
| self._client.send( | |
| return self._client.send( | |
| SMTPMessageDict( | |
| to=to, | |
| subject=subject, | |
| html=html, | |
| ) | |
| ) | |
| return self._client.send( |
Part of #32863 (
api/libs/)Summary
SMTPMessageDictTypedDict for the SMTP mail payloaddictparameter annotation onSMTPClient.send()withSMTPMessageDictWhy this change
SMTPClient.send()accepted a plaindict, even though it relies on a fixed 3-key payload shape (to,subject,html). That hid the contract from type checkers and readers. ATypedDictmakes the expected payload explicit without changing runtime behavior.Changes
api/libs/smtp.py: DefineSMTPMessageDict, annotateSMTPClient.send()to accept it