summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph Reiter <reiter.christoph@gmail.com>2015-01-27 16:06:03 +0100
committerChristoph Reiter <creiter@src.gnome.org>2015-03-03 13:17:20 +0100
commitf74acb38f1410982f3419acb134adf173600e497 (patch)
tree30116482b258ed94b481a2c611ef5222491b61fa
parenta10df7d28c01d70ee33d8e4e9ddaa23f1990ca60 (diff)
downloadpygobject-f74acb38f1410982f3419acb134adf173600e497.tar.gz
Emit PyGIDeprecationWarning when accessing deprecated override attributes.
Adds a new helper function for overrides to mark a module level attribute as deprecated. A warning will be emitted every time the attribute gets accessed. e.g. when marking GObject.STATUS_FOO as deprecated using STATUS_FOO = GLib.Status.FOO deprecated_attr("GObject", "STATUS_FOO", "GLib.Status.FOO") __all__.append("STATUS_FOO") accessing it will emit "GObject.STATUS_FOO is deprecated; use GLib.Status.FOO instead" https://bugzilla.gnome.org/show_bug.cgi?id=743514
-rw-r--r--gi/overrides/GLib.py44
-rw-r--r--gi/overrides/GObject.py90
-rw-r--r--gi/overrides/__init__.py74
-rw-r--r--tests/test_gi.py70
4 files changed, 221 insertions, 57 deletions
diff --git a/gi/overrides/GLib.py b/gi/overrides/GLib.py
index ce15da1f..c1f1691a 100644
--- a/gi/overrides/GLib.py
+++ b/gi/overrides/GLib.py
@@ -26,7 +26,7 @@ import sys
from ..module import get_introspection_module
from .._gi import (variant_type_from_string, source_new,
source_set_callback, io_channel_read)
-from ..overrides import override, deprecated
+from ..overrides import override, deprecated, deprecated_attr
from gi import PyGIDeprecationWarning, version_info
GLib = get_introspection_module('GLib')
@@ -481,8 +481,10 @@ __all__.append('markup_escape_text')
# backwards compatible names from old static bindings
for n in ['DESKTOP', 'DOCUMENTS', 'DOWNLOAD', 'MUSIC', 'PICTURES',
'PUBLIC_SHARE', 'TEMPLATES', 'VIDEOS']:
- globals()['USER_DIRECTORY_' + n] = getattr(GLib.UserDirectory, 'DIRECTORY_' + n)
- __all__.append('USER_DIRECTORY_' + n)
+ attr = 'USER_DIRECTORY_' + n
+ deprecated_attr("GLib", attr, "GLib.UserDirectory.DIRECTORY_" + n)
+ globals()[attr] = getattr(GLib.UserDirectory, 'DIRECTORY_' + n)
+ __all__.append(attr)
for n in ['ERR', 'HUP', 'IN', 'NVAL', 'OUT', 'PRI']:
globals()['IO_' + n] = getattr(GLib.IOCondition, n)
@@ -490,30 +492,42 @@ for n in ['ERR', 'HUP', 'IN', 'NVAL', 'OUT', 'PRI']:
for n in ['APPEND', 'GET_MASK', 'IS_READABLE', 'IS_SEEKABLE',
'MASK', 'NONBLOCK', 'SET_MASK']:
- globals()['IO_FLAG_' + n] = getattr(GLib.IOFlags, n)
- __all__.append('IO_FLAG_' + n)
+ attr = 'IO_FLAG_' + n
+ deprecated_attr("GLib", attr, "GLib.IOFlags." + n)
+ globals()[attr] = getattr(GLib.IOFlags, n)
+ __all__.append(attr)
+
# spelling for the win
IO_FLAG_IS_WRITEABLE = GLib.IOFlags.IS_WRITABLE
+deprecated_attr("GLib", "IO_FLAG_IS_WRITEABLE", "GLib.IOFlags.IS_WRITABLE")
__all__.append('IO_FLAG_IS_WRITEABLE')
for n in ['AGAIN', 'EOF', 'ERROR', 'NORMAL']:
- globals()['IO_STATUS_' + n] = getattr(GLib.IOStatus, n)
- __all__.append('IO_STATUS_' + n)
+ attr = 'IO_STATUS_' + n
+ globals()[attr] = getattr(GLib.IOStatus, n)
+ deprecated_attr("GLib", attr, "GLib.IOStatus." + n)
+ __all__.append(attr)
for n in ['CHILD_INHERITS_STDIN', 'DO_NOT_REAP_CHILD', 'FILE_AND_ARGV_ZERO',
'LEAVE_DESCRIPTORS_OPEN', 'SEARCH_PATH', 'STDERR_TO_DEV_NULL',
'STDOUT_TO_DEV_NULL']:
- globals()['SPAWN_' + n] = getattr(GLib.SpawnFlags, n)
- __all__.append('SPAWN_' + n)
+ attr = 'SPAWN_' + n
+ globals()[attr] = getattr(GLib.SpawnFlags, n)
+ deprecated_attr("GLib", attr, "GLib.SpawnFlags." + n)
+ __all__.append(attr)
for n in ['HIDDEN', 'IN_MAIN', 'REVERSE', 'NO_ARG', 'FILENAME', 'OPTIONAL_ARG',
'NOALIAS']:
- globals()['OPTION_FLAG_' + n] = getattr(GLib.OptionFlags, n)
- __all__.append('OPTION_FLAG_' + n)
+ attr = 'OPTION_FLAG_' + n
+ globals()[attr] = getattr(GLib.OptionFlags, n)
+ deprecated_attr("GLib", attr, "GLib.OptionFlags." + n)
+ __all__.append(attr)
for n in ['UNKNOWN_OPTION', 'BAD_VALUE', 'FAILED']:
- globals()['OPTION_ERROR_' + n] = getattr(GLib.OptionError, n)
- __all__.append('OPTION_ERROR_' + n)
+ attr = 'OPTION_ERROR_' + n
+ deprecated_attr("GLib", attr, "GLib.OptionError." + n)
+ globals()[attr] = getattr(GLib.OptionError, n)
+ __all__.append(attr)
class MainLoop(GLib.MainLoop):
@@ -901,5 +915,9 @@ if not hasattr(GLib, 'unix_signal_add_full'):
# obsolete constants for backwards compatibility
glib_version = (GLib.MAJOR_VERSION, GLib.MINOR_VERSION, GLib.MICRO_VERSION)
__all__.append('glib_version')
+deprecated_attr("GLib", "glib_version",
+ "(GLib.MAJOR_VERSION, GLib.MINOR_VERSION, GLib.MICRO_VERSION)")
+
pyglib_version = version_info
__all__.append('pyglib_version')
+deprecated_attr("GLib", "pyglib_version", "gi.version_info")
diff --git a/gi/overrides/GObject.py b/gi/overrides/GObject.py
index e922ac09..d0d52252 100644
--- a/gi/overrides/GObject.py
+++ b/gi/overrides/GObject.py
@@ -27,7 +27,7 @@ from collections import namedtuple
import gi.overrides
import gi.module
-from gi.overrides import override
+from gi.overrides import override, deprecated_attr
from gi.repository import GLib
from gi import PyGIDeprecationWarning
@@ -56,10 +56,11 @@ for name in ['markup_escape_text', 'get_application_name',
'idle_add', 'timeout_add', 'timeout_add_seconds',
'io_add_watch', 'child_watch_add', 'get_current_time',
'spawn_async']:
- globals()[name] = gi.overrides.deprecated(getattr(GLib, name), 'GLib.' + name)
+ globals()[name] = getattr(GLib, name)
+ deprecated_attr("GObject", name, "GLib." + name)
__all__.append(name)
-# constants are also deprecated, but cannot mark them as such
+# deprecated constants
for name in ['PRIORITY_DEFAULT', 'PRIORITY_DEFAULT_IDLE', 'PRIORITY_HIGH',
'PRIORITY_HIGH_IDLE', 'PRIORITY_LOW',
'IO_IN', 'IO_OUT', 'IO_PRI', 'IO_ERR', 'IO_HUP', 'IO_NVAL',
@@ -77,25 +78,21 @@ for name in ['PRIORITY_DEFAULT', 'PRIORITY_DEFAULT_IDLE', 'PRIORITY_HIGH',
'OPTION_FLAG_NOALIAS', 'OPTION_ERROR_UNKNOWN_OPTION',
'OPTION_ERROR_BAD_VALUE', 'OPTION_ERROR_FAILED', 'OPTION_REMAINING',
'glib_version']:
- globals()[name] = getattr(GLib, name)
+ with warnings.catch_warnings():
+ # TODO: this uses deprecated Glib attributes, silence for now
+ warnings.simplefilter('ignore', PyGIDeprecationWarning)
+ globals()[name] = getattr(GLib, name)
+ deprecated_attr("GObject", name, "GLib." + name)
__all__.append(name)
-G_MININT8 = GLib.MININT8
-G_MAXINT8 = GLib.MAXINT8
-G_MAXUINT8 = GLib.MAXUINT8
-G_MININT16 = GLib.MININT16
-G_MAXINT16 = GLib.MAXINT16
-G_MAXUINT16 = GLib.MAXUINT16
-G_MININT32 = GLib.MININT32
-G_MAXINT32 = GLib.MAXINT32
-G_MAXUINT32 = GLib.MAXUINT32
-G_MININT64 = GLib.MININT64
-G_MAXINT64 = GLib.MAXINT64
-G_MAXUINT64 = GLib.MAXUINT64
-__all__ += ['G_MININT8', 'G_MAXINT8', 'G_MAXUINT8', 'G_MININT16',
- 'G_MAXINT16', 'G_MAXUINT16', 'G_MININT32', 'G_MAXINT32',
- 'G_MAXUINT32', 'G_MININT64', 'G_MAXINT64', 'G_MAXUINT64']
+for name in ['G_MININT8', 'G_MAXINT8', 'G_MAXUINT8', 'G_MININT16',
+ 'G_MAXINT16', 'G_MAXUINT16', 'G_MININT32', 'G_MAXINT32',
+ 'G_MAXUINT32', 'G_MININT64', 'G_MAXINT64', 'G_MAXUINT64']:
+ new_name = name.split("_", 1)[-1]
+ globals()[name] = getattr(GLib, new_name)
+ deprecated_attr("GObject", name, "GLib." + new_name)
+ __all__.append(name)
# these are not currently exported in GLib gir, presumably because they are
# platform dependent; so get them from our static bindings
@@ -145,38 +142,44 @@ __all__ += ['TYPE_INVALID', 'TYPE_NONE', 'TYPE_INTERFACE', 'TYPE_CHAR',
# Deprecated, use GLib directly
-Pid = GLib.Pid
-GError = GLib.GError
-OptionGroup = GLib.OptionGroup
-OptionContext = GLib.OptionContext
-__all__ += ['Pid', 'GError', 'OptionGroup', 'OptionContext']
+for name in ['Pid', 'GError', 'OptionGroup', 'OptionContext']:
+ globals()[name] = getattr(GLib, name)
+ deprecated_attr("GObject", name, "GLib." + name)
+ __all__.append(name)
# Deprecated, use: GObject.ParamFlags.* directly
-PARAM_CONSTRUCT = GObjectModule.ParamFlags.CONSTRUCT
-PARAM_CONSTRUCT_ONLY = GObjectModule.ParamFlags.CONSTRUCT_ONLY
-PARAM_LAX_VALIDATION = GObjectModule.ParamFlags.LAX_VALIDATION
-PARAM_READABLE = GObjectModule.ParamFlags.READABLE
-PARAM_WRITABLE = GObjectModule.ParamFlags.WRITABLE
+for name in ['PARAM_CONSTRUCT', 'PARAM_CONSTRUCT_ONLY', 'PARAM_LAX_VALIDATION',
+ 'PARAM_READABLE', 'PARAM_WRITABLE']:
+ new_name = name.split("_", 1)[-1]
+ globals()[name] = getattr(GObjectModule.ParamFlags, new_name)
+ deprecated_attr("GObject", name, "GObject.ParamFlags." + new_name)
+ __all__.append(name)
+
# PARAM_READWRITE should come from the gi module but cannot due to:
# https://bugzilla.gnome.org/show_bug.cgi?id=687615
-PARAM_READWRITE = PARAM_READABLE | PARAM_WRITABLE
-__all__ += ['PARAM_CONSTRUCT', 'PARAM_CONSTRUCT_ONLY', 'PARAM_LAX_VALIDATION',
- 'PARAM_READABLE', 'PARAM_WRITABLE', 'PARAM_READWRITE']
+PARAM_READWRITE = GObjectModule.ParamFlags.READABLE | \
+ GObjectModule.ParamFlags.WRITABLE
+__all__.append("PARAM_READWRITE")
+# READWRITE is part of ParamFlags since glib 2.42. Only mark PARAM_READWRITE as
+# deprecated in case ParamFlags.READWRITE is available. Also include the glib
+# version in the warning so it's clear that this needs a newer glib, unlike
+# the other ParamFlags related deprecations.
+# https://bugzilla.gnome.org/show_bug.cgi?id=726037
+if hasattr(GObjectModule.ParamFlags, "READWRITE"):
+ deprecated_attr("GObject", "PARAM_READWRITE",
+ "GObject.ParamFlags.READWRITE (glib 2.42+)")
-# Deprecated, use: GObject.SignalFlags.* directly
-SIGNAL_ACTION = GObjectModule.SignalFlags.ACTION
-SIGNAL_DETAILED = GObjectModule.SignalFlags.DETAILED
-SIGNAL_NO_HOOKS = GObjectModule.SignalFlags.NO_HOOKS
-SIGNAL_NO_RECURSE = GObjectModule.SignalFlags.NO_RECURSE
-SIGNAL_RUN_CLEANUP = GObjectModule.SignalFlags.RUN_CLEANUP
-SIGNAL_RUN_FIRST = GObjectModule.SignalFlags.RUN_FIRST
-SIGNAL_RUN_LAST = GObjectModule.SignalFlags.RUN_LAST
-__all__ += ['SIGNAL_ACTION', 'SIGNAL_DETAILED', 'SIGNAL_NO_HOOKS',
- 'SIGNAL_NO_RECURSE', 'SIGNAL_RUN_CLEANUP', 'SIGNAL_RUN_FIRST',
- 'SIGNAL_RUN_LAST']
+# Deprecated, use: GObject.SignalFlags.* directly
+for name in ['SIGNAL_ACTION', 'SIGNAL_DETAILED', 'SIGNAL_NO_HOOKS',
+ 'SIGNAL_NO_RECURSE', 'SIGNAL_RUN_CLEANUP', 'SIGNAL_RUN_FIRST',
+ 'SIGNAL_RUN_LAST']:
+ new_name = name.split("_", 1)[-1]
+ globals()[name] = getattr(GObjectModule.SignalFlags, new_name)
+ deprecated_attr("GObject", name, "GObject.SignalFlags." + new_name)
+ __all__.append(name)
# Static types
GBoxed = _gobject.GBoxed
@@ -705,4 +708,5 @@ SignalOverride = signalhelper.SignalOverride
# Deprecated naming "property" available for backwards compatibility.
# Keep this at the end of the file to avoid clobbering the builtin.
property = Property
+deprecated_attr("GObject", "property", "GObject.Property")
__all__ += ['Property', 'Signal', 'SignalOverride', 'property']
diff --git a/gi/overrides/__init__.py b/gi/overrides/__init__.py
index b337b355..62cfd308 100644
--- a/gi/overrides/__init__.py
+++ b/gi/overrides/__init__.py
@@ -14,6 +14,10 @@ from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
+# namespace -> (attr, replacement)
+_deprecated_attrs = {}
+
+
def wraps(wrapped):
def assign(wrapper):
wrapper.__name__ = wrapped.__name__
@@ -43,6 +47,37 @@ class OverridesProxyModule(types.ModuleType):
return "<%s %r>" % (type(self).__name__, self._introspection_module)
+class _DeprecatedAttribute(object):
+ """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(
+ '%s.%s is deprecated; use %s instead' % (
+ namespace, attr, replacement))
+
+ 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.
@@ -58,7 +93,11 @@ def load_overrides(introspection_module):
has_old = module_key in sys.modules
old_module = sys.modules.get(module_key)
- proxy = OverridesProxyModule(introspection_module)
+ # Create a new sub type, so we can separate descriptors like
+ # _DeprecatedAttribute for each namespace.
+ proxy_type = type(namespace + "ProxyModule", (OverridesProxyModule, ), {})
+
+ proxy = proxy_type(introspection_module)
sys.modules[module_key] = proxy
# backwards compat:
@@ -90,6 +129,19 @@ def load_overrides(introspection_module):
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(
+ "%s was set deprecated but wasn't added to __all__" % attr)
+ delattr(proxy, attr)
+ deprecated_attr = _DeprecatedAttribute(
+ namespace, attr, value, replacement)
+ setattr(proxy_type, attr, deprecated_attr)
+
return proxy
@@ -152,6 +204,26 @@ def deprecated(fn, replacement):
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=tuple(),
deprecated_aliases={}, deprecated_defaults={},
category=PyGIDeprecationWarning,
diff --git a/tests/test_gi.py b/tests/test_gi.py
index dc40f3c5..a803529c 100644
--- a/tests/test_gi.py
+++ b/tests/test_gi.py
@@ -2789,6 +2789,76 @@ class TestDeprecation(unittest.TestCase):
self.assertTrue(issubclass(warn[0].category, DeprecationWarning))
self.assertEqual(str(warn[0].message), "GLib.strcasecmp is deprecated")
+ def test_deprecated_attribute_compat(self):
+ # test if the deprecation descriptor behaves like an instance attribute
+
+ # save the descriptor
+ desc = type(GLib).__dict__["IO_STATUS_ERROR"]
+
+ # the descriptor raises AttributeError for itself
+ self.assertFalse(hasattr(type(GLib), "IO_STATUS_ERROR"))
+
+ self.assertTrue(hasattr(GLib, "IO_STATUS_ERROR"))
+
+ try:
+ # check if replacing works
+ GLib.IO_STATUS_ERROR = "foo"
+ self.assertEqual(GLib.IO_STATUS_ERROR, "foo")
+ finally:
+ # restore descriptor
+ try:
+ del GLib.IO_STATUS_ERROR
+ except AttributeError:
+ pass
+ setattr(type(GLib), "IO_STATUS_ERROR", desc)
+
+ try:
+ # check if deleting works
+ del GLib.IO_STATUS_ERROR
+ self.assertFalse(hasattr(GLib, "IO_STATUS_ERROR"))
+ finally:
+ # restore descriptor
+ try:
+ del GLib.IO_STATUS_ERROR
+ except AttributeError:
+ pass
+ setattr(type(GLib), "IO_STATUS_ERROR", desc)
+
+ def test_deprecated_attribute_warning(self):
+ with warnings.catch_warnings(record=True) as warn:
+ warnings.simplefilter('always')
+ self.assertEqual(GLib.IO_STATUS_ERROR, GLib.IOStatus.ERROR)
+ GLib.IO_STATUS_ERROR
+ GLib.IO_STATUS_ERROR
+ self.assertEqual(len(warn), 3)
+ self.assertTrue(
+ issubclass(warn[0].category, PyGIDeprecationWarning))
+ self.assertRegexpMatches(
+ str(warn[0].message),
+ ".*GLib.IO_STATUS_ERROR.*GLib.IOStatus.ERROR.*")
+
+ def test_deprecated_attribute_warning_coverage(self):
+ with warnings.catch_warnings(record=True) as warn:
+ warnings.simplefilter('always')
+ GObject.markup_escape_text
+ GObject.PRIORITY_DEFAULT
+ GObject.GError
+ GObject.PARAM_CONSTRUCT
+ GObject.SIGNAL_ACTION
+ GObject.property
+ GObject.IO_STATUS_ERROR
+ GObject.G_MAXUINT64
+ GLib.IO_STATUS_ERROR
+ GLib.SPAWN_SEARCH_PATH
+ GLib.OPTION_FLAG_HIDDEN
+ GLib.IO_FLAG_IS_WRITEABLE
+ GLib.IO_FLAG_NONBLOCK
+ GLib.USER_DIRECTORY_DESKTOP
+ GLib.OPTION_ERROR_BAD_VALUE
+ GLib.glib_version
+ GLib.pyglib_version
+ self.assertEqual(len(warn), 17)
+
def test_deprecated_init_no_keywords(self):
def init(self, **kwargs):
self.assertDictEqual(kwargs, {'a': 1, 'b': 2, 'c': 3})