Skip to content

Commit 7e3e58c

Browse files
committed
Merge https://github.com/pypa/distutils into feature/distutils-a7cfb56a7b1eaa
2 parents e009a87 + a7cfb56 commit 7e3e58c

15 files changed

Lines changed: 129 additions & 115 deletions

File tree

.github/workflows/main.yml

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,13 @@ jobs:
6767
${{ matrix.python }}
6868
6969
test_cygwin:
70-
runs-on: windows-latest
70+
strategy:
71+
matrix:
72+
python:
73+
- 38
74+
platform:
75+
- windows-latest
76+
runs-on: ${{ matrix.platform }}
7177
timeout-minutes: 75
7278
steps:
7379
- uses: actions/checkout@v2
@@ -76,19 +82,14 @@ jobs:
7682
with:
7783
platform: x86_64
7884
packages: >-
79-
git,
85+
python${{ matrix.python }},
86+
python${{ matrix.python }}-devel,
87+
python${{ matrix.python }}-tox,
8088
gcc-core,
81-
python38,
82-
python38-devel,
83-
python38-pip
84-
- name: Install tox
85-
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0}
86-
run: |
87-
python3.8 -m pip install tox
89+
git,
8890
- name: Run tests
8991
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0}
90-
run: |
91-
tox -- --cov-report xml
92+
run: tox
9293

9394
integration-test:
9495
needs: test

docs/deprecated/distutils/examples.rst

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,9 +253,7 @@ Running the ``check`` command will display some warnings:
253253
254254
$ python setup.py check
255255
running check
256-
warning: check: missing required meta-data: version, url
257-
warning: check: missing meta-data: either (author and author_email) or
258-
(maintainer and maintainer_email) should be supplied
256+
warning: check: missing required meta-data: version
259257
260258
261259
If you use the reStructuredText syntax in the ``long_description`` field and

docs/deprecated/distutils/setupscript.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ This information includes:
582582
| ``maintainer_email`` | email address of the | email address | \(3) |
583583
| | package maintainer | | |
584584
+----------------------+---------------------------+-----------------+--------+
585-
| ``url`` | home page for the package | URL | \(1) |
585+
| ``url`` | home page for the package | URL | |
586586
+----------------------+---------------------------+-----------------+--------+
587587
| ``description`` | short, summary | short string | |
588588
| | description of the | | |
@@ -612,8 +612,8 @@ Notes:
612612
It is recommended that versions take the form *major.minor[.patch[.sub]]*.
613613

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

618618
(4)
619619
The ``long_description`` field is used by PyPI when you publish a package,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import functools
2+
3+
4+
# from jaraco.functools 3.5
5+
def pass_none(func):
6+
"""
7+
Wrap func so it's not called if its first param is None
8+
9+
>>> print_text = pass_none(print)
10+
>>> print_text('text')
11+
text
12+
>>> print_text(None)
13+
"""
14+
15+
@functools.wraps(func)
16+
def wrapper(param, *args, **kwargs):
17+
if param is not None:
18+
return func(param, *args, **kwargs)
19+
20+
return wrapper

setuptools/_distutils/command/bdist_msi.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,7 @@ def run(self):
231231
if os.path.exists(installer_name): os.unlink(installer_name)
232232

233233
metadata = self.distribution.metadata
234-
author = metadata.author
235-
if not author:
236-
author = metadata.maintainer
237-
if not author:
238-
author = "UNKNOWN"
234+
author = metadata.author or metadata.maintainer
239235
version = metadata.get_version()
240236
# ProductVersion must be strictly numeric
241237
# XXX need to deal with prerelease versions

setuptools/_distutils/command/bdist_rpm.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ def _make_spec_file(self):
399399
'%define unmangled_version ' + self.distribution.get_version(),
400400
'%define release ' + self.release.replace('-','_'),
401401
'',
402-
'Summary: ' + self.distribution.get_description(),
402+
'Summary: ' + (self.distribution.get_description() or "UNKNOWN"),
403403
]
404404

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

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

466466

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

470470
if self.distribution_name:
@@ -483,7 +483,7 @@ def _make_spec_file(self):
483483
spec_file.extend([
484484
'',
485485
'%description',
486-
self.distribution.get_long_description()
486+
self.distribution.get_long_description() or "",
487487
])
488488

489489
# put locale descriptions into spec file

setuptools/_distutils/command/check.py

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -82,54 +82,19 @@ def check_metadata(self):
8282
"""Ensures that all required elements of meta-data are supplied.
8383
8484
Required fields:
85-
name, version, URL
86-
87-
Recommended fields:
88-
(author and author_email) or (maintainer and maintainer_email))
85+
name, version
8986
9087
Warns if any are missing.
9188
"""
9289
metadata = self.distribution.metadata
9390

9491
missing = []
95-
for attr in ('name', 'version', 'url'):
96-
if not (hasattr(metadata, attr) and getattr(metadata, attr)):
92+
for attr in 'name', 'version':
93+
if not getattr(metadata, attr, None):
9794
missing.append(attr)
9895

9996
if missing:
100-
self.warn("missing required meta-data: %s" % ', '.join(missing))
101-
if not (
102-
self._check_contact("author", metadata) or
103-
self._check_contact("maintainer", metadata)
104-
):
105-
self.warn("missing meta-data: either (author and author_email) " +
106-
"or (maintainer and maintainer_email) " +
107-
"should be supplied")
108-
109-
def _check_contact(self, kind, metadata):
110-
"""
111-
Returns True if the contact's name is specified and False otherwise.
112-
This function will warn if the contact's email is not specified.
113-
"""
114-
name = getattr(metadata, kind) or ''
115-
email = getattr(metadata, kind + '_email') or ''
116-
117-
msg = ("missing meta-data: if '{}' supplied, " +
118-
"'{}' should be supplied too")
119-
120-
if name and email:
121-
return True
122-
123-
if name:
124-
self.warn(msg.format(kind, kind + '_email'))
125-
return True
126-
127-
addresses = [(alias, addr) for alias, addr in getaddresses([email])]
128-
if any(alias and addr for alias, addr in addresses):
129-
# The contact's name can be encoded in the email: `Name <email>`
130-
return True
131-
132-
return False
97+
self.warn("missing required meta-data: %s" % ', '.join(missing))
13398

13499
def check_restructuredtext(self):
135100
"""Checks if the long string fields are reST-compliant."""

setuptools/_distutils/dist.py

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,9 +1064,8 @@ def read_pkg_file(self, file):
10641064

10651065
def _read_field(name):
10661066
value = msg[name]
1067-
if value == 'UNKNOWN':
1068-
return None
1069-
return value
1067+
if value and value != "UNKNOWN":
1068+
return value
10701069

10711070
def _read_list(name):
10721071
values = msg.get_all(name, None)
@@ -1125,23 +1124,24 @@ def write_pkg_file(self, file):
11251124
self.classifiers or self.download_url):
11261125
version = '1.1'
11271126

1127+
# required fields
11281128
file.write('Metadata-Version: %s\n' % version)
11291129
file.write('Name: %s\n' % self.get_name())
11301130
file.write('Version: %s\n' % self.get_version())
1131-
file.write('Summary: %s\n' % self.get_description())
1132-
file.write('Home-page: %s\n' % self.get_url())
1133-
file.write('Author: %s\n' % self.get_contact())
1134-
file.write('Author-email: %s\n' % self.get_contact_email())
1135-
file.write('License: %s\n' % self.get_license())
1136-
if self.download_url:
1137-
file.write('Download-URL: %s\n' % self.download_url)
11381131

1139-
long_desc = rfc822_escape(self.get_long_description())
1140-
file.write('Description: %s\n' % long_desc)
1132+
def maybe_write(header, val):
1133+
if val:
1134+
file.write("{}: {}\n".format(header, val))
11411135

1142-
keywords = ','.join(self.get_keywords())
1143-
if keywords:
1144-
file.write('Keywords: %s\n' % keywords)
1136+
# optional fields
1137+
maybe_write("Summary", self.get_description())
1138+
maybe_write("Home-page", self.get_url())
1139+
maybe_write("Author", self.get_contact())
1140+
maybe_write("Author-email", self.get_contact_email())
1141+
maybe_write("License", self.get_license())
1142+
maybe_write("Download-URL", self.download_url)
1143+
maybe_write("Description", rfc822_escape(self.get_long_description() or ""))
1144+
maybe_write("Keywords", ",".join(self.get_keywords()))
11451145

11461146
self._write_list(file, 'Platform', self.get_platforms())
11471147
self._write_list(file, 'Classifier', self.get_classifiers())
@@ -1152,6 +1152,7 @@ def write_pkg_file(self, file):
11521152
self._write_list(file, 'Obsoletes', self.get_obsoletes())
11531153

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

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

11691170
def get_author(self):
1170-
return self.author or "UNKNOWN"
1171+
return self.author
11711172

11721173
def get_author_email(self):
1173-
return self.author_email or "UNKNOWN"
1174+
return self.author_email
11741175

11751176
def get_maintainer(self):
1176-
return self.maintainer or "UNKNOWN"
1177+
return self.maintainer
11771178

11781179
def get_maintainer_email(self):
1179-
return self.maintainer_email or "UNKNOWN"
1180+
return self.maintainer_email
11801181

11811182
def get_contact(self):
1182-
return self.maintainer or self.author or "UNKNOWN"
1183+
return self.maintainer or self.author
11831184

11841185
def get_contact_email(self):
1185-
return self.maintainer_email or self.author_email or "UNKNOWN"
1186+
return self.maintainer_email or self.author_email
11861187

11871188
def get_url(self):
1188-
return self.url or "UNKNOWN"
1189+
return self.url
11891190

11901191
def get_license(self):
1191-
return self.license or "UNKNOWN"
1192+
return self.license
11921193
get_licence = get_license
11931194

11941195
def get_description(self):
1195-
return self.description or "UNKNOWN"
1196+
return self.description
11961197

11971198
def get_long_description(self):
1198-
return self.long_description or "UNKNOWN"
1199+
return self.long_description
11991200

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

12061207
def get_platforms(self):
1207-
return self.platforms or ["UNKNOWN"]
1208+
return self.platforms
12081209

12091210
def set_platforms(self, value):
12101211
self.platforms = _ensure_list(value, 'platforms')
@@ -1216,7 +1217,7 @@ def set_classifiers(self, value):
12161217
self.classifiers = _ensure_list(value, 'classifiers')
12171218

12181219
def get_download_url(self):
1219-
return self.download_url or "UNKNOWN"
1220+
return self.download_url
12201221

12211222
# PEP 314
12221223
def get_requires(self):

setuptools/_distutils/sysconfig.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from .errors import DistutilsPlatformError
1818
from . import py39compat
19+
from ._functools import pass_none
1920

2021
IS_PYPY = '__pypy__' in sys.builtin_module_names
2122

@@ -51,12 +52,25 @@ def _is_python_source_dir(d):
5152

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

55+
56+
def _is_parent(dir_a, dir_b):
57+
"""
58+
Return True if a is a parent of b.
59+
"""
60+
return os.path.normcase(dir_a).startswith(os.path.normcase(dir_b))
61+
62+
5463
if os.name == 'nt':
64+
@pass_none
5565
def _fix_pcbuild(d):
56-
if d and os.path.normcase(d).startswith(
57-
os.path.normcase(os.path.join(PREFIX, "PCbuild"))):
58-
return PREFIX
59-
return d
66+
# In a venv, sys._home will be inside BASE_PREFIX rather than PREFIX.
67+
prefixes = PREFIX, BASE_PREFIX
68+
matched = (
69+
prefix
70+
for prefix in prefixes
71+
if _is_parent(d, os.path.join(prefix, "PCbuild"))
72+
)
73+
return next(matched, d)
6074
project_base = _fix_pcbuild(project_base)
6175
_sys_home = _fix_pcbuild(_sys_home)
6276

setuptools/_distutils/tests/test_check.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def test_check_metadata(self):
4343
# by default, check is checking the metadata
4444
# should have some warnings
4545
cmd = self._run()
46-
self.assertEqual(cmd._warnings, 2)
46+
self.assertEqual(cmd._warnings, 1)
4747

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

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

90-
# the check should warn if only the name is given
89+
# the check should not warn if only the name is given
9190
metadata[kind] = "Name"
9291
del metadata[kind + '_email']
9392
cmd = self._run(metadata)
94-
self.assertEqual(cmd._warnings, 1)
93+
self.assertEqual(cmd._warnings, 0)
9594

9695
@unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils")
9796
def test_check_document(self):

0 commit comments

Comments
 (0)