Skip to content

Commit

Permalink
Merge pull request #3311 from pypa/feature/distutils-a7cfb56a7b1eaa
Browse files Browse the repository at this point in the history
  • Loading branch information
jaraco authored May 10, 2022
2 parents e009a87 + f842f59 commit 5c660fb
Show file tree
Hide file tree
Showing 16 changed files with 130 additions and 116 deletions.
23 changes: 12 additions & 11 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,13 @@ jobs:
${{ matrix.python }}
test_cygwin:
runs-on: windows-latest
strategy:
matrix:
python:
- 39
platform:
- windows-latest
runs-on: ${{ matrix.platform }}
timeout-minutes: 75
steps:
- uses: actions/checkout@v2
Expand All @@ -76,19 +82,14 @@ jobs:
with:
platform: x86_64
packages: >-
git,
python${{ matrix.python }},
python${{ matrix.python }}-devel,
python${{ matrix.python }}-tox,
gcc-core,
python38,
python38-devel,
python38-pip
- name: Install tox
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0}
run: |
python3.8 -m pip install tox
git,
- name: Run tests
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0}
run: |
tox -- --cov-report xml
run: tox

integration-test:
needs: test
Expand Down
2 changes: 1 addition & 1 deletion changelog.d/3299.change.rst
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Optional metadata fields are now truly optional.
Optional metadata fields are now truly optional. Includes merge with pypa/distutils@a7cfb56 per pypa/distutils#138.
4 changes: 1 addition & 3 deletions docs/deprecated/distutils/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,7 @@ Running the ``check`` command will display some warnings:
$ python setup.py check
running check
warning: check: missing required meta-data: version, url
warning: check: missing meta-data: either (author and author_email) or
(maintainer and maintainer_email) should be supplied
warning: check: missing required meta-data: version
If you use the reStructuredText syntax in the ``long_description`` field and
Expand Down
6 changes: 3 additions & 3 deletions docs/deprecated/distutils/setupscript.rst
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ This information includes:
| ``maintainer_email`` | email address of the | email address | \(3) |
| | package maintainer | | |
+----------------------+---------------------------+-----------------+--------+
| ``url`` | home page for the package | URL | \(1) |
| ``url`` | home page for the package | URL | |
+----------------------+---------------------------+-----------------+--------+
| ``description`` | short, summary | short string | |
| | description of the | | |
Expand Down Expand Up @@ -612,8 +612,8 @@ Notes:
It is recommended that versions take the form *major.minor[.patch[.sub]]*.

(3)
Either the author or the maintainer must be identified. If maintainer is
provided, distutils lists it as the author in :file:`PKG-INFO`.
If maintainer is provided and author is not, distutils lists maintainer as
the author in :file:`PKG-INFO`.

(4)
The ``long_description`` field is used by PyPI when you publish a package,
Expand Down
20 changes: 20 additions & 0 deletions setuptools/_distutils/_functools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import functools


# from jaraco.functools 3.5
def pass_none(func):
"""
Wrap func so it's not called if its first param is None
>>> print_text = pass_none(print)
>>> print_text('text')
text
>>> print_text(None)
"""

@functools.wraps(func)
def wrapper(param, *args, **kwargs):
if param is not None:
return func(param, *args, **kwargs)

return wrapper
6 changes: 1 addition & 5 deletions setuptools/_distutils/command/bdist_msi.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,7 @@ def run(self):
if os.path.exists(installer_name): os.unlink(installer_name)

metadata = self.distribution.metadata
author = metadata.author
if not author:
author = metadata.maintainer
if not author:
author = "UNKNOWN"
author = metadata.author or metadata.maintainer
version = metadata.get_version()
# ProductVersion must be strictly numeric
# XXX need to deal with prerelease versions
Expand Down
8 changes: 4 additions & 4 deletions setuptools/_distutils/command/bdist_rpm.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ def _make_spec_file(self):
'%define unmangled_version ' + self.distribution.get_version(),
'%define release ' + self.release.replace('-','_'),
'',
'Summary: ' + self.distribution.get_description(),
'Summary: ' + (self.distribution.get_description() or "UNKNOWN"),
]

# Workaround for #14443 which affects some RPM based systems such as
Expand Down Expand Up @@ -438,7 +438,7 @@ def _make_spec_file(self):
spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz')

spec_file.extend([
'License: ' + self.distribution.get_license(),
'License: ' + (self.distribution.get_license() or "UNKNOWN"),
'Group: ' + self.group,
'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot',
'Prefix: %{_prefix}', ])
Expand All @@ -464,7 +464,7 @@ def _make_spec_file(self):
spec_file.append('%s: %s' % (field, val))


if self.distribution.get_url() != 'UNKNOWN':
if self.distribution.get_url():
spec_file.append('Url: ' + self.distribution.get_url())

if self.distribution_name:
Expand All @@ -483,7 +483,7 @@ def _make_spec_file(self):
spec_file.extend([
'',
'%description',
self.distribution.get_long_description()
self.distribution.get_long_description() or "",
])

# put locale descriptions into spec file
Expand Down
43 changes: 4 additions & 39 deletions setuptools/_distutils/command/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,54 +82,19 @@ def check_metadata(self):
"""Ensures that all required elements of meta-data are supplied.
Required fields:
name, version, URL
Recommended fields:
(author and author_email) or (maintainer and maintainer_email))
name, version
Warns if any are missing.
"""
metadata = self.distribution.metadata

missing = []
for attr in ('name', 'version', 'url'):
if not (hasattr(metadata, attr) and getattr(metadata, attr)):
for attr in 'name', 'version':
if not getattr(metadata, attr, None):
missing.append(attr)

if missing:
self.warn("missing required meta-data: %s" % ', '.join(missing))
if not (
self._check_contact("author", metadata) or
self._check_contact("maintainer", metadata)
):
self.warn("missing meta-data: either (author and author_email) " +
"or (maintainer and maintainer_email) " +
"should be supplied")

def _check_contact(self, kind, metadata):
"""
Returns True if the contact's name is specified and False otherwise.
This function will warn if the contact's email is not specified.
"""
name = getattr(metadata, kind) or ''
email = getattr(metadata, kind + '_email') or ''

msg = ("missing meta-data: if '{}' supplied, " +
"'{}' should be supplied too")

if name and email:
return True

if name:
self.warn(msg.format(kind, kind + '_email'))
return True

addresses = [(alias, addr) for alias, addr in getaddresses([email])]
if any(alias and addr for alias, addr in addresses):
# The contact's name can be encoded in the email: `Name <email>`
return True

return False
self.warn("missing required meta-data: %s" % ', '.join(missing))

def check_restructuredtext(self):
"""Checks if the long string fields are reST-compliant."""
Expand Down
55 changes: 28 additions & 27 deletions setuptools/_distutils/dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -1064,9 +1064,8 @@ def read_pkg_file(self, file):

def _read_field(name):
value = msg[name]
if value == 'UNKNOWN':
return None
return value
if value and value != "UNKNOWN":
return value

def _read_list(name):
values = msg.get_all(name, None)
Expand Down Expand Up @@ -1125,23 +1124,24 @@ def write_pkg_file(self, file):
self.classifiers or self.download_url):
version = '1.1'

# required fields
file.write('Metadata-Version: %s\n' % version)
file.write('Name: %s\n' % self.get_name())
file.write('Version: %s\n' % self.get_version())
file.write('Summary: %s\n' % self.get_description())
file.write('Home-page: %s\n' % self.get_url())
file.write('Author: %s\n' % self.get_contact())
file.write('Author-email: %s\n' % self.get_contact_email())
file.write('License: %s\n' % self.get_license())
if self.download_url:
file.write('Download-URL: %s\n' % self.download_url)

long_desc = rfc822_escape(self.get_long_description())
file.write('Description: %s\n' % long_desc)
def maybe_write(header, val):
if val:
file.write("{}: {}\n".format(header, val))

keywords = ','.join(self.get_keywords())
if keywords:
file.write('Keywords: %s\n' % keywords)
# optional fields
maybe_write("Summary", self.get_description())
maybe_write("Home-page", self.get_url())
maybe_write("Author", self.get_contact())
maybe_write("Author-email", self.get_contact_email())
maybe_write("License", self.get_license())
maybe_write("Download-URL", self.download_url)
maybe_write("Description", rfc822_escape(self.get_long_description() or ""))
maybe_write("Keywords", ",".join(self.get_keywords()))

self._write_list(file, 'Platform', self.get_platforms())
self._write_list(file, 'Classifier', self.get_classifiers())
Expand All @@ -1152,6 +1152,7 @@ def write_pkg_file(self, file):
self._write_list(file, 'Obsoletes', self.get_obsoletes())

def _write_list(self, file, name, values):
values = values or []
for value in values:
file.write('%s: %s\n' % (name, value))

Expand All @@ -1167,35 +1168,35 @@ def get_fullname(self):
return "%s-%s" % (self.get_name(), self.get_version())

def get_author(self):
return self.author or "UNKNOWN"
return self.author

def get_author_email(self):
return self.author_email or "UNKNOWN"
return self.author_email

def get_maintainer(self):
return self.maintainer or "UNKNOWN"
return self.maintainer

def get_maintainer_email(self):
return self.maintainer_email or "UNKNOWN"
return self.maintainer_email

def get_contact(self):
return self.maintainer or self.author or "UNKNOWN"
return self.maintainer or self.author

def get_contact_email(self):
return self.maintainer_email or self.author_email or "UNKNOWN"
return self.maintainer_email or self.author_email

def get_url(self):
return self.url or "UNKNOWN"
return self.url

def get_license(self):
return self.license or "UNKNOWN"
return self.license
get_licence = get_license

def get_description(self):
return self.description or "UNKNOWN"
return self.description

def get_long_description(self):
return self.long_description or "UNKNOWN"
return self.long_description

def get_keywords(self):
return self.keywords or []
Expand All @@ -1204,7 +1205,7 @@ def set_keywords(self, value):
self.keywords = _ensure_list(value, 'keywords')

def get_platforms(self):
return self.platforms or ["UNKNOWN"]
return self.platforms

def set_platforms(self, value):
self.platforms = _ensure_list(value, 'platforms')
Expand All @@ -1216,7 +1217,7 @@ def set_classifiers(self, value):
self.classifiers = _ensure_list(value, 'classifiers')

def get_download_url(self):
return self.download_url or "UNKNOWN"
return self.download_url

# PEP 314
def get_requires(self):
Expand Down
22 changes: 18 additions & 4 deletions setuptools/_distutils/sysconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from .errors import DistutilsPlatformError
from . import py39compat
from ._functools import pass_none

IS_PYPY = '__pypy__' in sys.builtin_module_names

Expand Down Expand Up @@ -51,12 +52,25 @@ def _is_python_source_dir(d):

_sys_home = getattr(sys, '_home', None)


def _is_parent(dir_a, dir_b):
"""
Return True if a is a parent of b.
"""
return os.path.normcase(dir_a).startswith(os.path.normcase(dir_b))


if os.name == 'nt':
@pass_none
def _fix_pcbuild(d):
if d and os.path.normcase(d).startswith(
os.path.normcase(os.path.join(PREFIX, "PCbuild"))):
return PREFIX
return d
# In a venv, sys._home will be inside BASE_PREFIX rather than PREFIX.
prefixes = PREFIX, BASE_PREFIX
matched = (
prefix
for prefix in prefixes
if _is_parent(d, os.path.join(prefix, "PCbuild"))
)
return next(matched, d)
project_base = _fix_pcbuild(project_base)
_sys_home = _fix_pcbuild(_sys_home)

Expand Down
11 changes: 5 additions & 6 deletions setuptools/_distutils/tests/test_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def test_check_metadata(self):
# by default, check is checking the metadata
# should have some warnings
cmd = self._run()
self.assertEqual(cmd._warnings, 2)
self.assertEqual(cmd._warnings, 1)

# now let's add the required fields
# and run it again, to make sure we don't get
Expand Down Expand Up @@ -81,17 +81,16 @@ def test_check_author_maintainer(self):
cmd = self._run(metadata)
self.assertEqual(cmd._warnings, 0)

# the check should warn if only email is given and it does not
# contain the name
# the check should not warn if only email is given
metadata[kind + '_email'] = 'name@email.com'
cmd = self._run(metadata)
self.assertEqual(cmd._warnings, 1)
self.assertEqual(cmd._warnings, 0)

# the check should warn if only the name is given
# the check should not warn if only the name is given
metadata[kind] = "Name"
del metadata[kind + '_email']
cmd = self._run(metadata)
self.assertEqual(cmd._warnings, 1)
self.assertEqual(cmd._warnings, 0)

@unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils")
def test_check_document(self):
Expand Down
Loading

0 comments on commit 5c660fb

Please sign in to comment.