diff --git a/Doc/distutils/setupscript.rst b/Doc/distutils/setupscript.rst --- a/Doc/distutils/setupscript.rst +++ b/Doc/distutils/setupscript.rst @@ -602,19 +602,19 @@ Notes: (2) It is recommended that versions take the form *major.minor[.patch[.sub]]*. (3) Either the author or the maintainer must be identified. If maintainer is 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 - `_. + This field must be a list. Otherwise, a :exc:`TypeError` is raised. The + valid classifiers are listed on `PyPI + `_. (5) The ``long_description`` field is used by PyPI when you are :ref:`registering ` a package, to :ref:`build its home page `. (6) The ``license`` field is a text indicating the license covering the @@ -645,17 +645,17 @@ information is sometimes used to indicat (for final pre-release release testing). Some examples: 0.1.0 the first, experimental release of a package 1.0.1a2 the second alpha release of the first patch version of 1.0 -:option:`classifiers` are specified in a Python list:: +:option:`classifiers` are must be specified in a Python list:: setup(..., classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Environment :: Web Environment', 'Intended Audience :: End Users/Desktop', 'Intended Audience :: Developers', @@ -666,28 +666,19 @@ 1.0.1a2 'Operating System :: POSIX', 'Programming Language :: Python', 'Topic :: Communications :: Email', 'Topic :: Office/Business', 'Topic :: Software Development :: Bug Tracking', ], ) -If you wish to include classifiers in your :file:`setup.py` file and also wish -to remain backwards-compatible with Python releases prior to 2.2.3, then you can -include the following code fragment in your :file:`setup.py` before the -:func:`setup` call. :: - - # patch distutils if it can't cope with the "classifiers" or - # "download_url" keywords - from sys import version - if version < '2.2.3': - from distutils.dist import DistributionMetadata - DistributionMetadata.classifiers = None - DistributionMetadata.download_url = None +.. versionchanged:: 3.5 + :class:`~distutils.dist.Distribution` now raises :exc:`TypeError` if + :option:`classifiers` are not specified in a Python list. .. _debug-setup-script: Debugging the setup script ========================== Sometimes things go wrong, and the setup script doesn't do what the developer diff --git a/Lib/distutils/dist.py b/Lib/distutils/dist.py --- a/Lib/distutils/dist.py +++ b/Lib/distutils/dist.py @@ -1191,16 +1191,22 @@ class DistributionMetadata: return self.keywords or [] def get_platforms(self): return self.platforms or ["UNKNOWN"] 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 '%s'" + raise TypeError(msg % type(value).__name__) + self.classifiers = value + def get_download_url(self): return self.download_url or "UNKNOWN" # PEP 314 def get_requires(self): return self.requires or [] def set_requires(self, value): diff --git a/Lib/distutils/tests/test_dist.py b/Lib/distutils/tests/test_dist.py --- a/Lib/distutils/tests/test_dist.py +++ b/Lib/distutils/tests/test_dist.py @@ -335,16 +335,32 @@ class MetadataTestCase(support.TempdirMa def test_classifier(self): attrs = {'name': 'Boa', 'version': '3.0', 'classifiers': ['Programming Language :: Python :: 3']} dist = Distribution(attrs) meta = self.format_metadata(dist) self.assertIn('Metadata-Version: 1.1', meta) + def test_classifier_list(self): + attrs = {'name': 'Boa', 'version': '3.0', + 'classifiers': ['Programming Language :: Python :: 3']} + dist = Distribution(attrs) + self.assertListEqual(dist.get_classifiers(), + ['Programming Language :: Python :: 3']) + + def test_classifier_tuple(self): + attrs = {'name': 'Boa', 'version': '3.0', + 'classifiers': ('Programming Language :: Python :: 3',)} + with self.assertRaises(TypeError) as cm: + dist = Distribution(attrs) + dist.get_classifiers() + self.assertEqual(str(cm.exception), + "classifiers should be a 'list', not 'tuple'") + def test_download_url(self): attrs = {'name': 'Boa', 'version': '3.0', 'download_url': 'http://example.org/boa'} dist = Distribution(attrs) meta = self.format_metadata(dist) self.assertIn('Metadata-Version: 1.1', meta) def test_long_description(self):