Skip to content

Drop support for Python 3.6-3.8 and add support for 3.12-3.13 #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 3 additions & 15 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,15 @@ jobs:
strategy:
matrix:
os:
- "ubuntu-20.04"
- "ubuntu-latest"
- "windows-latest"
- "macos-latest"
python:
- "3.6"
- "3.7"
- "3.8"
- "3.9"
- "3.10"
- "3.11"
# Workaround from https://github.com/actions/runner-images/issues/9770
exclude: # Python < v3.8 does not support Apple Silicon ARM64.
- python: "3.6"
os: macos-latest
- python: "3.7"
os: macos-latest
include: # So run those legacy versions on Intel CPUs.
- python: "3.6"
os: macos-13
- python: "3.7"
os: macos-13
- "3.12"
- "3.13"
steps:
- uses: actions/checkout@v4
- name: Setup python
Expand Down
32 changes: 16 additions & 16 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
repos:
- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
rev: 7.2.0
hooks:
- id: flake8
name: Style Guide Enforcement (flake8)
args:
- '--max-line-length=90'
- '--per-file-ignores=__init__.py:F401'
- repo: https://github.com/asottile/pyupgrade
rev: v2.31.0
rev: v3.19.0
hooks:
- id: pyupgrade
name: Upgrade syntax for newer versions of the language (pyupgrade)
args:
- '--py36-plus'
# - repo: https://github.com/pycqa/isort
# rev: 5.10.0
# hooks:
# - id: isort
# name: 'Reorder Python imports'
- repo: https://github.com/PyCQA/docformatter
rev: v1.5.1
- '--py39-plus'
- repo: https://github.com/pycqa/isort
rev: 6.0.1
hooks:
- id: docformatter
name: 'Formats docstrings'
args:
- '--in-place'
- id: isort
name: Reorder Python imports
# - repo: https://github.com/PyCQA/docformatter
# rev: v1.7.5 # incompatible with pre-commit > 4.0.0, but should be fixed in the next release
# hooks:
# - id: docformatter
# name: Formats docstrings
# args:
# - '--in-place '
- repo: 'https://github.com/pre-commit/pre-commit-hooks'
rev: v4.1.0
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-toml
- repo: https://github.com/python/black
rev: 22.8.0
rev: 25.1.0
hooks:
- id: black
name: Uncompromising Code Formatter (black)
8 changes: 3 additions & 5 deletions mailtrap/client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import Dict
from typing import List
from typing import NoReturn
from typing import Union

Expand All @@ -24,12 +22,12 @@ def __init__(
self.api_host = api_host
self.api_port = api_port

def send(self, mail: BaseMail) -> Dict[str, Union[bool, List[str]]]:
def send(self, mail: BaseMail) -> dict[str, Union[bool, list[str]]]:
url = f"{self.base_url}/api/send"
response = requests.post(url, headers=self.headers, json=mail.api_data)

if response.ok:
data = response.json() # type: Dict[str, Union[bool, List[str]]]
data: dict[str, Union[bool, list[str]]] = response.json()
return data

self._handle_failed_response(response)
Expand All @@ -39,7 +37,7 @@ def base_url(self) -> str:
return f"https://{self.api_host.rstrip('/')}:{self.api_port}"

@property
def headers(self) -> Dict[str, str]:
def headers(self) -> dict[str, str]:
return {
"Authorization": f"Bearer {self.token}",
"Content-Type": "application/json",
Expand Down
7 changes: 2 additions & 5 deletions mailtrap/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
from typing import List


class MailtrapError(Exception):
pass


class APIError(MailtrapError):
def __init__(self, status: int, errors: List[str]) -> None:
def __init__(self, status: int, errors: list[str]) -> None:
self.status = status
self.errors = errors

super().__init__("; ".join(errors))


class AuthorizationError(APIError):
def __init__(self, errors: List[str]) -> None:
def __init__(self, errors: list[str]) -> None:
super().__init__(status=401, errors=errors)
3 changes: 1 addition & 2 deletions mailtrap/mail/address.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from typing import Any
from typing import Dict
from typing import Optional

from mailtrap.mail.base_entity import BaseEntity
Expand All @@ -11,5 +10,5 @@ def __init__(self, email: str, name: Optional[str] = None) -> None:
self.name = name

@property
def api_data(self) -> Dict[str, Any]:
def api_data(self) -> dict[str, Any]:
return self.omit_none_values({"email": self.email, "name": self.name})
3 changes: 1 addition & 2 deletions mailtrap/mail/attachment.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from enum import Enum
from typing import Any
from typing import Dict
from typing import Optional

from mailtrap.mail.base_entity import BaseEntity
Expand All @@ -27,7 +26,7 @@ def __init__(
self.content_id = content_id

@property
def api_data(self) -> Dict[str, Any]:
def api_data(self) -> dict[str, Any]:
return self.omit_none_values(
{
"content": self.content.decode(),
Expand Down
20 changes: 9 additions & 11 deletions mailtrap/mail/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
from abc import ABCMeta
from collections.abc import Sequence
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from typing import Sequence

from mailtrap.mail.address import Address
from mailtrap.mail.attachment import Attachment
Expand All @@ -16,12 +14,12 @@ class BaseMail(BaseEntity, metaclass=ABCMeta):
def __init__(
self,
sender: Address,
to: List[Address],
cc: Optional[List[Address]] = None,
bcc: Optional[List[Address]] = None,
attachments: Optional[List[Attachment]] = None,
headers: Optional[Dict[str, str]] = None,
custom_variables: Optional[Dict[str, Any]] = None,
to: list[Address],
cc: Optional[list[Address]] = None,
bcc: Optional[list[Address]] = None,
attachments: Optional[list[Attachment]] = None,
headers: Optional[dict[str, str]] = None,
custom_variables: Optional[dict[str, Any]] = None,
) -> None:
self.sender = sender
self.to = to
Expand All @@ -32,7 +30,7 @@ def __init__(
self.custom_variables = custom_variables

@property
def api_data(self) -> Dict[str, Any]:
def api_data(self) -> dict[str, Any]:
return self.omit_none_values(
{
"from": self.sender.api_data,
Expand All @@ -48,7 +46,7 @@ def api_data(self) -> Dict[str, Any]:
@staticmethod
def get_api_data_from_list(
items: Optional[Sequence[BaseEntity]],
) -> Optional[List[Dict[str, Any]]]:
) -> Optional[list[dict[str, Any]]]:
if items is None:
return None

Expand Down
5 changes: 2 additions & 3 deletions mailtrap/mail/base_entity.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
from abc import ABCMeta
from abc import abstractmethod
from typing import Any
from typing import Dict


class BaseEntity(metaclass=ABCMeta):
@property
@abstractmethod
def api_data(self) -> Dict[str, Any]:
def api_data(self) -> dict[str, Any]:
raise NotImplementedError

@staticmethod
def omit_none_values(data: Dict[str, Any]) -> Dict[str, Any]:
def omit_none_values(data: dict[str, Any]) -> dict[str, Any]:
return {key: value for key, value in data.items() if value is not None}
18 changes: 8 additions & 10 deletions mailtrap/mail/from_template.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from typing import Any
from typing import Dict
from typing import List
from typing import Optional

from mailtrap.mail.address import Address
Expand All @@ -15,14 +13,14 @@ class MailFromTemplate(BaseMail):
def __init__(
self,
sender: Address,
to: List[Address],
to: list[Address],
template_uuid: str,
template_variables: Optional[Dict[str, Any]] = None,
cc: Optional[List[Address]] = None,
bcc: Optional[List[Address]] = None,
attachments: Optional[List[Attachment]] = None,
headers: Optional[Dict[str, str]] = None,
custom_variables: Optional[Dict[str, Any]] = None,
template_variables: Optional[dict[str, Any]] = None,
cc: Optional[list[Address]] = None,
bcc: Optional[list[Address]] = None,
attachments: Optional[list[Attachment]] = None,
headers: Optional[dict[str, str]] = None,
custom_variables: Optional[dict[str, Any]] = None,
) -> None:
super().__init__(
sender=sender,
Expand All @@ -37,7 +35,7 @@ def __init__(
self.template_variables = template_variables

@property
def api_data(self) -> Dict[str, Any]:
def api_data(self) -> dict[str, Any]:
return self.omit_none_values(
{
**super().api_data,
Expand Down
16 changes: 7 additions & 9 deletions mailtrap/mail/mail.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from typing import Any
from typing import Dict
from typing import List
from typing import Optional

from mailtrap.mail.address import Address
Expand All @@ -23,16 +21,16 @@ class Mail(BaseMail):
def __init__(
self,
sender: Address,
to: List[Address],
to: list[Address],
subject: str,
text: Optional[str] = None,
html: Optional[str] = None,
category: Optional[str] = None,
cc: Optional[List[Address]] = None,
bcc: Optional[List[Address]] = None,
attachments: Optional[List[Attachment]] = None,
headers: Optional[Dict[str, str]] = None,
custom_variables: Optional[Dict[str, Any]] = None,
cc: Optional[list[Address]] = None,
bcc: Optional[list[Address]] = None,
attachments: Optional[list[Attachment]] = None,
headers: Optional[dict[str, str]] = None,
custom_variables: Optional[dict[str, Any]] = None,
) -> None:
super().__init__(
sender=sender,
Expand All @@ -49,7 +47,7 @@ def __init__(
self.category = category

@property
def api_data(self) -> Dict[str, Any]:
def api_data(self) -> dict[str, Any]:
return self.omit_none_values(
{
**super().api_data,
Expand Down
7 changes: 3 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ classifiers = [
"Operating System :: OS Independent",
"Topic :: Software Development :: Libraries :: Application Frameworks",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
requires-python = ">=3.6"
requires-python = ">=3.9"
dependencies = [
"requests>=2.26.0",
]
Expand Down
6 changes: 3 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[tox]
isolated_build = true
envlist =
py{3.6,37,38,39,310,311}
py{39,310,311,312,313},
pre-commit
mypy
skip_missing_interpreters = true
Expand All @@ -15,13 +15,13 @@ commands =
pytest -q {posargs}

[testenv:pre-commit]
deps = pre-commit
deps = pre-commit==4.2.0
commands = pre-commit run --all-files

[testenv:mypy]
deps =
-r requirements.test.txt
mypy
mypy==1.15.0
types-requests
commands = mypy ./mailtrap

Expand Down