aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Stanley <fungi@yuggoth.org>2017-11-21 00:48:44 +0000
committerJeremy Stanley <fungi@yuggoth.org>2017-11-22 19:24:12 +0000
commitdf2246449c271c07586bcecc3eaa36e9b0e94e3d (patch)
treea729506809366de107bf68d4d9962cd043dc0e93
parentd45be2cc4f7a1e4ddc70b363baaa613c6b068b98 (diff)
downloadexternal_python_setuptools-df2246449c271c07586bcecc3eaa36e9b0e94e3d.tar.gz
external_python_setuptools-df2246449c271c07586bcecc3eaa36e9b0e94e3d.tar.bz2
external_python_setuptools-df2246449c271c07586bcecc3eaa36e9b0e94e3d.zip
Support PEP 345 Project-URL metadata
By including one or more Project-URL entries in PKG-INFO metadata, PyPI can display helpful hyperlinks in a generic manner. Add support here to be able to pass it through setup.cfg and setup.py with a project_urls dict. See the corresponding section of the Core Metadata Specifications from the Python Packaging User Guide for details: https://packaging.python.org/specifications/core-metadata/#project-url-multiple-use
-rw-r--r--docs/setuptools.txt1
-rwxr-xr-xsetup.py5
-rwxr-xr-xsetuptools/command/egg_info.py1
-rw-r--r--setuptools/config.py2
-rw-r--r--setuptools/dist.py11
-rw-r--r--setuptools/tests/test_config.py16
-rw-r--r--setuptools/tests/test_egg_info.py30
7 files changed, 65 insertions, 1 deletions
diff --git a/docs/setuptools.txt b/docs/setuptools.txt
index c2822c4f..e3154b46 100644
--- a/docs/setuptools.txt
+++ b/docs/setuptools.txt
@@ -2406,6 +2406,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
diff --git a/setup.py b/setup.py
index 25d44de3..7a05c718 100755
--- a/setup.py
+++ b/setup.py
@@ -98,6 +98,11 @@ 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={
+ "Bug Tracker": "https://github.com/pypa/setuptools/issues",
+ "Documentation": "http://setuptools.readthedocs.io/",
+ "Source Code": "https://github.com/pypa/setuptools",
+ },
src_root=None,
packages=setuptools.find_packages(exclude=['*.tests']),
package_data=package_data,
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
index a1d41b27..c8b2bd17 100755
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -597,6 +597,7 @@ def write_pkg_info(cmd, basename, filename):
metadata = cmd.distribution.metadata
metadata.version, oldver = cmd.egg_version, metadata.version
metadata.name, oldname = cmd.egg_name, metadata.name
+ metadata.project_urls = cmd.distribution.metadata.project_urls
metadata.long_description_content_type = getattr(
cmd.distribution,
'long_description_content_type'
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 aa304500..fa03cd27 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 = getattr(
self,
@@ -327,11 +329,18 @@ class Distribution(Distribution_parse_config_files, _Distribution):
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)
+
if isinstance(self.metadata.version, numbers.Number):
# Some people apparently take "version number" too literally :)
self.metadata.version = str(self.metadata.version)
diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py
index cdfa5af4..a69bca46 100644
--- a/setuptools/tests/test_config.py
+++ b/setuptools/tests/test_config.py
@@ -215,6 +215,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 a97d0c84..e05498b8 100644
--- a/setuptools/tests/test_egg_info.py
+++ b/setuptools/tests/test_egg_info.py
@@ -417,6 +417,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',""")