Skip to content

Commit 07eda18

Browse files
authored
Merge pull request pypa#2635 from cdce8p/refactor-dist
Refactor ``read_pkg_file``
2 parents 1d330f9 + c18ed87 commit 07eda18

File tree

2 files changed

+95
-28
lines changed

2 files changed

+95
-28
lines changed

setuptools/dist.py

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from distutils.debug import DEBUG
1717
from distutils.fancy_getopt import translate_longopt
1818
import itertools
19+
import textwrap
20+
from typing import List, Optional, TYPE_CHECKING
1921

2022
from collections import defaultdict
2123
from email import message_from_file
@@ -36,6 +38,9 @@
3638
from setuptools.config import parse_configuration
3739
import pkg_resources
3840

41+
if TYPE_CHECKING:
42+
from email.message import Message
43+
3944
__import__('setuptools.extern.packaging.specifiers')
4045
__import__('setuptools.extern.packaging.version')
4146

@@ -67,53 +72,75 @@ def get_metadata_version(self):
6772
return mv
6873

6974

70-
def read_pkg_file(self, file):
71-
"""Reads the metadata values from a file object."""
72-
msg = message_from_file(file)
75+
def rfc822_unescape(content: str) -> str:
76+
"""Reverse RFC-822 escaping by removing leading whitespaces from content."""
77+
lines = content.splitlines()
78+
if len(lines) == 1:
79+
return lines[0].lstrip()
80+
return '\n'.join(
81+
(lines[0].lstrip(),
82+
textwrap.dedent('\n'.join(lines[1:]))))
83+
84+
85+
def _read_field_from_msg(msg: "Message", field: str) -> Optional[str]:
86+
"""Read Message header field."""
87+
value = msg[field]
88+
if value == 'UNKNOWN':
89+
return None
90+
return value
91+
7392

74-
def _read_field(name):
75-
value = msg[name]
76-
if value == 'UNKNOWN':
77-
return None
93+
def _read_field_unescaped_from_msg(msg: "Message", field: str) -> Optional[str]:
94+
"""Read Message header field and apply rfc822_unescape."""
95+
value = _read_field_from_msg(msg, field)
96+
if value is None:
7897
return value
98+
return rfc822_unescape(value)
7999

80-
def _read_list(name):
81-
values = msg.get_all(name, None)
82-
if values == []:
83-
return None
84-
return values
100+
101+
def _read_list_from_msg(msg: "Message", field: str) -> Optional[List[str]]:
102+
"""Read Message header field and return all results as list."""
103+
values = msg.get_all(field, None)
104+
if values == []:
105+
return None
106+
return values
107+
108+
109+
def read_pkg_file(self, file):
110+
"""Reads the metadata values from a file object."""
111+
msg = message_from_file(file)
85112

86113
self.metadata_version = StrictVersion(msg['metadata-version'])
87-
self.name = _read_field('name')
88-
self.version = _read_field('version')
89-
self.description = _read_field('summary')
114+
self.name = _read_field_from_msg(msg, 'name')
115+
self.version = _read_field_from_msg(msg, 'version')
116+
self.description = _read_field_from_msg(msg, 'summary')
90117
# we are filling author only.
91-
self.author = _read_field('author')
118+
self.author = _read_field_from_msg(msg, 'author')
92119
self.maintainer = None
93-
self.author_email = _read_field('author-email')
120+
self.author_email = _read_field_from_msg(msg, 'author-email')
94121
self.maintainer_email = None
95-
self.url = _read_field('home-page')
96-
self.license = _read_field('license')
122+
self.url = _read_field_from_msg(msg, 'home-page')
123+
self.license = _read_field_from_msg(msg, 'license')
97124

98125
if 'download-url' in msg:
99-
self.download_url = _read_field('download-url')
126+
self.download_url = _read_field_from_msg(msg, 'download-url')
100127
else:
101128
self.download_url = None
102129

103-
self.long_description = _read_field('description')
104-
self.description = _read_field('summary')
130+
self.long_description = _read_field_unescaped_from_msg(msg, 'description')
131+
self.description = _read_field_from_msg(msg, 'summary')
105132

106133
if 'keywords' in msg:
107-
self.keywords = _read_field('keywords').split(',')
134+
self.keywords = _read_field_from_msg(msg, 'keywords').split(',')
108135

109-
self.platforms = _read_list('platform')
110-
self.classifiers = _read_list('classifier')
136+
self.platforms = _read_list_from_msg(msg, 'platform')
137+
self.classifiers = _read_list_from_msg(msg, 'classifier')
111138

112139
# PEP 314 - these fields only exist in 1.1
113140
if self.metadata_version == StrictVersion('1.1'):
114-
self.requires = _read_list('requires')
115-
self.provides = _read_list('provides')
116-
self.obsoletes = _read_list('obsoletes')
141+
self.requires = _read_list_from_msg(msg, 'requires')
142+
self.provides = _read_list_from_msg(msg, 'provides')
143+
self.obsoletes = _read_list_from_msg(msg, 'obsoletes')
117144
else:
118145
self.requires = None
119146
self.provides = None

setuptools/tests/test_dist.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
check_package_data,
1111
DistDeprecationWarning,
1212
check_specifier,
13+
rfc822_escape,
14+
rfc822_unescape,
1315
)
1416
from setuptools import sic
1517
from setuptools import Distribution
@@ -85,6 +87,9 @@ def __read_test_cases():
8587
('Metadata version 1.1: Provides', params(
8688
provides=['package'],
8789
)),
90+
('Metadata Version 1.0: Short long description', params(
91+
long_description='Short long description',
92+
)),
8893
('Metadata version 1.1: Obsoletes', params(
8994
obsoletes=['foo'],
9095
)),
@@ -162,6 +167,7 @@ def test_read_metadata(name, attrs):
162167
('metadata_version', dist_class.get_metadata_version),
163168
('provides', dist_class.get_provides),
164169
('description', dist_class.get_description),
170+
('long_description', dist_class.get_long_description),
165171
('download_url', dist_class.get_download_url),
166172
('keywords', dist_class.get_keywords),
167173
('platforms', dist_class.get_platforms),
@@ -336,3 +342,37 @@ def test_check_specifier():
336342
attrs = {'name': 'foo', 'python_requires': ['>=3.0', '!=3.1']}
337343
with pytest.raises(DistutilsSetupError):
338344
dist = Distribution(attrs)
345+
346+
347+
@pytest.mark.parametrize(
348+
'content, result',
349+
(
350+
pytest.param(
351+
"Just a single line",
352+
None,
353+
id="single_line",
354+
),
355+
pytest.param(
356+
"Multiline\nText\nwithout\nextra indents\n",
357+
None,
358+
id="multiline",
359+
),
360+
pytest.param(
361+
"Multiline\n With\n\nadditional\n indentation",
362+
None,
363+
id="multiline_with_indentation",
364+
),
365+
pytest.param(
366+
" Leading whitespace",
367+
"Leading whitespace",
368+
id="remove_leading_whitespace",
369+
),
370+
pytest.param(
371+
" Leading whitespace\nIn\n Multiline comment",
372+
"Leading whitespace\nIn\n Multiline comment",
373+
id="remove_leading_whitespace_multiline",
374+
),
375+
)
376+
)
377+
def test_rfc822_unescape(content, result):
378+
assert (result or content) == rfc822_unescape(rfc822_escape(content))

0 commit comments

Comments
 (0)