Skip to content

Commit

Permalink
Merge pull request #56 from marktennyson/development
Browse files Browse the repository at this point in the history
Bump version => 0.2.3
  • Loading branch information
marktennyson authored Sep 19, 2023
2 parents 6623dfd + 2679e3e commit 8023937
Show file tree
Hide file tree
Showing 16 changed files with 80 additions and 62 deletions.
10 changes: 10 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github: marktennyson
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: aniketsarkar
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://paytm.me/vA-DF7O', 'https://www.buymeacoffee.com/aniketsarkar']
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,7 @@
- `Updated` Readme file.
- `Fixed` version number issue.
- `Added` the compatibility for Python 3.11.

## 0.2.3
- `Added` compatibility for Pydantic V2.
- `Fixed` several bugs.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ Moreover, Flask-Mailing offers a simple and intuitive API that allows developers

Whether you're building a small-scale application or a large-scale enterprise system, Flask-Mailing provides a reliable and scalable solution for Asynchronous email messaging in Flask applications.

## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=marktennyson/flask-mailing&type=Date)](https://star-history.com/#marktennyson/flask-mailing&Date)



### Key Features :sparkles:

Expand Down Expand Up @@ -126,6 +131,7 @@ Thanks go to these wonderful people ([🚧]):
ahmetkurukose</b></sub></a><br /></td>
<td align="center"><a href="https://github.com/Sriram-bb63"><img src="https://avatars.githubusercontent.com/u/71959217?v=4" width="100px;" alt=""/><br /><sub><b>
Sriram</b></sub></a><br /></td>
<td align="center"><a href="https://github.com/CharlesTWood"><img src="https://avatars.githubusercontent.com/u/31315150?v=4" width="100px;" alt=""/><br/><sub><b>CharlesTWood</b></sub></a><br /></td>
</tr>
</table>

Expand Down
1 change: 0 additions & 1 deletion flask_mailing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ async def simple_send():
from .schemas import Message as Message
from .schemas import MultipartSubtypeEnum as MultipartSubtypeEnum


__author__ = "[email protected]"


Expand Down
6 changes: 3 additions & 3 deletions flask_mailing/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

from flask.globals import current_app
from jinja2 import Environment, FileSystemLoader
from pydantic import BaseSettings as Settings
from pydantic import DirectoryPath, EmailStr, conint, validator
from pydantic import DirectoryPath, EmailStr, conint, field_validator
from pydantic_settings import BaseSettings as Settings

from .errors import TemplateFolderDoesNotExist

Expand All @@ -25,7 +25,7 @@ class ConnectionConfig(Settings):
USE_CREDENTIALS: bool = True
VALIDATE_CERTS: bool = True

@validator("MAIL_TEMPLATE_FOLDER")
@field_validator("MAIL_TEMPLATE_FOLDER")
def template_folder_validator(cls, v):
"""Validate the template folder directory."""
if not v:
Expand Down
5 changes: 2 additions & 3 deletions flask_mailing/connection.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import aiosmtplib
from pydantic import BaseSettings as Settings
from pydantic_settings import BaseSettings as Settings

from .config import ConnectionConfig
from .errors import ConnectionErrors, PydanticClassRequired
Expand All @@ -11,15 +11,14 @@ class Connection:
"""

def __init__(self, settings: ConnectionConfig):

if not issubclass(settings.__class__, Settings):
raise PydanticClassRequired(
"""Email configuruation should be provided from ConnectionConfig class, check example below:
\nfrom flask_mailing import ConnectionConfig \nconf = Connection(\nMAIL_USERNAME = "your_username",\nMAIL_PASSWORD = "your_pass",\nMAIL_FROM = "your_from_email",\nMAIL_PORT = 587,\nMAIL_SERVER = "email_service",\nMAIL_TLS = True,\nMAIL_SSL = False)
"""
)

self.settings = settings.dict()
self.settings = settings.model_dump()

async def __aenter__(self): # setting up a connection
await self._configure_connection()
Expand Down
38 changes: 17 additions & 21 deletions flask_mailing/mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@
if t.TYPE_CHECKING:
from flask import Flask

version_info = (0, 2, 2)
version_info = (0, 2, 3)


class _MailMixin:

name = "Flask Mailing"
version = ".".join([str(v) for v in version_info])

Expand Down Expand Up @@ -58,12 +57,10 @@ class Mail(_MailMixin):
"""

def __init__(self, app: t.Optional["Flask"] = None) -> None:

if app is not None:
self.init_app(app)

def init_app(self, app: "Flask") -> None:

self.config = ConnectionConfig(
MAIL_USERNAME=app.config.get("MAIL_USERNAME"),
MAIL_PASSWORD=app.config.get("MAIL_PASSWORD"),
Expand Down Expand Up @@ -113,7 +110,7 @@ async def __prepare_message(self, message: Message, template=None):
else:
template_data = self.make_dict(template_body)
message.template_body = template.render(**template_data)
msg = MailMsg(**message.dict())
msg = MailMsg(**message.model_dump())
if self.config.MAIL_FROM_NAME is not None:
sender = f"{self.config.MAIL_FROM_NAME} <{self.config.MAIL_FROM}>"
else:
Expand All @@ -125,10 +122,10 @@ async def send_message(self, message: Message, template_name=None):
to send the message object.
:param `message`: The object of the Message pydantic class.
:param `template_name`: if you are about to render any template
:param `template_name`: if you are about to render any template
with the mail please provide the name of the template by using this param.
### For example =>
### For example =>
```python
@app.get("/email")
async def simple_send():
Expand Down Expand Up @@ -164,23 +161,23 @@ async def simple_send():

async def send_mail(
self,
subject:str,
message:str,
recipients:t.List[EmailStr],
html_message:t.Optional[str]=None,
**msgkwargs
) -> None:
subject: str,
message: str,
recipients: t.List[EmailStr],
html_message: t.Optional[str] = None,
**msgkwargs,
) -> None:
"""
send a simple email by using this simple `send_mail` method.
:param `subject`: A String containing the subject of the message.
:param `message`: A string containing the message body.
:param `recipients`: A list of strings, each an email address.
Each member of recipients will see the other recipients
:param `recipients`: A list of strings, each an email address.
Each member of recipients will see the other recipients
in the “To:” field of the email message
:param `msgkwargs` : the kwargs based parameters for `Message` class.
### For example =>
### For example =>
```python
@app.get('/send-mail')
async def send_mail():
Expand All @@ -193,14 +190,13 @@ async def send_mail():
recipients=recipients,
body=message,
html=html_message,
**msgkwargs
**msgkwargs,
)
await self.send_message(message)

async def send_mass_mail(
self,
datatuple:t.Tuple[t.Tuple[str, str, t.List[EmailStr]]]
):
self, datatuple: t.Tuple[t.Tuple[str, str, t.List[EmailStr]]]
):
"""
To handle mass mailing.
Expand All @@ -221,4 +217,4 @@ async def send_mass_mail(
Signal sent when an email is dispatched. This signal will also be sent
in testing mode, even though the email will not actually be sent.
""",
)
)
33 changes: 16 additions & 17 deletions flask_mailing/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
else:
from typing_extensions import Literal

from pydantic import BaseModel, EmailStr, validator
from pydantic import BaseModel, EmailStr, field_validator
from werkzeug.datastructures import FileStorage

from .errors import WrongFile
Expand All @@ -34,28 +34,34 @@ class MultipartSubtypeEnum(Enum):


class Message(BaseModel):
recipients: List["EmailStr"]
recipients: List[EmailStr]
attachments: List[Union[FileStorage, Dict, str]] = []
subject: str = ""
body: Optional[Union[str, list]] = None
template_body: Optional[Union[list, dict]] = None
template_params: Optional[Union[list, dict]] = None
html: Optional[Union[str, List, Dict]] = None
cc: List["EmailStr"] = []
bcc: List["EmailStr"] = []
reply_to: List["EmailStr"] = []
cc: List[EmailStr] = []
bcc: List[EmailStr] = []
reply_to: List[EmailStr] = []
charset: str = "utf-8"
subtype: Optional[str] = None
multipart_subtype: MultipartSubtypeEnum = MultipartSubtypeEnum.mixed

@validator("template_params")
def validate_template_params(cls, value, values):
@field_validator("template_params")
def validate_template_params(cls, value, info):
if info.data.get("template_body", None) is None:
info.data["template_body"] = value
return value

if values.get("template_body", None) is None:
values["template_body"] = value
@field_validator("subtype")
def validate_subtype(cls, value, info):
"""Validate subtype field."""
if info.data.get("template_body", None):
return "html"
return value

@validator("attachments")
@field_validator("attachments")
def validate_file(cls, v):
temp = []
mime = MimeTypes()
Expand Down Expand Up @@ -129,13 +135,6 @@ def attach(
self.attachments.append(fsob)
return True

@validator("subtype")
def validate_subtype(cls, value, values, config, field):
"""Validate subtype field."""
if values.get("template_body"):
return "html"
return value

class Config:
arbitrary_types_allowed = True

Expand Down
12 changes: 6 additions & 6 deletions flask_mailing/utils/email_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@
from abc import ABC, abstractmethod
from typing import Any, List, Set


import dns.exception
import dns.resolver

try:
import aioredis

redis_lib = True
except:
redis_lib = False

try:
import httpx

request_lib = True
except:
request_lib = False
Expand Down Expand Up @@ -97,12 +98,12 @@ def __init__(
):
if not redis_lib:
raise ImportError(
'You must install aioredis from https://pypi.org/project/aioredis in order to run functionality'
"You must install aioredis from https://pypi.org/project/aioredis in order to run functionality"
)

if not request_lib:
raise ImportError(
'You must install httpx from https://pypi.org/project/httpx in order to run functionality'
"You must install httpx from https://pypi.org/project/httpx in order to run functionality"
)

self.source = (
Expand Down Expand Up @@ -161,7 +162,7 @@ async def init_redis(self):

def validate_email(self, email: str) -> bool:
"""Validate email address"""
EmailStr.validate(email)
EmailStr._validate(email, {})
return True

async def fetch_temp_email_domains(self):
Expand Down Expand Up @@ -274,7 +275,6 @@ async def check_mx_record(self, domain: str, full_result: bool = False):
dns.resolver.NoNameservers,
dns.exception.Timeout,
):

return False

async def blocked_email_count(self):
Expand Down Expand Up @@ -359,7 +359,7 @@ async def fetch_info(self):

def validate_email(self, email: str):
"""Validate email address"""
if EmailStr.validate(email):
if EmailStr._validate(email, {}):
return True

def catch_all_check(self):
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ email-validator>=1.1.3
httpx>=0.21.3
pydantic>=1.8.2
typing-extensions>=3.10.0.0
Flask>=2.0.0
Flask>=2.0.0
pydantic-settings>=2.0.3
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
find_packages
)

VERSION = (0, 2, 2)
VERSION = (0, 2, 3)
AUTHOR = "Aniket Sarkar"
AUTHOR_EMAIL = "[email protected]"

Expand Down Expand Up @@ -43,6 +43,7 @@
"asgiref>=3.4.1",
"blinker>=1.4",
"pydantic>=1.8.2",
"pydantic-settings>=2.0.3",
"email-validator>=1.1.3",
"typing-extensions>=3.10.0.0",
"httpx>=0.21.3",
Expand Down
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pathlib import Path

import pytest

# import fakeredis.aioredis
from flask import Flask

Expand Down
4 changes: 0 additions & 4 deletions tests/test_checker.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import pytest
from pydantic import EmailError

from flask_mailing.utils.errors import DBProvaiderError

Expand Down Expand Up @@ -40,8 +39,5 @@ async def test_default_checker(default_checker):

assert default_checker.validate_email(email) is True

with pytest.raises(EmailError):
default_checker.validate_email("test#mail.com")

with pytest.raises(DBProvaiderError):
await default_checker.close_connections()
Loading

0 comments on commit 8023937

Please sign in to comment.