Skip to content

Commit b598a2a

Browse files
eli-schwartzdnicolodi
authored andcommitted
python module: support running the introspection script in process
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.
1 parent 6cfd2b4 commit b598a2a

File tree

2 files changed

+77
-58
lines changed

2 files changed

+77
-58
lines changed

mesonbuild/dependencies/python.py

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414
from __future__ import annotations
1515

16-
import functools, json, os, textwrap
16+
import functools, json, os, textwrap, sys
1717
from pathlib import Path
1818
import typing as T
1919

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

114-
import importlib.resources
115-
116-
with importlib.resources.path('mesonbuild.scripts', 'python_info.py') as f:
117-
cmd = self.get_command() + [str(f)]
118-
p, stdout, stderr = mesonlib.Popen_safe(cmd)
119-
120-
try:
121-
info = json.loads(stdout)
122-
except json.JSONDecodeError:
123-
info = None
124-
mlog.debug('Could not introspect Python (%s): exit code %d' % (str(p.args), p.returncode))
125-
mlog.debug('Program stdout:\n')
126-
mlog.debug(stdout)
127-
mlog.debug('Program stderr:\n')
128-
mlog.debug(stderr)
114+
if self.path == sys.executable:
115+
try:
116+
from ..scripts import python_info
117+
info = python_info.main() # type: ignore[attr-defined]
118+
except Exception:
119+
import traceback
120+
info = None
121+
mlog.debug('Could not introspect internal Python')
122+
mlog.debug('Error traceback:\n')
123+
mlog.debug(traceback.format_exc())
124+
else:
125+
import importlib.resources
126+
with importlib.resources.path('mesonbuild.scripts', 'python_info.py') as f:
127+
cmd = self.get_command() + [os.fspath(f)]
128+
print(f'running command: {cmd}')
129+
print(f'running command: {mesonlib.join_args(str(s) for s in cmd)}')
130+
p, stdout, stderr = mesonlib.Popen_safe(cmd)
131+
try:
132+
info = json.loads(stdout)
133+
except json.JSONDecodeError:
134+
info = None
135+
mlog.debug('Could not introspect Python (%s): exit code %d' % (str(p.args), p.returncode))
136+
mlog.debug('Program stdout:\n')
137+
mlog.debug(stdout)
138+
mlog.debug('Program stderr:\n')
139+
mlog.debug(stderr)
129140

130141
if info is not None and self._check_version(info['version']):
131142
self.info = T.cast('PythonIntrospectionDict', info)

mesonbuild/scripts/python_info.py

Lines changed: 50 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -39,56 +39,64 @@ def get_distutils_paths(scheme=None, prefix=None):
3939
# See https://github.com/mesonbuild/meson/issues/8739.
4040
# XXX: We should be using sysconfig, but Debian only patches distutils.
4141

42-
if 'deb_system' in distutils.command.install.INSTALL_SCHEMES:
43-
paths = get_distutils_paths(scheme='deb_system')
44-
install_paths = get_distutils_paths(scheme='deb_system', prefix='')
45-
else:
46-
paths = sysconfig.get_paths()
47-
empty_vars = {'base': '', 'platbase': '', 'installed_base': ''}
48-
install_paths = sysconfig.get_paths(vars=empty_vars)
42+
def get_install_paths():
43+
import distutils.command.install
44+
if 'deb_system' in distutils.command.install.INSTALL_SCHEMES:
45+
paths = get_distutils_paths(scheme='deb_system')
46+
install_paths = get_distutils_paths(scheme='deb_system', prefix='')
47+
else:
48+
paths = sysconfig.get_paths()
49+
empty_vars = {'base': '', 'platbase': '', 'installed_base': ''}
50+
install_paths = sysconfig.get_paths(vars=empty_vars)
51+
return paths, install_paths
4952

5053
def links_against_libpython():
5154
from distutils.core import Distribution, Extension
5255
cmd = Distribution().get_command_obj('build_ext')
5356
cmd.ensure_finalized()
5457
return bool(cmd.get_libraries(Extension('dummy', [])))
5558

56-
variables = sysconfig.get_config_vars()
57-
variables.update({'base_prefix': getattr(sys, 'base_prefix', sys.prefix)})
59+
def main():
60+
variables = sysconfig.get_config_vars()
61+
variables.update({'base_prefix': getattr(sys, 'base_prefix', sys.prefix)})
62+
paths, install_paths = get_install_paths()
5863

59-
if sys.version_info < (3, 0):
60-
suffix = variables.get('SO')
61-
elif sys.version_info < (3, 8, 7):
62-
# https://bugs.python.org/issue?@action=redirect&bpo=39825
63-
from distutils.sysconfig import get_config_var
64-
suffix = get_config_var('EXT_SUFFIX')
65-
else:
66-
suffix = variables.get('EXT_SUFFIX')
64+
if sys.version_info < (3, 0):
65+
suffix = variables.get('SO')
66+
elif sys.version_info < (3, 8, 7):
67+
# https://bugs.python.org/issue?@action=redirect&bpo=39825
68+
from distutils.sysconfig import get_config_var
69+
suffix = get_config_var('EXT_SUFFIX')
70+
else:
71+
suffix = variables.get('EXT_SUFFIX')
6772

68-
limited_api_suffix = None
69-
if sys.version_info >= (3, 2):
70-
try:
71-
from importlib.machinery import EXTENSION_SUFFIXES
72-
limited_api_suffix = EXTENSION_SUFFIXES[1]
73-
except Exception:
74-
pass
73+
limited_api_suffix = None
74+
if sys.version_info >= (3, 2):
75+
try:
76+
from importlib.machinery import EXTENSION_SUFFIXES
77+
limited_api_suffix = EXTENSION_SUFFIXES[1]
78+
except Exception:
79+
pass
7580

76-
# pypy supports modules targetting the limited api but
77-
# does not use a special suffix to distinguish them:
78-
# https://doc.pypy.org/en/latest/cpython_differences.html#permitted-abi-tags-in-extensions
79-
if '__pypy__' in sys.builtin_module_names:
80-
limited_api_suffix = suffix
81+
# pypy supports modules targetting the limited api but
82+
# does not use a special suffix to distinguish them:
83+
# https://doc.pypy.org/en/latest/cpython_differences.html#permitted-abi-tags-in-extensions
84+
if '__pypy__' in sys.builtin_module_names:
85+
limited_api_suffix = suffix
8186

82-
print(json.dumps({
83-
'variables': variables,
84-
'paths': paths,
85-
'sysconfig_paths': sysconfig.get_paths(),
86-
'install_paths': install_paths,
87-
'version': sysconfig.get_python_version(),
88-
'platform': sysconfig.get_platform(),
89-
'is_pypy': '__pypy__' in sys.builtin_module_names,
90-
'is_venv': sys.prefix != variables['base_prefix'],
91-
'link_libpython': links_against_libpython(),
92-
'suffix': suffix,
93-
'limited_api_suffix': limited_api_suffix,
94-
}))
87+
return {
88+
'variables': variables,
89+
'paths': paths,
90+
'sysconfig_paths': sysconfig.get_paths(),
91+
'install_paths': install_paths,
92+
'version': sysconfig.get_python_version(),
93+
'platform': sysconfig.get_platform(),
94+
'is_pypy': '__pypy__' in sys.builtin_module_names,
95+
'is_venv': sys.prefix != variables['base_prefix'],
96+
'link_libpython': links_against_libpython(),
97+
'suffix': suffix,
98+
'limited_api_suffix': limited_api_suffix,
99+
}
100+
101+
if __name__ == '__main__':
102+
print(json.dumps(main()))

0 commit comments

Comments
 (0)