diff --git a/Lib/packaging/install.py b/Lib/packaging/install.py index 4b82088a6ba..c5bda453074 100644 --- a/Lib/packaging/install.py +++ b/Lib/packaging/install.py @@ -13,7 +13,7 @@ import errno import shutil import logging import tempfile -from sysconfig import get_config_var, get_path +from sysconfig import get_config_var, get_path, is_python_build from packaging import logger from packaging.dist import Distribution @@ -488,20 +488,31 @@ def install(project): Returns True on success, False on failure """ + if is_python_build(): + # Python would try to install into the site-packages directory under + # $PREFIX, but when running from an uninstalled code checkout we don't + # want to create directories under the installation root + message = ('installing third-party projects from an uninstalled ' + 'Python is not supported') + logger.error(message) + return False + logger.info('Checking the installation location...') purelib_path = get_path('purelib') + # trying to write a file there try: with tempfile.NamedTemporaryFile(suffix=project, dir=purelib_path) as testfile: testfile.write(b'test') except OSError: - # was unable to write a file + # FIXME this should check the errno, or be removed altogether (race + # condition: the directory permissions could be changed between here + # and the actual install) logger.info('Unable to write in "%s". Do you have the permissions ?' % purelib_path) return False - logger.info('Getting information about %r...', project) try: info = get_infos(project) @@ -520,7 +531,7 @@ def install(project): except InstallationConflict as e: if logger.isEnabledFor(logging.INFO): - projects = ['%s %s' % (p.name, p.version) for p in e.args[0]] + projects = ['%r %s' % (p.name, p.version) for p in e.args[0]] logger.info('%r conflicts with %s', project, ','.join(projects)) return True diff --git a/Lib/packaging/tests/test_command_install_distinfo.py b/Lib/packaging/tests/test_command_install_distinfo.py index 6d40f66d92a..ade191c57a0 100644 --- a/Lib/packaging/tests/test_command_install_distinfo.py +++ b/Lib/packaging/tests/test_command_install_distinfo.py @@ -162,7 +162,7 @@ class InstallDistinfoTestCase(support.TempdirManager, expected = [] for f in install.get_outputs(): - if (f.endswith('.pyc') or f == os.path.join( + if (f.endswith(('.pyc', '.pyo')) or f == os.path.join( install_dir, 'foo-1.0.dist-info', 'RECORD')): expected.append([f, '', '']) else: diff --git a/Lib/packaging/tests/test_dist.py b/Lib/packaging/tests/test_dist.py index fb6d524fdc5..e1c5ff0a2d7 100644 --- a/Lib/packaging/tests/test_dist.py +++ b/Lib/packaging/tests/test_dist.py @@ -35,7 +35,7 @@ class DistributionTestCase(support.TempdirManager, support.EnvironRestorer, unittest.TestCase): - restore_environ = ['HOME'] + restore_environ = ['HOME', 'PLAT'] def setUp(self): super(DistributionTestCase, self).setUp() diff --git a/Lib/packaging/tests/test_install.py b/Lib/packaging/tests/test_install.py index c50a45e40e7..35733c8bf5d 100644 --- a/Lib/packaging/tests/test_install.py +++ b/Lib/packaging/tests/test_install.py @@ -1,5 +1,7 @@ """Tests for the packaging.install module.""" import os +import logging +from sysconfig import is_python_build from tempfile import mkstemp from packaging import install @@ -357,9 +359,17 @@ class TestInstall(LoggingCatcher, TempdirManager, unittest.TestCase): install._install_dist = old_install_dist def test_install_permission_denied(self): - # if we don't have the access to the installation - # path, we should abort immediatly + # if we don't have access to the installation path, we should abort + # immediately project = os.path.join(os.path.dirname(__file__), 'package.tgz') + + # when running from an uninstalled build, a warning is emitted and the + # installation is not attempted + if is_python_build(): + self.assertFalse(install.install(project)) + self.assertEqual(1, len(self.get_logs(logging.ERROR))) + return + install_path = self.mkdtemp() old_get_path = install.get_path install.get_path = lambda path: install_path diff --git a/Makefile.pre.in b/Makefile.pre.in index 07486fde918..2ca89cf36a4 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -950,7 +950,7 @@ libinstall: build_all $(srcdir)/Lib/$(PLATDIR) else true; \ fi; \ done - @for i in $(srcdir)/Lib/*.py $(srcdir)/Lib/*.egg-info $(srcdir)/Lib/*.cfg ; \ + @for i in $(srcdir)/Lib/*.py $(srcdir)/Lib/*.cfg ; \ do \ if test -x $$i; then \ $(INSTALL_SCRIPT) $$i $(DESTDIR)$(LIBDEST); \ diff --git a/Misc/ACKS b/Misc/ACKS index 900fae705ac..de5410c907e 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -550,6 +550,7 @@ Joerg Lehmann Robert Lehmann Petri Lehtinen Luke Kenneth Casson Leighton +Tshepang Lekhonkhobe Marc-Andre Lemburg John Lenton Christopher Tur Lesniewski-Laas diff --git a/Misc/NEWS b/Misc/NEWS index dd7dedebf72..f2d3ffe28cf 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -187,6 +187,10 @@ Core and Builtins Library ------- +- Issue #12246: Warn and fail when trying to install a third-party project from + an uninstalled Python (built in a source checkout). Original patch by + Tshepang Lekhonkhobe. + - Issue #10694: zipfile now ignores garbage at the end of a zipfile. - Issue #12283: Fixed regression in smtplib quoting of leading dots in DATA.