diff options
-rw-r--r-- | ChangeLog | 15 | ||||
-rw-r--r-- | examples/properties.py | 50 | ||||
-rw-r--r-- | gobject/Makefile.am | 21 | ||||
-rw-r--r-- | gobject/__init__.py | 70 | ||||
-rw-r--r-- | gobject/constants.py.in | 47 | ||||
-rw-r--r-- | gobject/generate-constants.c | 21 | ||||
-rw-r--r-- | gobject/propertyhelper.py | 285 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
-rw-r--r-- | tests/test_enum.py | 15 | ||||
-rw-r--r-- | tests/test_interface.py | 11 | ||||
-rw-r--r-- | tests/test_properties.py | 170 |
11 files changed, 586 insertions, 125 deletions
@@ -1,3 +1,18 @@ +2007-05-01 Johan Dahlin <johan@gnome.org> + + * examples/properties.py: + * gobject/Makefile.am: + * gobject/__init__.py: + * gobject/constants.py.in: + * gobject/generate-constants.c: (main): + * gobject/propertyhelper.py: + * tests/Makefile.am: + * tests/test_enum.py: + * tests/test_interface.py: + * tests/test_properties.py: + + Add a property helper, fixes #338098 + 2007-04-30 Gustavo J. A. M. Carneiro <gjc@inescporto.pt> * tests/common.py (importModules): Import testhelper first so that diff --git a/examples/properties.py b/examples/properties.py index 2d36afc..cc05920 100644 --- a/examples/properties.py +++ b/examples/properties.py @@ -1,43 +1,31 @@ import gobject class MyObject(gobject.GObject): - __gproperties__ = { - 'foo': (gobject.TYPE_STRING, 'foo property', 'the foo of the object', - 'bar', gobject.PARAM_READWRITE), - 'boolprop': (gobject.TYPE_BOOLEAN, 'bool prop', 'a test boolean prop', - 0, gobject.PARAM_READABLE), - } + + foo = gobject.property(type=str, default='bar') + boolprop = gobject.property(type=bool, default=False) def __init__(self): - self.__gobject_init__() - self.foo = 'bar' - def do_set_property(self, pspec, value): - print ' do_set_property called for %s=%r' % (pspec.name, value) - if pspec.name == 'foo': - self.foo = value - else: - raise AttributeError, 'unknown property %s' % pspec.name - def do_get_property(self, pspec): - print ' do_get_property called for %s' % pspec.name - if pspec.name == 'foo': - return self.foo - elif pspec.name == 'boolprop': - return 1 - else: - raise AttributeError, 'unknown property %s' % pspec.name + gobject.GObject.__init__(self) + + @gobject.property + def readonly(self): + return 'readonly' + gobject.type_register(MyObject) -print "MyObject properties: ", gobject.list_properties(MyObject) +print "MyObject properties: ", list(MyObject.props) + obj = MyObject() -val = obj.get_property('foo') -print "obj.get_property('foo') == ", val +print "obj.foo ==", obj.foo + +obj.foo = 'spam' +print "obj.foo = spam" -obj.set_property('foo', 'spam') -print "obj.set_property('foo', 'spam')" +print "obj.foo == ", obj.foo -val = obj.get_property('foo') -print "obj.get_property('foo') == ", val +print "obj.boolprop == ", obj.boolprop -val = obj.get_property('boolprop') -print "obj.get_property('boolprop') == ", val +print obj.readonly +obj.readonly = 'does-not-work' diff --git a/gobject/Makefile.am b/gobject/Makefile.am index 6c4f242..32b4305 100644 --- a/gobject/Makefile.am +++ b/gobject/Makefile.am @@ -9,15 +9,29 @@ pkgpyexecdir = $(pyexecdir)/gtk-2.0 # gobject python scripts pygobjectdir = $(pkgpyexecdir)/gobject -pygobject_PYTHON = __init__.py option.py +pygobject_PYTHON = \ + __init__.py \ + option.py \ + propertyhelper.py pygobject_LTLIBRARIES = _gobject.la - +nodist_pygobject_PYTHON = constants.py common_ldflags = -module -avoid-version if PLATFORM_WIN32 common_ldflags += -no-undefined endif +constants.py: generate-constants constants.py.in + rm -f constants.py + cp $(srcdir)/constants.py.in constants.py + chmod 644 constants.py + $(top_builddir)/gobject/generate-constants >> constants.py + chmod 444 constants.py + +generate_constants_CFLAGS = $(GLIB_CFLAGS) +noinst_PROGRAMS = generate-constants +CLEANFILES = constants.py +EXTRA_DIST = constants.py.in _gobject_la_CFLAGS = $(GLIB_CFLAGS) _gobject_la_LDFLAGS = $(common_ldflags) -export-symbols-regex init_gobject @@ -39,9 +53,10 @@ _gobject_la_SOURCES = \ pygtype.c \ pygoptioncontext.c \ pygoptiongroup.c +_gobject_la_DEPENDENCIES = constants.py if HAVE_LIBFFI -_gobject_la_SOURCES += ffi-marshaller.c +_gobject_la_SOURCES += ffi-marshaller.c ffi-marshaller.h endif if PLATFORM_WIN32 diff --git a/gobject/__init__.py b/gobject/__init__.py index 43a2cfd..d251d31 100644 --- a/gobject/__init__.py +++ b/gobject/__init__.py @@ -27,15 +27,61 @@ try: except ImportError: pass +from gobject.constants import * from _gobject import * _PyGObject_API = _gobject._PyGObject_API +from propertyhelper import property + class GObjectMeta(type): "Metaclass for automatically registering GObject classes" def __init__(cls, name, bases, dict_): type.__init__(cls, name, bases, dict_) + cls._install_properties() cls._type_register(cls.__dict__) + def _install_properties(cls): + gproperties = getattr(cls, '__gproperties__', {}) + props = {} + for name, prop in cls.__dict__.items(): + if isinstance(prop, property): # not same as the built-in + if name in gproperties: + raise ValueError + prop.name = name + props[name] = prop.get_pspec_args() + + if not props: + return + + if not gproperties: + cls.__gproperties__ = props + else: + gproperties.update(props) + + if (hasattr(cls, 'do_get_property') or + hasattr(cls, 'do_set_property')): + for prop in props: + if (prop.getter != prop.default_getter or + prop.setter != prop.default_setter): + raise TypeError( + "GObject subclass %r defines do_get/set_property" + " and it also uses a property which a custom setter" + " or getter. This is not allowed" % (cls,)) + + def obj_get_property(self, pspec): + name = pspec.name.replace('-', '_') + prop = getattr(cls, name, None) + if prop: + return prop.getter(self) + cls.do_get_property = obj_get_property + + def obj_set_property(self, pspec, value): + name = pspec.name.replace('-', '_') + prop = getattr(cls, name, None) + if prop: + prop.setter(self, value) + cls.do_set_property = obj_set_property + def _type_register(cls, namespace): ## don't register the class if already registered if '__gtype__' in namespace: @@ -50,28 +96,4 @@ class GObjectMeta(type): _gobject._install_metaclass(GObjectMeta) -# TYPE_INVALID defined in gobjectmodule.c -TYPE_NONE = type_from_name('void') -TYPE_INTERFACE = type_from_name('GInterface') -TYPE_CHAR = type_from_name('gchar') -TYPE_UCHAR = type_from_name('guchar') -TYPE_BOOLEAN = type_from_name('gboolean') -TYPE_INT = type_from_name('gint') -TYPE_UINT = type_from_name('guint') -TYPE_LONG = type_from_name('glong') -TYPE_ULONG = type_from_name('gulong') -TYPE_INT64 = type_from_name('gint64') -TYPE_UINT64 = type_from_name('guint64') -TYPE_ENUM = type_from_name('GEnum') -TYPE_FLAGS = type_from_name('GFlags') -TYPE_FLOAT = type_from_name('gfloat') -TYPE_DOUBLE = type_from_name('gdouble') -TYPE_STRING = type_from_name('gchararray') -TYPE_POINTER = type_from_name('gpointer') -TYPE_BOXED = type_from_name('GBoxed') -TYPE_PARAM = type_from_name('GParam') -TYPE_OBJECT = type_from_name('GObject') -TYPE_PYOBJECT = type_from_name('PyObject') -TYPE_UNICHAR = TYPE_UINT - del _gobject diff --git a/gobject/constants.py.in b/gobject/constants.py.in new file mode 100644 index 0000000..6cb2d4f --- /dev/null +++ b/gobject/constants.py.in @@ -0,0 +1,47 @@ +# -*- Mode: Python; py-indent-offset: 4 -*- +# pygobject - Python bindings for the GObject library +# Copyright (C) 2006-2007 Johan Dahlin +# +# gobject/constants.py: GObject type constants +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA + +from _gobject import type_from_name + +# TYPE_INVALID defined in gobjectmodule.c +TYPE_NONE = type_from_name('void') +TYPE_INTERFACE = type_from_name('GInterface') +TYPE_CHAR = type_from_name('gchar') +TYPE_UCHAR = type_from_name('guchar') +TYPE_BOOLEAN = type_from_name('gboolean') +TYPE_INT = type_from_name('gint') +TYPE_UINT = type_from_name('guint') +TYPE_LONG = type_from_name('glong') +TYPE_ULONG = type_from_name('gulong') +TYPE_INT64 = type_from_name('gint64') +TYPE_UINT64 = type_from_name('guint64') +TYPE_ENUM = type_from_name('GEnum') +TYPE_FLAGS = type_from_name('GFlags') +TYPE_FLOAT = type_from_name('gfloat') +TYPE_DOUBLE = type_from_name('gdouble') +TYPE_STRING = type_from_name('gchararray') +TYPE_POINTER = type_from_name('gpointer') +TYPE_BOXED = type_from_name('GBoxed') +TYPE_PARAM = type_from_name('GParam') +TYPE_OBJECT = type_from_name('GObject') +TYPE_PYOBJECT = type_from_name('PyObject') +TYPE_UNICHAR = TYPE_UINT + diff --git a/gobject/generate-constants.c b/gobject/generate-constants.c new file mode 100644 index 0000000..ed66a07 --- /dev/null +++ b/gobject/generate-constants.c @@ -0,0 +1,21 @@ +#include <stdio.h> +#include <glibconfig.h> + +int main(void) +{ + printf("# This part is generated by generate-constants.c\n"); + printf("G_MINFLOAT = %f\n", G_MINFLOAT); + printf("G_MINFLOAT = %f\n", G_MINFLOAT); + printf("G_MAXFLOAT = %f\n", G_MAXFLOAT); + printf("G_MINDOUBLE = %f\n", G_MINDOUBLE); + printf("G_MAXDOUBLE = %f\n", G_MAXDOUBLE); + printf("G_MINSHORT = %d\n", G_MINSHORT); + printf("G_MAXSHORT = %d\n", G_MAXSHORT); + printf("G_MAXUSHORT = %u\n", G_MAXUSHORT); + printf("G_MININT = %d\n", G_MININT); + printf("G_MAXINT = %d\n", G_MAXINT); + printf("G_MAXUINT = %u\n", G_MAXUINT); + printf("G_MINLONG = %ldL\n", G_MINLONG); + printf("G_MAXLONG = %ldL\n", G_MAXLONG); + printf("G_MAXULONG = %luL\n", G_MAXULONG); +} diff --git a/gobject/propertyhelper.py b/gobject/propertyhelper.py new file mode 100644 index 0000000..f0c01e4 --- /dev/null +++ b/gobject/propertyhelper.py @@ -0,0 +1,285 @@ +# -*- Mode: Python; py-indent-offset: 4 -*- +# pygobject - Python bindings for the GObject library +# Copyright (C) 2007 Johan Dahlin +# +# gobject/_property.py: GObject property wrapper/helper +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA + +import _gobject +from gobject.constants import \ + TYPE_NONE, TYPE_INTERFACE, TYPE_CHAR, TYPE_UCHAR, \ + TYPE_BOOLEAN, TYPE_INT, TYPE_UINT, TYPE_LONG, \ + TYPE_ULONG, TYPE_INT64, TYPE_UINT64, TYPE_ENUM, \ + TYPE_FLAGS, TYPE_FLOAT, TYPE_DOUBLE, TYPE_STRING, \ + TYPE_POINTER, TYPE_BOXED, TYPE_PARAM, TYPE_OBJECT, \ + TYPE_PYOBJECT +from gobject.constants import \ + G_MINFLOAT, G_MAXFLOAT, G_MINDOUBLE, G_MAXDOUBLE, \ + G_MININT, G_MAXINT, G_MAXUINT, G_MINLONG, G_MAXLONG, \ + G_MAXULONG + + +class PropertyMeta(type): + def __repr__(self): + return "<class 'gobject.property'>" + + +class property(object): + """ + Creates a new property which in conjunction with GObjectMeta will + create a GObject property proxy: + + >>> class MyObject(gobject.GObject): + >>> ... prop = gobject.property(type=str) + + >>> obj = MyObject() + >>> obj.prop = 'value' + + >>> obj.prop + 'value' + """ + + __metaclass__ = PropertyMeta + + def __init__(self, getter=None, setter=None, type=None, default=None, + nick='', blurb='', flags=_gobject.PARAM_READWRITE, + minimum=None, maximum=None): + """ + @param getter: getter to get the value of the property + @type getter: callable + @param setter: setter to set the value of the property + @type setter: callable + @param type: type of property + @type type: type + @param default: default value + @param nick: short description + @type bick: string + @param blurb: long description + @type blurb: string + @param flags: parameter flags, one of: + - gobject.PARAM_READABLE + - gobject.PARAM_READWRITE + - gobject.PARAM_WRITABLE + - gobject.PARAM_CONSTRUCT + - gobject.PARAM_CONSTRUCT_ONLY + - gobject.PARAM_LAX_VALIDATION + @keyword minimum: minimum allowed value (int, float, long only) + @keyword maximum: maximum allowed value (int, float, long only) + """ + + if getter and not setter: + setter = self._readonly_setter + elif setter and not getter: + getter = self._writeonly_getter + elif not setter and not getter: + getter = self._default_getter + setter = self._default_setter + self.getter = getter + self.setter = setter + + if type is None: + type = object + self.type = self._type_from_python(type) + self.default = self._get_default(default) + self._check_default() + + if not isinstance(nick, basestring): + raise TypeError("nick must be a string") + self.nick = nick + + if not isinstance(blurb, basestring): + raise TypeError("blurb must be a string") + self.blurb = blurb + + if flags < 0 or flags > 32: + raise TypeError("invalid flag value: %r" % (flags,)) + self.flags = flags + + if minimum is not None: + if minimum < self._get_minimum(): + raise TypeError( + "Minimum for type %s cannot be lower than %d" % ( + self.type, self._get_minimum())) + else: + minimum = self._get_minimum() + self.minimum = minimum + if maximum is not None: + if maximum > self._get_maximum(): + raise TypeError( + "Maximum for type %s cannot be higher than %d" % ( + self.type, self._get_maximum())) + else: + maximum = self._get_maximum() + self.maximum = maximum + + self.name = None + + self._value = self.default + self._exc = None + + def __repr__(self): + return '<gobject property %s (%s)>' % ( + self.name or '(uninitialized)', + _gobject.type_name(self.type)) + + def __get__(self, instance, klass): + if instance is None: + return self + + self._exc = None + value = instance.get_property(self.name) + if self._exc: + exc = self._exc + self._exc = None + raise exc + + return value + + def __set__(self, instance, value): + if instance is None: + raise TypeError + + self._exc = None + instance.set_property(self.name, value) + if self._exc: + exc = self._exc + self._exc = None + raise exc + + def _type_from_python(self, type): + if type == int: + return TYPE_INT + elif type == bool: + return TYPE_BOOLEAN + elif type == long: + return TYPE_LONG + elif type == float: + return TYPE_DOUBLE + elif type == str: + return TYPE_STRING + elif type == object: + return TYPE_PYOBJECT + elif type in [TYPE_NONE, TYPE_INTERFACE, TYPE_CHAR, TYPE_UCHAR, + TYPE_INT, TYPE_UINT, TYPE_BOOLEAN, TYPE_LONG, + TYPE_ULONG, TYPE_INT64, TYPE_UINT64, TYPE_ENUM, + TYPE_FLAGS, TYPE_FLOAT, TYPE_DOUBLE, TYPE_POINTER, + TYPE_BOXED, TYPE_PARAM, TYPE_OBJECT, TYPE_STRING, + TYPE_PYOBJECT]: + return type + else: + raise TypeError("Unsupported type: %r" % (type,)) + + def _get_default(self, default): + ptype = self.type + if default is not None: + return default + + if ptype in [TYPE_INT, TYPE_UINT, TYPE_LONG, TYPE_ULONG, + TYPE_INT64, TYPE_UINT64]: + return 0 + elif ptype == TYPE_STRING: + return '' + elif ptype == TYPE_FLOAT or ptype == TYPE_DOUBLE: + return 0.0 + else: + return None + + def _check_default(self): + ptype = self.type + default = self.default + if (ptype == TYPE_BOOLEAN and + (default is not True or + default is not False)): + raise TypeError( + "default must be True or False, not %r" % (default,)) + elif ptype == TYPE_PYOBJECT: + if default is not None: + raise TypeError("object types does not have default values") + + def _get_minimum(self): + ptype = self.type + if ptype in [TYPE_UINT, TYPE_ULONG, TYPE_UINT64]: + return 0 + elif ptype == TYPE_FLOAT: + return G_MINFLOAT + elif ptype == TYPE_DOUBLE: + return G_MINDOUBLE + elif ptype == TYPE_INT: + return G_MININT + elif ptype == TYPE_LONG: + return G_MINLONG + elif ptype == TYPE_INT64: + return -2 ** 62 - 1 + + return None + + def _get_maximum(self): + ptype = self.type + if ptype == TYPE_UINT: + return G_MAXUINT + elif ptype == TYPE_ULONG: + return G_MAXULONG + elif ptype == TYPE_INT64: + return 2 ** 62 - 1 + elif ptype == TYPE_UINT64: + return 2 ** 63 - 1 + elif ptype == TYPE_FLOAT: + return G_MAXFLOAT + elif ptype == TYPE_DOUBLE: + return G_MAXDOUBLE + elif ptype == TYPE_INT: + return G_MAXINT + elif ptype == TYPE_LONG: + return G_MAXLONG + + return None + + # + # Getter and Setter + # + + def _default_setter(self, instance, value): + self._value = value + + def _default_getter(self, instance): + return self._value + + def _readonly_setter(self, instance, value): + self._exc = TypeError("%s property of %s is read-only" % ( + self.name, type(instance).__name__)) + + def _writeonly_getter(self, instance): + self._exc = TypeError("%s property of %s is write-only" % ( + self.name, type(instance).__name__)) + + # + # Public API + # + + def get_pspec_args(self): + ptype = self.type + if ptype in [TYPE_INT, TYPE_UINT, TYPE_LONG, TYPE_ULONG, + TYPE_INT64, TYPE_UINT64, TYPE_FLOAT, TYPE_DOUBLE]: + args = self._get_minimum(), self._get_maximum(), self.default + elif ptype == TYPE_STRING or ptype == TYPE_BOOLEAN: + args = (self.default,) + elif ptype == TYPE_PYOBJECT: + args = () + else: + raise NotImplementedError(ptype) + + return (self.type, self.nick, self.blurb) + args + (self.flags,) diff --git a/tests/Makefile.am b/tests/Makefile.am index fe627c2..3b05018 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -40,10 +40,10 @@ testhelper.la: $(testhelper_la_OBJECTS) $(testhelper_la_DEPENDENCIES) check-local: $(top_srcdir)/gobject/__init__.py @if test "$(top_builddir)" != "$(top_srcdir)"; then \ - cp $(top_srcdir)/gobject/__init__.py $(top_builddir)/gobject/__init__.py; \ + cp $(top_srcdir)/gobject/*.py $(top_builddir)/gobject; \ fi @$(PYTHON) $(srcdir)/runtests.py $(top_builddir) $(top_srcdir) @if test "$(top_builddir)" != "$(top_srcdir)"; then \ - rm -f $(top_builddir)/gobject/__init__.py*; \ + rm -f $(top_builddir)/gobject/*.py; \ fi - @rm -fr *.pyc + @rm -fr $(top_builddir)/gobject/*.pyc diff --git a/tests/test_enum.py b/tests/test_enum.py index 88d8287..6543cec 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -1,20 +1,15 @@ import unittest import warnings +import gobject from gobject import GEnum, GFlags, GObject, GType, PARAM_READWRITE from common import gobject, atk, pango, gtk, gdk class PObject(GObject): - __gproperties__ = { - 'enum': (gtk.WindowType, 'blurb', 'description', - gtk.WINDOW_TOPLEVEL, PARAM_READWRITE), - 'enum2': (gtk.WindowType, 'blurb', 'description', - int(gtk.WINDOW_TOPLEVEL), PARAM_READWRITE), - 'flags': (gtk.AttachOptions, 'blurb', 'description', - gtk.EXPAND, PARAM_READWRITE), - 'flags2': (gtk.AttachOptions, 'blurb', 'description', - int(gtk.EXPAND), PARAM_READWRITE), - } + enum = gobject.property(type=gtk.WindowType, default=gtk.WINDOW_TOPLEVEL) + enum2 = gobject.property(type=gtk.WindowType, default=int(gtk.WINDOW_TOPLEVEL)) + flags = gobject.property(type=gtk.AttachOptions, default=gtk.EXPAND) + flags2 = gobject.property(type=gtk.AttachOptions, default=int(gtk.EXPAND)) class EnumTest(unittest.TestCase): def testEnums(self): diff --git a/tests/test_interface.py b/tests/test_interface.py index d54515d..4413d64 100644 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -3,15 +3,13 @@ import unittest import testmodule from common import gobject, testhelper from gobject import GObject, GInterface +import gobject GUnknown = gobject.type_from_name("TestUnknown") Unknown = GUnknown.pytype class MyUnknown(Unknown, testhelper.Interface): - __gproperties__ = { - 'some-property': (str, 'blurb', 'description', 'default', - gobject.PARAM_READWRITE), - } + some_property = gobject.property(type=str) def __init__(self): Unknown.__init__(self) @@ -24,10 +22,7 @@ class MyUnknown(Unknown, testhelper.Interface): gobject.type_register(MyUnknown) class MyObject(gobject.GObject, testhelper.Interface): - __gproperties__ = { - 'some-property': (str, 'blurb', 'description', 'default', - gobject.PARAM_READWRITE), - } + some_property = gobject.property(type=str) def __init__(self): GObject.__init__(self) diff --git a/tests/test_properties.py b/tests/test_properties.py index e155911..423a8f6 100644 --- a/tests/test_properties.py +++ b/tests/test_properties.py @@ -2,54 +2,26 @@ import struct import unittest -from common import testhelper -from gobject import GObject, GType, new, PARAM_READWRITE, \ - PARAM_CONSTRUCT, PARAM_READABLE, PARAM_WRITABLE, PARAM_CONSTRUCT_ONLY, \ - TYPE_INT, TYPE_UINT, TYPE_LONG, TYPE_ULONG, \ - TYPE_INT64, TYPE_UINT64 +import gobject +from gobject import GObject, GType, GEnum, new, PARAM_READWRITE, \ + PARAM_CONSTRUCT, PARAM_READABLE, PARAM_WRITABLE, PARAM_CONSTRUCT_ONLY +from gobject.constants import \ + TYPE_INT, TYPE_UINT, TYPE_LONG, \ + TYPE_ULONG, TYPE_INT64, TYPE_UINT64 +from gobject.constants import \ + G_MININT, G_MAXINT, G_MAXUINT, G_MINLONG, G_MAXLONG, \ + G_MAXULONG class PropertyObject(GObject): - __gproperties__ = { - 'normal': (str, 'blurb', 'description', 'default', - PARAM_READWRITE), - 'construct': (str, 'blurb', 'description', 'default', - PARAM_READWRITE|PARAM_CONSTRUCT), - 'construct-only': (str, 'blurb', 'description', 'default', - PARAM_READWRITE|PARAM_CONSTRUCT_ONLY), - 'uint64': (TYPE_UINT64, 'blurb', 'description', 0, 10, 0, - PARAM_READWRITE), - } - - def __init__(self): - GObject.__init__(self) - self._value = 'default' - self._construct_only = None - self._construct = None - self._uint64 = 0L - - def do_get_property(self, pspec): - if pspec.name == 'normal': - return self._value - elif pspec.name == 'construct': - return self._construct - elif pspec.name == 'construct-only': - return self._construct_only - elif pspec.name == 'uint64': - return self._uint64 - else: - raise AssertionError - - def do_set_property(self, pspec, value): - if pspec.name == 'normal': - self._value = value - elif pspec.name == 'construct': - self._construct = value - elif pspec.name == 'construct-only': - self._construct_only = value - elif pspec.name == 'uint64': - self._uint64 = value - else: - raise AssertionError + normal = gobject.property(type=str) + construct = gobject.property( + type=str, + flags=PARAM_READWRITE|PARAM_CONSTRUCT, default='default') + construct_only = gobject.property( + type=str, + flags=PARAM_READWRITE|PARAM_CONSTRUCT_ONLY) + uint64 = gobject.property( + type=TYPE_UINT64, flags=PARAM_READWRITE|PARAM_CONSTRUCT) class TestProperties(unittest.TestCase): def testGetSet(self): @@ -200,3 +172,109 @@ class TestProperties(unittest.TestCase): normal, uint64 = obj.get_properties("normal", "uint64") self.assertEqual(normal, "foo") self.assertEqual(uint64, 7) + +class TestProperty(unittest.TestCase): + def testSimple(self): + class C(gobject.GObject): + str = gobject.property(type=str) + int = gobject.property(type=int) + float = gobject.property(type=float) + long = gobject.property(type=long) + + self.failUnless(hasattr(C.props, 'str')) + self.failUnless(hasattr(C.props, 'int')) + self.failUnless(hasattr(C.props, 'float')) + self.failUnless(hasattr(C.props, 'long')) + + o = C() + self.assertEqual(o.str, '') + o.str = 'str' + self.assertEqual(o.str, 'str') + + self.assertEqual(o.int, 0) + o.int = 1138 + self.assertEqual(o.int, 1138) + + self.assertEqual(o.float, 0.0) + o.float = 3.14 + self.assertEqual(o.float, 3.14) + + self.assertEqual(o.long, 0L) + o.long = 100L + self.assertEqual(o.long, 100L) + + def testCustomGetter(self): + class C(gobject.GObject): + def get_prop(self): + return 'value' + prop = gobject.property(getter=get_prop) + + o = C() + self.assertEqual(o.prop, 'value') + self.assertRaises(TypeError, setattr, o, 'prop', 'xxx') + + def testCustomSetter(self): + class C(gobject.GObject): + def set_prop(self, value): + self._value = value + prop = gobject.property(setter=set_prop) + + def __init__(self): + self._value = None + gobject.GObject.__init__(self) + + o = C() + self.assertEquals(o._value, None) + o.prop = 'bar' + self.assertEquals(o._value, 'bar') + self.assertRaises(TypeError, getattr, o, 'prop') + + def testErrors(self): + self.assertRaises(TypeError, gobject.property, type='str') + self.assertRaises(TypeError, gobject.property, nick=False) + self.assertRaises(TypeError, gobject.property, blurb=False) + self.assertRaises(TypeError, gobject.property, type=bool, default=0) + self.assertRaises(TypeError, gobject.property, type=GEnum) + self.assertRaises(TypeError, gobject.property, type=GEnum, default=0) + self.assertRaises(TypeError, gobject.property, type=object, default=0) + self.assertRaises(TypeError, gobject.property, type=complex) + self.assertRaises(TypeError, gobject.property, flags=-10) + + def testNameWithUnderscore(self): + class C(gobject.GObject): + prop_name = gobject.property(type=int) + o = C() + o.prop_name = 10 + self.assertEqual(o.prop_name, 10) + + def testRange(self): + maxint64 = 2 ** 62 - 1 + minint64 = -2 ** 62 - 1 + maxuint64 = 2 ** 63 - 1 + + types = [ + (TYPE_INT, G_MININT, G_MAXINT), + (TYPE_UINT, 0, G_MAXUINT), + (TYPE_LONG, G_MINLONG, G_MAXLONG), + (TYPE_ULONG, 0, G_MAXULONG), + (TYPE_INT64, minint64, maxint64), + (TYPE_UINT64, 0, maxuint64), + ] + + for gtype, min, max in types: + # Normal, everything is alright + prop = gobject.property(type=gtype, minimum=min, maximum=max) + subtype = type('', (gobject.GObject,), + dict(prop=prop)) + self.assertEqual(subtype.props.prop.minimum, min) + self.assertEqual(subtype.props.prop.maximum, max) + + # Lower than minimum + self.assertRaises(TypeError, + gobject.property, type=gtype, minimum=min-1, + maximum=max) + + # Higher than maximum + self.assertRaises(TypeError, + gobject.property, type=gtype, minimum=min, + maximum=max+1) |