mirror of
https://github.com/python/cpython.git
synced 2025-08-01 23:53:15 +00:00

The code I fixed to comply with PEP 3147 still had one bug: When run under python -O, some paths for pyc files would be pyo, because I called imp.cache_from_source without explicit debug_override argument in some places, and under -O that would return .pyo (this is well explained in the imp docs). Now all code (util.byte_compile, build_py, install_lib) can create .pyo files according to options given by users, without interference from the calling Python’s own optimize mode. On a related topic, I also removed the code that prevented byte compilation under python -B. The rationale is that packaging gives control over the creation of pyc files to the user with its own explicit option, and the behavior should not be changed if the calling Python happens to run with -B for whatever reason. I will argue that this is a bug fix and ask to be allowed to backport this change to distutils. Finally, I moved one nugget of information about the --compile and --optimize options from the source into the doc. It clears up a misunderstanding that I (and maybe other people) had.
1016 lines
36 KiB
Python
1016 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.old_stdout = self.old_stdout
|
|
sys.old_stderr = self.old_stderr
|
|
|
|
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 setUp(self):
|
|
super(GlobTestCase, self).setUp()
|
|
self.cwd = os.getcwd()
|
|
|
|
def tearDown(self):
|
|
os.chdir(self.cwd)
|
|
super(GlobTestCase, self).tearDown()
|
|
|
|
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")
|