From fce67fcd84f7ab66dde11646eead6378698c04b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 10 Sep 2011 01:34:44 +0200 Subject: Slight cleanup in distutils test_dist. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have tests to add in this file and it’s always nice to start from a clean base. --- Lib/distutils/tests/test_dist.py | 98 +++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 51 deletions(-) diff --git a/Lib/distutils/tests/test_dist.py b/Lib/distutils/tests/test_dist.py index a20d6c8..7050cbe 100644 --- a/Lib/distutils/tests/test_dist.py +++ b/Lib/distutils/tests/test_dist.py @@ -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', - 'options': {}}) - finally: - warnings.warn = old_warn + dist = Distribution(attrs={'author': 'xxx', 'name': 'xxx', + 'version': 'xxx', 'url': 'xxxx', + 'options': {}}) 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,20 @@ 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_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 +282,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 +311,8 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, output = [line for line in s.getvalue().split('\n') if line.strip() != ''] - self.assertTrue(len(output) > 0) - - 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} + self.assertTrue(output) - 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() -- cgit v0.12 From 13e8c8e7216ff52b92d9ba51125b939c021e26d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 10 Sep 2011 01:51:40 +0200 Subject: =?UTF-8?q?Fix=20determination=20of=20Metadata=20version=20(#8933)?= =?UTF-8?q?.=20=20Patch=20by=20Filip=20Gruszczy=C5=84ski.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/distutils/dist.py | 3 ++- Lib/distutils/tests/test_dist.py | 14 ++++++++++++++ Misc/NEWS | 4 ++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Lib/distutils/dist.py b/Lib/distutils/dist.py index 8ca5b6f..69825f2 100644 --- a/Lib/distutils/dist.py +++ b/Lib/distutils/dist.py @@ -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) diff --git a/Lib/distutils/tests/test_dist.py b/Lib/distutils/tests/test_dist.py index 7050cbe..8aaae88 100644 --- a/Lib/distutils/tests/test_dist.py +++ b/Lib/distutils/tests/test_dist.py @@ -244,6 +244,20 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, "version": "1.0", "obsoletes": ["my.pkg (splat)"]}) + 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:: diff --git a/Misc/NEWS b/Misc/NEWS index 401e022..12ac26e 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -25,6 +25,10 @@ Core and Builtins Library ------- +- Issue #8933: distutils' PKG-INFO files will now correctly report + Metadata-Version: 1.1 instead of 1.0 if a Classifier or Download-URL field is + present. + - Issue #9561: distutils now reads and writes egg-info files using UTF-8, instead of the locale encoding. -- cgit v0.12 From 6bbd775377ae4a6e87ccce990750ac02afe83573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 10 Sep 2011 05:18:20 +0200 Subject: Consolidate tests for packaging.metadata. New tests were added in test_metadata and old tests inherited from distutils were still in test_dist, so I moved them into test_metadata (except for one which was more at home in test_run) and merged duplicates. I also added some skips to lure contributors , optimized the Metadata.update method a trifle, and added notes about a number of issues. A note: The tests in test_dist used to dump the Metadata objects to a file in the METADATA format and look for strings in its contents; I updated them to use the mapping API of Metadata instead. For some fields with special writing rules, I have added tests to ensure my conversion did not lose anything. --- Lib/packaging/metadata.py | 14 +- Lib/packaging/tests/test_dist.py | 252 +++------------------- Lib/packaging/tests/test_metadata.py | 395 +++++++++++++++++++++++++---------- Lib/packaging/tests/test_run.py | 23 +- 4 files changed, 333 insertions(+), 351 deletions(-) diff --git a/Lib/packaging/metadata.py b/Lib/packaging/metadata.py index 104600b..7d7fc6b 100644 --- a/Lib/packaging/metadata.py +++ b/Lib/packaging/metadata.py @@ -354,11 +354,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 +377,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.""" diff --git a/Lib/packaging/tests/test_dist.py b/Lib/packaging/tests/test_dist.py index 1d78fa9..bd86245 100644 --- a/Lib/packaging/tests/test_dist.py +++ b/Lib/packaging/tests/test_dist.py @@ -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', - 'options': {}}) + 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") diff --git a/Lib/packaging/tests/test_metadata.py b/Lib/packaging/tests/test_metadata.py index 904019c..b79f566 100644 --- a/Lib/packaging/tests/test_metadata.py +++ b/Lib/packaging/tests/test_metadata.py @@ -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') - self.assertEqual(list(metadata), list(metadata.keys())) - - def test_versions(self): - 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') - - self.assertRaises(MetadataConflictError, metadata.set, - 'Obsoletes', 'ok') - - del metadata['Obsoletes'] - del metadata['Obsoletes-Dist'] - metadata['Version'] = '1' - self.assertEqual(metadata['Metadata-Version'], '1.0') - - 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') + # make sure update method checks values like the set method does + metadata.update({'version': '1--2'}) + self.assertEqual(len(self.get_logs()), 1) - 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') - - # 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_warnings(self): - metadata = Metadata() - - # these should raise a warning - values = (('Requires-Dist', 'Funky (Groovie)'), - ('Requires-Python', '1-4')) - - for name, value in values: - metadata.set(name, value) - - # we should have a certain amount of warnings - self.assertEqual(len(self.get_logs()), 2) + # 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_multiple_predicates(self): - metadata = Metadata() + 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']} + + metadata = Metadata(mapping=fields) + PKG_INFO = StringIO() + metadata.write_file(PKG_INFO) + PKG_INFO.seek(0) + + metadata = Metadata(fileobj=PKG_INFO) + + 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']) + + def test_write_metadata(self): + # check support of non-ASCII values + tmp_dir = self.mkdtemp() + my_file = os.path.join(tmp_dir, 'f') + + 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) + + # the file should use UTF-8 + metadata2 = Metadata() + with open(my_file, encoding='utf-8') as fp: + metadata2.read_file(fp) + + # 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()) + + # 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) + + metadata2 = Metadata() + with open(my_file, encoding='utf-8') as fp: + metadata2.read_file(fp) - # 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)'] + 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) - self.assertEqual([], self.get_logs(logging.WARNING)) + out.seek(0) + res = Metadata() + res.read_file(out) + self.assertEqual(metadata.values(), res.values()) - 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,203 @@ 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.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) + metadata['Obsoletes'] = 'ok' + self.assertEqual(metadata['Metadata-Version'], '1.1') + + del metadata['Obsoletes'] + metadata['Obsoletes-Dist'] = 'ok' + self.assertEqual(metadata['Metadata-Version'], '1.2') + + self.assertRaises(MetadataConflictError, metadata.set, + 'Obsoletes', 'ok') + + del metadata['Obsoletes'] + del metadata['Obsoletes-Dist'] + metadata['Version'] = '1' + self.assertEqual(metadata['Metadata-Version'], '1.0') + + 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) - file_.seek(0) + out.seek(0) metadata = Metadata() - metadata.read_file(file_) + 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) diff --git a/Lib/packaging/tests/test_run.py b/Lib/packaging/tests/test_run.py index cb576b7..6a3b8fb 100644 --- a/Lib/packaging/tests/test_run.py +++ b/Lib/packaging/tests/test_run.py @@ -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, - unittest.TestCase): +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") -- cgit v0.12 From e6db7a3a298e3bfe5064de2298ba6cdcb470793e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 10 Sep 2011 05:22:48 +0200 Subject: Fix determination of Metadata version in packaging (#8933). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original patch by Filip Gruszczyński. --- Lib/packaging/metadata.py | 3 ++- Lib/packaging/tests/test_metadata.py | 15 ++++++++++++++- Misc/NEWS | 6 +++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Lib/packaging/metadata.py b/Lib/packaging/metadata.py index 7d7fc6b..dbb53b2 100644 --- a/Lib/packaging/metadata.py +++ b/Lib/packaging/metadata.py @@ -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', diff --git a/Lib/packaging/tests/test_metadata.py b/Lib/packaging/tests/test_metadata.py index b79f566..68b3d97 100644 --- a/Lib/packaging/tests/test_metadata.py +++ b/Lib/packaging/tests/test_metadata.py @@ -251,7 +251,11 @@ class MetadataTestCase(LoggingCatcher, self.assertNotIn('Obsoletes', metadata) metadata['Classifier'] = ['ok'] - self.assertEqual(metadata['Metadata-Version'], '1.2') + 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' @@ -269,6 +273,15 @@ class MetadataTestCase(LoggingCatcher, 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['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) diff --git a/Misc/NEWS b/Misc/NEWS index 67b8cf8..3d35823 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -274,9 +274,9 @@ Core and Builtins Library ------- -- Issue #8933: distutils' PKG-INFO files will now correctly report - Metadata-Version: 1.1 instead of 1.0 if a Classifier or Download-URL field is - present. +- 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. -- cgit v0.12 From c8f9c81cfa7fb46d3c0be9e9e5f18bfda9247984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 10 Sep 2011 18:10:23 +0200 Subject: Fix usage of dry-run in packaging bdist_wininst and install_distinfo. In dry-run mode, packaging commands should log the same info as in real operation and should collect the same files in self.outputs, so that users can run a command in verbose and dry-run mode to see exactly what operations will be done in the real run. --- Lib/packaging/command/bdist_wininst.py | 5 +-- Lib/packaging/command/install_distinfo.py | 74 +++++++++++++++---------------- 2 files changed, 39 insertions(+), 40 deletions(-) diff --git a/Lib/packaging/command/bdist_wininst.py b/Lib/packaging/command/bdist_wininst.py index 7dbb39b..4ba2b2d 100644 --- a/Lib/packaging/command/bdist_wininst.py +++ b/Lib/packaging/command/bdist_wininst.py @@ -186,9 +186,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: + logger.info('removing %s', self.bdist_dir) + if not self.dry_run: rmtree(self.bdist_dir) def get_inidata(self): diff --git a/Lib/packaging/command/install_distinfo.py b/Lib/packaging/command/install_distinfo.py index 0e1577e..1f48eed 100644 --- a/Lib/packaging/command/install_distinfo.py +++ b/Lib/packaging/command/install_distinfo.py @@ -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,56 +70,56 @@ 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 + target = self.distinfo_dir - if os.path.isdir(target) and not os.path.islink(target): + 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,), - "removing " + target) + elif os.path.exists(target): + self.execute(os.unlink, (self.distinfo_dir,), + "removing " + target) - self.execute(os.makedirs, (target,), "creating " + target) + 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.outfiles.append(metadata_path) + metadata_path = os.path.join(self.distinfo_dir, 'METADATA') + 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) + 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) + self.outfiles.append(installer_path) - if self.requested: - requested_path = os.path.join(self.distinfo_dir, 'REQUESTED') - logger.info('creating %s', requested_path) + 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) + 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) + 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.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', @@ -141,7 +141,7 @@ class install_distinfo(Command): # add the RECORD file itself writer.writerow((record_path, '', '')) - self.outfiles.append(record_path) + self.outfiles.append(record_path) def get_outputs(self): return self.outfiles -- cgit v0.12 From 030cfe26a336826d08362cd60c8ea4be7775844b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 10 Sep 2011 18:10:58 +0200 Subject: Use bytes regex instead of decoding whole pages --- Lib/packaging/pypi/simple.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/Lib/packaging/pypi/simple.py b/Lib/packaging/pypi/simple.py index 710355d..76aad02 100644 --- a/Lib/packaging/pypi/simple.py +++ b/Lib/packaging/pypi/simple.py @@ -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(']*>(%s)' % name, re.I) - matching_projects = [] + if '*' in name: + name.replace('*', '.*') + else: + name = "%s%s%s" % ('*.?', name, '*.?') + name = name.replace('*', '[^<]*') # avoid matching end tag + pattern = (']*>(%s)' % 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 -- cgit v0.12 From c6d52eddaaa5fb8b8091480103188beda35a9f60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 10 Sep 2011 18:14:08 +0200 Subject: Fix usage of bytes in packaging's bdist_wininst. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is copied from the namesake distutils command; there is no automated test, so buildbots won’t call for my head this time, but it should be okay as Python 3 users have tested the distutils command. --- Lib/packaging/command/bdist_wininst.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Lib/packaging/command/bdist_wininst.py b/Lib/packaging/command/bdist_wininst.py index 4ba2b2d..6c1e225 100644 --- a/Lib/packaging/command/bdist_wininst.py +++ b/Lib/packaging/command/bdist_wininst.py @@ -1,7 +1,5 @@ """Create an executable installer for Windows.""" -# FIXME synchronize bytes/str use with same file in distutils - import sys import os @@ -264,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 -- cgit v0.12 From 1f2bcd35bb49fc17ee7842497ee5e46f9d2f5bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 10 Sep 2011 18:22:04 +0200 Subject: =?UTF-8?q?Don=E2=80=99t=20let=20invalid=20line=20in=20setup.cfg?= =?UTF-8?q?=20pass=20silently?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/packaging/config.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/packaging/config.py b/Lib/packaging/config.py index e02800e..366faea 100644 --- a/Lib/packaging/config.py +++ b/Lib/packaging/config.py @@ -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() -- cgit v0.12 From fb7d24492f89ed1a3e076cca9f759c5f40ba2905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 10 Sep 2011 18:22:31 +0200 Subject: Remove obsolete comment (yes, build_ext supports C++) --- Lib/packaging/command/build_ext.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Lib/packaging/command/build_ext.py b/Lib/packaging/command/build_ext.py index fa8d11f..2bffae3 100644 --- a/Lib/packaging/command/build_ext.py +++ b/Lib/packaging/command/build_ext.py @@ -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 -- cgit v0.12 From cde65768203618f084592fa5441a4c92618ed7e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Mon, 12 Sep 2011 16:45:38 +0200 Subject: =?UTF-8?q?Remove=20unneeded=20--all=20option=20of=20=E2=80=9Cpyse?= =?UTF-8?q?tup=20list=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The command without arguments already prints all installed distributions found. In addition, change “releases” for “projects” in the description of the list action. Strictly speaking, one installed distribution satisfies the requirement for a release (i.e. version) of a project, but as currently only one release per project can be installed at a time, the two are somewhat equivalent, and “project” is more understandable in help texts (which call their argument “dist”, by the way..) --- Doc/install/pysetup.rst | 17 +++++++++-------- Lib/packaging/run.py | 20 ++++++++------------ Lib/packaging/tests/test_command_install_data.py | 1 + 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/Doc/install/pysetup.rst b/Doc/install/pysetup.rst index 08ba08e..f6f1f83 100644 --- a/Doc/install/pysetup.rst +++ b/Doc/install/pysetup.rst @@ -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: diff --git a/Lib/packaging/run.py b/Lib/packaging/run.py index ef20f35..5affb17 100644 --- a/Lib/packaging/run.py +++ b/Lib/packaging/run.py @@ -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), diff --git a/Lib/packaging/tests/test_command_install_data.py b/Lib/packaging/tests/test_command_install_data.py index 0486427..35ce847 100644 --- a/Lib/packaging/tests/test_command_install_data.py +++ b/Lib/packaging/tests/test_command_install_data.py @@ -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]) -- cgit v0.12