From 6a1b83423051673a15c95dcd11ee0c918b5e3b03 Mon Sep 17 00:00:00 2001 From: eitani Date: Thu, 15 Nov 2007 18:30:48 +0000 Subject: [PATCH] * accessible.py: Added new caching (bug #495077). Put in a mixin for remote objects that need to be unreffed at deletion time (bug #446277). git-svn-id: http://svn.gnome.org/svn/at-spi/trunk@970 e2bd861d-eb25-0410-b326-f6ed22b6b98c --- pyatspi/ChangeLog | 6 ++ pyatspi/accessible.py | 196 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 134 insertions(+), 68 deletions(-) diff --git a/pyatspi/ChangeLog b/pyatspi/ChangeLog index 7b94346..450c5d4 100644 --- a/pyatspi/ChangeLog +++ b/pyatspi/ChangeLog @@ -1,3 +1,9 @@ +2007-11-15 Eitan Isaacson + + * accessible.py: Added new caching (bug #495077). Put in a mixin + for remote objects that need to be unreffed at deletion time + (bug #446277). + 2007-11-01 Eitan Isaacson * event.py (Event.__init__): Ref() the host_application attribute diff --git a/pyatspi/accessible.py b/pyatspi/accessible.py index faf5b25..3766985 100644 --- a/pyatspi/accessible.py +++ b/pyatspi/accessible.py @@ -45,13 +45,17 @@ import Accessibility import constants import utils import registry +import weakref -_ACCESSIBLE_CACHE = {} +_ACCESSIBLE_CACHE = weakref.WeakValueDictionary() +_ACCESSIBLE_USER_DATA = weakref.WeakValueDictionary() _CACHE_LEVEL = None -class _PropertyCache(object): - '''Fixed-size bag class for holding cached values.''' - __slots__ = ('name', 'description', 'rolename') +class _PropertyCache: + pass + +class _UserData: + value = None def getCacheLevel(): ''' @@ -115,6 +119,34 @@ def _updateCache(event): except KeyError: return +def _getAndCache(acc, value_name, get_method): + if _CACHE_LEVEL != constants.CACHE_PROPERTIES: + return get_method() + + cache = _ACCESSIBLE_CACHE + h = hash(acc) + + try: + pc = acc._property_cache + except AttributeError: + try: + pc = cache[h] + except KeyError: + # no cached info for this accessible yet + pc = _PropertyCache() + cache[h] = pc + acc._property_cache = pc + + try: + value = getattr(pc, value_name) + except AttributeError: + # no cached property of this type + value = get_method() + setattr(pc, value_name, value) + + return value + + def _makeQuery(interface): ''' Builds a function querying to a specific interface and returns it. @@ -327,7 +359,7 @@ class _AccessibleMixin(object): @type SLOTS: tuple ''' SLOTTED_CLASSES = {} - SLOTS = ('_icache', 'user_data') + SLOTS = ('_icache', '_property_cache', '_user_data') def __new__(cls): ''' @@ -369,10 +401,6 @@ class _AccessibleMixin(object): we're caching properties. ''' try: - del _ACCESSIBLE_CACHE[hash(self)] - except Exception: - pass - try: self.unref() except Exception: pass @@ -437,6 +465,54 @@ class _AccessibleMixin(object): ''' return self.childCount + def _get_user_data(self): + ''' + Get user_data from global dictionay fo this accessible. + + @return: Any data the user assigned, or None. + @rtype: object + ''' + global _ACCESSIBLE_USER_DATA + h = hash(self) + + try: + ud = self._user_data + except AttributeError: + try: + ud = _ACCESSIBLE_USER_DATA[h] + except KeyError: + # no cached info for this object yet + ud = _UserData() + _ACCESSIBLE_USER_DATA[h] = ud + + self._user_data = ud + return ud.value + + def _set_user_data(self, value): + ''' + Set arbitrary data to user_data. + + @param value: Value to set in user_data + @type value: object + ''' + global _ACCESSIBLE_USER_DATA + h = hash(self) + + try: + ud = self._user_data + except AttributeError: + try: + ud = _ACCESSIBLE_USER_DATA[h] + except KeyError: + # no cached info for this object yet + ud = _UserData() + _ACCESSIBLE_USER_DATA[h] = ud + + self._user_data = ud + ud.value = value + + user_data = property(_get_user_data, _set_user_data) + def _get_name(self): ''' Gets the name of the accessible from the cache if it is available, @@ -445,27 +521,21 @@ class _AccessibleMixin(object): @return: Name of the accessible @rtype: string ''' - if _CACHE_LEVEL != constants.CACHE_PROPERTIES: - return self._get_name() - - cache = _ACCESSIBLE_CACHE - h = hash(self) - try: - return cache[h].name - except KeyError: - # no cached info for this object yet - name = self._get_name() - pc = _PropertyCache() - pc.name = name - cache[h] = pc - return name - except AttributeError: - # no cached name for this object yet - name = self._get_name() - cache[h].name = name - return name - + return _getAndCache(self, 'name', self._get_name) + name = property(_get_name, Accessibility.Accessible._set_name) + + def _get_parent(self): + ''' + Gets the parent of the accessible from the cache if it is available, + otherwise, fetches it remotely. + + @return: Parent of the accessible + @rtype: Accessibility.Accessible + ''' + return _getAndCache(self, 'parent', self._get_parent) + + parent = property(_get_parent) def getRoleName(self): ''' @@ -475,26 +545,18 @@ class _AccessibleMixin(object): @return: Role name of the accessible @rtype: string ''' - if _CACHE_LEVEL != constants.CACHE_PROPERTIES: - return self._mix_getRoleName() + return _getAndCache(self, 'rolename', self._mix_getRoleName) + + def getRole(self): + ''' + Gets the role of the accessible from the cache if it is + available, otherwise, fetches it remotely. + + @return: Role of the accessible + @rtype: Accessibility.Role + ''' + return _getAndCache(self, 'role', self._mix_getRole) - cache = _ACCESSIBLE_CACHE - h = hash(self) - try: - return cache[h].rolename - except KeyError, e: - # no cached info for this object yet - rolename = self._mix_getRoleName() - pc = _PropertyCache() - pc.rolename = rolename - cache[h] = pc - return rolename - except AttributeError, e: - # no cached name for this object yet - rolename = self._mix_getRoleName() - cache[h].rolename = rolename - return rolename - def _get_description(self): ''' Gets the description of the accessible from the cache if it is available, @@ -503,25 +565,7 @@ class _AccessibleMixin(object): @return: Description of the accessible @rtype: string ''' - if _CACHE_LEVEL != constants.CACHE_PROPERTIES: - return self._get_description() - - cache = _ACCESSIBLE_CACHE - h = hash(self) - try: - return cache[h].description - except KeyError: - # no cached info for this object yet - description = self._get_description() - pc = _PropertyCache() - pc.description = description - cache[h] = pc - return description - except AttributeError: - # no cached name for this object yet - description = self._get_description() - cache[h].description = description - return description + return _getAndCache(self, 'description', self._get_description) description = property(_get_description, Accessibility.Accessible._set_description) @@ -589,14 +633,30 @@ class _RelationMixin(object): target.ref() return target._narrow(Accessibility.Accessible) +class _UnrefMixin(object): + ''' + This mixin addresses the issue we have with unreferencing non-primitives. + ''' + def __del__(self): + ''' + Unrefence the instance when Python GCs it. Why do we need this twice? + ''' + try: + self.unref() + except Exception: + pass + # 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, - ['_get_name', '_get_description']) + ['_get_name', '_get_description', '_get_parent']) # 4. mix queryInterface convenience methods _mixInterfaces(Accessibility.Accessible, constants.ALL_INTERFACES) # 5. mix Relation class _mixClass(Accessibility.Relation, _RelationMixin) +# 6. mix in neccessary unrefs +map(lambda cls: _mixClass(cls, _UnrefMixin), + (Accessibility.StateSet,Accessibility.Relation)) -- 2.7.4