Branch merge

This commit is contained in:
Éric Araujo 2011-09-12 17:34:40 +02:00
commit faabef548d
15 changed files with 485 additions and 481 deletions

View File

@ -19,13 +19,12 @@ Finding out what's installed
Pysetup makes it easy to find out what Python packages are installed::
$ pysetup search virtualenv
virtualenv 1.6 at /opt/python3.3/lib/python3.3/site-packages/virtualenv-1.6-py3.3.egg-info
$ pysetup list virtualenv
'virtualenv' 1.6 at '/opt/python3.3/lib/python3.3/site-packages/virtualenv-1.6-py3.3.egg-info'
$ pysetup search --all
pyverify 0.8.1 at /opt/python3.3/lib/python3.3/site-packages/pyverify-0.8.1.dist-info
virtualenv 1.6 at /opt/python3.3/lib/python3.3/site-packages/virtualenv-1.6-py3.3.egg-info
wsgiref 0.1.2 at /opt/python3.3/lib/python3.3/wsgiref.egg-info
$ pysetup list
'pyverify' 0.8.1 at '/opt/python3.3/lib/python3.3/site-packages/pyverify-0.8.1.dist-info'
'virtualenv' 1.6 at '/opt/python3.3/lib/python3.3/site-packages/virtualenv-1.6-py3.3.egg-info'
...
@ -146,9 +145,11 @@ Getting a list of all pysetup actions and global options::
metadata: Display the metadata of a project
install: Install a project
remove: Remove a project
search: Search for a project
search: Search for a project in the indexes
list: List installed projects
graph: Display a graph
create: Create a Project
create: Create a project
generate-setup: Generate a backward-comptatible setup.py
To get more help on an action, use:

View File

@ -1018,7 +1018,8 @@ class DistributionMetadata:
"""Write the PKG-INFO format data to a file object.
"""
version = '1.0'
if self.provides or self.requires or self.obsoletes:
if (self.provides or self.requires or self.obsoletes or
self.classifiers or self.download_url):
version = '1.1'
file.write('Metadata-Version: %s\n' % version)

View File

@ -74,7 +74,7 @@ class DistributionTestCase(support.LoggingSilencer,
self.assertEqual(d.get_command_packages(),
["distutils.command", "foo.bar", "distutils.tests"])
cmd = d.get_command_obj("test_dist")
self.assertTrue(isinstance(cmd, test_dist))
self.assertIsInstance(cmd, test_dist)
self.assertEqual(cmd.sample_option, "sometext")
def test_command_packages_configfile(self):
@ -106,28 +106,23 @@ class DistributionTestCase(support.LoggingSilencer,
def test_empty_options(self):
# an empty options dictionary should not stay in the
# list of attributes
klass = Distribution
# catching warnings
warns = []
def _warn(msg):
warns.append(msg)
old_warn = warnings.warn
self.addCleanup(setattr, warnings, 'warn', warnings.warn)
warnings.warn = _warn
try:
dist = klass(attrs={'author': 'xxx',
'name': 'xxx',
'version': 'xxx',
'url': 'xxxx',
dist = Distribution(attrs={'author': 'xxx', 'name': 'xxx',
'version': 'xxx', 'url': 'xxxx',
'options': {}})
finally:
warnings.warn = old_warn
self.assertEqual(len(warns), 0)
self.assertNotIn('options', dir(dist))
def test_finalize_options(self):
attrs = {'keywords': 'one,two',
'platforms': 'one,two'}
@ -150,7 +145,6 @@ class DistributionTestCase(support.LoggingSilencer,
cmds = dist.get_command_packages()
self.assertEqual(cmds, ['distutils.command', 'one', 'two'])
def test_announce(self):
# make sure the level is known
dist = Distribution()
@ -158,6 +152,7 @@ class DistributionTestCase(support.LoggingSilencer,
kwargs = {'level': 'ok2'}
self.assertRaises(ValueError, dist.announce, args, kwargs)
class MetadataTestCase(support.TempdirManager, support.EnvironGuard,
unittest.TestCase):
@ -170,15 +165,20 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard,
sys.argv[:] = self.argv[1]
super(MetadataTestCase, self).tearDown()
def format_metadata(self, dist):
sio = io.StringIO()
dist.metadata.write_pkg_file(sio)
return sio.getvalue()
def test_simple_metadata(self):
attrs = {"name": "package",
"version": "1.0"}
dist = Distribution(attrs)
meta = self.format_metadata(dist)
self.assertTrue("Metadata-Version: 1.0" in meta)
self.assertTrue("provides:" not in meta.lower())
self.assertTrue("requires:" not in meta.lower())
self.assertTrue("obsoletes:" not in meta.lower())
self.assertIn("Metadata-Version: 1.0", meta)
self.assertNotIn("provides:", meta.lower())
self.assertNotIn("requires:", meta.lower())
self.assertNotIn("obsoletes:", meta.lower())
def test_provides(self):
attrs = {"name": "package",
@ -190,9 +190,9 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard,
self.assertEqual(dist.get_provides(),
["package", "package.sub"])
meta = self.format_metadata(dist)
self.assertTrue("Metadata-Version: 1.1" in meta)
self.assertTrue("requires:" not in meta.lower())
self.assertTrue("obsoletes:" not in meta.lower())
self.assertIn("Metadata-Version: 1.1", meta)
self.assertNotIn("requires:", meta.lower())
self.assertNotIn("obsoletes:", meta.lower())
def test_provides_illegal(self):
self.assertRaises(ValueError, Distribution,
@ -210,11 +210,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard,
self.assertEqual(dist.get_requires(),
["other", "another (==1.0)"])
meta = self.format_metadata(dist)
self.assertTrue("Metadata-Version: 1.1" in meta)
self.assertTrue("provides:" not in meta.lower())
self.assertTrue("Requires: other" in meta)
self.assertTrue("Requires: another (==1.0)" in meta)
self.assertTrue("obsoletes:" not in meta.lower())
self.assertIn("Metadata-Version: 1.1", meta)
self.assertNotIn("provides:", meta.lower())
self.assertIn("Requires: other", meta)
self.assertIn("Requires: another (==1.0)", meta)
self.assertNotIn("obsoletes:", meta.lower())
def test_requires_illegal(self):
self.assertRaises(ValueError, Distribution,
@ -232,11 +232,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard,
self.assertEqual(dist.get_obsoletes(),
["other", "another (<1.0)"])
meta = self.format_metadata(dist)
self.assertTrue("Metadata-Version: 1.1" in meta)
self.assertTrue("provides:" not in meta.lower())
self.assertTrue("requires:" not in meta.lower())
self.assertTrue("Obsoletes: other" in meta)
self.assertTrue("Obsoletes: another (<1.0)" in meta)
self.assertIn("Metadata-Version: 1.1", meta)
self.assertNotIn("provides:", meta.lower())
self.assertNotIn("requires:", meta.lower())
self.assertIn("Obsoletes: other", meta)
self.assertIn("Obsoletes: another (<1.0)", meta)
def test_obsoletes_illegal(self):
self.assertRaises(ValueError, Distribution,
@ -244,10 +244,34 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard,
"version": "1.0",
"obsoletes": ["my.pkg (splat)"]})
def format_metadata(self, dist):
sio = io.StringIO()
dist.metadata.write_pkg_file(sio)
return sio.getvalue()
def test_classifier(self):
attrs = {'name': 'Boa', 'version': '3.0',
'classifiers': ['Programming Language :: Python :: 3']}
dist = Distribution(attrs)
meta = self.format_metadata(dist)
self.assertIn('Metadata-Version: 1.1', meta)
def test_download_url(self):
attrs = {'name': 'Boa', 'version': '3.0',
'download_url': 'http://example.org/boa'}
dist = Distribution(attrs)
meta = self.format_metadata(dist)
self.assertIn('Metadata-Version: 1.1', meta)
def test_long_description(self):
long_desc = textwrap.dedent("""\
example::
We start here
and continue here
and end here.""")
attrs = {"name": "package",
"version": "1.0",
"long_description": long_desc}
dist = Distribution(attrs)
meta = self.format_metadata(dist)
meta = meta.replace('\n' + 8 * ' ', '\n')
self.assertIn(long_desc, meta)
def test_custom_pydistutils(self):
# fixes #2166
@ -272,14 +296,14 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard,
if sys.platform in ('linux', 'darwin'):
os.environ['HOME'] = temp_dir
files = dist.find_config_files()
self.assertTrue(user_filename in files)
self.assertIn(user_filename, files)
# win32-style
if sys.platform == 'win32':
# home drive should be found
os.environ['HOME'] = temp_dir
files = dist.find_config_files()
self.assertTrue(user_filename in files,
self.assertIn(user_filename, files,
'%r not found in %r' % (user_filename, files))
finally:
os.remove(user_filename)
@ -301,22 +325,8 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard,
output = [line for line in s.getvalue().split('\n')
if line.strip() != '']
self.assertTrue(len(output) > 0)
self.assertTrue(output)
def test_long_description(self):
long_desc = textwrap.dedent("""\
example::
We start here
and continue here
and end here.""")
attrs = {"name": "package",
"version": "1.0",
"long_description": long_desc}
dist = Distribution(attrs)
meta = self.format_metadata(dist)
meta = meta.replace('\n' + 8 * ' ', '\n')
self.assertTrue(long_desc in meta)
def test_suite():
suite = unittest.TestSuite()

View File

@ -1,7 +1,5 @@
"""Create an executable installer for Windows."""
# FIXME synchronize bytes/str use with same file in distutils
import sys
import os
@ -186,9 +184,8 @@ class bdist_wininst(Command):
os.remove(arcname)
if not self.keep_temp:
if self.dry_run:
logger.info('removing %s', self.bdist_dir)
else:
if not self.dry_run:
rmtree(self.bdist_dir)
def get_inidata(self):
@ -265,14 +262,17 @@ class bdist_wininst(Command):
cfgdata = cfgdata.encode("mbcs")
# Append the pre-install script
cfgdata = cfgdata + "\0"
cfgdata = cfgdata + b"\0"
if self.pre_install_script:
with open(self.pre_install_script) as fp:
script_data = fp.read()
cfgdata = cfgdata + script_data + "\n\0"
# We need to normalize newlines, so we open in text mode and
# convert back to bytes. "latin-1" simply avoids any possible
# failures.
with open(self.pre_install_script, encoding="latin-1") as fp:
script_data = fp.read().encode("latin-1")
cfgdata = cfgdata + script_data + b"\n\0"
else:
# empty pre-install script
cfgdata = cfgdata + "\0"
cfgdata = cfgdata + b"\0"
file.write(cfgdata)
# The 'magic number' 0x1234567B is used to make sure that the

View File

@ -1,9 +1,5 @@
"""Build extension modules."""
# FIXME Is this module limited to C extensions or do C++ extensions work too?
# The docstring of this module said that C++ was not supported, but other
# comments contradict that.
import os
import re
import sys

View File

@ -28,7 +28,7 @@ class install_distinfo(Command):
('no-record', None,
"do not generate a RECORD file"),
('no-resources', None,
"do not generate a RESSOURCES list installed file")
"do not generate a RESSOURCES list installed file"),
]
boolean_options = ['requested', 'no-record', 'no-resources']
@ -70,13 +70,10 @@ class install_distinfo(Command):
self.distinfo_dir = os.path.join(self.distinfo_dir, basename)
def run(self):
# FIXME dry-run should be used at a finer level, so that people get
# useful logging output and can have an idea of what the command would
# have done
if not self.dry_run:
target = self.distinfo_dir
if os.path.isdir(target) and not os.path.islink(target):
if not self.dry_run:
rmtree(target)
elif os.path.exists(target):
self.execute(os.unlink, (self.distinfo_dir,),
@ -85,12 +82,13 @@ class install_distinfo(Command):
self.execute(os.makedirs, (target,), "creating " + target)
metadata_path = os.path.join(self.distinfo_dir, 'METADATA')
logger.info('creating %s', metadata_path)
self.distribution.metadata.write(metadata_path)
self.execute(self.distribution.metadata.write, (metadata_path,),
"creating " + metadata_path)
self.outfiles.append(metadata_path)
installer_path = os.path.join(self.distinfo_dir, 'INSTALLER')
logger.info('creating %s', installer_path)
if not self.dry_run:
with open(installer_path, 'w') as f:
f.write(self.installer)
self.outfiles.append(installer_path)
@ -98,28 +96,30 @@ class install_distinfo(Command):
if self.requested:
requested_path = os.path.join(self.distinfo_dir, 'REQUESTED')
logger.info('creating %s', requested_path)
if not self.dry_run:
open(requested_path, 'wb').close()
self.outfiles.append(requested_path)
if not self.no_resources:
install_data = self.get_finalized_command('install_data')
if install_data.get_resources_out() != []:
resources_path = os.path.join(self.distinfo_dir,
'RESOURCES')
logger.info('creating %s', resources_path)
if not self.dry_run:
with open(resources_path, 'wb') as f:
writer = csv.writer(f, delimiter=',',
lineterminator='\n',
quotechar='"')
for tuple in install_data.get_resources_out():
writer.writerow(tuple)
for row in install_data.get_resources_out():
writer.writerow(row)
self.outfiles.append(resources_path)
if not self.no_record:
record_path = os.path.join(self.distinfo_dir, 'RECORD')
logger.info('creating %s', record_path)
if not self.dry_run:
with open(record_path, 'w', encoding='utf-8') as f:
writer = csv.writer(f, delimiter=',',
lineterminator='\n',

View File

@ -227,10 +227,11 @@ class Config:
self.dist.scripts = [self.dist.scripts]
self.dist.package_data = {}
for data in files.get('package_data', []):
data = data.split('=')
for line in files.get('package_data', []):
data = line.split('=')
if len(data) != 2:
continue # FIXME errors should never pass silently
raise ValueError('invalid line for package_data: %s '
'(misses "=")' % line)
key, value = data
self.dist.package_data[key.strip()] = value.strip()

View File

@ -61,7 +61,8 @@ _314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
'License', 'Classifier', 'Download-URL', 'Obsoletes',
'Provides', 'Requires')
_314_MARKERS = ('Obsoletes', 'Provides', 'Requires')
_314_MARKERS = ('Obsoletes', 'Provides', 'Requires', 'Classifier',
'Download-URL')
_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
'Supported-Platform', 'Summary', 'Description',
@ -354,11 +355,20 @@ class Metadata:
Keys that don't match a metadata field or that have an empty value are
dropped.
"""
# XXX the code should just use self.set, which does tbe same checks and
# conversions already, but that would break packaging.pypi: it uses the
# update method, which does not call _set_best_version (which set
# does), and thus allows having a Metadata object (as long as you don't
# modify or write it) with extra fields from PyPI that are not fields
# defined in Metadata PEPs. to solve it, the best_version system
# should be reworked so that it's called only for writing, or in a new
# strict mode, or with a new, more lax Metadata subclass in p7g.pypi
def _set(key, value):
if key in _ATTR2FIELD and value:
self.set(self._convert_name(key), value)
if other is None:
if not other:
# other is None or empty container
pass
elif hasattr(other, 'keys'):
for k in other.keys():
@ -368,7 +378,8 @@ class Metadata:
_set(k, v)
if kwargs:
self.update(kwargs)
for k, v in kwargs.items():
_set(k, v)
def set(self, name, value):
"""Control then set a metadata field."""

View File

@ -159,22 +159,20 @@ class Crawler(BaseClient):
Return a list of names.
"""
with self._open_url(self.index_url) as index:
if '*' in name:
name.replace('*', '.*')
else:
name = "%s%s%s" % ('*.?', name, '*.?')
name = name.replace('*', '[^<]*') # avoid matching end tag
projectname = re.compile('<a[^>]*>(%s)</a>' % name, re.I)
pattern = ('<a[^>]*>(%s)</a>' % name).encode('utf-8')
projectname = re.compile(pattern, re.I)
matching_projects = []
with self._open_url(self.index_url) as index:
index_content = index.read()
# FIXME should use bytes I/O and regexes instead of decoding
index_content = index_content.decode()
for match in projectname.finditer(index_content):
project_name = match.group(1)
project_name = match.group(1).decode('utf-8')
matching_projects.append(self._get_project(project_name))
return matching_projects

View File

@ -290,27 +290,23 @@ def _run(dispatcher, args, **kw):
@action_help("""\
Usage: pysetup list dist [dist ...]
Usage: pysetup list [dist ...]
or: pysetup list --help
or: pysetup list --all
Print name, version and location for the matching installed distributions.
positional arguments:
dist installed distribution name
optional arguments:
--all list all installed distributions
dist installed distribution name; omit to get all distributions
""")
def _list(dispatcher, args, **kw):
opts = _parse_args(args[1:], '', ['all'])
opts = _parse_args(args[1:], '', [])
dists = get_distributions(use_egg_info=True)
if 'all' in opts or opts['args'] == []:
results = dists
listall = True
else:
if opts['args']:
results = (d for d in dists if d.name.lower() in opts['args'])
listall = False
else:
results = dists
listall = True
number = 0
for dist in results:
@ -368,7 +364,7 @@ actions = [
('install', 'Install a project', _install),
('remove', 'Remove a project', _remove),
('search', 'Search for a project in the indexes', _search),
('list', 'List installed releases', _list),
('list', 'List installed projects', _list),
('graph', 'Display a graph', _graph),
('create', 'Create a project', _create),
('generate-setup', 'Generate a backward-comptatible setup.py', _generate),

View File

@ -35,6 +35,7 @@ class InstallDataTestCase(support.TempdirManager,
two = os.path.join(pkg_dir, 'two')
self.write_file(two, 'xxx')
# FIXME this creates a literal \{inst2\} directory!
cmd.data_files = {one: '{inst}/one', two: '{inst2}/two'}
self.assertCountEqual(cmd.get_inputs(), [one, two])

View File

@ -1,16 +1,13 @@
"""Tests for packaging.dist."""
import os
import io
import sys
import logging
import textwrap
import sysconfig
import packaging.dist
from packaging.dist import Distribution
from packaging.command import set_command
from packaging.command.cmd import Command
from packaging.metadata import Metadata
from packaging.errors import PackagingModuleError, PackagingOptionError
from packaging.tests import TESTFN, captured_stdout
from packaging.tests import support, unittest
@ -49,6 +46,7 @@ class DistributionTestCase(support.TempdirManager,
sys.argv[:] = self.argv[1]
super(DistributionTestCase, self).tearDown()
@unittest.skip('needs to be updated')
def test_debug_mode(self):
self.addCleanup(os.unlink, TESTFN)
with open(TESTFN, "w") as f:
@ -59,6 +57,8 @@ class DistributionTestCase(support.TempdirManager,
sys.argv.append("build")
__, stdout = captured_stdout(create_distribution, files)
self.assertEqual(stdout, '')
# XXX debug mode does not exist anymore, test logging levels in this
# test instead
packaging.dist.DEBUG = True
try:
__, stdout = captured_stdout(create_distribution, files)
@ -66,34 +66,6 @@ class DistributionTestCase(support.TempdirManager,
finally:
packaging.dist.DEBUG = False
def test_write_pkg_file(self):
# Check Metadata handling of Unicode fields
tmp_dir = self.mkdtemp()
my_file = os.path.join(tmp_dir, 'f')
cls = Distribution
dist = cls(attrs={'author': 'Mister Café',
'name': 'my.package',
'maintainer': 'Café Junior',
'summary': 'Café torréfié',
'description': 'Héhéhé'})
# let's make sure the file can be written
# with Unicode fields. they are encoded with
# PKG_INFO_ENCODING
with open(my_file, 'w', encoding='utf-8') as fp:
dist.metadata.write_file(fp)
# regular ascii is of course always usable
dist = cls(attrs={'author': 'Mister Cafe',
'name': 'my.package',
'maintainer': 'Cafe Junior',
'summary': 'Cafe torrefie',
'description': 'Hehehe'})
with open(my_file, 'w') as fp:
dist.metadata.write_file(fp)
def test_bad_attr(self):
Distribution(attrs={'author': 'xxx',
'name': 'xxx',
@ -101,28 +73,18 @@ class DistributionTestCase(support.TempdirManager,
'home-page': 'xxxx',
'badoptname': 'xxx'})
logs = self.get_logs(logging.WARNING)
self.assertEqual(1, len(logs))
self.assertEqual(len(logs), 1)
self.assertIn('unknown argument', logs[0])
def test_bad_version(self):
Distribution(attrs={'author': 'xxx',
'name': 'xxx',
'version': 'xxx',
'home-page': 'xxxx'})
logs = self.get_logs(logging.WARNING)
self.assertEqual(1, len(logs))
self.assertIn('not a valid version', logs[0])
def test_empty_options(self):
# an empty options dictionary should not stay in the
# list of attributes
Distribution(attrs={'author': 'xxx',
'name': 'xxx',
'version': '1.2',
'home-page': 'xxxx',
dist = Distribution(attrs={'author': 'xxx', 'name': 'xxx',
'version': '1.2', 'home-page': 'xxxx',
'options': {}})
self.assertEqual([], self.get_logs(logging.WARNING))
self.assertNotIn('options', dir(dist))
def test_non_empty_options(self):
# TODO: how to actually use options is not documented except
@ -141,7 +103,6 @@ class DistributionTestCase(support.TempdirManager,
self.assertIn('owner', dist.get_option_dict('sdist'))
def test_finalize_options(self):
attrs = {'keywords': 'one,two',
'platform': 'one,two'}
@ -152,6 +113,24 @@ class DistributionTestCase(support.TempdirManager,
self.assertEqual(dist.metadata['platform'], ['one', 'two'])
self.assertEqual(dist.metadata['keywords'], ['one', 'two'])
def test_custom_pydistutils(self):
# Bug #2166: make sure pydistutils.cfg is found
if os.name == 'posix':
user_filename = ".pydistutils.cfg"
else:
user_filename = "pydistutils.cfg"
temp_dir = self.mkdtemp()
user_filename = os.path.join(temp_dir, user_filename)
with open(user_filename, 'w') as f:
f.write('.')
dist = Distribution()
os.environ['HOME'] = temp_dir
files = dist.find_config_files()
self.assertIn(user_filename, files)
def test_find_config_files_disable(self):
# Bug #1180: Allow users to disable their own config file.
temp_home = self.mkdtemp()
@ -270,185 +249,8 @@ class DistributionTestCase(support.TempdirManager,
self.assertRaises(PackagingOptionError, d.run_command, 'test_dist')
class MetadataTestCase(support.TempdirManager,
support.LoggingCatcher,
support.EnvironRestorer,
unittest.TestCase):
restore_environ = ['HOME']
def setUp(self):
super(MetadataTestCase, self).setUp()
self.argv = sys.argv, sys.argv[:]
def tearDown(self):
sys.argv = self.argv[0]
sys.argv[:] = self.argv[1]
super(MetadataTestCase, self).tearDown()
def test_simple_metadata(self):
attrs = {"name": "package",
"version": "1.0"}
dist = Distribution(attrs)
meta = self.format_metadata(dist)
self.assertIn("Metadata-Version: 1.0", meta)
self.assertNotIn("provides:", meta.lower())
self.assertNotIn("requires:", meta.lower())
self.assertNotIn("obsoletes:", meta.lower())
def test_provides_dist(self):
attrs = {"name": "package",
"version": "1.0",
"provides_dist": ["package", "package.sub"]}
dist = Distribution(attrs)
self.assertEqual(dist.metadata['Provides-Dist'],
["package", "package.sub"])
meta = self.format_metadata(dist)
self.assertIn("Metadata-Version: 1.2", meta)
self.assertNotIn("requires:", meta.lower())
self.assertNotIn("obsoletes:", meta.lower())
def _test_provides_illegal(self):
# XXX to do: check the versions
self.assertRaises(ValueError, Distribution,
{"name": "package",
"version": "1.0",
"provides_dist": ["my.pkg (splat)"]})
def test_requires_dist(self):
attrs = {"name": "package",
"version": "1.0",
"requires_dist": ["other", "another (==1.0)"]}
dist = Distribution(attrs)
self.assertEqual(dist.metadata['Requires-Dist'],
["other", "another (==1.0)"])
meta = self.format_metadata(dist)
self.assertIn("Metadata-Version: 1.2", meta)
self.assertNotIn("provides:", meta.lower())
self.assertIn("Requires-Dist: other", meta)
self.assertIn("Requires-Dist: another (==1.0)", meta)
self.assertNotIn("obsoletes:", meta.lower())
def _test_requires_illegal(self):
# XXX
self.assertRaises(ValueError, Distribution,
{"name": "package",
"version": "1.0",
"requires": ["my.pkg (splat)"]})
def test_obsoletes_dist(self):
attrs = {"name": "package",
"version": "1.0",
"obsoletes_dist": ["other", "another (<1.0)"]}
dist = Distribution(attrs)
self.assertEqual(dist.metadata['Obsoletes-Dist'],
["other", "another (<1.0)"])
meta = self.format_metadata(dist)
self.assertIn("Metadata-Version: 1.2", meta)
self.assertNotIn("provides:", meta.lower())
self.assertNotIn("requires:", meta.lower())
self.assertIn("Obsoletes-Dist: other", meta)
self.assertIn("Obsoletes-Dist: another (<1.0)", meta)
def _test_obsoletes_illegal(self):
# XXX
self.assertRaises(ValueError, Distribution,
{"name": "package",
"version": "1.0",
"obsoletes": ["my.pkg (splat)"]})
def format_metadata(self, dist):
sio = io.StringIO()
dist.metadata.write_file(sio)
return sio.getvalue()
def test_custom_pydistutils(self):
# fixes #2166
# make sure pydistutils.cfg is found
if os.name == 'posix':
user_filename = ".pydistutils.cfg"
else:
user_filename = "pydistutils.cfg"
temp_dir = self.mkdtemp()
user_filename = os.path.join(temp_dir, user_filename)
with open(user_filename, 'w') as f:
f.write('.')
dist = Distribution()
# linux-style
if sys.platform in ('linux', 'darwin'):
os.environ['HOME'] = temp_dir
files = dist.find_config_files()
self.assertIn(user_filename, files)
# win32-style
if sys.platform == 'win32':
# home drive should be found
os.environ['HOME'] = temp_dir
files = dist.find_config_files()
self.assertIn(user_filename, files)
def test_show_help(self):
# smoke test, just makes sure some help is displayed
dist = Distribution()
sys.argv = []
dist.help = True
dist.script_name = os.path.join(sysconfig.get_path('scripts'),
'pysetup')
__, stdout = captured_stdout(dist.parse_command_line)
output = [line for line in stdout.split('\n')
if line.strip() != '']
self.assertGreater(len(output), 0)
def test_description(self):
desc = textwrap.dedent("""\
example::
We start here
and continue here
and end here.""")
attrs = {"name": "package",
"version": "1.0",
"description": desc}
dist = packaging.dist.Distribution(attrs)
meta = self.format_metadata(dist)
meta = meta.replace('\n' + 7 * ' ' + '|', '\n')
self.assertIn(desc, meta)
def test_read_metadata(self):
attrs = {"name": "package",
"version": "1.0",
"description": "desc",
"summary": "xxx",
"download_url": "http://example.com",
"keywords": ['one', 'two'],
"requires_dist": ['foo']}
dist = Distribution(attrs)
PKG_INFO = io.StringIO()
dist.metadata.write_file(PKG_INFO)
PKG_INFO.seek(0)
metadata = Metadata()
metadata.read_file(PKG_INFO)
self.assertEqual(metadata['name'], "package")
self.assertEqual(metadata['version'], "1.0")
self.assertEqual(metadata['summary'], "xxx")
self.assertEqual(metadata['download_url'], 'http://example.com')
self.assertEqual(metadata['keywords'], ['one', 'two'])
self.assertEqual(metadata['platform'], [])
self.assertEqual(metadata['obsoletes'], [])
self.assertEqual(metadata['requires-dist'], ['foo'])
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(DistributionTestCase))
suite.addTest(unittest.makeSuite(MetadataTestCase))
return suite
return unittest.makeSuite(DistributionTestCase)
if __name__ == "__main__":
unittest.main(defaultTest="test_suite")

View File

@ -2,6 +2,7 @@
import os
import sys
import logging
from textwrap import dedent
from io import StringIO
from packaging.errors import (MetadataConflictError, MetadataMissingError,
@ -9,12 +10,29 @@ from packaging.errors import (MetadataConflictError, MetadataMissingError,
from packaging.metadata import Metadata, PKG_INFO_PREFERRED_VERSION
from packaging.tests import unittest
from packaging.tests.support import LoggingCatcher
from packaging.tests.support import (LoggingCatcher, TempdirManager,
EnvironRestorer)
class MetadataTestCase(LoggingCatcher,
TempdirManager,
EnvironRestorer,
unittest.TestCase):
maxDiff = None
restore_environ = ['HOME']
def setUp(self):
super(MetadataTestCase, self).setUp()
self.argv = sys.argv, sys.argv[:]
def tearDown(self):
sys.argv = self.argv[0]
sys.argv[:] = self.argv[1]
super(MetadataTestCase, self).tearDown()
#### Test various methods of the Metadata class
def test_instantiation(self):
PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
with open(PKG_INFO, 'r', encoding='utf-8') as f:
@ -43,17 +61,6 @@ class MetadataTestCase(LoggingCatcher,
self.assertRaises(TypeError, Metadata,
PKG_INFO, mapping=m, fileobj=fp)
def test_metadata_read_write(self):
PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
metadata = Metadata(PKG_INFO)
out = StringIO()
metadata.write_file(out)
out.seek(0)
res = Metadata()
res.read_file(out)
for k in metadata:
self.assertEqual(metadata[k], res[k])
def test_metadata_markers(self):
# see if we can be platform-aware
PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
@ -70,31 +77,10 @@ class MetadataTestCase(LoggingCatcher,
# test with context
context = {'sys.platform': 'okook'}
metadata = Metadata(platform_dependent=True,
execution_context=context)
metadata = Metadata(platform_dependent=True, execution_context=context)
metadata.read_file(StringIO(content))
self.assertEqual(metadata['Requires-Dist'], ['foo'])
def test_description(self):
PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
with open(PKG_INFO, 'r', encoding='utf-8') as f:
content = f.read() % sys.platform
metadata = Metadata()
metadata.read_file(StringIO(content))
# see if we can read the description now
DESC = os.path.join(os.path.dirname(__file__), 'LONG_DESC.txt')
with open(DESC) as f:
wanted = f.read()
self.assertEqual(wanted, metadata['Description'])
# save the file somewhere and make sure we can read it back
out = StringIO()
metadata.write_file(out)
out.seek(0)
metadata.read_file(out)
self.assertEqual(wanted, metadata['Description'])
def test_mapping_api(self):
PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
with open(PKG_INFO, 'r', encoding='utf-8') as f:
@ -109,73 +95,86 @@ class MetadataTestCase(LoggingCatcher,
metadata.update([('version', '0.7')])
self.assertEqual(metadata['Version'], '0.7')
# make sure update method checks values like the set method does
metadata.update({'version': '1--2'})
self.assertEqual(len(self.get_logs()), 1)
# XXX caveat: the keys method and friends are not 3.x-style views
# should be changed or documented
self.assertEqual(list(metadata), list(metadata.keys()))
def test_versions(self):
metadata = Metadata()
metadata['Obsoletes'] = 'ok'
self.assertEqual(metadata['Metadata-Version'], '1.1')
def test_read_metadata(self):
fields = {'name': 'project',
'version': '1.0',
'description': 'desc',
'summary': 'xxx',
'download_url': 'http://example.com',
'keywords': ['one', 'two'],
'requires_dist': ['foo']}
del metadata['Obsoletes']
metadata['Obsoletes-Dist'] = 'ok'
self.assertEqual(metadata['Metadata-Version'], '1.2')
metadata = Metadata(mapping=fields)
PKG_INFO = StringIO()
metadata.write_file(PKG_INFO)
PKG_INFO.seek(0)
self.assertRaises(MetadataConflictError, metadata.set,
'Obsoletes', 'ok')
metadata = Metadata(fileobj=PKG_INFO)
del metadata['Obsoletes']
del metadata['Obsoletes-Dist']
metadata['Version'] = '1'
self.assertEqual(metadata['Metadata-Version'], '1.0')
self.assertEqual(metadata['name'], 'project')
self.assertEqual(metadata['version'], '1.0')
self.assertEqual(metadata['summary'], 'xxx')
self.assertEqual(metadata['download_url'], 'http://example.com')
self.assertEqual(metadata['keywords'], ['one', 'two'])
self.assertEqual(metadata['platform'], [])
self.assertEqual(metadata['obsoletes'], [])
self.assertEqual(metadata['requires-dist'], ['foo'])
PKG_INFO = os.path.join(os.path.dirname(__file__),
'SETUPTOOLS-PKG-INFO')
with open(PKG_INFO, 'r', encoding='utf-8') as f:
content = f.read()
metadata.read_file(StringIO(content))
self.assertEqual(metadata['Metadata-Version'], '1.0')
def test_write_metadata(self):
# check support of non-ASCII values
tmp_dir = self.mkdtemp()
my_file = os.path.join(tmp_dir, 'f')
PKG_INFO = os.path.join(os.path.dirname(__file__),
'SETUPTOOLS-PKG-INFO2')
with open(PKG_INFO, 'r', encoding='utf-8') as f:
content = f.read()
metadata.read_file(StringIO(content))
self.assertEqual(metadata['Metadata-Version'], '1.1')
metadata = Metadata(mapping={'author': 'Mister Café',
'name': 'my.project',
'author': 'Café Junior',
'summary': 'Café torréfié',
'description': 'Héhéhé',
'keywords': ['café', 'coffee']})
metadata.write(my_file)
# Update the _fields dict directly to prevent 'Metadata-Version'
# from being updated by the _set_best_version() method.
metadata._fields['Metadata-Version'] = '1.618'
self.assertRaises(MetadataUnrecognizedVersionError, metadata.keys)
# the file should use UTF-8
metadata2 = Metadata()
with open(my_file, encoding='utf-8') as fp:
metadata2.read_file(fp)
def test_warnings(self):
metadata = Metadata()
# XXX when keywords are not defined, metadata will have
# 'Keywords': [] but metadata2 will have 'Keywords': ['']
# because of a value.split(',') in Metadata.get
self.assertEqual(metadata.items(), metadata2.items())
# these should raise a warning
values = (('Requires-Dist', 'Funky (Groovie)'),
('Requires-Python', '1-4'))
# ASCII also works, it's a subset of UTF-8
metadata = Metadata(mapping={'author': 'Mister Cafe',
'name': 'my.project',
'author': 'Cafe Junior',
'summary': 'Cafe torrefie',
'description': 'Hehehe'})
metadata.write(my_file)
for name, value in values:
metadata.set(name, value)
metadata2 = Metadata()
with open(my_file, encoding='utf-8') as fp:
metadata2.read_file(fp)
# we should have a certain amount of warnings
self.assertEqual(len(self.get_logs()), 2)
def test_metadata_read_write(self):
PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
metadata = Metadata(PKG_INFO)
out = StringIO()
metadata.write_file(out)
def test_multiple_predicates(self):
metadata = Metadata()
out.seek(0)
res = Metadata()
res.read_file(out)
self.assertEqual(metadata.values(), res.values())
# see for "3" instead of "3.0" ???
# its seems like the MINOR VERSION can be omitted
metadata['Requires-Python'] = '>=2.6, <3.0'
metadata['Requires-Dist'] = ['Foo (>=2.6, <3.0)']
self.assertEqual([], self.get_logs(logging.WARNING))
def test_project_url(self):
metadata = Metadata()
metadata['Project-URL'] = [('one', 'http://ok')]
self.assertEqual(metadata['Project-URL'],
[('one', 'http://ok')])
self.assertEqual(metadata['Metadata-Version'], '1.2')
#### Test checks
def test_check_version(self):
metadata = Metadata()
@ -238,39 +237,216 @@ class MetadataTestCase(LoggingCatcher,
metadata['Requires-dist'] = ['Foo (a)']
metadata['Obsoletes-dist'] = ['Foo (a)']
metadata['Provides-dist'] = ['Foo (a)']
if metadata.docutils_support:
missing, warnings = metadata.check()
self.assertEqual(len(warnings), 4)
metadata.docutils_support = False
missing, warnings = metadata.check()
self.assertEqual(len(warnings), 4)
def test_best_choice(self):
metadata = Metadata()
metadata['Version'] = '1.0'
#### Test fields and metadata versions
def test_metadata_versions(self):
metadata = Metadata(mapping={'name': 'project', 'version': '1.0'})
self.assertEqual(metadata['Metadata-Version'],
PKG_INFO_PREFERRED_VERSION)
self.assertNotIn('Provides', metadata)
self.assertNotIn('Requires', metadata)
self.assertNotIn('Obsoletes', metadata)
metadata['Classifier'] = ['ok']
self.assertEqual(metadata['Metadata-Version'], '1.1')
metadata = Metadata()
metadata['Download-URL'] = 'ok'
self.assertEqual(metadata['Metadata-Version'], '1.1')
metadata = Metadata()
metadata['Obsoletes'] = 'ok'
self.assertEqual(metadata['Metadata-Version'], '1.1')
del metadata['Obsoletes']
metadata['Obsoletes-Dist'] = 'ok'
self.assertEqual(metadata['Metadata-Version'], '1.2')
def test_project_urls(self):
# project-url is a bit specific, make sure we write it
# properly in PKG-INFO
metadata = Metadata()
metadata['Version'] = '1.0'
metadata['Project-Url'] = [('one', 'http://ok')]
self.assertEqual(metadata['Project-Url'], [('one', 'http://ok')])
file_ = StringIO()
metadata.write_file(file_)
file_.seek(0)
res = file_.read().split('\n')
self.assertIn('Project-URL: one,http://ok', res)
self.assertRaises(MetadataConflictError, metadata.set,
'Obsoletes', 'ok')
file_.seek(0)
del metadata['Obsoletes']
del metadata['Obsoletes-Dist']
metadata['Version'] = '1'
self.assertEqual(metadata['Metadata-Version'], '1.0')
# make sure the _best_version function works okay with
# non-conflicting fields from 1.1 and 1.2 (i.e. we want only the
# requires/requires-dist and co. pairs to cause a conflict, not all
# fields in _314_MARKERS)
metadata = Metadata()
metadata.read_file(file_)
metadata['Requires-Python'] = '3'
metadata['Classifier'] = ['Programming language :: Python :: 3']
self.assertEqual(metadata['Metadata-Version'], '1.2')
PKG_INFO = os.path.join(os.path.dirname(__file__),
'SETUPTOOLS-PKG-INFO')
metadata = Metadata(PKG_INFO)
self.assertEqual(metadata['Metadata-Version'], '1.0')
PKG_INFO = os.path.join(os.path.dirname(__file__),
'SETUPTOOLS-PKG-INFO2')
metadata = Metadata(PKG_INFO)
self.assertEqual(metadata['Metadata-Version'], '1.1')
# Update the _fields dict directly to prevent 'Metadata-Version'
# from being updated by the _set_best_version() method.
metadata._fields['Metadata-Version'] = '1.618'
self.assertRaises(MetadataUnrecognizedVersionError, metadata.keys)
def test_version(self):
Metadata(mapping={'author': 'xxx',
'name': 'xxx',
'version': 'xxx',
'home-page': 'xxxx'})
logs = self.get_logs(logging.WARNING)
self.assertEqual(1, len(logs))
self.assertIn('not a valid version', logs[0])
def test_description(self):
PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
with open(PKG_INFO, 'r', encoding='utf-8') as f:
content = f.read() % sys.platform
metadata = Metadata()
metadata.read_file(StringIO(content))
# see if we can read the description now
DESC = os.path.join(os.path.dirname(__file__), 'LONG_DESC.txt')
with open(DESC) as f:
wanted = f.read()
self.assertEqual(wanted, metadata['Description'])
# save the file somewhere and make sure we can read it back
out = StringIO()
metadata.write_file(out)
out.seek(0)
out.seek(0)
metadata = Metadata()
metadata.read_file(out)
self.assertEqual(wanted, metadata['Description'])
def test_description_folding(self):
# make sure the indentation is preserved
out = StringIO()
desc = dedent("""\
example::
We start here
and continue here
and end here.
""")
metadata = Metadata()
metadata['description'] = desc
metadata.write_file(out)
folded_desc = desc.replace('\n', '\n' + (7 * ' ') + '|')
self.assertIn(folded_desc, out.getvalue())
def test_project_url(self):
metadata = Metadata()
metadata['Project-URL'] = [('one', 'http://ok')]
self.assertEqual(metadata['Project-URL'], [('one', 'http://ok')])
self.assertEqual(metadata['Metadata-Version'], '1.2')
# make sure this particular field is handled properly when written
fp = StringIO()
metadata.write_file(fp)
self.assertIn('Project-URL: one,http://ok', fp.getvalue().split('\n'))
fp.seek(0)
metadata = Metadata()
metadata.read_file(fp)
self.assertEqual(metadata['Project-Url'], [('one', 'http://ok')])
# TODO copy tests for v1.1 requires, obsoletes and provides from distutils
# (they're useless but we support them so we should test them anyway)
def test_provides_dist(self):
fields = {'name': 'project',
'version': '1.0',
'provides_dist': ['project', 'my.project']}
metadata = Metadata(mapping=fields)
self.assertEqual(metadata['Provides-Dist'],
['project', 'my.project'])
self.assertEqual(metadata['Metadata-Version'], '1.2', metadata)
self.assertNotIn('Requires', metadata)
self.assertNotIn('Obsoletes', metadata)
@unittest.skip('needs to be implemented')
def test_provides_illegal(self):
# TODO check the versions (like distutils does for old provides field)
self.assertRaises(ValueError, Metadata,
mapping={'name': 'project',
'version': '1.0',
'provides_dist': ['my.pkg (splat)']})
def test_requires_dist(self):
fields = {'name': 'project',
'version': '1.0',
'requires_dist': ['other', 'another (==1.0)']}
metadata = Metadata(mapping=fields)
self.assertEqual(metadata['Requires-Dist'],
['other', 'another (==1.0)'])
self.assertEqual(metadata['Metadata-Version'], '1.2')
self.assertNotIn('Provides', metadata)
self.assertEqual(metadata['Requires-Dist'],
['other', 'another (==1.0)'])
self.assertNotIn('Obsoletes', metadata)
# make sure write_file uses one RFC 822 header per item
fp = StringIO()
metadata.write_file(fp)
lines = fp.getvalue().split('\n')
self.assertIn('Requires-Dist: other', lines)
self.assertIn('Requires-Dist: another (==1.0)', lines)
# test warnings for invalid version predicates
# XXX this would cause no warnings if we used update (or the mapping
# argument of the constructor), see comment in Metadata.update
metadata = Metadata()
metadata['Requires-Dist'] = 'Funky (Groovie)'
metadata['Requires-Python'] = '1-4'
self.assertEqual(len(self.get_logs()), 2)
# test multiple version predicates
metadata = Metadata()
# XXX check PEP and see if 3 == 3.0
metadata['Requires-Python'] = '>=2.6, <3.0'
metadata['Requires-Dist'] = ['Foo (>=2.6, <3.0)']
self.assertEqual([], self.get_logs(logging.WARNING))
@unittest.skip('needs to be implemented')
def test_requires_illegal(self):
self.assertRaises(ValueError, Metadata,
mapping={'name': 'project',
'version': '1.0',
'requires': ['my.pkg (splat)']})
def test_obsoletes_dist(self):
fields = {'name': 'project',
'version': '1.0',
'obsoletes_dist': ['other', 'another (<1.0)']}
metadata = Metadata(mapping=fields)
self.assertEqual(metadata['Obsoletes-Dist'],
['other', 'another (<1.0)'])
self.assertEqual(metadata['Metadata-Version'], '1.2')
self.assertNotIn('Provides', metadata)
self.assertNotIn('Requires', metadata)
self.assertEqual(metadata['Obsoletes-Dist'],
['other', 'another (<1.0)'])
@unittest.skip('needs to be implemented')
def test_obsoletes_illegal(self):
self.assertRaises(ValueError, Metadata,
mapping={'name': 'project',
'version': '1.0',
'obsoletes': ['my.pkg (splat)']})
def test_suite():
return unittest.makeSuite(MetadataTestCase)

View File

@ -3,16 +3,16 @@
import os
import sys
import shutil
from tempfile import mkstemp
from io import StringIO
from packaging import install
from packaging.tests import unittest, support, TESTFN
from packaging.run import main
from test.script_helper import assert_python_ok
# setup script that uses __file__
setup_using___file__ = """\
__file__
from packaging.run import setup
@ -20,7 +20,6 @@ setup()
"""
setup_prints_cwd = """\
import os
print os.getcwd()
@ -29,11 +28,12 @@ setup()
"""
class CoreTestCase(support.TempdirManager, support.LoggingCatcher,
class RunTestCase(support.TempdirManager,
support.LoggingCatcher,
unittest.TestCase):
def setUp(self):
super(CoreTestCase, self).setUp()
super(RunTestCase, self).setUp()
self.old_stdout = sys.stdout
self.cleanup_testfn()
self.old_argv = sys.argv, sys.argv[:]
@ -43,7 +43,7 @@ class CoreTestCase(support.TempdirManager, support.LoggingCatcher,
self.cleanup_testfn()
sys.argv = self.old_argv[0]
sys.argv[:] = self.old_argv[1]
super(CoreTestCase, self).tearDown()
super(RunTestCase, self).tearDown()
def cleanup_testfn(self):
path = TESTFN
@ -77,9 +77,16 @@ class CoreTestCase(support.TempdirManager, support.LoggingCatcher,
os.chmod(install_path, old_mod)
install.get_path = old_get_path
def test_show_help(self):
# smoke test, just makes sure some help is displayed
status, out, err = assert_python_ok('-m', 'packaging.run', '--help')
self.assertEqual(status, 0)
self.assertGreater(out, b'')
self.assertEqual(err, b'')
def test_suite():
return unittest.makeSuite(CoreTestCase)
return unittest.makeSuite(RunTestCase)
if __name__ == "__main__":
unittest.main(defaultTest="test_suite")

View File

@ -279,6 +279,10 @@ Library
- Issue #12959: Add collections.ChainMap to collections.__all__.
- Issue #8933: distutils' PKG-INFO files and packaging's METADATA files will
now correctly report Metadata-Version: 1.1 instead of 1.0 if a Classifier or
Download-URL field is present.
- Issue #12567: Add curses.unget_wch() function. Push a character so the next
get_wch() will return it.