forked from openkylin/flit
366 lines
15 KiB
Python
366 lines
15 KiB
Python
import json
|
|
import os
|
|
import pathlib
|
|
import sys
|
|
import tempfile
|
|
from unittest import TestCase, SkipTest
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from testpath import (
|
|
assert_isfile, assert_isdir, assert_islink, assert_not_path_exists, MockCommand
|
|
)
|
|
|
|
from flit import install
|
|
from flit.install import Installer, _requires_dist_to_pip_requirement, DependencyError
|
|
import flit_core.tests
|
|
|
|
samples_dir = pathlib.Path(__file__).parent / 'samples'
|
|
core_samples_dir = pathlib.Path(flit_core.tests.__file__).parent / 'samples'
|
|
|
|
class InstallTests(TestCase):
|
|
def setUp(self):
|
|
td = tempfile.TemporaryDirectory()
|
|
self.addCleanup(td.cleanup)
|
|
self.get_dirs_patch = patch('flit.install.get_dirs',
|
|
return_value={
|
|
'scripts': os.path.join(td.name, 'scripts'),
|
|
'purelib': os.path.join(td.name, 'site-packages'),
|
|
'data': os.path.join(td.name, 'data'),
|
|
})
|
|
self.get_dirs_patch.start()
|
|
self.tmpdir = pathlib.Path(td.name)
|
|
|
|
def tearDown(self):
|
|
self.get_dirs_patch.stop()
|
|
|
|
def _assert_direct_url(self, directory, package, version, expected_editable):
|
|
direct_url_file = (
|
|
self.tmpdir
|
|
/ 'site-packages'
|
|
/ '{}-{}.dist-info'.format(package, version)
|
|
/ 'direct_url.json'
|
|
)
|
|
assert_isfile(direct_url_file)
|
|
with direct_url_file.open() as f:
|
|
direct_url = json.load(f)
|
|
assert direct_url['url'].startswith('file:///')
|
|
assert direct_url['url'] == directory.as_uri()
|
|
assert direct_url['dir_info'].get('editable') is expected_editable
|
|
|
|
def test_install_module(self):
|
|
Installer.from_ini_path(samples_dir / 'module1_toml' / 'pyproject.toml').install_directly()
|
|
assert_isfile(self.tmpdir / 'site-packages' / 'module1.py')
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'module1-0.1.dist-info')
|
|
self._assert_direct_url(
|
|
samples_dir / 'module1_toml', 'module1', '0.1', expected_editable=False
|
|
)
|
|
|
|
def test_install_module_pep621(self):
|
|
Installer.from_ini_path(
|
|
core_samples_dir / 'pep621_nodynamic' / 'pyproject.toml',
|
|
).install_directly()
|
|
assert_isfile(self.tmpdir / 'site-packages' / 'module1.py')
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'module1-0.3.dist-info')
|
|
self._assert_direct_url(
|
|
core_samples_dir / 'pep621_nodynamic', 'module1', '0.3',
|
|
expected_editable=False
|
|
)
|
|
|
|
def test_install_package(self):
|
|
oldcwd = os.getcwd()
|
|
os.chdir(str(samples_dir / 'package1'))
|
|
try:
|
|
Installer.from_ini_path(pathlib.Path('pyproject.toml')).install_directly()
|
|
finally:
|
|
os.chdir(oldcwd)
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'package1')
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'package1-0.1.dist-info')
|
|
assert_isfile(self.tmpdir / 'scripts' / 'pkg_script')
|
|
with (self.tmpdir / 'scripts' / 'pkg_script').open() as f:
|
|
assert f.readline().strip() == "#!" + sys.executable
|
|
self._assert_direct_url(
|
|
samples_dir / 'package1', 'package1', '0.1', expected_editable=False
|
|
)
|
|
|
|
def test_install_module_in_src(self):
|
|
oldcwd = os.getcwd()
|
|
os.chdir(samples_dir / 'packageinsrc')
|
|
try:
|
|
Installer.from_ini_path(pathlib.Path('pyproject.toml')).install_directly()
|
|
finally:
|
|
os.chdir(oldcwd)
|
|
assert_isfile(self.tmpdir / 'site-packages' / 'module1.py')
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'module1-0.1.dist-info')
|
|
|
|
def test_install_ns_package_native(self):
|
|
Installer.from_ini_path(samples_dir / 'ns1-pkg' / 'pyproject.toml').install_directly()
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'ns1')
|
|
assert_isfile(self.tmpdir / 'site-packages' / 'ns1' / 'pkg' / '__init__.py')
|
|
assert_not_path_exists(self.tmpdir / 'site-packages' / 'ns1' / '__init__.py')
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'ns1_pkg-0.1.dist-info')
|
|
|
|
def test_install_ns_package_module_native(self):
|
|
Installer.from_ini_path(samples_dir / 'ns1-pkg-mod' / 'pyproject.toml').install_directly()
|
|
assert_isfile(self.tmpdir / 'site-packages' / 'ns1' / 'module.py')
|
|
assert_not_path_exists(self.tmpdir / 'site-packages' / 'ns1' / '__init__.py')
|
|
|
|
def test_install_ns_package_native_symlink(self):
|
|
if os.name == 'nt':
|
|
raise SkipTest('symlink')
|
|
Installer.from_ini_path(
|
|
samples_dir / 'ns1-pkg' / 'pyproject.toml', symlink=True
|
|
).install_directly()
|
|
Installer.from_ini_path(
|
|
samples_dir / 'ns1-pkg2' / 'pyproject.toml', symlink=True
|
|
).install_directly()
|
|
Installer.from_ini_path(
|
|
samples_dir / 'ns1-pkg-mod' / 'pyproject.toml', symlink=True
|
|
).install_directly()
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'ns1')
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'ns1' / 'pkg')
|
|
assert_islink(self.tmpdir / 'site-packages' / 'ns1' / 'pkg',
|
|
to=str(samples_dir / 'ns1-pkg' / 'ns1' / 'pkg'))
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'ns1_pkg-0.1.dist-info')
|
|
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'ns1' / 'pkg2')
|
|
assert_islink(self.tmpdir / 'site-packages' / 'ns1' / 'pkg2',
|
|
to=str(samples_dir / 'ns1-pkg2' / 'ns1' / 'pkg2'))
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'ns1_pkg2-0.1.dist-info')
|
|
|
|
assert_islink(self.tmpdir / 'site-packages' / 'ns1' / 'module.py',
|
|
to=samples_dir / 'ns1-pkg-mod' / 'ns1' / 'module.py')
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'ns1_module-0.1.dist-info')
|
|
|
|
def test_install_ns_package_pth_file(self):
|
|
Installer.from_ini_path(
|
|
samples_dir / 'ns1-pkg' / 'pyproject.toml', pth=True
|
|
).install_directly()
|
|
|
|
pth_file = self.tmpdir / 'site-packages' / 'ns1.pkg.pth'
|
|
assert_isfile(pth_file)
|
|
assert pth_file.read_text('utf-8').strip() == str(samples_dir / 'ns1-pkg')
|
|
|
|
def test_symlink_package(self):
|
|
if os.name == 'nt':
|
|
raise SkipTest("symlink")
|
|
Installer.from_ini_path(samples_dir / 'package1' / 'pyproject.toml', symlink=True).install()
|
|
assert_islink(self.tmpdir / 'site-packages' / 'package1',
|
|
to=samples_dir / 'package1' / 'package1')
|
|
assert_isfile(self.tmpdir / 'scripts' / 'pkg_script')
|
|
with (self.tmpdir / 'scripts' / 'pkg_script').open() as f:
|
|
assert f.readline().strip() == "#!" + sys.executable
|
|
self._assert_direct_url(
|
|
samples_dir / 'package1', 'package1', '0.1', expected_editable=True
|
|
)
|
|
|
|
def test_symlink_module_pep621(self):
|
|
if os.name == 'nt':
|
|
raise SkipTest("symlink")
|
|
Installer.from_ini_path(
|
|
core_samples_dir / 'pep621_nodynamic' / 'pyproject.toml', symlink=True
|
|
).install_directly()
|
|
assert_islink(self.tmpdir / 'site-packages' / 'module1.py',
|
|
to=core_samples_dir / 'pep621_nodynamic' / 'module1.py')
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'module1-0.3.dist-info')
|
|
self._assert_direct_url(
|
|
core_samples_dir / 'pep621_nodynamic', 'module1', '0.3',
|
|
expected_editable=True
|
|
)
|
|
|
|
def test_symlink_module_in_src(self):
|
|
if os.name == 'nt':
|
|
raise SkipTest("symlink")
|
|
oldcwd = os.getcwd()
|
|
os.chdir(samples_dir / 'packageinsrc')
|
|
try:
|
|
Installer.from_ini_path(
|
|
pathlib.Path('pyproject.toml'), symlink=True
|
|
).install_directly()
|
|
finally:
|
|
os.chdir(oldcwd)
|
|
assert_islink(self.tmpdir / 'site-packages' / 'module1.py',
|
|
to=(samples_dir / 'packageinsrc' / 'src' / 'module1.py'))
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'module1-0.1.dist-info')
|
|
|
|
def test_pth_package(self):
|
|
Installer.from_ini_path(samples_dir / 'package1' / 'pyproject.toml', pth=True).install()
|
|
assert_isfile(self.tmpdir / 'site-packages' / 'package1.pth')
|
|
with open(str(self.tmpdir / 'site-packages' / 'package1.pth')) as f:
|
|
assert f.read() == str(samples_dir / 'package1')
|
|
assert_isfile(self.tmpdir / 'scripts' / 'pkg_script')
|
|
self._assert_direct_url(
|
|
samples_dir / 'package1', 'package1', '0.1', expected_editable=True
|
|
)
|
|
|
|
def test_pth_module_in_src(self):
|
|
oldcwd = os.getcwd()
|
|
os.chdir(samples_dir / 'packageinsrc')
|
|
try:
|
|
Installer.from_ini_path(
|
|
pathlib.Path('pyproject.toml'), pth=True
|
|
).install_directly()
|
|
finally:
|
|
os.chdir(oldcwd)
|
|
pth_path = self.tmpdir / 'site-packages' / 'module1.pth'
|
|
assert_isfile(pth_path)
|
|
assert pth_path.read_text('utf-8').strip() == str(
|
|
samples_dir / 'packageinsrc' / 'src'
|
|
)
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'module1-0.1.dist-info')
|
|
|
|
def test_dist_name(self):
|
|
Installer.from_ini_path(samples_dir / 'altdistname' / 'pyproject.toml').install_directly()
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'package1')
|
|
assert_isdir(self.tmpdir / 'site-packages' / 'package_dist1-0.1.dist-info')
|
|
|
|
def test_entry_points(self):
|
|
Installer.from_ini_path(samples_dir / 'entrypoints_valid' / 'pyproject.toml').install_directly()
|
|
assert_isfile(self.tmpdir / 'site-packages' / 'package1-0.1.dist-info' / 'entry_points.txt')
|
|
|
|
def test_pip_install(self):
|
|
ins = Installer.from_ini_path(samples_dir / 'package1' / 'pyproject.toml', python='mock_python',
|
|
user=False)
|
|
|
|
with MockCommand('mock_python') as mock_py:
|
|
ins.install()
|
|
|
|
calls = mock_py.get_calls()
|
|
assert len(calls) == 1
|
|
cmd = calls[0]['argv']
|
|
assert cmd[1:4] == ['-m', 'pip', 'install']
|
|
assert cmd[4].endswith('package1')
|
|
|
|
def test_symlink_other_python(self):
|
|
if os.name == 'nt':
|
|
raise SkipTest('symlink')
|
|
(self.tmpdir / 'site-packages2').mkdir()
|
|
(self.tmpdir / 'scripts2').mkdir()
|
|
|
|
# Called by Installer._auto_user() :
|
|
script1 = ("#!{python}\n"
|
|
"import sysconfig\n"
|
|
"print(True)\n" # site.ENABLE_USER_SITE
|
|
"print({purelib!r})" # sysconfig.get_path('purelib')
|
|
).format(python=sys.executable,
|
|
purelib=str(self.tmpdir / 'site-packages2'))
|
|
|
|
# Called by Installer._get_dirs() :
|
|
script2 = ("#!{python}\n"
|
|
"import json, sys\n"
|
|
"json.dump({{'purelib': {purelib!r}, 'scripts': {scripts!r}, 'data': {data!r} }}, "
|
|
"sys.stdout)"
|
|
).format(python=sys.executable,
|
|
purelib=str(self.tmpdir / 'site-packages2'),
|
|
scripts=str(self.tmpdir / 'scripts2'),
|
|
data=str(self.tmpdir / 'data'),
|
|
)
|
|
|
|
with MockCommand('mock_python', content=script1):
|
|
ins = Installer.from_ini_path(samples_dir / 'package1' / 'pyproject.toml', python='mock_python',
|
|
symlink=True)
|
|
with MockCommand('mock_python', content=script2):
|
|
ins.install()
|
|
|
|
assert_islink(self.tmpdir / 'site-packages2' / 'package1',
|
|
to=samples_dir / 'package1' / 'package1')
|
|
assert_isfile(self.tmpdir / 'scripts2' / 'pkg_script')
|
|
with (self.tmpdir / 'scripts2' / 'pkg_script').open() as f:
|
|
assert f.readline().strip() == "#!mock_python"
|
|
|
|
def test_install_requires(self):
|
|
ins = Installer.from_ini_path(samples_dir / 'requires-requests.toml',
|
|
user=False, python='mock_python')
|
|
|
|
with MockCommand('mock_python') as mockpy:
|
|
ins.install_requirements()
|
|
calls = mockpy.get_calls()
|
|
assert len(calls) == 1
|
|
assert calls[0]['argv'][1:5] == ['-m', 'pip', 'install', '-r']
|
|
|
|
def test_install_reqs_my_python_if_needed_pep621(self):
|
|
ins = Installer.from_ini_path(
|
|
core_samples_dir / 'pep621_nodynamic' / 'pyproject.toml',
|
|
deps='none',
|
|
)
|
|
|
|
# This shouldn't try to get version & docstring from the module
|
|
ins.install_reqs_my_python_if_needed()
|
|
|
|
def test_extras_error(self):
|
|
with pytest.raises(DependencyError):
|
|
Installer.from_ini_path(samples_dir / 'requires-requests.toml',
|
|
user=False, deps='none', extras='dev')
|
|
|
|
def test_install_data_dir(self):
|
|
Installer.from_ini_path(
|
|
core_samples_dir / 'with_data_dir' / 'pyproject.toml',
|
|
).install_directly()
|
|
assert_isfile(self.tmpdir / 'site-packages' / 'module1.py')
|
|
assert_isfile(self.tmpdir / 'data' / 'share' / 'man' / 'man1' / 'foo.1')
|
|
|
|
def test_symlink_data_dir(self):
|
|
if os.name == 'nt':
|
|
raise SkipTest("symlink")
|
|
Installer.from_ini_path(
|
|
core_samples_dir / 'with_data_dir' / 'pyproject.toml', symlink=True
|
|
).install_directly()
|
|
assert_isfile(self.tmpdir / 'site-packages' / 'module1.py')
|
|
assert_islink(
|
|
self.tmpdir / 'data' / 'share' / 'man' / 'man1' / 'foo.1',
|
|
to=core_samples_dir / 'with_data_dir' / 'data' / 'share' / 'man' / 'man1' / 'foo.1'
|
|
)
|
|
|
|
@pytest.mark.parametrize(('deps', 'extras', 'installed'), [
|
|
('none', [], set()),
|
|
('develop', [], {'pytest ;', 'toml ;'}),
|
|
('production', [], {'toml ;'}),
|
|
('all', [], {'toml ;', 'pytest ;', 'requests ;'}),
|
|
])
|
|
def test_install_requires_extra(deps, extras, installed):
|
|
it = InstallTests()
|
|
try:
|
|
it.setUp()
|
|
ins = Installer.from_ini_path(samples_dir / 'extras' / 'pyproject.toml', python='mock_python',
|
|
user=False, deps=deps, extras=extras)
|
|
|
|
cmd = MockCommand('mock_python')
|
|
get_reqs = (
|
|
"#!{python}\n"
|
|
"import sys\n"
|
|
"with open({recording_file!r}, 'wb') as w, open(sys.argv[-1], 'rb') as r:\n"
|
|
" w.write(r.read())"
|
|
).format(python=sys.executable, recording_file=cmd.recording_file)
|
|
cmd.content = get_reqs
|
|
|
|
with cmd as mock_py:
|
|
ins.install_requirements()
|
|
with open(mock_py.recording_file) as f:
|
|
str_deps = f.read()
|
|
deps = str_deps.split('\n') if str_deps else []
|
|
|
|
assert set(deps) == installed
|
|
finally:
|
|
it.tearDown()
|
|
|
|
def test_requires_dist_to_pip_requirement():
|
|
rd = 'pathlib2 (>=2.3); python_version == "2.7"'
|
|
assert _requires_dist_to_pip_requirement(rd) == \
|
|
'pathlib2>=2.3 ; python_version == "2.7"'
|
|
|
|
def test_test_writable_dir_win():
|
|
with tempfile.TemporaryDirectory() as td:
|
|
assert install._test_writable_dir_win(td) is True
|
|
|
|
# Ironically, I don't know how to make a non-writable dir on Windows,
|
|
# so although the functionality is for Windows, the test is for Posix
|
|
if os.name != 'posix':
|
|
return
|
|
|
|
# Remove write permissions from the directory
|
|
os.chmod(td, 0o444)
|
|
try:
|
|
assert install._test_writable_dir_win(td) is False
|
|
finally:
|
|
os.chmod(td, 0o644)
|