Skip to content

Commit

Permalink
modules/python: detect debian distutils missing and show an error
Browse files Browse the repository at this point in the history
Signed-off-by: Filipe Laíns <[email protected]>
  • Loading branch information
FFY00 committed Oct 7, 2021
1 parent bd2fcb2 commit 9f5cb9d
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 32 deletions.
91 changes: 59 additions & 32 deletions mesonbuild/modules/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,19 @@ def set_env(name, value):
import sysconfig
import json
import sys
import distutils.command.install
def debian_distutils_missing():
# Debian partially splits the distutils module from the python3, they
# keep distutils and distutils.version in python3 and put the rest of
# the submodules in python3-distutils
try:
import distutils.version
import distutils.core
except ModuleNotFoundError as e:
if e.name == 'distutils.core':
# distutils.version was successfully imported, but distutils.core was not
return True
return False
def get_distutils_paths(scheme=None, prefix=None):
import distutils.dist
Expand All @@ -299,37 +311,47 @@ def get_distutils_paths(scheme=None, prefix=None):
'scripts': install_cmd.install_scripts,
}
# On Debian derivatives, the Python interpreter shipped by the distribution uses
# a custom install scheme, deb_system, for the system install, and changes the
# default scheme to a custom one pointing to /usr/local and replacing
# site-packages with dist-packages.
# See https://github.com/mesonbuild/meson/issues/8739.
# XXX: We should be using sysconfig, but Debian only patches distutils.
if 'deb_system' in distutils.command.install.INSTALL_SCHEMES:
paths = get_distutils_paths(scheme='deb_system')
install_paths = get_distutils_paths(scheme='deb_system', prefix='')
else:
paths = sysconfig.get_paths()
empty_vars = {'base': '', 'platbase': '', 'installed_base': ''}
install_paths = sysconfig.get_paths(vars=empty_vars)
def get_paths():
# On Debian derivatives, the Python interpreter shipped by the distribution uses
# a custom install scheme, deb_system, for the system install, and changes the
# default scheme to a custom one pointing to /usr/local and replacing
# site-packages with dist-packages.
# See https://github.com/mesonbuild/meson/issues/8739.
# XXX: We should be using sysconfig, but Debian only patches distutils.
import distutils.command.install
if 'deb_system' in distutils.command.install.INSTALL_SCHEMES:
paths = get_distutils_paths(scheme='deb_system')
install_paths = get_distutils_paths(scheme='deb_system', prefix='')
else:
paths = sysconfig.get_paths()
empty_vars = {'base': '', 'platbase': '', 'installed_base': ''}
install_paths = sysconfig.get_paths(vars=empty_vars)
return paths, install_paths
def links_against_libpython():
from distutils.core import Distribution, Extension
cmd = Distribution().get_command_obj('build_ext')
cmd.ensure_finalized()
return bool(cmd.get_libraries(Extension('dummy', [])))
print(json.dumps({
'variables': sysconfig.get_config_vars(),
'paths': paths,
'install_paths': install_paths,
'sys_paths': sys.path,
'version': sysconfig.get_python_version(),
'platform': sysconfig.get_platform(),
'is_pypy': '__pypy__' in sys.builtin_module_names,
'link_libpython': links_against_libpython(),
}))
if debian_distutils_missing():
data = {
'error': 'Debian distutils is missing, please install python3-distutils',
}
else:
paths, install_paths = get_paths()
data = {
'variables': sysconfig.get_config_vars(),
'paths': paths,
'install_paths': install_paths,
'sys_paths': sys.path,
'version': sysconfig.get_python_version(),
'platform': sysconfig.get_platform(),
'is_pypy': '__pypy__' in sys.builtin_module_names,
'link_libpython': links_against_libpython(),
}
print(json.dumps(data))
'''

if T.TYPE_CHECKING:
Expand Down Expand Up @@ -390,13 +412,18 @@ def sanity(self) -> bool:
mlog.debug('Program stderr:\n')
mlog.debug(stderr)

if info is not None and self._check_version(info['version']):
variables = info['variables']
info['suffix'] = variables.get('EXT_SUFFIX') or variables.get('SO') or variables.get('.so')
self.info = T.cast('PythonIntrospectionDict', info)
self.platlib = self._get_path('platlib')
self.purelib = self._get_path('purelib')
return True
if info is not None:
if 'error' in info:
assert isinstance(info['error'], str)
mlog.log('Python interpreter introspection failed: {}'.format(info['error']))
return False
elif self._check_version(info['version']):
variables = info['variables']
info['suffix'] = variables.get('EXT_SUFFIX') or variables.get('SO') or variables.get('.so')
self.info = T.cast('PythonIntrospectionDict', info)
self.platlib = self._get_path('platlib')
self.purelib = self._get_path('purelib')
return True
else:
return False

Expand Down
10 changes: 10 additions & 0 deletions unittests/pythontests.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@

import os
import unittest
import unittest.mock

from run_tests import (
Backend
)

from mesonbuild.modules.python import PythonExternalProgram
from .baseplatformtests import BasePlatformTests

class PythonTests(BasePlatformTests):
Expand Down Expand Up @@ -80,3 +82,11 @@ def test_versions(self):
with self.assertRaises(unittest.SkipTest):
self.init(testdir, extra_args=['-Dpython=dir'])
self.wipe()

@unittest.mock.patch('mesonbuild.mesonlib.Popen_safe', return_value=(None, '{"error": "hello!"}', None))
@unittest.mock.patch('mesonbuild.mlog.log')
def test_introspection_error(self, mlog_log, Popen_safe):
python = PythonExternalProgram('some-python', ['some-python'])

assert not python.sanity()
mlog_log.assert_called_with('Python interpreter introspection failed: hello!')

0 comments on commit 9f5cb9d

Please sign in to comment.