? gobject/utils.py
Index: examples/properties.py
===================================================================
RCS file: /cvs/gnome/gnome-python/pygobject/examples/properties.py,v
retrieving revision 1.1
diff -u -p -d -r1.1 properties.py
--- examples/properties.py	18 Dec 2001 08:19:51 -0000	1.1
+++ examples/properties.py	16 Apr 2006 15:14:04 -0000
@@ -1,16 +1,14 @@
 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(str, default='bar')
+    boolprop = gobject.property(bool, default=False)
 
     def __init__(self):
-        self.__gobject_init__()
+        gobject.GObject.__init__(self)
         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':
Index: examples/signal.py
===================================================================
RCS file: /cvs/gnome/gnome-python/pygobject/examples/signal.py,v
retrieving revision 1.6
diff -u -p -d -r1.6 signal.py
--- examples/signal.py	11 Apr 2006 14:11:09 -0000	1.6
+++ examples/signal.py	16 Apr 2006 15:14:04 -0000
@@ -1,13 +1,8 @@
-import pygtk; pygtk.require("2.0")
 import gobject
 
 class C(gobject.GObject):
-    __gsignals__ = {
-        'my_signal': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
-                      (gobject.TYPE_INT,))
-    }
-    def __init__(self):
-        self.__gobject_init__() # default constructor using our new GType
+    my_signal = gobject.signal(int)
+
     def do_my_signal(self, arg):
         print "C: class closure for `my_signal' called with argument", arg
 
Index: gobject/__init__.py
===================================================================
RCS file: /cvs/gnome/gnome-python/pygobject/gobject/__init__.py,v
retrieving revision 1.6
diff -u -p -d -r1.6 __init__.py
--- gobject/__init__.py	15 Apr 2006 16:47:06 -0000	1.6
+++ gobject/__init__.py	16 Apr 2006 15:14:04 -0000
@@ -30,10 +30,19 @@ except ImportError:
 from _gobject import *
 _PyGObject_API = _gobject._PyGObject_API
 
+class _Property(list):
+    pass
+
+class _Signal(list):
+    pass
+
+
 class GObjectMeta(type):
     "Metaclass for automatically registering GObject classes"
     def __init__(cls, name, bases, dict_):
         type.__init__(cls, name, bases, dict_)
+        cls._extract_properties()
+        cls._extract_signals()
         cls._type_register(cls.__dict__)
 
     def _type_register(cls, namespace):
@@ -45,9 +54,39 @@ class GObjectMeta(type):
                 '__gsignals__' in namespace or
                 '__gtype_name__' in namespace):
             return
-
         type_register(cls, namespace.get('__gtype_name__'))
 
+    def _extract_properties(cls):
+        prop_dict = None
+        for name, prop in cls.__dict__.items():
+            if not isinstance(prop, _Property):
+                continue
+            if prop_dict is None:
+                try:
+                    prop_dict = getattr(cls, '__gproperties__')
+                except AttributeError:
+                    prop_dict = {}
+                    setattr(cls, '__gproperties__', prop_dict)
+            ## if nick is None, replace with name
+            if prop[1] is None:
+                prop[1] = name
+            prop_dict[name] = tuple(prop)
+            delattr(cls, name)
+
+    def _extract_signals(cls):
+        signal_dict = None
+        for name, signal in cls.__dict__.items():
+            if not isinstance(signal, _Signal):
+                continue
+            if signal_dict is None:
+                try:
+                    signal_dict = getattr(cls, '__gsignals__')
+                except AttributeError:
+                    signal_dict = {}
+                    setattr(cls, '__gsignals__', signal_dict)
+            signal_dict[name] = tuple(signal)
+            delattr(cls, name)
+
 _gobject._install_metaclass(GObjectMeta)
 
 # TYPE_INVALID defined in gobjectmodule.c
@@ -75,3 +114,119 @@ TYPE_PYOBJECT = type_from_name('PyObject
 TYPE_UNICHAR = TYPE_UINT
 
 del _gobject
+
+import struct
+
+def signal(*args, **kwargs):
+    """
+    Add a GObject signal to the current object.
+    It current supports the following types:
+      - str, int, float, long, object, enum
+    @param args:     types for signal parameters,
+      if the first one is a string 'override', the signal will be
+      overridden and must therefor exists in the parent GObject.
+    @keyword flags: One of the following:
+      - gobject.SIGNAL_RUN_FIRST
+      - gobject.SIGNAL_RUN_LAST
+      - gobject.SIGNAL_RUN_CLEANUP
+      - gobject.SIGNAL_NO_RECURSE
+      - gobject.SIGNAL_DETAILED
+      - gobject.SIGNAL_ACTION
+      - gobject.SIGNAL_NO_HOOKS
+    @keyword retval: return value in signal callback
+    """
+
+    if args and args[0] == 'override':
+        dict[name] = 'override'
+    else:
+        retval = kwargs.get('retval', None)
+        if retval is None:
+            default_flags = SIGNAL_RUN_FIRST
+        else:
+            default_flags = SIGNAL_RUN_LAST
+
+        flags = kwargs.get('flags', default_flags)
+        if retval is not None and flags != SIGNAL_RUN_LAST:
+            raise TypeError(
+                "You cannot use a return value without setting flags to "
+                "SIGNAL_RUN_LAST")
+
+        return _Signal((flags, retval, args))
+
+def _max(c):
+    # Python 2.3 does not like bitshifting here
+    return 2 ** ((8 * struct.calcsize(c)) - 1) - 1
+
+_MAX_VALUES = {int : _max('i'),
+               float : _max('f'),
+               long : _max('l') }
+_DEFAULT_VALUES = {str : '', float : 0.0, int : 0, long : 0L}
+
+def property(ptype, default=None, nick='', blurb='',
+             flags=PARAM_READWRITE, **kwargs):
+    """
+    Add a GObject property to the current object.
+    @param ptype:   type of property
+    @type ptype:    type
+    @param default:  default value
+    @param nick:     short description
+    @param blurb:    long description
+    @param flags:    parameter flags, one of:
+      - PARAM_READABLE
+      - PARAM_READWRITE
+      - PARAM_WRITABLE
+      - PARAM_CONSTRUCT
+      - PARAM_CONSTRUCT_ONLY
+      - PARAM_LAX_VALIDATION
+    Optional, only for int, float, long types:
+    @keyword minimum:  minimum allowed value
+    @keyword maximum:  maximum allowed value
+    """
+
+    # General type checking
+    if default is None:
+        default = _DEFAULT_VALUES.get(ptype)
+    elif not isinstance(default, ptype):
+        raise TypeError("default must be of type %s, not %r" % (
+            ptype, default))
+    if not isinstance(nick, str):
+        raise TypeError('nick must be a string, not %r' % (
+            nick,))
+    if not isinstance(blurb, str):
+        raise TypeError('blurb for property %s must be a string, not %r' % (
+            name, blurb))
+
+    # Specific type checking
+    if ptype == int or ptype == float or ptype == long:
+        default = (kwargs.get('minimum', ptype(0)),
+                   kwargs.get('maximum', _MAX_VALUES[ptype]),
+                   default)
+    elif ptype == bool:
+        if (default is not True and
+            default is not False):
+            raise TypeError("default must be True or False, not %r" % (
+                default,))
+        default = default,
+    elif type_is_a(ptype, GEnum):
+        if default is None:
+            raise TypeError("enum properties needs a default value")
+        elif not isinstance(default, ptype):
+            raise TypeError("enum value %s must be an instance of %r" %
+                            (default, ptype))
+        default = default,
+    elif ptype == str:
+        default = default,
+    elif ptype == object:
+        if default is not None:
+            raise TypeError("object types does not have default values")
+        default = ()
+    else:
+        raise NotImplementedError("type %r" % (ptype,))
+
+    if flags not in (PARAM_READABLE, PARAM_READWRITE,
+                     PARAM_WRITABLE, PARAM_CONSTRUCT,
+                     PARAM_CONSTRUCT_ONLY,
+                     PARAM_LAX_VALIDATION):
+        raise TypeError("invalid flag value: %r" % (flags,))
+
+    return _Property((ptype, nick, blurb) + default + (flags,))
