From 05c961b808bfd8d2e87e569e5694694cfd35702b Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 23 Feb 2022 11:14:26 +0000 Subject: [PATCH] Don't warn on false positive for author/maintainer's email While I was working to support pyproject.toml metadata in setuptools, I received as a feedback from the community[^1] that setuptools warns the following message when `author_email` and `maintainer_email` are given in the form of `Person Name `: > warning: check: missing meta-data: either (author and author_email) > or (maintainer and maintainer_email) should be supplied This can be seen as a false positive, because indeed both author's name and email are provided. This warning seems to happen because distutils define the `check` command as a subcommand for `sdist`. This change aims to remove this false positive result from the checks. [^1]: https://discuss.python.org/t/help-testing-experimental-features-in-setuptools/13821/18 --- distutils/command/check.py | 40 +++++++++++++++++++++++++++-------- distutils/tests/test_check.py | 22 +++++++++++++++++++ 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/distutils/command/check.py b/distutils/command/check.py index 525540b6..af311ca9 100644 --- a/distutils/command/check.py +++ b/distutils/command/check.py @@ -2,6 +2,8 @@ Implements the Distutils 'check' command. """ +from email.utils import getaddresses + from distutils.core import Command from distutils.errors import DistutilsSetupError @@ -96,19 +98,39 @@ def check_metadata(self): if missing: self.warn("missing required meta-data: %s" % ', '.join(missing)) - if metadata.author: - if not metadata.author_email: - self.warn("missing meta-data: if 'author' supplied, " + - "'author_email' should be supplied too") - elif metadata.maintainer: - if not metadata.maintainer_email: - self.warn("missing meta-data: if 'maintainer' supplied, " + - "'maintainer_email' should be supplied too") - else: + if not ( + self._check_contact("author", metadata) or + self._check_contact("maintainer", metadata) + ): self.warn("missing meta-data: either (author and author_email) " + "or (maintainer and maintainer_email) " + "should be supplied") + def _check_contact(self, kind, metadata): + """ + Returns True if the contact's name is specified and False otherwise. + This function will warn if the contact's email is not specified. + """ + name = getattr(metadata, kind) or '' + email = getattr(metadata, kind + '_email') or '' + + msg = ("missing meta-data: if '{}' supplied, " + + "'{}' should be supplied too") + + if name and email: + return True + + if name: + self.warn(msg.format(kind, kind + '_email')) + return True + + addresses = [(alias, addr) for alias, addr in getaddresses([email])] + if any(alias and addr for alias, addr in addresses): + # The contact's name can be encoded in the email: `Name ` + return True + + return False + def check_restructuredtext(self): """Checks if the long string fields are reST-compliant.""" data = self.distribution.get_long_description() diff --git a/distutils/tests/test_check.py b/distutils/tests/test_check.py index 91bcdceb..b41dba3d 100644 --- a/distutils/tests/test_check.py +++ b/distutils/tests/test_check.py @@ -71,6 +71,28 @@ def test_check_metadata(self): cmd = self._run(metadata) self.assertEqual(cmd._warnings, 0) + def test_check_author_maintainer(self): + for kind in ("author", "maintainer"): + # ensure no warning when author_email or maintainer_email is given + # (the spec allows these fields to take the form "Name ") + metadata = {'url': 'xxx', + kind + '_email': 'Name ', + 'name': 'xxx', 'version': 'xxx'} + cmd = self._run(metadata) + self.assertEqual(cmd._warnings, 0) + + # the check should warn if only email is given and it does not + # contain the name + metadata[kind + '_email'] = 'name@email.com' + cmd = self._run(metadata) + self.assertEqual(cmd._warnings, 1) + + # the check should warn if only the name is given + metadata[kind] = "Name" + del metadata[kind + '_email'] + cmd = self._run(metadata) + self.assertEqual(cmd._warnings, 1) + @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") def test_check_document(self): pkg_info, dist = self.create_dist()