summaryrefslogtreecommitdiffstats
path: root/tests/test_install.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_install.py')
-rw-r--r--tests/test_install.py365
1 files changed, 365 insertions, 0 deletions
diff --git a/tests/test_install.py b/tests/test_install.py
new file mode 100644
index 0000000..b4e9068
--- /dev/null
+++ b/tests/test_install.py
@@ -0,0 +1,365 @@
+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)