summaryrefslogtreecommitdiffstats
path: root/gobject
diff options
context:
space:
mode:
authorJohan Dahlin <johan@src.gnome.org>2007-05-01 15:54:05 +0000
committerJohan Dahlin <johan@src.gnome.org>2007-05-01 15:54:05 +0000
commita57fde73b509adcf56a73ae1fb97474d136fd2af (patch)
tree808278f44be0091211dd0113d6293ea0d6510583 /gobject
parentecbf8b6315dda927f4f90d8651033eb67c46a187 (diff)
downloadpygobject-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.am21
-rw-r--r--gobject/__init__.py70
-rw-r--r--gobject/constants.py.in47
-rw-r--r--gobject/generate-constants.c21
-rw-r--r--gobject/propertyhelper.py285
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,)