diff options
Diffstat (limited to 'Lib/packaging/tests/test_util.py')
-rw-r--r-- | Lib/packaging/tests/test_util.py | 1013 |
1 files changed, 1013 insertions, 0 deletions
diff --git a/Lib/packaging/tests/test_util.py b/Lib/packaging/tests/test_util.py new file mode 100644 index 0000000..7f7ed18 --- /dev/null +++ b/Lib/packaging/tests/test_util.py @@ -0,0 +1,1013 @@ +"""Tests for packaging.util.""" +import os +import sys +import time +import logging +import tempfile +import textwrap +import warnings +import subprocess +from io import StringIO + +from packaging.errors import ( + PackagingPlatformError, PackagingFileError, + PackagingExecError, InstallationException) +from packaging import util +from packaging.dist import Distribution +from packaging.util import ( + convert_path, change_root, split_quoted, strtobool, run_2to3, + get_compiler_versions, _MAC_OS_X_LD_VERSION, byte_compile, find_packages, + spawn, get_pypirc_path, generate_pypirc, read_pypirc, resolve_name, iglob, + RICH_GLOB, egginfo_to_distinfo, is_setuptools, is_distutils, is_packaging, + get_install_method, cfg_to_args, generate_setup_py, encode_multipart) + +from packaging.tests import support, unittest +from packaging.tests.test_config import SETUP_CFG +from test.script_helper import assert_python_ok, assert_python_failure + + +PYPIRC = """\ +[distutils] +index-servers = + pypi + server1 + +[pypi] +username:me +password:xxxx + +[server1] +repository:http://example.com +username:tarek +password:secret +""" + +PYPIRC_OLD = """\ +[server-login] +username:tarek +password:secret +""" + +WANTED = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:tarek +password:xxx +""" + +EXPECTED_MULTIPART_OUTPUT = [ + b'---x', + b'Content-Disposition: form-data; name="username"', + b'', + b'wok', + b'---x', + b'Content-Disposition: form-data; name="password"', + b'', + b'secret', + b'---x', + b'Content-Disposition: form-data; name="picture"; filename="wok.png"', + b'', + b'PNG89', + b'---x--', + b'', +] + + +class FakePopen: + test_class = None + + def __init__(self, args, bufsize=0, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=False, + shell=False, cwd=None, env=None, universal_newlines=False, + startupinfo=None, creationflags=0, + restore_signals=True, start_new_session=False, + pass_fds=()): + if isinstance(args, str): + args = args.split() + self.cmd = args[0] + exes = self.test_class._exes + if self.cmd not in exes: + # we don't want to call the system, returning an empty + # output so it doesn't match + self.stdout = StringIO() + self.stderr = StringIO() + else: + self.stdout = StringIO(exes[self.cmd]) + self.stderr = StringIO() + + def communicate(self, input=None, timeout=None): + return self.stdout.read(), self.stderr.read() + + def wait(self, timeout=None): + return 0 + + +class UtilTestCase(support.EnvironRestorer, + support.TempdirManager, + support.LoggingCatcher, + unittest.TestCase): + + restore_environ = ['HOME', 'PLAT'] + + def setUp(self): + super(UtilTestCase, self).setUp() + self.addCleanup(os.chdir, os.getcwd()) + tempdir = self.mkdtemp() + self.rc = os.path.join(tempdir, '.pypirc') + os.environ['HOME'] = tempdir + os.chdir(tempdir) + # saving the environment + self.name = os.name + self.platform = sys.platform + self.version = sys.version + self.sep = os.sep + self.join = os.path.join + self.isabs = os.path.isabs + self.splitdrive = os.path.splitdrive + + # patching os.uname + if hasattr(os, 'uname'): + self.uname = os.uname + self._uname = os.uname() + else: + self.uname = None + self._uname = None + os.uname = self._get_uname + + def _get_uname(self): + return self._uname + + def tearDown(self): + # getting back the environment + os.name = self.name + sys.platform = self.platform + sys.version = self.version + os.sep = self.sep + os.path.join = self.join + os.path.isabs = self.isabs + os.path.splitdrive = self.splitdrive + if self.uname is not None: + os.uname = self.uname + else: + del os.uname + super(UtilTestCase, self).tearDown() + + def mock_popen(self): + self.old_find_executable = util.find_executable + util.find_executable = self._find_executable + self._exes = {} + self.old_popen = subprocess.Popen + self.old_stdout = sys.stdout + self.old_stderr = sys.stderr + FakePopen.test_class = self + subprocess.Popen = FakePopen + self.addCleanup(self.unmock_popen) + + def unmock_popen(self): + util.find_executable = self.old_find_executable + subprocess.Popen = self.old_popen + sys.stdout = self.old_stdout + sys.stderr = self.old_stderr + + def test_set_platform(self): + self.addCleanup(util.set_platform, util.get_platform()) + util.set_platform("fake") + self.assertEqual("fake", util.get_platform()) + + def test_convert_path(self): + # linux/mac + os.sep = '/' + + def _join(path): + return '/'.join(path) + os.path.join = _join + + self.assertEqual(convert_path('/home/to/my/stuff'), + '/home/to/my/stuff') + + # win + os.sep = '\\' + + def _join(*path): + return '\\'.join(path) + os.path.join = _join + + self.assertRaises(ValueError, convert_path, '/home/to/my/stuff') + self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/') + + self.assertEqual(convert_path('home/to/my/stuff'), + 'home\\to\\my\\stuff') + self.assertEqual(convert_path('.'), + os.curdir) + + def test_change_root(self): + # linux/mac + os.name = 'posix' + + def _isabs(path): + return path[0] == '/' + os.path.isabs = _isabs + + def _join(*path): + return '/'.join(path) + os.path.join = _join + + self.assertEqual(change_root('/root', '/old/its/here'), + '/root/old/its/here') + self.assertEqual(change_root('/root', 'its/here'), + '/root/its/here') + + # windows + os.name = 'nt' + + def _isabs(path): + return path.startswith('c:\\') + os.path.isabs = _isabs + + def _splitdrive(path): + if path.startswith('c:'): + return '', path.replace('c:', '') + return '', path + os.path.splitdrive = _splitdrive + + def _join(*path): + return '\\'.join(path) + os.path.join = _join + + self.assertEqual(change_root('c:\\root', 'c:\\old\\its\\here'), + 'c:\\root\\old\\its\\here') + self.assertEqual(change_root('c:\\root', 'its\\here'), + 'c:\\root\\its\\here') + + # BugsBunny os (it's a great os) + os.name = 'BugsBunny' + self.assertRaises(PackagingPlatformError, + change_root, 'c:\\root', 'its\\here') + + # XXX platforms to be covered: os2, mac + + def test_split_quoted(self): + self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'), + ['one', 'two', 'three', 'four']) + + def test_strtobool(self): + yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1') + no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N') + + for y in yes: + self.assertTrue(strtobool(y)) + + for n in no: + self.assertFalse(strtobool(n)) + + def test_find_exe_version(self): + # the ld version scheme under MAC OS is: + # ^@(#)PROGRAM:ld PROJECT:ld64-VERSION + # + # where VERSION is a 2-digit number for major + # revisions. For instance under Leopard, it's + # currently 77 + # + # Dots are used when branching is done. + # + # The SnowLeopard ld64 is currently 95.2.12 + + for output, version in (('@(#)PROGRAM:ld PROJECT:ld64-77', '77'), + ('@(#)PROGRAM:ld PROJECT:ld64-95.2.12', + '95.2.12')): + result = _MAC_OS_X_LD_VERSION.search(output) + self.assertEqual(result.group(1), version) + + def _find_executable(self, name): + if name in self._exes: + return name + return None + + def test_get_compiler_versions(self): + self.mock_popen() + # get_versions calls distutils.spawn.find_executable on + # 'gcc', 'ld' and 'dllwrap' + self.assertEqual(get_compiler_versions(), (None, None, None)) + + # Let's fake we have 'gcc' and it returns '3.4.5' + self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF' + res = get_compiler_versions() + self.assertEqual(str(res[0]), '3.4.5') + + # and let's see what happens when the version + # doesn't match the regular expression + # (\d+\.\d+(\.\d+)*) + self._exes['gcc'] = 'very strange output' + res = get_compiler_versions() + self.assertEqual(res[0], None) + + # same thing for ld + if sys.platform != 'darwin': + self._exes['ld'] = 'GNU ld version 2.17.50 20060824' + res = get_compiler_versions() + self.assertEqual(str(res[1]), '2.17.50') + self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77' + res = get_compiler_versions() + self.assertEqual(res[1], None) + else: + self._exes['ld'] = 'GNU ld version 2.17.50 20060824' + res = get_compiler_versions() + self.assertEqual(res[1], None) + self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77' + res = get_compiler_versions() + self.assertEqual(str(res[1]), '77') + + # and dllwrap + self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF' + res = get_compiler_versions() + self.assertEqual(str(res[2]), '2.17.50') + self._exes['dllwrap'] = 'Cheese Wrap' + res = get_compiler_versions() + self.assertEqual(res[2], None) + + def test_byte_compile_under_B(self): + # make sure byte compilation works under -B (dont_write_bytecode) + self.addCleanup(setattr, sys, 'dont_write_bytecode', + sys.dont_write_bytecode) + sys.dont_write_bytecode = True + byte_compile([]) + + def test_newer(self): + self.assertRaises(PackagingFileError, util.newer, 'xxx', 'xxx') + self.newer_f1 = self.mktempfile() + time.sleep(1) + self.newer_f2 = self.mktempfile() + self.assertTrue(util.newer(self.newer_f2.name, self.newer_f1.name)) + + def test_find_packages(self): + # let's create a structure we want to scan: + # + # pkg1 + # __init__ + # pkg2 + # __init__ + # pkg3 + # __init__ + # pkg6 + # __init__ + # pkg4 <--- not a pkg + # pkg8 + # __init__ + # pkg5 + # __init__ + # + root = self.mkdtemp() + pkg1 = os.path.join(root, 'pkg1') + os.makedirs(os.path.join(pkg1, 'pkg2')) + os.makedirs(os.path.join(pkg1, 'pkg3', 'pkg6')) + os.makedirs(os.path.join(pkg1, 'pkg4', 'pkg8')) + os.makedirs(os.path.join(root, 'pkg5')) + self.write_file((pkg1, '__init__.py')) + self.write_file((pkg1, 'pkg2', '__init__.py')) + self.write_file((pkg1, 'pkg3', '__init__.py')) + self.write_file((pkg1, 'pkg3', 'pkg6', '__init__.py')) + self.write_file((pkg1, 'pkg4', 'pkg8', '__init__.py')) + self.write_file((root, 'pkg5', '__init__.py')) + + res = find_packages([root], ['pkg1.pkg2']) + self.assertEqual(sorted(res), + ['pkg1', 'pkg1.pkg3', 'pkg1.pkg3.pkg6', 'pkg5']) + + def test_resolve_name(self): + # test raw module name + tmpdir = self.mkdtemp() + sys.path.append(tmpdir) + self.addCleanup(sys.path.remove, tmpdir) + self.write_file((tmpdir, 'hello.py'), '') + + os.makedirs(os.path.join(tmpdir, 'a', 'b')) + self.write_file((tmpdir, 'a', '__init__.py'), '') + self.write_file((tmpdir, 'a', 'b', '__init__.py'), '') + self.write_file((tmpdir, 'a', 'b', 'c.py'), 'class Foo: pass') + self.write_file((tmpdir, 'a', 'b', 'd.py'), textwrap.dedent("""\ + class FooBar: + class Bar: + def baz(self): + pass + """)) + + # check Python, C and built-in module + self.assertEqual(resolve_name('hello').__name__, 'hello') + self.assertEqual(resolve_name('_csv').__name__, '_csv') + self.assertEqual(resolve_name('sys').__name__, 'sys') + + # test module.attr + self.assertIs(resolve_name('builtins.str'), str) + self.assertIsNone(resolve_name('hello.__doc__')) + self.assertEqual(resolve_name('a.b.c.Foo').__name__, 'Foo') + self.assertEqual(resolve_name('a.b.d.FooBar.Bar.baz').__name__, 'baz') + + # error if module not found + self.assertRaises(ImportError, resolve_name, 'nonexistent') + self.assertRaises(ImportError, resolve_name, 'non.existent') + self.assertRaises(ImportError, resolve_name, 'a.no') + self.assertRaises(ImportError, resolve_name, 'a.b.no') + self.assertRaises(ImportError, resolve_name, 'a.b.no.no') + self.assertRaises(ImportError, resolve_name, 'inva-lid') + + # looking up built-in names is not supported + self.assertRaises(ImportError, resolve_name, 'str') + + # error if module found but not attr + self.assertRaises(ImportError, resolve_name, 'a.b.Spam') + self.assertRaises(ImportError, resolve_name, 'a.b.c.Spam') + + @support.skip_2to3_optimize + def test_run_2to3_on_code(self): + content = "print 'test'" + converted_content = "print('test')" + file_handle = self.mktempfile() + file_name = file_handle.name + file_handle.write(content) + file_handle.flush() + file_handle.seek(0) + run_2to3([file_name]) + new_content = "".join(file_handle.read()) + file_handle.close() + self.assertEqual(new_content, converted_content) + + @support.skip_2to3_optimize + def test_run_2to3_on_doctests(self): + # to check if text files containing doctests only get converted. + content = ">>> print 'test'\ntest\n" + converted_content = ">>> print('test')\ntest\n\n" + file_handle = self.mktempfile() + file_name = file_handle.name + file_handle.write(content) + file_handle.flush() + file_handle.seek(0) + run_2to3([file_name], doctests_only=True) + new_content = "".join(file_handle.readlines()) + file_handle.close() + self.assertEqual(new_content, converted_content) + + @unittest.skipUnless(os.name in ('nt', 'posix'), + 'runs only under posix or nt') + def test_spawn(self): + tmpdir = self.mkdtemp() + + # creating something executable + # through the shell that returns 1 + if os.name == 'posix': + exe = os.path.join(tmpdir, 'foo.sh') + self.write_file(exe, '#!/bin/sh\nexit 1') + os.chmod(exe, 0o777) + else: + exe = os.path.join(tmpdir, 'foo.bat') + self.write_file(exe, 'exit 1') + + os.chmod(exe, 0o777) + self.assertRaises(PackagingExecError, spawn, [exe]) + + # now something that works + if os.name == 'posix': + exe = os.path.join(tmpdir, 'foo.sh') + self.write_file(exe, '#!/bin/sh\nexit 0') + os.chmod(exe, 0o777) + else: + exe = os.path.join(tmpdir, 'foo.bat') + self.write_file(exe, 'exit 0') + + os.chmod(exe, 0o777) + spawn([exe]) # should work without any error + + def test_server_registration(self): + # This test makes sure we know how to: + # 1. handle several sections in .pypirc + # 2. handle the old format + + # new format + self.write_file(self.rc, PYPIRC) + config = read_pypirc() + + config = sorted(config.items()) + expected = [('password', 'xxxx'), ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi'), + ('server', 'pypi'), ('username', 'me')] + self.assertEqual(config, expected) + + # old format + self.write_file(self.rc, PYPIRC_OLD) + config = read_pypirc() + config = sorted(config.items()) + expected = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi'), + ('server', 'server-login'), ('username', 'tarek')] + self.assertEqual(config, expected) + + def test_server_empty_registration(self): + rc = get_pypirc_path() + self.assertFalse(os.path.exists(rc)) + generate_pypirc('tarek', 'xxx') + self.assertTrue(os.path.exists(rc)) + with open(rc) as f: + content = f.read() + self.assertEqual(content, WANTED) + + def test_cfg_to_args(self): + opts = {'description-file': 'README', 'extra-files': '', + 'setup-hooks': 'packaging.tests.test_config.version_hook'} + self.write_file('setup.cfg', SETUP_CFG % opts, encoding='utf-8') + self.write_file('README', 'loooong description') + + with warnings.catch_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + args = cfg_to_args() + # use Distribution to get the contents of the setup.cfg file + dist = Distribution() + dist.parse_config_files() + metadata = dist.metadata + + self.assertEqual(args['name'], metadata['Name']) + # + .dev1 because the test SETUP_CFG also tests a hook function in + # test_config.py for appending to the version string + self.assertEqual(args['version'] + '.dev1', metadata['Version']) + self.assertEqual(args['author'], metadata['Author']) + self.assertEqual(args['author_email'], metadata['Author-Email']) + self.assertEqual(args['maintainer'], metadata['Maintainer']) + self.assertEqual(args['maintainer_email'], + metadata['Maintainer-Email']) + self.assertEqual(args['description'], metadata['Summary']) + self.assertEqual(args['long_description'], metadata['Description']) + self.assertEqual(args['classifiers'], metadata['Classifier']) + self.assertEqual(args['requires'], metadata['Requires-Dist']) + self.assertEqual(args['provides'], metadata['Provides-Dist']) + + self.assertEqual(args['package_dir'].get(''), dist.package_dir) + self.assertEqual(args['packages'], dist.packages) + self.assertEqual(args['scripts'], dist.scripts) + self.assertEqual(args['py_modules'], dist.py_modules) + + def test_generate_setup_py(self): + os.chdir(self.mkdtemp()) + self.write_file('setup.cfg', textwrap.dedent("""\ + [metadata] + name = SPAM + classifier = Programming Language :: Python + """)) + generate_setup_py() + self.assertTrue(os.path.exists('setup.py'), 'setup.py not created') + rc, out, err = assert_python_ok('setup.py', '--name') + self.assertEqual(out, b'SPAM\n') + self.assertEqual(err, b'') + + # a generated setup.py should complain if no setup.cfg is present + os.unlink('setup.cfg') + rc, out, err = assert_python_failure('setup.py', '--name') + self.assertIn(b'setup.cfg', err) + + def test_encode_multipart(self): + fields = [('username', 'wok'), ('password', 'secret')] + files = [('picture', 'wok.png', b'PNG89')] + content_type, body = encode_multipart(fields, files, b'-x') + self.assertEqual(b'multipart/form-data; boundary=-x', content_type) + self.assertEqual(EXPECTED_MULTIPART_OUTPUT, body.split(b'\r\n')) + + +class GlobTestCaseBase(support.TempdirManager, + support.LoggingCatcher, + unittest.TestCase): + + def build_files_tree(self, files): + tempdir = self.mkdtemp() + for filepath in files: + is_dir = filepath.endswith('/') + filepath = os.path.join(tempdir, *filepath.split('/')) + if is_dir: + dirname = filepath + else: + dirname = os.path.dirname(filepath) + if dirname and not os.path.exists(dirname): + os.makedirs(dirname) + if not is_dir: + self.write_file(filepath, 'babar') + return tempdir + + @staticmethod + def os_dependent_path(path): + path = path.rstrip('/').split('/') + return os.path.join(*path) + + def clean_tree(self, spec): + files = [] + for path, includes in spec.items(): + if includes: + files.append(self.os_dependent_path(path)) + return files + + +class GlobTestCase(GlobTestCaseBase): + + def assertGlobMatch(self, glob, spec): + tempdir = self.build_files_tree(spec) + expected = self.clean_tree(spec) + os.chdir(tempdir) + result = list(iglob(glob)) + self.assertCountEqual(expected, result) + + def test_regex_rich_glob(self): + matches = RICH_GLOB.findall( + r"babar aime les {fraises} est les {huitres}") + self.assertEqual(["fraises", "huitres"], matches) + + def test_simple_glob(self): + glob = '*.tp?' + spec = {'coucou.tpl': True, + 'coucou.tpj': True, + 'Donotwant': False} + self.assertGlobMatch(glob, spec) + + def test_simple_glob_in_dir(self): + glob = os.path.join('babar', '*.tp?') + spec = {'babar/coucou.tpl': True, + 'babar/coucou.tpj': True, + 'babar/toto.bin': False, + 'Donotwant': False} + self.assertGlobMatch(glob, spec) + + def test_recursive_glob_head(self): + glob = os.path.join('**', 'tip', '*.t?l') + spec = {'babar/zaza/zuzu/tip/coucou.tpl': True, + 'babar/z/tip/coucou.tpl': True, + 'babar/tip/coucou.tpl': True, + 'babar/zeop/tip/babar/babar.tpl': False, + 'babar/z/tip/coucou.bin': False, + 'babar/toto.bin': False, + 'zozo/zuzu/tip/babar.tpl': True, + 'zozo/tip/babar.tpl': True, + 'Donotwant': False} + self.assertGlobMatch(glob, spec) + + def test_recursive_glob_tail(self): + glob = os.path.join('babar', '**') + spec = {'babar/zaza/': True, + 'babar/zaza/zuzu/': True, + 'babar/zaza/zuzu/babar.xml': True, + 'babar/zaza/zuzu/toto.xml': True, + 'babar/zaza/zuzu/toto.csv': True, + 'babar/zaza/coucou.tpl': True, + 'babar/bubu.tpl': True, + 'zozo/zuzu/tip/babar.tpl': False, + 'zozo/tip/babar.tpl': False, + 'Donotwant': False} + self.assertGlobMatch(glob, spec) + + def test_recursive_glob_middle(self): + glob = os.path.join('babar', '**', 'tip', '*.t?l') + spec = {'babar/zaza/zuzu/tip/coucou.tpl': True, + 'babar/z/tip/coucou.tpl': True, + 'babar/tip/coucou.tpl': True, + 'babar/zeop/tip/babar/babar.tpl': False, + 'babar/z/tip/coucou.bin': False, + 'babar/toto.bin': False, + 'zozo/zuzu/tip/babar.tpl': False, + 'zozo/tip/babar.tpl': False, + 'Donotwant': False} + self.assertGlobMatch(glob, spec) + + def test_glob_set_tail(self): + glob = os.path.join('bin', '*.{bin,sh,exe}') + spec = {'bin/babar.bin': True, + 'bin/zephir.sh': True, + 'bin/celestine.exe': True, + 'bin/cornelius.bat': False, + 'bin/cornelius.xml': False, + 'toto/yurg': False, + 'Donotwant': False} + self.assertGlobMatch(glob, spec) + + def test_glob_set_middle(self): + glob = os.path.join('xml', '{babar,toto}.xml') + spec = {'xml/babar.xml': True, + 'xml/toto.xml': True, + 'xml/babar.xslt': False, + 'xml/cornelius.sgml': False, + 'xml/zephir.xml': False, + 'toto/yurg.xml': False, + 'Donotwant': False} + self.assertGlobMatch(glob, spec) + + def test_glob_set_head(self): + glob = os.path.join('{xml,xslt}', 'babar.*') + spec = {'xml/babar.xml': True, + 'xml/toto.xml': False, + 'xslt/babar.xslt': True, + 'xslt/toto.xslt': False, + 'toto/yurg.xml': False, + 'Donotwant': False} + self.assertGlobMatch(glob, spec) + + def test_glob_all(self): + dirs = '{%s,%s}' % (os.path.join('xml', '*'), + os.path.join('xslt', '**')) + glob = os.path.join(dirs, 'babar.xml') + spec = {'xml/a/babar.xml': True, + 'xml/b/babar.xml': True, + 'xml/a/c/babar.xml': False, + 'xslt/a/babar.xml': True, + 'xslt/b/babar.xml': True, + 'xslt/a/c/babar.xml': True, + 'toto/yurg.xml': False, + 'Donotwant': False} + self.assertGlobMatch(glob, spec) + + def test_invalid_glob_pattern(self): + invalids = [ + 'ppooa**', + 'azzaeaz4**/', + '/**ddsfs', + '**##1e"&e', + 'DSFb**c009', + '{', + '{aaQSDFa', + '}', + 'aQSDFSaa}', + '{**a,', + ',**a}', + '{a**,', + ',b**}', + '{a**a,babar}', + '{bob,b**z}', + ] + for pattern in invalids: + self.assertRaises(ValueError, iglob, pattern) + + +class EggInfoToDistInfoTestCase(support.TempdirManager, + support.LoggingCatcher, + unittest.TestCase): + + def get_metadata_file_paths(self, distinfo_path): + req_metadata_files = ['METADATA', 'RECORD', 'INSTALLER'] + metadata_file_paths = [] + for metadata_file in req_metadata_files: + path = os.path.join(distinfo_path, metadata_file) + metadata_file_paths.append(path) + return metadata_file_paths + + def test_egginfo_to_distinfo_setuptools(self): + distinfo = 'hello-0.1.1-py3.3.dist-info' + egginfo = 'hello-0.1.1-py3.3.egg-info' + dirs = [egginfo] + files = ['hello.py', 'hello.pyc'] + extra_metadata = ['dependency_links.txt', 'entry_points.txt', + 'not-zip-safe', 'PKG-INFO', 'top_level.txt', + 'SOURCES.txt'] + for f in extra_metadata: + files.append(os.path.join(egginfo, f)) + + tempdir, record_file = self.build_dist_tree(files, dirs) + distinfo_path = os.path.join(tempdir, distinfo) + egginfo_path = os.path.join(tempdir, egginfo) + metadata_file_paths = self.get_metadata_file_paths(distinfo_path) + + egginfo_to_distinfo(record_file) + # test that directories and files get created + self.assertTrue(os.path.isdir(distinfo_path)) + self.assertTrue(os.path.isdir(egginfo_path)) + + for mfile in metadata_file_paths: + self.assertTrue(os.path.isfile(mfile)) + + def test_egginfo_to_distinfo_distutils(self): + distinfo = 'hello-0.1.1-py3.3.dist-info' + egginfo = 'hello-0.1.1-py3.3.egg-info' + # egginfo is a file in distutils which contains the metadata + files = ['hello.py', 'hello.pyc', egginfo] + + tempdir, record_file = self.build_dist_tree(files, dirs=[]) + distinfo_path = os.path.join(tempdir, distinfo) + egginfo_path = os.path.join(tempdir, egginfo) + metadata_file_paths = self.get_metadata_file_paths(distinfo_path) + + egginfo_to_distinfo(record_file) + # test that directories and files get created + self.assertTrue(os.path.isdir(distinfo_path)) + self.assertTrue(os.path.isfile(egginfo_path)) + + for mfile in metadata_file_paths: + self.assertTrue(os.path.isfile(mfile)) + + def build_dist_tree(self, files, dirs): + tempdir = self.mkdtemp() + record_file_path = os.path.join(tempdir, 'RECORD') + file_paths, dir_paths = ([], []) + for d in dirs: + path = os.path.join(tempdir, d) + os.makedirs(path) + dir_paths.append(path) + for f in files: + path = os.path.join(tempdir, f) + with open(path, 'w') as _f: + _f.write(f) + file_paths.append(path) + + with open(record_file_path, 'w') as record_file: + for fpath in file_paths: + record_file.write(fpath + '\n') + for dpath in dir_paths: + record_file.write(dpath + '\n') + + return (tempdir, record_file_path) + + +class PackagingLibChecks(support.TempdirManager, + support.LoggingCatcher, + unittest.TestCase): + + def setUp(self): + super(PackagingLibChecks, self).setUp() + self._empty_dir = self.mkdtemp() + + def test_empty_package_is_not_based_on_anything(self): + self.assertFalse(is_setuptools(self._empty_dir)) + self.assertFalse(is_distutils(self._empty_dir)) + self.assertFalse(is_packaging(self._empty_dir)) + + def test_setup_py_importing_setuptools_is_setuptools_based(self): + self.assertTrue(is_setuptools(self._setuptools_setup_py_pkg())) + + def test_egg_info_dir_and_setup_py_is_setuptools_based(self): + self.assertTrue(is_setuptools(self._setuptools_egg_info_pkg())) + + def test_egg_info_and_non_setuptools_setup_py_is_setuptools_based(self): + self.assertTrue(is_setuptools(self._egg_info_with_no_setuptools())) + + def test_setup_py_not_importing_setuptools_is_not_setuptools_based(self): + self.assertFalse(is_setuptools(self._random_setup_py_pkg())) + + def test_setup_py_importing_distutils_is_distutils_based(self): + self.assertTrue(is_distutils(self._distutils_setup_py_pkg())) + + def test_pkg_info_file_and_setup_py_is_distutils_based(self): + self.assertTrue(is_distutils(self._distutils_pkg_info())) + + def test_pkg_info_and_non_distutils_setup_py_is_distutils_based(self): + self.assertTrue(is_distutils(self._pkg_info_with_no_distutils())) + + def test_setup_py_not_importing_distutils_is_not_distutils_based(self): + self.assertFalse(is_distutils(self._random_setup_py_pkg())) + + def test_setup_cfg_with_no_metadata_section_is_not_packaging_based(self): + self.assertFalse(is_packaging(self._setup_cfg_with_no_metadata_pkg())) + + def test_setup_cfg_with_valid_metadata_section_is_packaging_based(self): + self.assertTrue(is_packaging(self._valid_setup_cfg_pkg())) + + def test_setup_cfg_and_invalid_setup_cfg_is_not_packaging_based(self): + self.assertFalse(is_packaging(self._invalid_setup_cfg_pkg())) + + def test_get_install_method_with_setuptools_pkg(self): + path = self._setuptools_setup_py_pkg() + self.assertEqual("setuptools", get_install_method(path)) + + def test_get_install_method_with_distutils_pkg(self): + path = self._distutils_pkg_info() + self.assertEqual("distutils", get_install_method(path)) + + def test_get_install_method_with_packaging_pkg(self): + path = self._valid_setup_cfg_pkg() + self.assertEqual("packaging", get_install_method(path)) + + def test_get_install_method_with_unknown_pkg(self): + path = self._invalid_setup_cfg_pkg() + self.assertRaises(InstallationException, get_install_method, path) + + def test_is_setuptools_logs_setup_py_text_found(self): + is_setuptools(self._setuptools_setup_py_pkg()) + expected = ['setup.py file found.', + 'No egg-info directory found.', + 'Found setuptools text in setup.py.'] + self.assertEqual(expected, self.get_logs(logging.DEBUG)) + + def test_is_setuptools_logs_setup_py_text_not_found(self): + directory = self._random_setup_py_pkg() + is_setuptools(directory) + expected = ['setup.py file found.', 'No egg-info directory found.', + 'No setuptools text found in setup.py.'] + self.assertEqual(expected, self.get_logs(logging.DEBUG)) + + def test_is_setuptools_logs_egg_info_dir_found(self): + is_setuptools(self._setuptools_egg_info_pkg()) + expected = ['setup.py file found.', 'Found egg-info directory.'] + self.assertEqual(expected, self.get_logs(logging.DEBUG)) + + def test_is_distutils_logs_setup_py_text_found(self): + is_distutils(self._distutils_setup_py_pkg()) + expected = ['setup.py file found.', + 'No PKG-INFO file found.', + 'Found distutils text in setup.py.'] + self.assertEqual(expected, self.get_logs(logging.DEBUG)) + + def test_is_distutils_logs_setup_py_text_not_found(self): + directory = self._random_setup_py_pkg() + is_distutils(directory) + expected = ['setup.py file found.', 'No PKG-INFO file found.', + 'No distutils text found in setup.py.'] + self.assertEqual(expected, self.get_logs(logging.DEBUG)) + + def test_is_distutils_logs_pkg_info_file_found(self): + is_distutils(self._distutils_pkg_info()) + expected = ['setup.py file found.', 'PKG-INFO file found.'] + self.assertEqual(expected, self.get_logs(logging.DEBUG)) + + def test_is_packaging_logs_setup_cfg_found(self): + is_packaging(self._valid_setup_cfg_pkg()) + expected = ['setup.cfg file found.'] + self.assertEqual(expected, self.get_logs(logging.DEBUG)) + + def test_is_packaging_logs_setup_cfg_not_found(self): + is_packaging(self._empty_dir) + expected = ['No setup.cfg file found.'] + self.assertEqual(expected, self.get_logs(logging.DEBUG)) + + def _write_setuptools_setup_py(self, directory): + self.write_file((directory, 'setup.py'), + "from setuptools import setup") + + def _write_distutils_setup_py(self, directory): + self.write_file([directory, 'setup.py'], + "from distutils.core import setup") + + def _write_packaging_setup_cfg(self, directory): + self.write_file([directory, 'setup.cfg'], + ("[metadata]\n" + "name = mypackage\n" + "version = 0.1.0\n")) + + def _setuptools_setup_py_pkg(self): + tmp = self.mkdtemp() + self._write_setuptools_setup_py(tmp) + return tmp + + def _distutils_setup_py_pkg(self): + tmp = self.mkdtemp() + self._write_distutils_setup_py(tmp) + return tmp + + def _valid_setup_cfg_pkg(self): + tmp = self.mkdtemp() + self._write_packaging_setup_cfg(tmp) + return tmp + + def _setuptools_egg_info_pkg(self): + tmp = self.mkdtemp() + self._write_setuptools_setup_py(tmp) + tempfile.mkdtemp(suffix='.egg-info', dir=tmp) + return tmp + + def _distutils_pkg_info(self): + tmp = self._distutils_setup_py_pkg() + self.write_file([tmp, 'PKG-INFO'], '', encoding='UTF-8') + return tmp + + def _setup_cfg_with_no_metadata_pkg(self): + tmp = self.mkdtemp() + self.write_file([tmp, 'setup.cfg'], + ("[othersection]\n" + "foo = bar\n")) + return tmp + + def _invalid_setup_cfg_pkg(self): + tmp = self.mkdtemp() + self.write_file([tmp, 'setup.cfg'], + ("[metadata]\n" + "name = john\n" + "last_name = doe\n")) + return tmp + + def _egg_info_with_no_setuptools(self): + tmp = self._random_setup_py_pkg() + tempfile.mkdtemp(suffix='.egg-info', dir=tmp) + return tmp + + def _pkg_info_with_no_distutils(self): + tmp = self._random_setup_py_pkg() + self.write_file([tmp, 'PKG-INFO'], '', encoding='UTF-8') + return tmp + + def _random_setup_py_pkg(self): + tmp = self.mkdtemp() + self.write_file((tmp, 'setup.py'), "from mypackage import setup") + return tmp + + +def test_suite(): + suite = unittest.makeSuite(UtilTestCase) + suite.addTest(unittest.makeSuite(GlobTestCase)) + suite.addTest(unittest.makeSuite(EggInfoToDistInfoTestCase)) + suite.addTest(unittest.makeSuite(PackagingLibChecks)) + return suite + + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") |