summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2017-09-03 13:33:20 -0400
committerJason R. Coombs <jaraco@jaraco.com>2017-09-03 13:33:20 -0400
commit45bc4b99137a4f7a627439239a193d89ff6cfee9 (patch)
treea3a2650f355e0a26ef8b04aa41e93aa0de0d4737
parente5461b6ccc57596c7e9cf7837084f8a20eeec9b7 (diff)
parent09e14c771f8dfd08122c63dc7bc1027d0040c26e (diff)
downloadpython-setuptools-git-45bc4b99137a4f7a627439239a193d89ff6cfee9.tar.gz
Merge branch 'master' into pr1127
-rw-r--r--CHANGES.rst16
-rw-r--r--docs/setuptools.txt47
-rwxr-xr-xsetup.cfg4
-rwxr-xr-xsetup.py3
-rw-r--r--setuptools/command/bdist_egg.py16
-rwxr-xr-xsetuptools/command/egg_info.py4
-rw-r--r--setuptools/config.py34
-rw-r--r--setuptools/dist.py10
-rw-r--r--setuptools/tests/test_config.py18
-rw-r--r--setuptools/tests/test_egg_info.py25
10 files changed, 133 insertions, 44 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 19afc8cb..24a3e4d0 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,19 @@
+v36.4.0
+-------
+
+* #1075: Add new ``Description-Content-Type`` metadata field. `See here for
+ documentation on how to use this field.
+ <https://packaging.python.org/specifications/#description-content-type>`_
+
+* #1068: Sort files and directories when building eggs for
+ deterministic order.
+
+v36.3.0
+-------
+
+* #1131: Make possible using several files within ``file:`` directive
+ in metadata.long_description in ``setup.cfg``.
+
v36.2.7
-------
diff --git a/docs/setuptools.txt b/docs/setuptools.txt
index eb9fdbd3..531d727c 100644
--- a/docs/setuptools.txt
+++ b/docs/setuptools.txt
@@ -2306,7 +2306,7 @@ boilerplate code in some cases.
name = my_package
version = attr: src.VERSION
description = My package description
- long_description = file: README.rst
+ long_description = file: README.rst, CHANGELOG.rst, LICENSE.rst
keywords = one, two
license = BSD 3-Clause License
classifiers =
@@ -2379,7 +2379,7 @@ Type names used below:
Special directives:
* ``attr:`` - value could be read from module attribute
-* ``file:`` - value could be read from a file
+* ``file:`` - value could be read from a list of files and then concatenated
.. note::
@@ -2394,27 +2394,28 @@ Metadata
Aliases given below are supported for compatibility reasons,
but not advised.
-================= ================= =====
-Key Aliases Accepted value type
-================= ================= =====
-name str
-version attr:, str
-url home-page str
-download_url download-url str
-author str
-author_email author-email str
-maintainer str
-maintainer_email maintainer-email str
-classifiers classifier file:, list-comma
-license file:, str
-description summary file:, str
-long_description long-description file:, str
-keywords list-comma
-platforms platform list-comma
-provides list-comma
-requires list-comma
-obsoletes list-comma
-================= ================= =====
+============================== ================= =====
+Key Aliases Accepted value type
+============================== ================= =====
+name str
+version attr:, str
+url home-page str
+download_url download-url str
+author str
+author_email author-email str
+maintainer str
+maintainer_email maintainer-email str
+classifiers classifier file:, list-comma
+license file:, str
+description summary file:, str
+long_description long-description file:, str
+long_description_content_type str
+keywords list-comma
+platforms platform list-comma
+provides list-comma
+requires list-comma
+obsoletes list-comma
+============================== ================= =====
.. note::
diff --git a/setup.cfg b/setup.cfg
index 42531f28..ddc2a722 100755
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 36.2.7
+current_version = 36.3.0
commit = True
tag = True
@@ -19,7 +19,7 @@ repository = https://upload.pypi.org/legacy/
[sdist]
formats = zip
-[wheel]
+[bdist_wheel]
universal = 1
[bumpversion:file:setup.py]
diff --git a/setup.py b/setup.py
index 3c6d4027..a02e41aa 100755
--- a/setup.py
+++ b/setup.py
@@ -89,12 +89,13 @@ def pypi_link(pkg_filename):
setup_params = dict(
name="setuptools",
- version="36.2.7",
+ version="36.3.0",
description="Easily download, build, install, upgrade, and uninstall "
"Python packages",
author="Python Packaging Authority",
author_email="distutils-sig@python.org",
long_description=long_description,
+ long_description_content_type='text/x-rst; charset=UTF-8',
keywords="CPAN PyPI distutils eggs package management",
url="https://github.com/pypa/setuptools",
src_root=None,
diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py
index 8cd9dfef..51755d52 100644
--- a/setuptools/command/bdist_egg.py
+++ b/setuptools/command/bdist_egg.py
@@ -38,6 +38,14 @@ def strip_module(filename):
filename = filename[:-6]
return filename
+def sorted_walk(dir):
+ """Do os.walk in a reproducible way,
+ independent of indeterministic filesystem readdir order
+ """
+ for base, dirs, files in os.walk(dir):
+ dirs.sort()
+ files.sort()
+ yield base, dirs, files
def write_stub(resource, pyfile):
_stub_template = textwrap.dedent("""
@@ -302,7 +310,7 @@ class bdist_egg(Command):
ext_outputs = []
paths = {self.bdist_dir: ''}
- for base, dirs, files in os.walk(self.bdist_dir):
+ for base, dirs, files in sorted_walk(self.bdist_dir):
for filename in files:
if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS:
all_outputs.append(paths[base] + filename)
@@ -329,7 +337,7 @@ NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split())
def walk_egg(egg_dir):
"""Walk an unpacked egg's contents, skipping the metadata directory"""
- walker = os.walk(egg_dir)
+ walker = sorted_walk(egg_dir)
base, dirs, files = next(walker)
if 'EGG-INFO' in dirs:
dirs.remove('EGG-INFO')
@@ -463,10 +471,10 @@ def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=True,
compression = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED
if not dry_run:
z = zipfile.ZipFile(zip_filename, mode, compression=compression)
- for dirname, dirs, files in os.walk(base_dir):
+ for dirname, dirs, files in sorted_walk(base_dir):
visit(z, dirname, files)
z.close()
else:
- for dirname, dirs, files in os.walk(base_dir):
+ for dirname, dirs, files in sorted_walk(base_dir):
visit(None, dirname, files)
return zip_filename
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
index 6c00b0b7..a183d15d 100755
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -599,6 +599,10 @@ 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.long_description_content_type = getattr(
+ cmd.distribution,
+ 'long_description_content_type'
+ )
try:
# write unescaped data to PKG-INFO, so older pkg_resources
# can still parse it
diff --git a/setuptools/config.py b/setuptools/config.py
index 06a61d16..9a62e2ec 100644
--- a/setuptools/config.py
+++ b/setuptools/config.py
@@ -245,33 +245,39 @@ class ConfigHandler(object):
directory with setup.py.
Examples:
- include: LICENSE
- include: src/file.txt
+ file: LICENSE
+ file: README.rst, CHANGELOG.md, src/file.txt
:param str value:
:rtype: str
"""
+ include_directive = 'file:'
+
if not isinstance(value, string_types):
return value
- include_directive = 'file:'
if not value.startswith(include_directive):
return value
- current_directory = os.getcwd()
-
- filepath = value.replace(include_directive, '').strip()
- filepath = os.path.abspath(filepath)
-
- if not filepath.startswith(current_directory):
+ spec = value[len(include_directive):]
+ filepaths = (os.path.abspath(path.strip()) for path in spec.split(','))
+ return '\n'.join(
+ cls._read_file(path)
+ for path in filepaths
+ if (cls._assert_local(path) or True)
+ and os.path.isfile(path)
+ )
+
+ @staticmethod
+ def _assert_local(filepath):
+ if not filepath.startswith(os.getcwd()):
raise DistutilsOptionError(
'`file:` directive can not access %s' % filepath)
- if os.path.isfile(filepath):
- with io.open(filepath, encoding='utf-8') as f:
- value = f.read()
-
- return value
+ @staticmethod
+ def _read_file(filepath):
+ with io.open(filepath, encoding='utf-8') as f:
+ return f.read()
@classmethod
def _parse_attr(cls, value):
diff --git a/setuptools/dist.py b/setuptools/dist.py
index e1510b6f..a2ca8795 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -58,6 +58,13 @@ def write_pkg_file(self, file):
if self.download_url:
file.write('Download-URL: %s\n' % self.download_url)
+ long_desc_content_type = getattr(
+ self,
+ 'long_description_content_type',
+ None
+ ) or 'UNKNOWN'
+ file.write('Description-Content-Type: %s\n' % long_desc_content_type)
+
long_desc = rfc822_escape(self.get_long_description())
file.write('Description: %s\n' % long_desc)
@@ -317,6 +324,9 @@ class Distribution(Distribution_parse_config_files, _Distribution):
self.dist_files = []
self.src_root = attrs and attrs.pop("src_root", None)
self.patch_missing_pkg_info(attrs)
+ self.long_description_content_type = _attrs_dict.get(
+ 'long_description_content_type'
+ )
# Make sure we have any eggs needed to interpret 'attrs'
if attrs is not None:
self.dependency_links = attrs.pop('dependency_links', [])
diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py
index dbabd69e..cdfa5af4 100644
--- a/setuptools/tests/test_config.py
+++ b/setuptools/tests/test_config.py
@@ -139,6 +139,24 @@ class TestMetadata:
assert metadata.download_url == 'http://test.test.com/test/'
assert metadata.maintainer_email == 'test@test.com'
+ def test_file_mixed(self, tmpdir):
+
+ fake_env(
+ tmpdir,
+ '[metadata]\n'
+ 'long_description = file: README.rst, CHANGES.rst\n'
+ '\n'
+ )
+
+ tmpdir.join('README.rst').write('readme contents\nline2')
+ tmpdir.join('CHANGES.rst').write('changelog contents\nand stuff')
+
+ with get_dist(tmpdir) as dist:
+ assert dist.metadata.long_description == (
+ 'readme contents\nline2\n'
+ 'changelog contents\nand stuff'
+ )
+
def test_file_sandboxed(self, tmpdir):
fake_env(
diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py
index 33d6cc52..e454694d 100644
--- a/setuptools/tests/test_egg_info.py
+++ b/setuptools/tests/test_egg_info.py
@@ -398,6 +398,31 @@ class TestEggInfo(object):
self._run_install_command(tmpdir_cwd, env)
assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == []
+ def test_long_description_content_type(self, tmpdir_cwd, env):
+ # Test that specifying a `long_description_content_type` keyword arg to
+ # the `setup` function results in writing a `Description-Content-Type`
+ # line to the `PKG-INFO` file in the `<distribution>.egg-info`
+ # directory.
+ # `Description-Content-Type` is described at
+ # https://github.com/pypa/python-packaging-user-guide/pull/258
+
+ self._setup_script_with_requires(
+ """long_description_content_type='text/markdown',""")
+ 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 = 'Description-Content-Type: text/markdown'
+ 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',""")