Commit 07cd2e4e authored by Ian Wienand's avatar Ian Wienand

Allow setting long_description_content_type externally

Some tools, such as PBR, might want to set
long_description_content_type during the parent object's
Distribution.__init__() call (during distutils setup_keywords entry
points).  However, that field is currently unconditionally overwritten
after these calls, erasing the value.

We would rather not duplicate the existing method of copying into
dist.metadata as done with project_urls.

This preserves the fields within Distribution.metadata described by
self._DISTUTIULS_UNUPPORTED_METADATA, or otherwise takes it from
arguments.

A test case that simulates setting the long description and overriding
the arguments is added.
parent 1252d1b3
The ``setuptools`` specific ``long_description_content_type``,
``project_urls`` and ``provides_extras`` fields are now set
consistently after any ``distutils`` ``setup_keywords`` calls,
allowing them to override values.
......@@ -328,6 +328,12 @@ class Distribution(Distribution_parse_config_files, _Distribution):
distribution for the included and excluded features.
"""
_DISTUTILS_UNSUPPORTED_METADATA = {
'long_description_content_type': None,
'project_urls': dict,
'provides_extras': set,
}
_patched_dist = None
def patch_missing_pkg_info(self, attrs):
......@@ -353,25 +359,29 @@ class Distribution(Distribution_parse_config_files, _Distribution):
self.require_features = []
self.features = {}
self.dist_files = []
# Filter-out setuptools' specific options.
self.src_root = attrs.pop("src_root", None)
self.patch_missing_pkg_info(attrs)
self.project_urls = attrs.get('project_urls', {})
self.dependency_links = attrs.pop('dependency_links', [])
self.setup_requires = attrs.pop('setup_requires', [])
for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
vars(self).setdefault(ep.name, None)
_Distribution.__init__(self, attrs)
# The project_urls attribute may not be supported in distutils, so
# prime it here from our value if not automatically set
self.metadata.project_urls = getattr(
self.metadata, 'project_urls', self.project_urls)
self.metadata.long_description_content_type = attrs.get(
'long_description_content_type'
)
self.metadata.provides_extras = getattr(
self.metadata, 'provides_extras', set()
)
_Distribution.__init__(self, {
k: v for k, v in attrs.items()
if k not in self._DISTUTILS_UNSUPPORTED_METADATA
})
# Fill-in missing metadata fields not supported by distutils.
# Note some fields may have been set by other tools (e.g. pbr)
# above; they are taken preferrentially to setup() arguments
for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items():
for source in self.metadata.__dict__, attrs:
if option in source:
value = source[option]
break
else:
value = default() if default else None
setattr(self.metadata, option, value)
if isinstance(self.metadata.version, numbers.Number):
# Some people apparently take "version number" too literally :)
......
import contextlib
import pytest
from distutils.errors import DistutilsOptionError, DistutilsFileError
from setuptools.dist import Distribution
from mock import patch
from setuptools.dist import Distribution, _Distribution
from setuptools.config import ConfigHandler, read_configuration
......@@ -598,3 +599,43 @@ class TestOptions:
with get_dist(tmpdir) as dist:
assert dist.entry_points == expected
saved_dist_init = _Distribution.__init__
class TestExternalSetters:
# During creation of the setuptools Distribution() object, we call
# the init of the parent distutils Distribution object via
# _Distribution.__init__ ().
#
# It's possible distutils calls out to various keyword
# implementations (i.e. distutils.setup_keywords entry points)
# that may set a range of variables.
#
# This wraps distutil's Distribution.__init__ and simulates
# pbr or something else setting these values.
def _fake_distribution_init(self, dist, attrs):
saved_dist_init(dist, attrs)
# see self._DISTUTUILS_UNSUPPORTED_METADATA
setattr(dist.metadata, 'long_description_content_type',
'text/something')
# Test overwrite setup() args
setattr(dist.metadata, 'project_urls', {
'Link One': 'https://example.com/one/',
'Link Two': 'https://example.com/two/',
})
return None
@patch.object(_Distribution, '__init__', autospec=True)
def test_external_setters(self, mock_parent_init, tmpdir):
mock_parent_init.side_effect = self._fake_distribution_init
dist = Distribution(attrs={
'project_urls': {
'will_be': 'ignored'
}
})
assert dist.metadata.long_description_content_type == 'text/something'
assert dist.metadata.project_urls == {
'Link One': 'https://example.com/one/',
'Link Two': 'https://example.com/two/',
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment