1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # pygobject - Python bindings for the GObject library
3 # Copyright (C) 2007 Johan Dahlin
5 # gi/_propertyhelper.py: GObject property wrapper/helper
7 # This library is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Lesser General Public
9 # License as published by the Free Software Foundation; either
10 # version 2.1 of the License, or (at your option) any later version.
12 # This library is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # Lesser General Public License for more details.
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this library; if not, see <http://www.gnu.org/licenses/>.
25 from ._constants
import \
26 TYPE_NONE
, TYPE_INTERFACE
, TYPE_CHAR
, TYPE_UCHAR
, \
27 TYPE_BOOLEAN
, TYPE_INT
, TYPE_UINT
, TYPE_LONG
, \
28 TYPE_ULONG
, TYPE_INT64
, TYPE_UINT64
, TYPE_ENUM
, TYPE_FLAGS
, \
29 TYPE_FLOAT
, TYPE_DOUBLE
, TYPE_STRING
, \
30 TYPE_POINTER
, TYPE_BOXED
, TYPE_PARAM
, TYPE_OBJECT
, \
31 TYPE_PYOBJECT
, TYPE_GTYPE
, TYPE_STRV
, TYPE_VARIANT
33 G_MAXFLOAT
= _gi
.G_MAXFLOAT
34 G_MAXDOUBLE
= _gi
.G_MAXDOUBLE
35 G_MININT
= _gi
.G_MININT
36 G_MAXINT
= _gi
.G_MAXINT
37 G_MAXUINT
= _gi
.G_MAXUINT
38 G_MINLONG
= _gi
.G_MINLONG
39 G_MAXLONG
= _gi
.G_MAXLONG
40 G_MAXULONG
= _gi
.G_MAXULONG
42 if sys
.version_info
>= (3, 0):
46 _basestring
= basestring
50 class Property(object):
51 """Creates a new Property which when used in conjunction with
52 GObject subclass will create a Python property accessor for the
55 :param callable getter:
56 getter to get the value of the property
57 :param callable setter:
58 setter to set the value of the property
62 default value, must match the property type.
67 :param GObject.ParamFlags flags:
70 minimum allowed value (int, float, long only)
72 maximum allowed value (int, float, long only)
74 .. code-block:: python
76 class MyObject(GObject.Object):
77 prop = GObject.Property(type=str)
82 obj.prop # now is 'value'
84 The API is similar to the builtin :py:func:`property`:
86 .. code-block:: python
88 class AnotherObject(GObject.Object):
96 @GObject.Property(type=int)
98 'Read-write integer property.'
102 def propInt(self, value):
105 _type_from_pytype_lookup
= {
106 # Put long_ first in case long_ and int are the same so int clobbers long_
112 object: TYPE_PYOBJECT
,
115 _min_value_lookup
= {
119 # Remember that G_MINFLOAT and G_MINDOUBLE are something different.
120 TYPE_FLOAT
: -G_MAXFLOAT
,
121 TYPE_DOUBLE
: -G_MAXDOUBLE
,
123 TYPE_LONG
: G_MINLONG
,
124 TYPE_INT64
: -2 ** 63,
127 _max_value_lookup
= {
128 TYPE_UINT
: G_MAXUINT
,
129 TYPE_ULONG
: G_MAXULONG
,
130 TYPE_INT64
: 2 ** 63 - 1,
131 TYPE_UINT64
: 2 ** 64 - 1,
132 TYPE_FLOAT
: G_MAXFLOAT
,
133 TYPE_DOUBLE
: G_MAXDOUBLE
,
135 TYPE_LONG
: G_MAXLONG
,
150 class __metaclass__(type):
152 return "<class 'GObject.Property'>"
154 def __init__(self
, getter
=None, setter
=None, type=None, default
=None,
155 nick
='', blurb
='', flags
=_gi
.PARAM_READWRITE
,
156 minimum
=None, maximum
=None):
161 self
.type = self
._type
_from
_python
(type)
162 self
.default
= self
._get
_default
(default
)
163 self
._check
_default
()
165 if not isinstance(nick
, _basestring
):
166 raise TypeError("nick must be a string")
169 if not isinstance(blurb
, _basestring
):
170 raise TypeError("blurb must be a string")
172 # Always clobber __doc__ with blurb even if blurb is empty because
173 # we don't want the lengthy Property class documentation showing up
178 # Call after setting blurb for potential __doc__ usage.
179 if getter
and not setter
:
180 setter
= self
._readonly
_setter
181 elif setter
and not getter
:
182 getter
= self
._writeonly
_getter
183 elif not setter
and not getter
:
184 getter
= self
._default
_getter
185 setter
= self
._default
_setter
187 # do not call self.setter() here, as this defines the property name
191 if minimum
is not None:
192 if minimum
< self
._get
_minimum
():
194 "Minimum for type %s cannot be lower than %d" %
195 (self
.type, self
._get
_minimum
()))
197 minimum
= self
._get
_minimum
()
198 self
.minimum
= minimum
199 if maximum
is not None:
200 if maximum
> self
._get
_maximum
():
202 "Maximum for type %s cannot be higher than %d" %
203 (self
.type, self
._get
_maximum
()))
205 maximum
= self
._get
_maximum
()
206 self
.maximum
= maximum
211 return '<GObject Property %s (%s)>' % (
212 self
.name
or '(uninitialized)',
213 _gi
.type_name(self
.type))
215 def __get__(self
, instance
, klass
):
221 # Simply return the result of fget directly, no need to go through GObject.
222 # See: https://bugzilla.gnome.org/show_bug.cgi?id=723872
223 # We catch and print any exception occurring within the fget for compatibility
224 # prior to the fast path addition from bug 723872, this should eventually
225 # be removed and exceptions raised directly to the caller as in:
226 # https://bugzilla.gnome.org/show_bug.cgi?id=575652
228 value
= self
.fget(instance
)
230 traceback
.print_exc()
240 def __set__(self
, instance
, value
):
245 instance
.set_property(self
.name
, value
)
251 def __call__(self
, fget
):
252 """Allows application of the getter along with init arguments."""
253 return self
.getter(fget
)
255 def getter(self
, fget
):
256 """Set the getter function to fget. For use as a decorator."""
258 # Always clobber docstring and blurb with the getter docstring.
259 self
.blurb
= fget
.__doc
__
260 self
.__doc
__ = fget
.__doc
__
264 def setter(self
, fset
):
265 """Set the setter function to fset. For use as a decorator."""
267 # with a setter decorator, we must ignore the name of the method in
268 # install_properties, as this does not need to be a valid property name
269 # and does not define the property name. So set the name here.
271 self
.name
= self
.fget
.__name
__
274 def _type_from_python(self
, type_
):
275 if type_
in self
._type
_from
_pytype
_lookup
:
276 return self
._type
_from
_pytype
_lookup
[type_
]
277 elif (isinstance(type_
, type) and
278 issubclass(type_
, (_gi
.GObject
,
283 return type_
.__gtype
__
284 elif type_
in (TYPE_NONE
, TYPE_INTERFACE
, TYPE_CHAR
, TYPE_UCHAR
,
285 TYPE_INT
, TYPE_UINT
, TYPE_BOOLEAN
, TYPE_LONG
,
286 TYPE_ULONG
, TYPE_INT64
, TYPE_UINT64
,
287 TYPE_FLOAT
, TYPE_DOUBLE
, TYPE_POINTER
,
288 TYPE_BOXED
, TYPE_PARAM
, TYPE_OBJECT
, TYPE_STRING
,
289 TYPE_PYOBJECT
, TYPE_GTYPE
, TYPE_STRV
, TYPE_VARIANT
):
292 raise TypeError("Unsupported type: %r" % (type_
,))
294 def _get_default(self
, default
):
295 if default
is not None:
297 return self
._default
_lookup
.get(self
.type, None)
299 def _check_default(self
):
301 default
= self
.default
302 if (ptype
== TYPE_BOOLEAN
and (default
not in (True, False))):
304 "default must be True or False, not %r" % (default
,))
305 elif ptype
== TYPE_PYOBJECT
:
306 if default
is not None:
307 raise TypeError("object types does not have default values")
308 elif ptype
== TYPE_GTYPE
:
309 if default
is not None:
310 raise TypeError("GType types does not have default values")
311 elif _gi
.type_is_a(ptype
, TYPE_ENUM
):
313 raise TypeError("enum properties needs a default value")
314 elif not _gi
.type_is_a(default
, ptype
):
315 raise TypeError("enum value %s must be an instance of %r" %
317 elif _gi
.type_is_a(ptype
, TYPE_FLAGS
):
318 if not _gi
.type_is_a(default
, ptype
):
319 raise TypeError("flags value %s must be an instance of %r" %
321 elif _gi
.type_is_a(ptype
, TYPE_STRV
) and default
is not None:
322 if not isinstance(default
, list):
323 raise TypeError("Strv value %s must be a list" % repr(default
))
325 if type(val
) not in (str, bytes
):
326 raise TypeError("Strv value %s must contain only strings" % str(default
))
327 elif _gi
.type_is_a(ptype
, TYPE_VARIANT
) and default
is not None:
328 if not hasattr(default
, '__gtype__') or not _gi
.type_is_a(default
, TYPE_VARIANT
):
329 raise TypeError("variant value %s must be an instance of %r" %
332 def _get_minimum(self
):
333 return self
._min
_value
_lookup
.get(self
.type, None)
335 def _get_maximum(self
):
336 return self
._max
_value
_lookup
.get(self
.type, None)
342 def _default_setter(self
, instance
, value
):
343 setattr(instance
, '_property_helper_' + self
.name
, value
)
345 def _default_getter(self
, instance
):
346 return getattr(instance
, '_property_helper_' + self
.name
, self
.default
)
348 def _readonly_setter(self
, instance
, value
):
349 self
._exc
= TypeError("%s property of %s is read-only" % (
350 self
.name
, type(instance
).__name
__))
352 def _writeonly_getter(self
, instance
):
353 self
._exc
= TypeError("%s property of %s is write-only" % (
354 self
.name
, type(instance
).__name
__))
360 def get_pspec_args(self
):
362 if ptype
in (TYPE_INT
, TYPE_UINT
, TYPE_LONG
, TYPE_ULONG
,
363 TYPE_INT64
, TYPE_UINT64
, TYPE_FLOAT
, TYPE_DOUBLE
):
364 args
= self
.minimum
, self
.maximum
, self
.default
365 elif (ptype
== TYPE_STRING
or ptype
== TYPE_BOOLEAN
or
366 ptype
.is_a(TYPE_ENUM
) or ptype
.is_a(TYPE_FLAGS
) or
367 ptype
.is_a(TYPE_VARIANT
)):
368 args
= (self
.default
,)
369 elif ptype
in (TYPE_PYOBJECT
, TYPE_GTYPE
):
371 elif ptype
.is_a(TYPE_OBJECT
) or ptype
.is_a(TYPE_BOXED
):
374 raise NotImplementedError(ptype
)
376 return (self
.type, self
.nick
, self
.blurb
) + args
+ (self
.flags
,)
379 def install_properties(cls
):
381 Scans the given class for instances of Property and merges them
382 into the classes __gproperties__ dict if it exists or adds it if not.
384 gproperties
= cls
.__dict
__.get('__gproperties__', {})
387 for name
, prop
in cls
.__dict
__.items():
388 if isinstance(prop
, Property
): # not same as the built-in
389 # if a property was defined with a decorator, it may already have
390 # a name; if it was defined with an assignment (prop = Property(...))
391 # we set the property's name to the member name
394 # we will encounter the same property multiple times in case of
395 # custom setter methods
396 if prop
.name
in gproperties
:
397 if gproperties
[prop
.name
] == prop
.get_pspec_args():
399 raise ValueError('Property %s was already found in __gproperties__' % prop
.name
)
400 gproperties
[prop
.name
] = prop
.get_pspec_args()
406 cls
.__gproperties
__ = gproperties
408 if 'do_get_property' in cls
.__dict
__ or 'do_set_property' in cls
.__dict
__:
410 if prop
.fget
!= prop
._default
_getter
or prop
.fset
!= prop
._default
_setter
:
412 "GObject subclass %r defines do_get/set_property"
413 " and it also uses a property with a custom setter"
414 " or getter. This is not allowed" %
417 def obj_get_property(self
, pspec
):
418 name
= pspec
.name
.replace('-', '_')
419 return getattr(self
, name
, None)
420 cls
.do_get_property
= obj_get_property
422 def obj_set_property(self
, pspec
, value
):
423 name
= pspec
.name
.replace('-', '_')
424 prop
= getattr(cls
, name
, None)
426 prop
.fset(self
, value
)
427 cls
.do_set_property
= obj_set_property