Skip to content

Commit

Permalink
python module: move the introspection script into an external script
Browse files Browse the repository at this point in the history
We write this out as an embedded string to a tempfile in order to run
it, which is pretty awkward. And usually Meson's files are already files
on disk, not packed into a zip, so we can simply run it directly. Since
python 3.7, which is our new minimum, we can handle this well via the
stdlib. (There's also mesonbuild.mesondata, but we do not need
persistence in the builddir.)

This also solves the problem that has always been there, of giant python
programs inside strings occasionally confusing syntax highlighters. Or
even, it would be nice if we had syntax highlighting for this
introspection program. :D
  • Loading branch information
eli-schwartz authored and dcbaker committed Feb 22, 2023
1 parent b6ba054 commit 3bbfd4d
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 79 deletions.
85 changes: 6 additions & 79 deletions mesonbuild/modules/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,78 +338,6 @@ def set_env(name: str, value: str) -> None:
return candidates


INTROSPECT_COMMAND = '''\
import os.path
import sysconfig
import json
import sys
import distutils.command.install
def get_distutils_paths(scheme=None, prefix=None):
import distutils.dist
distribution = distutils.dist.Distribution()
install_cmd = distribution.get_command_obj('install')
if prefix is not None:
install_cmd.prefix = prefix
if scheme:
install_cmd.select_scheme(scheme)
install_cmd.finalize_options()
return {
'data': install_cmd.install_data,
'include': os.path.dirname(install_cmd.install_headers),
'platlib': install_cmd.install_platlib,
'purelib': install_cmd.install_purelib,
'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 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)})
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')
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,
}))
'''


class PythonExternalProgram(ExternalProgram):
def __init__(self, name: str, command: T.Optional[T.List[str]] = None,
ext_prog: T.Optional[ExternalProgram] = None):
Expand Down Expand Up @@ -447,13 +375,12 @@ def _check_version(self, version: str) -> bool:

def sanity(self, state: T.Optional['ModuleState'] = None) -> bool:
# Sanity check, we expect to have something that at least quacks in tune
from tempfile import NamedTemporaryFile
with NamedTemporaryFile(suffix='.py', delete=False, mode='w', encoding='utf-8') as tf:
tmpfilename = tf.name
tf.write(INTROSPECT_COMMAND)
cmd = self.get_command() + [tmpfilename]
p, stdout, stderr = mesonlib.Popen_safe(cmd)
os.unlink(tmpfilename)

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:
Expand Down
75 changes: 75 additions & 0 deletions mesonbuild/scripts/python_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env python

# ignore all lints for this file, since it is run by python2 as well

# type: ignore
# pylint: disable=deprecated-module

import os.path
import sysconfig
import json
import sys
import distutils.command.install

def get_distutils_paths(scheme=None, prefix=None):
import distutils.dist
distribution = distutils.dist.Distribution()
install_cmd = distribution.get_command_obj('install')
if prefix is not None:
install_cmd.prefix = prefix
if scheme:
install_cmd.select_scheme(scheme)
install_cmd.finalize_options()
return {
'data': install_cmd.install_data,
'include': os.path.dirname(install_cmd.install_headers),
'platlib': install_cmd.install_platlib,
'purelib': install_cmd.install_purelib,
'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 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)})

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')

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,
}))

0 comments on commit 3bbfd4d

Please sign in to comment.