diff options
| author | Johan Dahlin <johan@src.gnome.org> | 2007-05-01 15:54:05 +0000 |
|---|---|---|
| committer | Johan Dahlin <johan@src.gnome.org> | 2007-05-01 15:54:05 +0000 |
| commit | a57fde73b509adcf56a73ae1fb97474d136fd2af (patch) | |
| tree | 808278f44be0091211dd0113d6293ea0d6510583 /gobject | |
| parent | ecbf8b6315dda927f4f90d8651033eb67c46a187 (diff) | |
| download | pygobject-a57fde73b509adcf56a73ae1fb97474d136fd2af.tar.gz pygobject-a57fde73b509adcf56a73ae1fb97474d136fd2af.tar.xz pygobject-a57fde73b509adcf56a73ae1fb97474d136fd2af.zip | |
Add a property helper, fixes #338098
* 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
svn path=/trunk/; revision=662
Diffstat (limited to 'gobject')
| -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 |
5 files changed, 417 insertions, 27 deletions
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,) |
