Commit 45bc4b99 authored by Jason R. Coombs's avatar Jason R. Coombs

Merge branch 'master' into pr1127

parents e5461b6c 09e14c77
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
-------
......
......@@ -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::
......
[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]
......
......@@ -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,
......
......@@ -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
......@@ -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
......
......@@ -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):
......
......@@ -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', [])
......
......@@ -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(
......
......@@ -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',""")
......
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