-
-
Notifications
You must be signed in to change notification settings - Fork 31.4k
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
bpo-19610: Warn if distutils is provided something other than a list to some fields #4685
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needed to update Doc/distutils/apiref.rst
too.
Lib/distutils/tests/test_dist.py
Outdated
@@ -354,8 +354,11 @@ def test_classifier_invalid_type(self): | |||
attrs = {'name': 'Boa', 'version': '3.0', | |||
'classifiers': ('Programming Language :: Python :: 3',)} | |||
msg = "'classifiers' should be a 'list', not 'tuple'" | |||
with self.assertRaises(TypeError, msg=msg): | |||
Distribution(attrs) | |||
with self.assertWarns(RuntimeWarning) as warns: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would is better to use assertWarnsRegex()
here and in other tests. Otherwise msg
isn't used.
Lib/distutils/tests/test_dist.py
Outdated
Distribution(attrs) | ||
with self.assertWarns(RuntimeWarning) as warns: | ||
d = Distribution(attrs) | ||
self.assertIsInstance(d.metadata.classifiers, list) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be better to move these checks outside of the with
block. Otherwise it isn't clear what operation raises a RuntimeWarning.
Lib/distutils/dist.py
Outdated
import warnings | ||
except ImportError: | ||
warnings = None | ||
import warnings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It’s possible that the import was how it was because distutils is used to build CPython’s own extension modules. In doubt, let’s not change this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that distutils also has its own PEP-282-style logging system, which may or may not easier to use here. The output is controlled by verbose/quiet options passed to setup.py rather than the interpreter warning configuration system, so testing with pip would be needed to make sure that package authors would see the messages by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The try/except dates back to Python 1.5 + 2.0 days. It was added because those versions didn't have 'warnings' and distutils was targeting those versions as well. I think removing it is safe but I will revert as it is a separate change from this issue.
Using the logging system sounds like a good idea. I'll look into that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have made the requested changes; please review again.
I tweaked the wording of the warning message, not sure it is the best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the patch. Tests look good; left comment about conditional use of warnings.
When you're done making the requested changes, leave the comment: |
While we a here could you please add |
I have made the requested changes; please review again. |
Thanks for making the requested changes! @serhiy-storchaka, @merwok: please review the changes made to this pull request. |
warnings.warn(msg) | ||
else: | ||
sys.stderr.write(msg + "\n") | ||
warnings.warn(msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought you wanted to move this in a separate PR?
elif not isinstance(value, list): | ||
# passing a tuple or an iterator perhaps, warn and convert | ||
typename = type(value).__name__ | ||
msg = f"Warning: '{fieldname}' should be a list, got type '{typename}'" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the prefix Warning
redundant? Otherwise message seems good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another style nit: Can't we use {fieldname!r}
instead of '{fieldname}'
?
Lib/distutils/dist.py
Outdated
@@ -26,6 +26,18 @@ | |||
# to look for a Python module named after the command. | |||
command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$') | |||
|
|||
def _ensure_list(value, fieldname): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: would you mind keeping two blank lines before and after this function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks pretty good to me, thank you! I just left some comments on code style.
Lib/distutils/tests/test_dist.py
Outdated
@@ -11,7 +11,8 @@ | |||
from distutils.dist import Distribution, fix_help_options, DistributionMetadata | |||
from distutils.cmd import Command | |||
|
|||
from test.support import TESTFN, captured_stdout, run_unittest | |||
from test.support import TESTFN, captured_stdout, captured_stderr, \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: Replacing \
with
from test.support import (
TESTFN, captured_stdout, captured_stderr, run_unittest,
)
may be better.
@@ -1231,7 +1223,7 @@ def set_requires(self, value): | |||
import distutils.versionpredicate | |||
for v in value: | |||
distutils.versionpredicate.VersionPredicate(v) | |||
self.requires = value | |||
self.requires = list(value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add tests for the requires and obsolotes fields similar to what you already added for the classifiers field?:
attrs = {...,
'classifiers': ('Programming Language :: Python :: 3',)}
self.assertIsInstance(d.metadata.classifiers, list)
self.assertEqual(d.metadata.classifiers,
list(attrs['classifiers']))
If I don't miss something, the line list(value)
is not covered by the current tests:
https://github.com/python/cpython/blob/bce095d9b0595a528a95bae3f764ee195a0c0d63/Lib/distutils/tests/test_dist.py#L301-L315
https://github.com/python/cpython/blob/bce095d9b0595a528a95bae3f764ee195a0c0d63/Lib/distutils/tests/test_dist.py#L323-L337
elif not isinstance(value, list): | ||
# passing a tuple or an iterator perhaps, warn and convert | ||
typename = type(value).__name__ | ||
msg = f"Warning: '{fieldname}' should be a list, got type '{typename}'" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another style nit: Can't we use {fieldname!r}
instead of '{fieldname}'
?
Lib/distutils/tests/test_dist.py
Outdated
msg = "'platforms' should be a 'list', not 'tuple'" | ||
with self.assertRaises(TypeError, msg=msg): | ||
Distribution(attrs) | ||
msg = "should be a list" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: I think we can now inline this in line 395:
self.assertIn(msg, error.getvalue())
Introduce a helper function _ensure_list() that reduces code duplication.
bce095d
to
67503b5
Compare
The commit of dcaed6b causes some 3rd party packages to fail to install, due to them passing a tuple for meta data fields. Rather than raise TypeError, generate a RuntimeWarning and convert the value using list().
https://bugs.python.org/issue19610