From 3612e45df6cdc542b6af1e7b4ab0cd41740a48ba Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Mon, 2 May 2022 11:31:05 -0700 Subject: [PATCH 1/2] Add casts for package metadata In the latest mypy (0.942), importlib.metadata.metadata now returns a Protocol named PackageMetadata that doesn't implement the get method. The actual object remains a Message class, however, so the get method works correctly. Only mypy checking fails. Pending https://github.com/python/typeshed/issues/7767, which may reveal that it is intentional that get not be supported, cast the return value to Message so that the existing functions work. This seems better than adding try/catch blocks for every piece of metadata of interest given that the omission of get appears to be unintentional. --- src/safir/metadata.py | 4 ++-- tests/metadata_test.py | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/safir/metadata.py b/src/safir/metadata.py index 50848f6b..a23ce88b 100644 --- a/src/safir/metadata.py +++ b/src/safir/metadata.py @@ -5,7 +5,7 @@ from email.message import Message from importlib.metadata import metadata -from typing import Optional +from typing import Optional, cast from pydantic import BaseModel, Field @@ -66,7 +66,7 @@ def get_metadata(*, package_name: str, application_name: str) -> Metadata: project_urls, Source code Used as the ``respository_url``. """ - pkg_metadata: Message = metadata(package_name) + pkg_metadata: Message = cast(Message, metadata(package_name)) return Metadata( name=application_name, version=pkg_metadata.get("Version", "0.0.0"), diff --git a/tests/metadata_test.py b/tests/metadata_test.py index 8bf2bbba..7ef6361c 100644 --- a/tests/metadata_test.py +++ b/tests/metadata_test.py @@ -3,20 +3,18 @@ from __future__ import annotations +from email.message import Message from importlib.metadata import metadata -from typing import TYPE_CHECKING +from typing import cast import pytest from safir.metadata import get_metadata, get_project_url -if TYPE_CHECKING: - from email.message import Message - @pytest.fixture(scope="session") def safir_metadata() -> Message: - return metadata("safir") + return cast(Message, metadata("safir")) def test_get_project_url(safir_metadata: Message) -> None: From 3d1256ff172f9acff785968844777455c5f01c12 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Mon, 2 May 2022 12:23:47 -0700 Subject: [PATCH 2/2] Make PackageMetadata casts dependent on version mypy also complains when casting the return value of metadata to Message on older versions where that's the default return value, so make the cast conditional on the Python version. --- src/safir/metadata.py | 6 +++++- tests/metadata_test.py | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/safir/metadata.py b/src/safir/metadata.py index a23ce88b..a9ab7807 100644 --- a/src/safir/metadata.py +++ b/src/safir/metadata.py @@ -3,6 +3,7 @@ from __future__ import annotations +import sys from email.message import Message from importlib.metadata import metadata from typing import Optional, cast @@ -66,7 +67,10 @@ def get_metadata(*, package_name: str, application_name: str) -> Metadata: project_urls, Source code Used as the ``respository_url``. """ - pkg_metadata: Message = cast(Message, metadata(package_name)) + if sys.version_info >= (3, 10): + pkg_metadata = cast(Message, metadata(package_name)) + else: + pkg_metadata = metadata(package_name) return Metadata( name=application_name, version=pkg_metadata.get("Version", "0.0.0"), diff --git a/tests/metadata_test.py b/tests/metadata_test.py index 7ef6361c..20ab33be 100644 --- a/tests/metadata_test.py +++ b/tests/metadata_test.py @@ -3,6 +3,7 @@ from __future__ import annotations +import sys from email.message import Message from importlib.metadata import metadata from typing import cast @@ -14,7 +15,10 @@ @pytest.fixture(scope="session") def safir_metadata() -> Message: - return cast(Message, metadata("safir")) + if sys.version_info >= (3, 10): + return cast(Message, metadata("safir")) + else: + return metadata("safir") def test_get_project_url(safir_metadata: Message) -> None: