mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1013 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1013 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """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")
 | 
