%PDF- %PDF-
| Direktori : /snap/gnome-46-2404/current/usr/lib/python3/dist-packages/gi/overrides/ |
| Current File : //snap/gnome-46-2404/current/usr/lib/python3/dist-packages/gi/overrides/__init__.py |
import functools
import types
import warnings
import importlib
import sys
from gi import PyGIDeprecationWarning
from gi._gi import CallableInfo, pygobject_new_full
from gi._constants import TYPE_NONE, TYPE_INVALID
# support overrides in different directories than our gi module
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
# namespace -> (attr, replacement)
_deprecated_attrs = {}
class OverridesProxyModule(types.ModuleType):
"""Wraps a introspection module and contains all overrides"""
__slots__ = ("_deprecations", "_introspection_module")
def __init__(self, introspection_module):
super().__init__(introspection_module.__name__)
self._introspection_module = introspection_module
self._deprecations = {}
def __getattr__(self, name):
if name in self._deprecations:
value, warning = self._deprecations[name]
warnings.warn(warning, stacklevel=2)
return value
return getattr(self._introspection_module, name)
def __delattr__(self, name):
found = False
if name in self.__dict__:
del self.__dict__[name]
found = True
if name in self._deprecations:
del self._deprecations[name]
found = True
try:
delattr(self._introspection_module, name)
except AttributeError:
# Silence the exception if the attribute was previously found.
if not found:
raise
def __dir__(self):
result = set(super().__dir__())
result.update(self._deprecations.keys())
result.update(dir(self._introspection_module))
return sorted(result)
def __repr__(self):
return f"<{type(self).__name__} {self._introspection_module!r}>"
class _DeprecatedAttribute:
"""A deprecation descriptor for OverridesProxyModule subclasses.
Emits a PyGIDeprecationWarning on every access and tries to act as a
normal instance attribute (can be replaced and deleted).
"""
def __init__(self, namespace, attr, value, replacement):
self._attr = attr
self._value = value
self._warning = PyGIDeprecationWarning(
f"{namespace}.{attr} is deprecated; use {replacement} instead"
)
def __get__(self, instance, owner):
if instance is None:
raise AttributeError(self._attr)
warnings.warn(self._warning, stacklevel=2)
return self._value
def __set__(self, instance, value):
attr = self._attr
# delete the descriptor, then set the instance value
delattr(type(instance), attr)
setattr(instance, attr, value)
def __delete__(self, instance):
# delete the descriptor
delattr(type(instance), self._attr)
def load_overrides(introspection_module):
"""Loads overrides for an introspection module.
Either returns the same module again in case there are no overrides or a
proxy module including overrides. Doesn't cache the result.
"""
namespace = introspection_module.__name__.rsplit(".", 1)[-1]
module_key = "gi.repository." + namespace
# We use sys.modules so overrides can import from gi.repository
# but restore everything at the end so this doesn't have any side effects
has_old = module_key in sys.modules
old_module = sys.modules.get(module_key)
proxy = OverridesProxyModule(introspection_module)
sys.modules[module_key] = proxy
# backwards compat:
# gedit uses gi.importer.modules['Gedit']._introspection_module
from ..importer import modules
assert hasattr(proxy, "_introspection_module")
modules[namespace] = proxy
try:
override_package_name = "gi.overrides." + namespace
spec = importlib.util.find_spec(override_package_name)
override_loader = spec.loader if spec is not None else None
# Avoid checking for an ImportError, an override might
# depend on a missing module thus causing an ImportError
if override_loader is None:
return introspection_module
override_mod = importlib.import_module(override_package_name)
finally:
del modules[namespace]
del sys.modules[module_key]
if has_old:
sys.modules[module_key] = old_module
# backwards compat: for gst-python/gstmodule.c,
# which tries to access Gst.Fraction through
# Gst._overrides_module.Fraction. We assign the proxy instead as that
# contains all overridden classes like Fraction during import anyway and
# there is no need to keep the real override module alive.
proxy._overrides_module = proxy
override_all = []
if hasattr(override_mod, "__all__"):
override_all = override_mod.__all__
for var in override_all:
try:
item = getattr(override_mod, var)
except (AttributeError, TypeError):
# Gedit puts a non-string in __all__, so catch TypeError here
continue
setattr(proxy, var, item)
# Replace deprecated module level attributes with a descriptor
# which emits a warning when accessed.
for attr, replacement in _deprecated_attrs.pop(namespace, []):
try:
value = getattr(proxy, attr)
except AttributeError:
raise AssertionError(
f"{attr} was set deprecated but wasn't added to __all__"
)
delattr(proxy, attr)
proxy._deprecations[attr] = (
value,
PyGIDeprecationWarning(
f"{namespace}.{attr} is deprecated; use {replacement} instead"
),
)
return proxy
def override(type_):
"""Decorator for registering an override.
Other than objects added to __all__, these can get referenced in the same
override module via the gi.repository module (get_parent_for_object() does
for example), so they have to be added to the module immediately.
"""
if isinstance(type_, CallableInfo):
func = type_
namespace = func.__module__.rsplit(".", 1)[-1]
module = sys.modules["gi.repository." + namespace]
def wrapper(func):
setattr(module, func.__name__, func)
return func
return wrapper
if isinstance(type_, types.FunctionType):
raise TypeError(f"func must be a gi function, got {type_}")
try:
info = getattr(type_, "__info__")
except AttributeError:
raise TypeError(
f"Can not override a type {type_.__name__}, which is not in a gobject "
"introspection typelib"
)
if not type_.__module__.startswith("gi.overrides"):
raise KeyError(
"You have tried override outside of the overrides module. "
f"This is not allowed ({type_}, {type_.__module__})"
)
g_type = info.get_g_type()
assert g_type != TYPE_NONE
if g_type != TYPE_INVALID:
g_type.pytype = type_
namespace = type_.__module__.rsplit(".", 1)[-1]
module = sys.modules["gi.repository." + namespace]
setattr(module, type_.__name__, type_)
return type_
overridefunc = override
"""Deprecated"""
def deprecated(fn, replacement):
"""Decorator for marking methods and classes as deprecated."""
@functools.wraps(fn)
def wrapped(*args, **kwargs):
warnings.warn(
f"{fn.__name__} is deprecated; use {replacement} instead",
PyGIDeprecationWarning,
stacklevel=2,
)
return fn(*args, **kwargs)
return wrapped
def deprecated_attr(namespace, attr, replacement):
"""Marks a module level attribute as deprecated. Accessing it will emit
a PyGIDeprecationWarning warning.
e.g. for ``deprecated_attr("GObject", "STATUS_FOO", "GLib.Status.FOO")``
accessing GObject.STATUS_FOO will emit:
"GObject.STATUS_FOO is deprecated; use GLib.Status.FOO instead"
:param str namespace:
The namespace of the override this is called in.
:param str namespace:
The attribute name (which gets added to __all__).
:param str replacement:
The replacement text which will be included in the warning.
"""
_deprecated_attrs.setdefault(namespace, []).append((attr, replacement))
def deprecated_init(
super_init_func,
arg_names,
ignore=(),
deprecated_aliases={},
deprecated_defaults={},
category=PyGIDeprecationWarning,
stacklevel=2,
):
"""Wrapper for deprecating GObject based __init__ methods which specify
defaults already available or non-standard defaults.
:param callable super_init_func:
Initializer to wrap.
:param list arg_names:
Ordered argument name list.
:param list ignore:
List of argument names to ignore when calling the wrapped function.
This is useful for function which take a non-standard keyword that is munged elsewhere.
:param dict deprecated_aliases:
Dictionary mapping a keyword alias to the actual g_object_newv keyword.
:param dict deprecated_defaults:
Dictionary of non-standard defaults that will be used when the
keyword is not explicitly passed.
:param Exception category:
Exception category of the error.
:param int stacklevel:
Stack level for the deprecation passed on to warnings.warn
:returns: Wrapped version of ``super_init_func`` which gives a deprecation
warning when non-keyword args or aliases are used.
:rtype: callable
"""
# We use a list of argument names to maintain order of the arguments
# being deprecated. This allows calls with positional arguments to
# continue working but with a deprecation message.
def new_init(self, *args, **kwargs):
"""Initializer for a GObject based classes with support for property
sets through the use of explicit keyword arguments.
"""
# Print warnings for calls with positional arguments.
if args:
warnings.warn(
"Using positional arguments with the GObject constructor has been deprecated. "
'Please specify keyword(s) for "{}" or use a class specific constructor. '
"See: https://wiki.gnome.org/Projects/PyGObject/InitializerDeprecations".format(
", ".join(arg_names[: len(args)])
),
category,
stacklevel=stacklevel,
)
new_kwargs = dict(zip(arg_names, args))
else:
new_kwargs = {}
new_kwargs.update(kwargs)
# Print warnings for alias usage and transfer them into the new key.
aliases_used = []
for key, alias in deprecated_aliases.items():
if alias in new_kwargs:
new_kwargs[key] = new_kwargs.pop(alias)
aliases_used.append(key)
if aliases_used:
warnings.warn(
'The keyword(s) "{}" have been deprecated in favor of "{}" respectively. '
"See: https://wiki.gnome.org/Projects/PyGObject/InitializerDeprecations".format(
", ".join(deprecated_aliases[k] for k in sorted(aliases_used)),
", ".join(sorted(aliases_used)),
),
category,
stacklevel=stacklevel,
)
# Print warnings for defaults different than what is already provided by the property
defaults_used = []
for key in deprecated_defaults:
if key not in new_kwargs:
new_kwargs[key] = deprecated_defaults[key]
defaults_used.append(key)
if defaults_used:
warnings.warn(
"Initializer is relying on deprecated non-standard "
"defaults. Please update to explicitly use: {} "
"See: https://wiki.gnome.org/Projects/PyGObject/InitializerDeprecations".format(
", ".join(
f"{k}={deprecated_defaults[k]}" for k in sorted(defaults_used)
)
),
category,
stacklevel=stacklevel,
)
# Remove keywords that should be ignored.
for key in ignore:
if key in new_kwargs:
new_kwargs.pop(key)
return super_init_func(self, **new_kwargs)
return new_init
def strip_boolean_result(method, exc_type=None, exc_str=None, fail_ret=None):
"""Translate method's return value for stripping off success flag.
There are a lot of methods which return a "success" boolean and have
several out arguments. Translate such a method to return the out arguments
on success and None on failure.
"""
@functools.wraps(method)
def wrapped(*args, **kwargs):
ret = method(*args, **kwargs)
if ret[0]:
if len(ret) == 2:
return ret[1]
return ret[1:]
if exc_type:
raise exc_type(exc_str or "call failed")
return fail_ret
return wrapped
def wrap_list_store_sort_func(func):
def wrap(a, b, *user_data):
a = pygobject_new_full(a, False)
b = pygobject_new_full(b, False)
return func(a, b, *user_data)
return wrap
def wrap_list_store_equal_func(func):
def wrap(a, b, *user_data):
a = pygobject_new_full(a, False)
b = pygobject_new_full(b, False)
return func(a, b, *user_data)
return wrap