diff options
| -rw-r--r-- | docs/setuptools.txt | 11 | ||||
| -rwxr-xr-x | setup.py | 3 | ||||
| -rw-r--r-- | setuptools/config.py | 2 | ||||
| -rw-r--r-- | setuptools/dist.py | 12 | ||||
| -rw-r--r-- | setuptools/tests/test_config.py | 16 | ||||
| -rw-r--r-- | setuptools/tests/test_egg_info.py | 30 |
6 files changed, 73 insertions, 1 deletions
diff --git a/docs/setuptools.txt b/docs/setuptools.txt index c2822c4f..bea80181 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -145,6 +145,11 @@ dependencies, and perhaps some data files and scripts:: license="PSF", keywords="hello world example examples", url="http://example.com/HelloWorld/", # project home page, if any + project_urls={ + "Bug Tracker": "https://bugs.example.com/HelloWorld/", + "Documentation": "https://docs.example.com/HelloWorld/", + "Source Code": "https://code.example.com/HelloWorld/", + } # could also include long_description, download_url, classifiers, etc. ) @@ -408,6 +413,11 @@ unless you need the associated ``setuptools`` feature. A list of modules to search for additional fixers to be used during the 2to3 conversion. See :doc:`python3` for more details. +``project_urls`` + An arbitrary map of URL names to hyperlinks, allowing more extensible + documentation of where various resources can be found than the simple + ``url`` and ``download_url`` options provide. + Using ``find_packages()`` ------------------------- @@ -2406,6 +2416,7 @@ name str version attr:, str url home-page str download_url download-url str +project_urls dict author str author_email author-email str maintainer str @@ -98,6 +98,9 @@ setup_params = dict( long_description_content_type='text/x-rst; charset=UTF-8', keywords="CPAN PyPI distutils eggs package management", url="https://github.com/pypa/setuptools", + project_urls={ + "Documentation": "https://setuptools.readthedocs.io/", + }, src_root=None, packages=setuptools.find_packages(exclude=['*.tests']), package_data=package_data, diff --git a/setuptools/config.py b/setuptools/config.py index 53828447..a70794a4 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -404,6 +404,7 @@ class ConfigMetadataHandler(ConfigHandler): """Metadata item name to parser function mapping.""" parse_list = self._parse_list parse_file = self._parse_file + parse_dict = self._parse_dict return { 'platforms': parse_list, @@ -416,6 +417,7 @@ class ConfigMetadataHandler(ConfigHandler): 'description': parse_file, 'long_description': parse_file, 'version': self._parse_version, + 'project_urls': parse_dict, } def _parse_version(self, value): diff --git a/setuptools/dist.py b/setuptools/dist.py index e79058c3..fc5db5d1 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -44,7 +44,7 @@ def write_pkg_file(self, file): self.classifiers or self.download_url): version = '1.1' # Setuptools specific for PEP 345 - if hasattr(self, 'python_requires'): + if hasattr(self, 'python_requires') or self.project_urls: version = '1.2' file.write('Metadata-Version: %s\n' % version) @@ -57,6 +57,8 @@ def write_pkg_file(self, file): file.write('License: %s\n' % self.get_license()) if self.download_url: file.write('Download-URL: %s\n' % self.download_url) + for project_url in self.project_urls.items(): + file.write('Project-URL: %s, %s\n' % project_url) long_desc_content_type = \ self.long_description_content_type or 'UNKNOWN' @@ -323,12 +325,20 @@ class Distribution(Distribution_parse_config_files, _Distribution): self.dist_files = [] self.src_root = attrs.pop("src_root", None) self.patch_missing_pkg_info(attrs) + self.long_description_content_type = attrs.get( + 'long_description_content_type' + ) + 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' ) diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 15b0cee1..383e0d30 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -217,6 +217,22 @@ class TestMetadata: 'Programming Language :: Python :: 3.5', ] + def test_dict(self, tmpdir): + + fake_env( + tmpdir, + '[metadata]\n' + 'project_urls =\n' + ' Link One = https://example.com/one/\n' + ' Link Two = https://example.com/two/\n' + ) + with get_dist(tmpdir) as dist: + metadata = dist.metadata + assert metadata.project_urls == { + 'Link One': 'https://example.com/one/', + 'Link Two': 'https://example.com/two/', + } + def test_version(self, tmpdir): _, config = fake_env( diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 66ca9164..7d12434e 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -445,6 +445,36 @@ class TestEggInfo(object): expected_line = 'Description-Content-Type: text/markdown' assert expected_line in pkg_info_lines + def test_project_urls(self, tmpdir_cwd, env): + # Test that specifying a `project_urls` dict to the `setup` + # function results in writing multiple `Project-URL` lines to + # the `PKG-INFO` file in the `<distribution>.egg-info` + # directory. + # `Project-URL` is described at https://packaging.python.org + # /specifications/core-metadata/#project-url-multiple-use + + self._setup_script_with_requires( + """project_urls={ + 'Link One': 'https://example.com/one/', + 'Link Two': 'https://example.com/two/', + },""") + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + code, data = environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO')) as pkginfo_file: + pkg_info_lines = pkginfo_file.read().split('\n') + expected_line = 'Project-URL: Link One, https://example.com/one/' + assert expected_line in pkg_info_lines + expected_line = 'Project-URL: Link Two, https://example.com/two/' + assert expected_line in pkg_info_lines + def test_python_requires_egg_info(self, tmpdir_cwd, env): self._setup_script_with_requires( """python_requires='>=2.7.12',""") |
