Skip to content
Merged
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
4 changes: 4 additions & 0 deletions Doc/distutils/apiref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ the full reference.
See the :func:`setup` function for a list of keyword arguments accepted by the
Distribution constructor. :func:`setup` creates a Distribution instance.

.. versionchanged:: 3.7
:class:`~distutils.core.Distribution` now raises a :exc:`TypeError` if
``classifiers``, ``keywords`` and ``platforms`` fields are not specified
as a list.

.. class:: Command

Expand Down
39 changes: 27 additions & 12 deletions Doc/distutils/setupscript.rst
Original file line number Diff line number Diff line change
Expand Up @@ -581,17 +581,19 @@ This information includes:
| | description of the | | |
| | package | | |
+----------------------+---------------------------+-----------------+--------+
| ``long_description`` | longer description of the | long string | \(5) |
| ``long_description`` | longer description of the | long string | \(4) |
| | package | | |
+----------------------+---------------------------+-----------------+--------+
| ``download_url`` | location where the | URL | \(4) |
| ``download_url`` | location where the | URL | |
| | package may be downloaded | | |
+----------------------+---------------------------+-----------------+--------+
| ``classifiers`` | a list of classifiers | list of strings | \(4) |
| ``classifiers`` | a list of classifiers | list of strings | (6)(7) |
+----------------------+---------------------------+-----------------+--------+
| ``platforms`` | a list of platforms | list of strings | |
| ``platforms`` | a list of platforms | list of strings | (6)(8) |
+----------------------+---------------------------+-----------------+--------+
| ``license`` | license for the package | short string | \(6) |
| ``keywords`` | a list of keywords | list of strings | (6)(8) |
+----------------------+---------------------------+-----------------+--------+
| ``license`` | license for the package | short string | \(5) |
+----------------------+---------------------------+-----------------+--------+

Notes:
Expand All @@ -607,22 +609,30 @@ Notes:
provided, distutils lists it as the author in :file:`PKG-INFO`.

(4)
These fields should not be used if your package is to be compatible with Python
versions prior to 2.2.3 or 2.3. The list is available from the `PyPI website
<https://pypi.python.org/pypi>`_.

(5)
The ``long_description`` field is used by PyPI when you are
:ref:`registering <package-register>` a package, to
:ref:`build its home page <package-display>`.

(6)
(5)
The ``license`` field is a text indicating the license covering the
package where the license is not a selection from the "License" Trove
classifiers. See the ``Classifier`` field. Notice that
there's a ``licence`` distribution option which is deprecated but still
acts as an alias for ``license``.

(6)
This field must be a list.

(7)
The valid classifiers are listed on
`PyPI <http://pypi.python.org/pypi?:action=list_classifiers>`_.

(8)
To preserve backward compatibility, this field also accepts a string. If
you pass a comma-separated string ``'foo, bar'``, it will be converted to
``['foo', 'bar']``, Otherwise, it will be converted to a list of one
string.

'short string'
A single line of text, not more than 200 characters.

Expand Down Expand Up @@ -650,7 +660,7 @@ information is sometimes used to indicate sub-releases. These are
1.0.1a2
the second alpha release of the first patch version of 1.0

``classifiers`` are specified in a Python list::
``classifiers`` must be specified in a list::

setup(...,
classifiers=[
Expand All @@ -671,6 +681,11 @@ information is sometimes used to indicate sub-releases. These are
],
)

.. versionchanged:: 3.7
:class:`~distutils.core.setup` now raises a :exc:`TypeError` if
``classifiers``, ``keywords`` and ``platforms`` fields are not specified
as a list.

.. _debug-setup-script:

Debugging the setup script
Expand Down
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,12 @@ README.rst is now included in the list of distutils standard READMEs and
therefore included in source distributions.
(Contributed by Ryan Gonzalez in :issue:`11913`.)

:class:`distutils.core.setup` now raises a :exc:`TypeError` if
``classifiers``, ``keywords`` and ``platforms`` fields are not specified
as a list. However, to minimize backwards incompatibility concerns,
``keywords`` and ``platforms`` fields still accept a comma separated string.
(Contributed by Berker Peksag in :issue:`19610`.)

http.client
-----------

Expand Down
26 changes: 26 additions & 0 deletions Lib/distutils/dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -1188,12 +1188,38 @@ def get_long_description(self):
def get_keywords(self):
return self.keywords or []

def set_keywords(self, value):
# If 'keywords' is a string, it will be converted to a list
# by Distribution.finalize_options(). To maintain backwards
# compatibility, do not raise an exception if 'keywords' is
# a string.
if not isinstance(value, (list, str)):
msg = "'keywords' should be a 'list', not %r"
raise TypeError(msg % type(value).__name__)
self.keywords = value

def get_platforms(self):
return self.platforms or ["UNKNOWN"]

def set_platforms(self, value):
# If 'platforms' is a string, it will be converted to a list
# by Distribution.finalize_options(). To maintain backwards
# compatibility, do not raise an exception if 'platforms' is
# a string.
if not isinstance(value, (list, str)):
msg = "'platforms' should be a 'list', not %r"
raise TypeError(msg % type(value).__name__)
self.platforms = value

def get_classifiers(self):
return self.classifiers or []

def set_classifiers(self, value):
if not isinstance(value, list):
msg = "'classifiers' should be a 'list', not %r"
raise TypeError(msg % type(value).__name__)
self.classifiers = value

def get_download_url(self):
return self.download_url or "UNKNOWN"

Expand Down
44 changes: 44 additions & 0 deletions Lib/distutils/tests/test_dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,13 @@ def test_finalize_options(self):
self.assertEqual(dist.metadata.platforms, ['one', 'two'])
self.assertEqual(dist.metadata.keywords, ['one', 'two'])

attrs = {'keywords': 'foo bar',
'platforms': 'foo bar'}
dist = Distribution(attrs=attrs)
dist.finalize_options()
self.assertEqual(dist.metadata.platforms, ['foo bar'])
self.assertEqual(dist.metadata.keywords, ['foo bar'])

def test_get_command_packages(self):
dist = Distribution()
self.assertEqual(dist.command_packages, None)
Expand Down Expand Up @@ -338,9 +345,46 @@ def test_classifier(self):
attrs = {'name': 'Boa', 'version': '3.0',
'classifiers': ['Programming Language :: Python :: 3']}
dist = Distribution(attrs)
self.assertEqual(dist.get_classifiers(),
['Programming Language :: Python :: 3'])
meta = self.format_metadata(dist)
self.assertIn('Metadata-Version: 1.1', meta)

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)

def test_keywords(self):
attrs = {'name': 'Monty', 'version': '1.0',
'keywords': ['spam', 'eggs', 'life of brian']}
dist = Distribution(attrs)
self.assertEqual(dist.get_keywords(),
['spam', 'eggs', 'life of brian'])

def test_keywords_invalid_type(self):
attrs = {'name': 'Monty', 'version': '1.0',
'keywords': ('spam', 'eggs', 'life of brian')}
msg = "'keywords' should be a 'list', not 'tuple'"
with self.assertRaises(TypeError, msg=msg):
Distribution(attrs)

def test_platforms(self):
attrs = {'name': 'Monty', 'version': '1.0',
'platforms': ['GNU/Linux', 'Some Evil Platform']}
dist = Distribution(attrs)
self.assertEqual(dist.get_platforms(),
['GNU/Linux', 'Some Evil Platform'])

def test_platforms_invalid_types(self):
attrs = {'name': 'Monty', 'version': '1.0',
'platforms': ('GNU/Linux', 'Some Evil Platform')}
msg = "'platforms' should be a 'list', not 'tuple'"
with self.assertRaises(TypeError, msg=msg):
Distribution(attrs)

def test_download_url(self):
attrs = {'name': 'Boa', 'version': '3.0',
'download_url': 'http://example.org/boa'}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
``setup()`` now raises :exc:`TypeError` for invalid types.

The ``distutils.dist.Distribution`` class now explicitly raises an exception
when ``classifiers``, ``keywords`` and ``platforms`` fields are not
specified as a list.