* accessible.py (_RelationMixin.getTarget): Add a ref() to the
[platform/core/uifw/at-spi2-atk.git] / pyatspi / accessible.py
index b5ab01d..5aadb9e 100644 (file)
@@ -115,12 +115,12 @@ def _updateCache(event):
   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
   '''
@@ -132,12 +132,13 @@ def _makeQuery(iid):
     @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:
@@ -154,17 +155,16 @@ def _makeQuery(iid):
     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
@@ -207,7 +207,7 @@ def _mixInterfaces(cls, interfaces):
     # 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)
@@ -327,16 +327,22 @@ class _AccessibleMixin(object):
   @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
@@ -353,8 +359,6 @@ class _AccessibleMixin(object):
                       '__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):
@@ -565,6 +569,26 @@ class _AccessibleMixin(object):
     # 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
@@ -574,3 +598,5 @@ _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)