Skip to content

Commit

Permalink
python module: support running the introspection script in process
Browse files Browse the repository at this point in the history
If the found program happens to be sys.executable, we can do an
optimization and get the function return value instead of communicating
via subprocess + json. The results are identical either way.
  • Loading branch information
eli-schwartz authored and dnicolodi committed Sep 8, 2023
1 parent 6cfd2b4 commit 432518e
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 58 deletions.
43 changes: 27 additions & 16 deletions mesonbuild/dependencies/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.
from __future__ import annotations

import functools, json, os, textwrap
import functools, json, os, textwrap, sys
from pathlib import Path
import typing as T

Expand Down Expand Up @@ -111,21 +111,32 @@ def _check_version(self, version: str) -> bool:
def sanity(self) -> bool:
# Sanity check, we expect to have something that at least quacks in tune

import importlib.resources

with importlib.resources.path('mesonbuild.scripts', 'python_info.py') as f:
cmd = self.get_command() + [str(f)]
p, stdout, stderr = mesonlib.Popen_safe(cmd)

try:
info = json.loads(stdout)
except json.JSONDecodeError:
info = None
mlog.debug('Could not introspect Python (%s): exit code %d' % (str(p.args), p.returncode))
mlog.debug('Program stdout:\n')
mlog.debug(stdout)
mlog.debug('Program stderr:\n')
mlog.debug(stderr)
if self.path == sys.executable:
try:
from ..scripts import python_info
info = python_info.main() # type: ignore[attr-defined]
except Exception:
import traceback
info = None
mlog.debug('Could not introspect internal Python')
mlog.debug('Error traceback:\n')
mlog.debug(traceback.format_exc())
else:
import importlib.resources
with importlib.resources.path('mesonbuild.scripts', 'python_info.py') as f:
cmd = self.get_command() + [os.fspath(f)]
print(f'running command: {cmd}')
print(f'running command: {mesonlib.join_args(str(s) for s in cmd)}')
p, stdout, stderr = mesonlib.Popen_safe(cmd)
try:
info = json.loads(stdout)
except json.JSONDecodeError:
info = None
mlog.debug('Could not introspect Python (%s): exit code %d' % (str(p.args), p.returncode))
mlog.debug('Program stdout:\n')
mlog.debug(stdout)
mlog.debug('Program stderr:\n')
mlog.debug(stderr)

if info is not None and self._check_version(info['version']):
self.info = T.cast('PythonIntrospectionDict', info)
Expand Down
86 changes: 44 additions & 42 deletions mesonbuild/scripts/python_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,56 +39,58 @@ def get_distutils_paths(scheme=None, prefix=None):
# 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_install_paths():
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', [])))

variables = sysconfig.get_config_vars()
variables.update({'base_prefix': getattr(sys, 'base_prefix', sys.prefix)})
def main():
variables = sysconfig.get_config_vars()
variables.update({'base_prefix': getattr(sys, 'base_prefix', sys.prefix)})
paths, install_paths = get_install_paths()

if sys.version_info < (3, 0):
suffix = variables.get('SO')
elif sys.version_info < (3, 8, 7):
# https://bugs.python.org/issue?@action=redirect&bpo=39825
from distutils.sysconfig import get_config_var
suffix = get_config_var('EXT_SUFFIX')
else:
suffix = variables.get('EXT_SUFFIX')
if sys.version_info < (3, 0):
suffix = variables.get('SO')
elif sys.version_info < (3, 8, 7):
# https://bugs.python.org/issue?@action=redirect&bpo=39825
from distutils.sysconfig import get_config_var
suffix = get_config_var('EXT_SUFFIX')
else:
suffix = variables.get('EXT_SUFFIX')

limited_api_suffix = None
if sys.version_info >= (3, 2):
try:
from importlib.machinery import EXTENSION_SUFFIXES
limited_api_suffix = EXTENSION_SUFFIXES[1]
except Exception:
pass
limited_api_suffix = None
if sys.version_info >= (3, 2):
try:
from importlib.machinery import EXTENSION_SUFFIXES
limited_api_suffix = EXTENSION_SUFFIXES[1]
except Exception:
pass

# pypy supports modules targetting the limited api but
# does not use a special suffix to distinguish them:
# https://doc.pypy.org/en/latest/cpython_differences.html#permitted-abi-tags-in-extensions
if '__pypy__' in sys.builtin_module_names:
limited_api_suffix = suffix
return {
'variables': variables,
'paths': paths,
'sysconfig_paths': sysconfig.get_paths(),
'install_paths': install_paths,
'version': sysconfig.get_python_version(),
'platform': sysconfig.get_platform(),
'is_pypy': '__pypy__' in sys.builtin_module_names,
'is_venv': sys.prefix != variables['base_prefix'],
'link_libpython': links_against_libpython(),
'suffix': suffix,
'limited_api_suffix': limited_api_suffix,
}

print(json.dumps({
'variables': variables,
'paths': paths,
'sysconfig_paths': sysconfig.get_paths(),
'install_paths': install_paths,
'version': sysconfig.get_python_version(),
'platform': sysconfig.get_platform(),
'is_pypy': '__pypy__' in sys.builtin_module_names,
'is_venv': sys.prefix != variables['base_prefix'],
'link_libpython': links_against_libpython(),
'suffix': suffix,
'limited_api_suffix': limited_api_suffix,
}))
if __name__ == '__main__':
print(json.dumps(main()))

0 comments on commit 432518e

Please sign in to comment.