File: //proc/self/root/lib/mysqlsh/lib/python3.8/site-packages/setuptools/tests/test_core_metadata.py
import functools
import importlib
import io
from email import message_from_string
import pytest
from packaging.metadata import Metadata
from setuptools import _reqs, sic
from setuptools._core_metadata import rfc822_escape, rfc822_unescape
from setuptools.command.egg_info import egg_info, write_requirements
from setuptools.dist import Distribution
EXAMPLE_BASE_INFO = dict(
name="package",
version="0.0.1",
author="Foo Bar",
author_email="foo@bar.net",
long_description="Long\ndescription",
description="Short description",
keywords=["one", "two"],
)
@pytest.mark.parametrize(
'content, result',
(
pytest.param(
"Just a single line",
None,
id="single_line",
),
pytest.param(
"Multiline\nText\nwithout\nextra indents\n",
None,
id="multiline",
),
pytest.param(
"Multiline\n With\n\nadditional\n indentation",
None,
id="multiline_with_indentation",
),
pytest.param(
" Leading whitespace",
"Leading whitespace",
id="remove_leading_whitespace",
),
pytest.param(
" Leading whitespace\nIn\n Multiline comment",
"Leading whitespace\nIn\n Multiline comment",
id="remove_leading_whitespace_multiline",
),
),
)
def test_rfc822_unescape(content, result):
assert (result or content) == rfc822_unescape(rfc822_escape(content))
def __read_test_cases():
base = EXAMPLE_BASE_INFO
params = functools.partial(dict, base)
return [
('Metadata version 1.0', params()),
(
'Metadata Version 1.0: Short long description',
params(
long_description='Short long description',
),
),
(
'Metadata version 1.1: Classifiers',
params(
classifiers=[
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'License :: OSI Approved :: MIT License',
],
),
),
(
'Metadata version 1.1: Download URL',
params(
download_url='https://example.com',
),
),
(
'Metadata Version 1.2: Requires-Python',
params(
python_requires='>=3.7',
),
),
pytest.param(
'Metadata Version 1.2: Project-Url',
params(project_urls=dict(Foo='https://example.bar')),
marks=pytest.mark.xfail(
reason="Issue #1578: project_urls not read",
),
),
(
'Metadata Version 2.1: Long Description Content Type',
params(
long_description_content_type='text/x-rst; charset=UTF-8',
),
),
(
'License',
params(
license='MIT',
),
),
(
'License multiline',
params(
license='This is a long license \nover multiple lines',
),
),
pytest.param(
'Metadata Version 2.1: Provides Extra',
params(provides_extras=['foo', 'bar']),
marks=pytest.mark.xfail(reason="provides_extras not read"),
),
(
'Missing author',
dict(
name='foo',
version='1.0.0',
author_email='snorri@sturluson.name',
),
),
(
'Missing author e-mail',
dict(
name='foo',
version='1.0.0',
author='Snorri Sturluson',
),
),
(
'Missing author and e-mail',
dict(
name='foo',
version='1.0.0',
),
),
(
'Bypass normalized version',
dict(
name='foo',
version=sic('1.0.0a'),
),
),
]
@pytest.mark.parametrize('name,attrs', __read_test_cases())
def test_read_metadata(name, attrs):
dist = Distribution(attrs)
metadata_out = dist.metadata
dist_class = metadata_out.__class__
# Write to PKG_INFO and then load into a new metadata object
PKG_INFO = io.StringIO()
metadata_out.write_pkg_file(PKG_INFO)
PKG_INFO.seek(0)
pkg_info = PKG_INFO.read()
assert _valid_metadata(pkg_info)
PKG_INFO.seek(0)
metadata_in = dist_class()
metadata_in.read_pkg_file(PKG_INFO)
tested_attrs = [
('name', dist_class.get_name),
('version', dist_class.get_version),
('author', dist_class.get_contact),
('author_email', dist_class.get_contact_email),
('metadata_version', dist_class.get_metadata_version),
('provides', dist_class.get_provides),
('description', dist_class.get_description),
('long_description', dist_class.get_long_description),
('download_url', dist_class.get_download_url),
('keywords', dist_class.get_keywords),
('platforms', dist_class.get_platforms),
('obsoletes', dist_class.get_obsoletes),
('requires', dist_class.get_requires),
('classifiers', dist_class.get_classifiers),
('project_urls', lambda s: getattr(s, 'project_urls', {})),
('provides_extras', lambda s: getattr(s, 'provides_extras', {})),
]
for attr, getter in tested_attrs:
assert getter(metadata_in) == getter(metadata_out)
def __maintainer_test_cases():
attrs = {"name": "package", "version": "1.0", "description": "xxx"}
def merge_dicts(d1, d2):
d1 = d1.copy()
d1.update(d2)
return d1
return [
('No author, no maintainer', attrs.copy()),
(
'Author (no e-mail), no maintainer',
merge_dicts(attrs, {'author': 'Author Name'}),
),
(
'Author (e-mail), no maintainer',
merge_dicts(
attrs, {'author': 'Author Name', 'author_email': 'author@name.com'}
),
),
(
'No author, maintainer (no e-mail)',
merge_dicts(attrs, {'maintainer': 'Maintainer Name'}),
),
(
'No author, maintainer (e-mail)',
merge_dicts(
attrs,
{
'maintainer': 'Maintainer Name',
'maintainer_email': 'maintainer@name.com',
},
),
),
(
'Author (no e-mail), Maintainer (no-email)',
merge_dicts(
attrs, {'author': 'Author Name', 'maintainer': 'Maintainer Name'}
),
),
(
'Author (e-mail), Maintainer (e-mail)',
merge_dicts(
attrs,
{
'author': 'Author Name',
'author_email': 'author@name.com',
'maintainer': 'Maintainer Name',
'maintainer_email': 'maintainer@name.com',
},
),
),
(
'No author (e-mail), no maintainer (e-mail)',
merge_dicts(
attrs,
{
'author_email': 'author@name.com',
'maintainer_email': 'maintainer@name.com',
},
),
),
('Author unicode', merge_dicts(attrs, {'author': '鉄沢寛'})),
('Maintainer unicode', merge_dicts(attrs, {'maintainer': 'Jan Łukasiewicz'})),
]
@pytest.mark.parametrize('name,attrs', __maintainer_test_cases())
def test_maintainer_author(name, attrs, tmpdir):
tested_keys = {
'author': 'Author',
'author_email': 'Author-email',
'maintainer': 'Maintainer',
'maintainer_email': 'Maintainer-email',
}
# Generate a PKG-INFO file
dist = Distribution(attrs)
fn = tmpdir.mkdir('pkg_info')
fn_s = str(fn)
dist.metadata.write_pkg_info(fn_s)
with open(str(fn.join('PKG-INFO')), 'r', encoding='utf-8') as f:
pkg_info = f.read()
assert _valid_metadata(pkg_info)
# Drop blank lines and strip lines from default description
raw_pkg_lines = pkg_info.splitlines()
pkg_lines = list(filter(None, raw_pkg_lines[:-2]))
pkg_lines_set = set(pkg_lines)
# Duplicate lines should not be generated
assert len(pkg_lines) == len(pkg_lines_set)
for fkey, dkey in tested_keys.items():
val = attrs.get(dkey, None)
if val is None:
for line in pkg_lines:
assert not line.startswith(fkey + ':')
else:
line = '%s: %s' % (fkey, val)
assert line in pkg_lines_set
def test_parity_with_metadata_from_pypa_wheel(tmp_path):
attrs = dict(
**EXAMPLE_BASE_INFO,
# Example with complex requirement definition
python_requires=">=3.8",
install_requires="""
packaging==23.2
ordered-set==3.1.1
more-itertools==8.8.0; extra == "other"
jaraco.text==3.7.0
importlib-resources==5.10.2; python_version<"3.8"
importlib-metadata==6.0.0 ; python_version<"3.8"
colorama>=0.4.4; sys_platform == "win32"
""",
extras_require={
"testing": """
pytest >= 6
pytest-checkdocs >= 2.4
tomli ; \\
# Using stdlib when possible
python_version < "3.11"
ini2toml[lite]>=0.9
""",
"other": [],
},
)
# Generate a PKG-INFO file using setuptools
dist = Distribution(attrs)
with io.StringIO() as fp:
dist.metadata.write_pkg_file(fp)
pkg_info = fp.getvalue()
assert _valid_metadata(pkg_info)
# Ensure Requires-Dist is present
expected = [
'Metadata-Version:',
'Requires-Python: >=3.8',
'Provides-Extra: other',
'Provides-Extra: testing',
'Requires-Dist: tomli; python_version < "3.11" and extra == "testing"',
'Requires-Dist: more-itertools==8.8.0; extra == "other"',
'Requires-Dist: ini2toml[lite]>=0.9; extra == "testing"',
]
for line in expected:
assert line in pkg_info
# Generate a METADATA file using pypa/wheel for comparison
wheel_metadata = importlib.import_module("wheel.metadata")
pkginfo_to_metadata = getattr(wheel_metadata, "pkginfo_to_metadata", None)
if pkginfo_to_metadata is None:
pytest.xfail(
"wheel.metadata.pkginfo_to_metadata is undefined, "
"(this is likely to be caused by API changes in pypa/wheel"
)
# Generate an simplified "egg-info" dir for pypa/wheel to convert
egg_info_dir = tmp_path / "pkg.egg-info"
egg_info_dir.mkdir(parents=True)
(egg_info_dir / "PKG-INFO").write_text(pkg_info, encoding="utf-8")
write_requirements(egg_info(dist), egg_info_dir, egg_info_dir / "requires.txt")
# Get pypa/wheel generated METADATA but normalize requirements formatting
metadata_msg = pkginfo_to_metadata(egg_info_dir, egg_info_dir / "PKG-INFO")
metadata_deps = set(_reqs.parse(metadata_msg.get_all("Requires-Dist")))
metadata_extras = set(metadata_msg.get_all("Provides-Extra"))
del metadata_msg["Requires-Dist"]
del metadata_msg["Provides-Extra"]
pkg_info_msg = message_from_string(pkg_info)
pkg_info_deps = set(_reqs.parse(pkg_info_msg.get_all("Requires-Dist")))
pkg_info_extras = set(pkg_info_msg.get_all("Provides-Extra"))
del pkg_info_msg["Requires-Dist"]
del pkg_info_msg["Provides-Extra"]
# Compare setuptools PKG-INFO x pypa/wheel METADATA
assert metadata_msg.as_string() == pkg_info_msg.as_string()
assert metadata_deps == pkg_info_deps
assert metadata_extras == pkg_info_extras
def _valid_metadata(text: str) -> bool:
metadata = Metadata.from_email(text, validate=True) # can raise exceptions
return metadata is not None