python: Fix using overrides when not building PyGObject

Since 547570cd79 we do not always build
PyGObject and our development environment is broken when trying to use
GStreamer python when built against system PyGObject with the following
error importing Gst in there:

```
12345678** (gst-plugin-scanner:710617): CRITICAL **: 11:45:02.343: can't find gi.repository.Gst
Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/gi/repository/__init__.py", line 23, in <module>
    from ..importer import DynamicImporter
  File "/usr/lib64/python3.9/site-packages/gi/importer.py", line 33, in <module>
    from .overrides import load_overrides
ImportError: cannot import name 'load_overrides' from 'gi.overrides' (/var/home/thiblahute/devel/gstreamer/gstreamer/subprojects/gst-editing-services/bindings/python/gi/overrides/__init__.py)
Factory Details:
```

The approach to fixing it is to implement override `gi` in
`gst-python/gi/` which we add to `PYTHONPATH`) and in there reset the
`gi` module to the right place and we get overrides from paths from
`_GI_OVERRIDES_PATH` we set in `gst-env.py` which points to all the
overrides that will be installed.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1155>
This commit is contained in:
Thibault Saunier 2021-10-14 17:01:01 -03:00 committed by GStreamer Marge Bot
parent ba79339d56
commit 1babccfe50
6 changed files with 98 additions and 4 deletions

View file

@ -386,6 +386,7 @@ def get_subprocess_env(options, gst_version):
presets = set() presets = set()
encoding_targets = set() encoding_targets = set()
python_dirs = setup_gdb(options) python_dirs = setup_gdb(options)
overrides_dirs = set()
if '--installed' in subprocess.check_output(meson + ['introspect', '-h']).decode(): if '--installed' in subprocess.check_output(meson + ['introspect', '-h']).decode():
installed_s = subprocess.check_output(meson + ['introspect', options.builddir, '--installed']) installed_s = subprocess.check_output(meson + ['introspect', options.builddir, '--installed'])
for path, installpath in json.loads(installed_s.decode()).items(): for path, installpath in json.loads(installed_s.decode()).items():
@ -406,7 +407,10 @@ def get_subprocess_env(options, gst_version):
if 'site-packages' in installpath_parts: if 'site-packages' in installpath_parts:
install_subpath = os.path.join(*installpath_parts[installpath_parts.index('site-packages') + 1:]) install_subpath = os.path.join(*installpath_parts[installpath_parts.index('site-packages') + 1:])
if path.endswith(install_subpath): if path.endswith(install_subpath):
python_dirs.add(path[:len (install_subpath) * -1]) if os.path.commonprefix(["gi/overrides", install_subpath]):
overrides_dirs.add(os.path.dirname(path))
else:
python_dirs.add(path[:len (install_subpath) * -1])
if path.endswith('.prs'): if path.endswith('.prs'):
presets.add(os.path.dirname(path)) presets.add(os.path.dirname(path))
@ -432,11 +436,18 @@ def get_subprocess_env(options, gst_version):
for python_dir in sorted(python_dirs): for python_dir in sorted(python_dirs):
prepend_env_var(env, 'PYTHONPATH', python_dir, options.sysroot) prepend_env_var(env, 'PYTHONPATH', python_dir, options.sysroot)
for python_dir in sorted(overrides_dirs):
prepend_env_var(env, '_GI_OVERRIDES_PATH', python_dir, options.sysroot)
mesonpath = os.path.join(SCRIPTDIR, "meson") mesonpath = os.path.join(SCRIPTDIR, "meson")
if os.path.join(mesonpath): if os.path.join(mesonpath):
# Add meson/ into PYTHONPATH if we are using a local meson # Add meson/ into PYTHONPATH if we are using a local meson
prepend_env_var(env, 'PYTHONPATH', mesonpath, options.sysroot) prepend_env_var(env, 'PYTHONPATH', mesonpath, options.sysroot)
# Ensure that gst-python/gi is used first
prepend_env_var(env, "PYTHONPATH", os.path.join(SCRIPTDIR, 'subprojects', 'gst-python'),
options.sysroot)
# For devhelp books # For devhelp books
if 'XDG_DATA_DIRS' not in env or not env['XDG_DATA_DIRS']: if 'XDG_DATA_DIRS' not in env or not env['XDG_DATA_DIRS']:
# Preserve default paths when empty # Preserve default paths when empty

View file

@ -368,14 +368,12 @@ endforeach
message('Building subprojects: ' + ', '.join(subprojects_names)) message('Building subprojects: ' + ', '.join(subprojects_names))
subdir('tests')
setenv = find_program('gst-env.py') setenv = find_program('gst-env.py')
devenv_cmd = [setenv, '--builddir=@0@'.format(meson.build_root()), devenv_cmd = [setenv, '--builddir=@0@'.format(meson.build_root()),
'--gstbuilddir=@0@'.format(meson.current_build_dir()), '--gstbuilddir=@0@'.format(meson.current_build_dir()),
'--srcdir=@0@'.format(meson.source_root())] '--srcdir=@0@'.format(meson.source_root())]
subdir('tests')
if meson.has_exe_wrapper() and build_machine.system() == 'linux' and host_machine.system() == 'windows' if meson.has_exe_wrapper() and build_machine.system() == 'linux' and host_machine.system() == 'windows'
# FIXME: Ideally we could get the wrapper directly from meson # FIXME: Ideally we could get the wrapper directly from meson
devenv_cmd += ['--wine', host_machine.cpu_family() == 'x86_64' ? 'wine64' : 'wine32'] devenv_cmd += ['--wine', host_machine.cpu_family() == 'x86_64' ? 'wine64' : 'wine32']

View file

@ -0,0 +1,45 @@
import gi
import os
import sys
import imp
from pathlib import Path
# Remove this dummy module the python path and
# try to import the actual gi module
sys.path.remove(str(Path(__file__).parents[1]))
del sys.modules["gi"]
import gi
class GstOverrideImport:
def find_module(self, fullname, path=None, target=None):
if fullname.startswith('gi.overrides'):
fp = None
try:
fp, _, _ = imp.find_module(fullname.split(
'.')[-1], os.environ.get('_GI_OVERRIDES_PATH', '').split(os.pathsep),)
except ImportError:
return None
finally:
if fp:
fp.close()
return self
return None
def load_module(self, name):
if name in sys.modules:
return sys.modules[name]
fp, pathname, description = imp.find_module(name.split(
'.')[-1], os.environ.get('_GI_OVERRIDES_PATH', '').split(os.pathsep),)
try:
module = imp.load_module(name, fp, pathname, description)
finally:
if fp:
fp.close()
sys.modules[name] = module
return module
sys.meta_path.insert(0, GstOverrideImport())

View file

@ -2,3 +2,4 @@ if get_option('tests').disabled()
subdir_done() subdir_done()
endif endif
subdir('static-plugins') subdir('static-plugins')
subdir('python')

19
tests/python/meson.build Normal file
View file

@ -0,0 +1,19 @@
gst_python = subproject('gst-python', required: false)
gir = find_program('g-ir-scanner', required : get_option('introspection'))
if not gst_python.found() or not gir.found()
message('Not running python devenv tests: gst_python: @0@ gir: @1@'.format(gst_python.found(), gir.found()))
subdir_done()
endif
root_rel = '../..'
python = import('python').find_installation()
if run_command(python, '-c', 'import gi').returncode() != 0
message('PyGObject not found, not running PyGObject tests')
subdir_done()
endif
test('python-overrides-devenv', setenv, args: ['--builddir=@0@'.format(meson.build_root()),
'--gstbuilddir=@0@'.format(meson.current_build_dir() / '..' / '..'),
'--srcdir=@0@'.format(meson.source_root()),
meson.current_source_dir() / 'python-devenv-overrides.py'])

View file

@ -0,0 +1,20 @@
#!/usr/bin/python3
import unittest
from pathlib import Path
from unittest import TestCase
from gi.repository import Gst
class TestBin(TestCase):
def test_overrides(self):
from gi.overrides import Gst
self.assertEqual(Path(Gst.__file__), Path(__file__).parents[2] / "subprojects/gst-python/gi/overrides/Gst.py")
def simple_functional_test(self):
Gst.init(None)
self.assertEqual(Gst.ElementFactory.make("bin", None).sinkpads, [])
self.assertEqual(float(Gst.Fraction(1, 2)), 0.5)
if __name__ == "__main__":
unittest.main()