except KeyError:
return
-def _makeQuery(iid):
+def _makeQuery(interface):
'''
Builds a function querying to a specific interface and returns it.
- @param iid: Interface identifier to use when querying
- @type iid: string
+ @param interface: Class representing an AT-SPI interface
+ @type interface: class
@return: Function querying to the given interface
@rtype: function
'''
@rtype: object
@raise NotImplementedError: When the desired interface is not supported
'''
+ iid = utils.getInterfaceIID(interface)
try:
i = self._icache[iid]
except KeyError:
# interface not cached
caching = True
- except TypeError:
+ except AttributeError:
# determine if we're caching
caching = _CACHE_LEVEL is not None
if caching:
try:
# do the query remotely
i = self.queryInterface(iid)
+ if i is not None:
+ i = i._narrow(interface)
except Exception, e:
- raise LookupError(e)
+ raise LookupError(e)
if i is None:
# cache that the interface is not supported
if caching:
self._icache[iid] = None
raise NotImplementedError
- # not needed according to ORBit2 spec, but makes Java queries work
- # more reliably according to Orca experience
- i = i._narrow(i.__class__)
if caching:
# cache the narrow'ed result, but only if we're caching for this object
self._icache[iid] = i
# build name of converter from the name of the interface
name = 'query%s' % utils.getInterfaceName(interface)
# build a function that queries to the given interface
- func = _makeQuery(utils.getInterfaceIID(interface))
+ func = _makeQuery(interface)
# build a new method that is a clone of the original function
method = new.function(func.func_code, func.func_globals, name,
func.func_defaults, func.func_closure)
@param cls: Class to mix interface methods into
@type cls: class
'''
+ # get a method type as a reference from a known method
+ method_type = Accessibility.Accessible.getRole.__class__
# loop over all names in the new class
for name in cls.__dict__.keys():
obj = cls.__dict__[name]
if name.startswith('_'):
continue
# check if we're on a method
- elif isinstance(obj, types.FunctionType):
+ elif isinstance(obj, method_type):
# wrap the function in an exception handler
method = _makeExceptionHandler(obj)
- # add the wrpped function to the class
+ # add the wrapped function to the class
setattr(cls, name, method)
# check if we're on a property
elif isinstance(obj, property):
setter = None
setattr(cls, name, property(getter, setter))
-def _mixClass(cls, new_cls):
+def _mixClass(cls, new_cls, ignore=[]):
'''
Adds the methods in new_cls to cls. After mixing, all instances of cls will
have the new methods. If there is a method name clash, the method already in
@type cls: class
@param new_cls: Class containing features to add
@type new_cls: class
+ @param ignore: Ignore these methods from the mixin
+ @type ignore: iterable
'''
# loop over all names in the new class
for name, func in new_cls.__dict__.items():
- if name in ['_get_name', '_get_description']:
+ if name in ignore:
continue
if isinstance(func, types.FunctionType):
# build a new function that is a clone of the one from new_cls
@type SLOTS: tuple
'''
SLOTTED_CLASSES = {}
- SLOTS = ('_icache',)
+ SLOTS = ('_icache', 'user_data')
def __new__(cls):
'''
- Creates a new class mimicking the one requested, but with an extra _cache
- attribute set in the __slots__ tuple. This field can be set to a dictionary
- or other object to allow caching to occur.
+ Creates a new class mimicking the one requested, but with extra named
+ defined in __slots__. The _cache attribute is used internally for interface
+ caching. The user_data field may be populated with whatever data structure
+ a client wishes to use. Neither is set to a default value by default.
Note that we can't simply mix __slots__ into this class because __slots__
- has an effect only at class creation time.
+ has an effect only at class creation time.
+
+ We also do not completely obliterate __slots__ to allow __dict__ to be
+ instantiated as normal as reducing the initialization and memory overhead
+ of the millions of accessible objects that are created is a good thing for
+ many clients.
@param cls: Accessibility object class
@type cls: class
'__slots__' : _AccessibleMixin.SLOTS})
_AccessibleMixin.SLOTTED_CLASSES[cls] = new_cls
obj = cls._mix___new__(new_cls)
- # don't create the interface cache until we need it
- obj._icache = None
return obj
def __del__(self):
@return: Accessible child
@rtype: Accessibility.Accessible
'''
+ n = self.childCount
+ if index >= n:
+ raise IndexError
+ elif index < -n:
+ raise IndexError
+ elif index < 0:
+ index += n
return self.getChildAtIndex(index)
def __len__(self):
# return None if the application isn't reachable for any reason
return None
+class _RelationMixin(object):
+ '''
+ Defines methods to be added to the Relation class. At this time it only
+ overrides L{_RelationMixin.getTarget} which by the IDL's standard is
+ supposed to return CORBA.Objects but we expect LAccessibility.Accessible
+ objects (see http://bugzilla.gnome.org/show_bug.cgi?id=435833).
+ This seems to be a problem especially with the Java implementation of CORBA.
+ '''
+ def getTarget(self, index):
+ '''
+ Overrides the regular getTarget to return Accessibility.Accessible
+ objects.
+
+ @return: The 'nth' target of this Relation.
+ @rtype: Accessibility.Accessible
+ '''
+ target = self._mix_getTarget(index)
+ target.ref()
+ return target._narrow(Accessibility.Accessible)
+
# 1. mix the exception handlers into all queryable interfaces
map(_mixExceptions, constants.ALL_INTERFACES)
# 2. mix the exception handlers into other Accessibility objects
map(_mixExceptions, [Accessibility.StateSet])
# 3. mix the new functions
-_mixClass(Accessibility.Accessible, _AccessibleMixin)
+_mixClass(Accessibility.Accessible, _AccessibleMixin,
+ ['_get_name', '_get_description'])
# 4. mix queryInterface convenience methods
_mixInterfaces(Accessibility.Accessible, constants.ALL_INTERFACES)
+# 5. mix Relation class
+_mixClass(Accessibility.Relation, _RelationMixin)