From 2699c157fc3eab4471d0b66c1c1cae0960864686 Mon Sep 17 00:00:00 2001 From: "John T. Wodder II" Date: Tue, 28 Oct 2025 11:10:36 -0400 Subject: [PATCH 1/4] Test against python 3.14 --- .github/workflows/test.yml | 1 + CHANGELOG.md | 1 + pyproject.toml | 1 + tox.ini | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ca281b9..d869051 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,6 +25,7 @@ jobs: - '3.11' - '3.12' - '3.13' + - '3.14' - 'pypy-3.8' - 'pypy-3.9' - 'pypy-3.10' diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ef61e2..3181b10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ v0.3.0 (in development) ----------------------- - Remove unused `typing_extensions` dependency +- Support Python 3.14 v0.2.2 (2024-12-01) ------------------- diff --git a/pyproject.toml b/pyproject.toml index 90597a2..cff65d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Intended Audience :: Developers", diff --git a/tox.ini b/tox.ini index 09baac6..eb9cf31 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = lint,typing,py38,py39,py310,py311,py312,py313,pypy3 +envlist = lint,typing,py38,py39,py310,py311,py312,py313,py314,pypy3 skip_missing_interpreters = True isolated_build = True minversion = 3.3.0 From 61eef5d595ac5cfd524b317fd1c4828e03fc3926 Mon Sep 17 00:00:00 2001 From: "John T. Wodder II" Date: Tue, 28 Oct 2025 11:11:22 -0400 Subject: [PATCH 2/4] Drop support for Python 3.8 & 3.9 --- .github/workflows/test.yml | 8 ++------ CHANGELOG.md | 1 + README.rst | 2 +- pyproject.toml | 4 +--- tox.ini | 2 +- 5 files changed, 6 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d869051..f8c56af 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,22 +19,18 @@ jobs: fail-fast: false matrix: python-version: - - '3.8' - - '3.9' - '3.10' - '3.11' - '3.12' - '3.13' - '3.14' - - 'pypy-3.8' - - 'pypy-3.9' - 'pypy-3.10' - 'pypy-3.11' toxenv: [py] include: - - python-version: '3.8' + - python-version: '3.10' toxenv: lint - - python-version: '3.8' + - python-version: '3.10' toxenv: typing steps: - name: Check out repository diff --git a/CHANGELOG.md b/CHANGELOG.md index 3181b10..716d6db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ v0.3.0 (in development) ----------------------- - Remove unused `typing_extensions` dependency - Support Python 3.14 +- Drop support for Python 3.8 and 3.9 v0.2.2 (2024-12-01) ------------------- diff --git a/README.rst b/README.rst index 5fc898c..b2f66a3 100644 --- a/README.rst +++ b/README.rst @@ -35,7 +35,7 @@ recipients' raw e-mail addresses from an ``EmailMessage``. Installation ============ -``mailbits`` requires Python 3.8 or higher. Just use `pip +``mailbits`` requires Python 3.10 or higher. Just use `pip `_ for Python 3 (You have pip, right?) to install it:: python3 -m pip install mailbits diff --git a/pyproject.toml b/pyproject.toml index cff65d4..5ea0d67 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "mailbits" dynamic = ["version"] description = "Assorted e-mail utility functions" readme = "README.rst" -requires-python = ">=3.8" +requires-python = ">=3.10" license = "MIT" license-files = ["LICENSE"] authors = [ @@ -28,8 +28,6 @@ keywords = [ classifiers = [ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3", - "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", diff --git a/tox.ini b/tox.ini index eb9cf31..8620e72 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = lint,typing,py38,py39,py310,py311,py312,py313,py314,pypy3 +envlist = lint,typing,py310,py311,py312,py313,py314,pypy3 skip_missing_interpreters = True isolated_build = True minversion = 3.3.0 From 4fc57cb4dffdc4abb50e70dd148ae0cd7693de10 Mon Sep 17 00:00:00 2001 From: "John T. Wodder II" Date: Tue, 28 Oct 2025 11:12:09 -0400 Subject: [PATCH 3/4] Run pyupgrade --- src/mailbits/email2dict.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mailbits/email2dict.py b/src/mailbits/email2dict.py index f1e3f76..a48ec73 100644 --- a/src/mailbits/email2dict.py +++ b/src/mailbits/email2dict.py @@ -4,16 +4,16 @@ from email import headerregistry as hr from email.message import Message import inspect -from typing import Any, Optional, TypedDict +from typing import Any, TypedDict from .misc import message2email, parse_addresses class MessageDict(TypedDict): - unixfrom: Optional[str] + unixfrom: str | None headers: dict[str, Any] - preamble: Optional[str] + preamble: str | None content: Any - epilogue: Optional[str] + epilogue: str | None def process_unique_string_header(ush: list[Any]) -> str: @@ -108,7 +108,7 @@ def process_cte_header(cteh: list[Any]) -> str: return cteh[0].cte # TODO: When is this different from `str(cteh[0])`? -def process_mime_version_header(mvh: list[Any]) -> Optional[str]: +def process_mime_version_header(mvh: list[Any]) -> str | None: assert len(mvh) == 1 assert isinstance(mvh[0], hr.MIMEVersionHeader) return mvh[0].version From 8e61c23167f118ca09cfcab517b2b645d668c2fe Mon Sep 17 00:00:00 2001 From: "John T. Wodder II" Date: Tue, 28 Oct 2025 11:13:04 -0400 Subject: [PATCH 4/4] Update annotations in README --- README.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index b2f66a3..49ecfd2 100644 --- a/README.rst +++ b/README.rst @@ -78,11 +78,11 @@ b'text/plain; charset="utf-8"; name*=utf-8\'\'r%C3%A9sum%C3%A9.txt' .. code:: python class MessageDict(TypedDict): - unixfrom: Optional[str] - headers: Dict[str, Any] - preamble: Optional[str] + unixfrom: str | None + headers: dict[str, Any] + preamble: str | None content: Any - epilogue: Optional[str] + epilogue: str | None mailbits.email2dict(msg: email.message.Message, include_all: bool = False) -> MessageDict @@ -300,7 +300,7 @@ __ https://docs.python.org/3/library/email.examples.html .. code:: python - mailbits.format_addresses(addresses: Iterable[Union[str, Address, Group]], encode: bool = False) -> str + mailbits.format_addresses(addresses: Iterable[str | Address | Group], encode: bool = False) -> str Convert an iterable of e-mail address strings (of the form "``foo@example.com``", without angle brackets or a display name), @@ -341,8 +341,8 @@ into an ``Address`` object. .. code:: python - mailbits.parse_addresses(s: Union[str, email.headerregistry.AddressHeader]) \ - -> List[Union[email.headerregistry.Address, email.headerregistry.Group]] + mailbits.parse_addresses(s: str | email.headerregistry.AddressHeader) \ + -> list[email.headerregistry.Address | email.headerregistry.Group] Parse a formatted list of e-mail addresses or the contents of an ``EmailMessage``'s "To", "CC", "BCC", etc. header into a list of ``Address`` @@ -354,7 +354,7 @@ and/or ``Group`` objects. .. code:: python - mailbits.recipient_addresses(msg: email.message.EmailMessage) -> List[str] + mailbits.recipient_addresses(msg: email.message.EmailMessage) -> list[str] Return a sorted list of all of the distinct e-mail addresses (not including display names) in an ``EmailMessage``'s combined "To", "CC", and "BCC" headers.