From dabefc293e75651ed4c420ef875ec758ebaeacfc Mon Sep 17 00:00:00 2001 From: Mark Doffman Date: Fri, 15 Aug 2008 13:30:17 +0100 Subject: [PATCH] 2008-08-15 Mark Doffman * pyatspi/ tests/pyatspi Changes to enable the most basic unit test of pyatspi to work. --- pyatspi/__init__.py | 24 ++- pyatspi/accessible.py | 190 ------------------------ pyatspi/base.py | 320 ++++++++++++++++++++++++++++++++-------- pyatspi/cache.py | 127 ++++------------ pyatspi/constants.py | 35 ++--- pyatspi/desktop.py | 207 -------------------------- pyatspi/factory.py | 84 +++++------ pyatspi/other.py | 84 +++++------ pyatspi/test.py | 49 ++++++ pyatspi/utils.py | 2 + tests/apps/object-app.c | 4 +- tests/apps/test-application.c | 5 +- tests/pyatspi/accessibletest.py | 50 +++++-- tests/pyatspi/pasytest/Pasy.py | 51 +++++-- tests/pyatspi/setvars.sh | 2 +- tests/pyatspi/testrunner.py | 40 +++-- 16 files changed, 572 insertions(+), 702 deletions(-) delete mode 100644 pyatspi/accessible.py delete mode 100644 pyatspi/desktop.py create mode 100644 pyatspi/test.py diff --git a/pyatspi/__init__.py b/pyatspi/__init__.py index 2b916c9..be6f0d5 100644 --- a/pyatspi/__init__.py +++ b/pyatspi/__init__.py @@ -30,7 +30,29 @@ def clearCache(): def printCache(): print "Print cache function is deprecated"; +import other + +# Build a dictionary mapping state values to names based on the prefix of the +# enum constants. +STATE_VALUE_TO_NAME = dict(((value, name[6:].lower().replace('_', ' ')) + for name, value + in vars(other).items() + if name.startswith('STATE_'))) + +# Build a dictionary mapping relation values to names based on the prefix of +# the enum constants. +RELATION_VALUE_TO_NAME = dict(((value, name[9:].lower().replace('_', ' ')) + for name, value + in vars(other).items() + if name.startswith('RELATION_'))) + +del other + from constants import * + +from base import * from other import * -from accessible import * + +from test import * + #from utils import * diff --git a/pyatspi/accessible.py b/pyatspi/accessible.py deleted file mode 100644 index 54be0cd..0000000 --- a/pyatspi/accessible.py +++ /dev/null @@ -1,190 +0,0 @@ -#Copyright (C) 2008 Codethink Ltd - -#This library is free software; you can redistribute it and/or -#modify it under the terms of the GNU Lesser General Public -#License version 2 as published by the Free Software Foundation. - -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU Lesser General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -from base import BaseProxy - -class Accessible(BaseProxy): - """ - The base interface which is implemented by all accessible objects. - All objects support interfaces for querying their contained - 'children' and position in the accessible-object hierarchy, - whether or not they actually have children. - """ - - def getApplication(self, *args, **kwargs): - """ - Get the containing Application for this object. - @return the Application instance to which this object belongs. - """ - func = self.get_dbus_method("getApplication") - return func(*args, **kwargs) - - def getAttributes(self, *args, **kwargs): - """ - Get a list of properties applied to this object as a whole, as - an AttributeSet consisting of name-value pairs. As such these - attributes may be considered weakly-typed properties or annotations, - as distinct from the strongly-typed interface instance data declared - using the IDL "attribute" keyword. - Not all objects have explicit "name-value pair" AttributeSet - properties. - Attribute names and values may have any UTF-8 string value, however - where possible, in order to facilitate consistent use and exposure - of "attribute" properties by applications and AT clients, attribute - names and values should chosen from a publicly-specified namespace - where appropriate. - Where possible, the names and values in the name-value pairs - should be chosen from well-established attribute namespaces using - standard semantics. For example, attributes of Accessible objects - corresponding to XHTML content elements should correspond to - attribute names and values specified in the w3c XHTML specification, - at http://www.w3.org/TR/xhtml2, where such values are not already - exposed via a more strongly-typed aspect of the AT-SPI API. Metadata - names and values should be chosen from the 'Dublin Core' Metadata - namespace using Dublin Core semantics: http://dublincore.org/dcregistry/ - Similarly, relevant structural metadata should be exposed using - attribute names and values chosen from the CSS2 and WICD specification: - http://www.w3.org/TR/1998/REC-CSS2-19980512 WICD (http://www.w3.org/TR/2005/WD-WICD-20051121/). - @return : an AttributeSet encapsulating any "attribute values" - currently defined for the object. - """ - func = self.get_dbus_method("getAttributes") - return func(*args, **kwargs) - - def getChildAtIndex(self, *args, **kwargs): - """ - Get the accessible child of this object at index. - @param : index - an in parameter indicating which child is requested (zero-indexed). - @return : the 'nth' Accessible child of this object. - """ - func = self.get_dbus_method("getChildAtIndex") - return func(*args, **kwargs) - - def getIndexInParent(self, *args, **kwargs): - """ - Get the index of this object in its parent's child list. - @return : a long integer indicating this object's index in the - parent's list. - """ - func = self.get_dbus_method("getIndexInParent") - return func(*args, **kwargs) - - def getLocalizedRoleName(self, *args, **kwargs): - """ - Get a string indicating the type of UI role played by this object, - translated to the current locale. - @return : a UTF-8 string indicating the type of UI role played - by this object. - """ - func = self.get_dbus_method("getLocalizedRoleName") - return func(*args, **kwargs) - - def getRelationSet(self, *args, **kwargs): - """ - Get a set defining this object's relationship to other accessible - objects. - @return : a RelationSet defining this object's relationships. - """ - func = self.get_dbus_method("getRelationSet") - return func(*args, **kwargs) - - def getRole(self, *args, **kwargs): - """ - Get the Role indicating the type of UI role played by this object. - @return : a Role indicating the type of UI role played by this - object. - """ - func = self.get_dbus_method("getRole") - return func(*args, **kwargs) - - def getRoleName(self, *args, **kwargs): - """ - Get a string indicating the type of UI role played by this object. - @return : a UTF-8 string indicating the type of UI role played - by this object. - """ - func = self.get_dbus_method("getRoleName") - return func(*args, **kwargs) - - def getState(self, *args, **kwargs): - """ - Get the current state of the object as a StateSet. - @return : a StateSet encapsulating the currently true states - of the object. - """ - func = self.get_dbus_method("getState") - return func(*args, **kwargs) - - def isEqual(self, *args, **kwargs): - """ - Determine whether an Accessible refers to the same object as - another. This method should be used rather than brute-force comparison - of object references (i.e. "by-value" comparison), as two object - references may have different apparent values yet refer to the - same object. - @param : obj - an Accessible object reference to compare to - @return : a boolean indicating whether the two object references - point to the same object. - """ - func = self.get_dbus_method("isEqual") - return func(*args, **kwargs) - - def unimplemented(self, *args, **kwargs): - """ - /cond future expansion - """ - func = self.get_dbus_method("unimplemented") - return func(*args, **kwargs) - - def get_childCount(self): - self._pgetter(self._dbus_interface, "childCount") - def set_childCount(self, value): - self._psetter(self._dbus_interface, "childCount", value) - _childCountDoc = \ - """ - childCount: the number of children contained by this object. - """ - childCount = property(fget=get_childCount, fset=set_childCount, doc=_childCountDoc) - - def get_description(self): - self._pgetter(self._dbus_interface, "description") - def set_description(self, value): - self._psetter(self._dbus_interface, "description", value) - _descriptionDoc = \ - """ - a string describing the object in more detail than name. - """ - description = property(fget=get_description, fset=set_description, doc=_descriptionDoc) - - def get_name(self): - self._pgetter(self._dbus_interface, "name") - def set_name(self, value): - self._psetter(self._dbus_interface, "name", value) - _nameDoc = \ - """ - a (short) string representing the object's name. - """ - name = property(fget=get_name, fset=set_name, doc=_nameDoc) - - def get_parent(self): - self._pgetter(self._dbus_interface, "parent") - def set_parent(self, value): - self._psetter(self._dbus_interface, "parent", value) - _parentDoc = \ - """ - an Accessible object which is this object's containing object. - """ - parent = property(fget=get_parent, fset=set_parent, doc=_parentDoc) diff --git a/pyatspi/base.py b/pyatspi/base.py index 0060ea0..8ff71d3 100644 --- a/pyatspi/base.py +++ b/pyatspi/base.py @@ -12,51 +12,47 @@ #along with this program; if not, write to the Free Software #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -from weakref import proxy +import dbus as _dbus +from dbus.proxies import Interface as _Interface -from dbus.proxies import Interface, ProxyObject from dbus.exceptions import * +from factory import create_accessible, add_accessible_class + +import constants class AccessibleObjectNoLongerExists(Exception): pass #------------------------------------------------------------------------------ -class Enum(int): +class _Enum(int): def __str__(self): return self._enum_lookup(int(self)) #------------------------------------------------------------------------------ -class BaseProxy(Interface): - """ - The base D-Bus proxy for a remote object that implements one or more - of the AT-SPI interfaces. - This class must be further specialised as the cache stores are different - for Desktop objects and other Accessible objects. - """ - - def __new__(cls, *args, **kwargs): - Interface.__new__(cls, *args, **kwargs) +class _BaseProxyMeta(type): + def __init__(cls, *args, **kwargs): + type.__init__(cls, *args, **kwargs) queryable_interfaces = { - 'Accessible':ATSPI_ACCESSIBLE, - 'Action':ATSPI_ACTION, - 'Application':ATSPI_APPLICATION, - 'Collection':ATSPI_COLLECTION, - 'Component':ATSPI_COMPONENT, - 'Desktop':ATSPI_DESKTOP, - 'Document':ATSPI_DOCUMENT, - 'EditableText':ATSPI_EDITABLE_TEXT, - 'Hypertext':ATSPI_HYPERTEXT, - 'Hyperlink':ATSPI_HYPERLINK, - 'Image':ATSPI_IMAGE, - 'Selection':ATSPI_SELECTION, - 'StreamableContent':ATSPI_STREAMABLE_CONTENT, - 'Table':ATSPI_TABLE, - 'Text':ATSPI_TEXT, - 'Value':ATSPI_VALUE, + 'Accessible':constants.ATSPI_ACCESSIBLE, + 'Action':constants.ATSPI_ACTION, + 'Application':constants.ATSPI_APPLICATION, + 'Collection':constants.ATSPI_COLLECTION, + 'Component':constants.ATSPI_COMPONENT, + 'Desktop':constants.ATSPI_DESKTOP, + 'Document':constants.ATSPI_DOCUMENT, + 'EditableText':constants.ATSPI_EDITABLE_TEXT, + 'Hypertext':constants.ATSPI_HYPERTEXT, + 'Hyperlink':constants.ATSPI_HYPERLINK, + 'Image':constants.ATSPI_IMAGE, + 'Selection':constants.ATSPI_SELECTION, + 'StreamableContent':constants.ATSPI_STREAMABLE_CONTENT, + 'Table':constants.ATSPI_TABLE, + 'Text':constants.ATSPI_TEXT, + 'Value':constants.ATSPI_VALUE, } for interface in queryable_interfaces.keys(): @@ -65,61 +61,267 @@ class BaseProxy(Interface): return self.queryInterface(object, queryable_interfaces[interface]) setattr(cls, name, new_query) - def __init__(self, obj, cache, path, interface): +#------------------------------------------------------------------------------ + +class _BaseProxy(_Interface): + """ + The base D-Bus proxy for a remote object that implements one or more + of the AT-SPI interfaces. + """ + + __metaclass__ = _BaseProxyMeta + + def __init__(self, cache, app_name, acc_path, parent, interface, dbus_object=None, connection=None): """ Create a D-Bus Proxy for an ATSPI interface. - obj - The D-Bus proxy object this interface uses for D-Bus calls. - cache - Cache storing data for this object. - path - The object path of the remote object. - interface - The name of the ATSPI interface that this proxy implements. + cache - ApplicationCache, where the cached data for the accessible can be obtained. + app_name - D-Bus bus name of the application this accessible belongs to. + acc_path - D-Bus object path of the server side accessible object. + parent - Parent accessible. + interface - D-Bus interface of the object. Used to decide which accessible class to instanciate. + dbus_object(kwarg) - The D-Bus proxy object used by the accessible for D-Bus method calls. """ - Interface.__init__(self, obj, interface) + self._cache = cache + self._app_name = app_name + self._acc_path = acc_path + self._parent = parent - self._cobj = ref(cache) - self._path = path + if not dbus_object: + dbus_object = connection.get_object(self._app_name, self._acc_path, introspect=False) + self._dbus_object = dbus_object + + _Interface.__init__(self, self._dbus_object, interface) self._pgetter = self.get_dbus_method("Get", dbus_interface="org.freedesktop.DBus.Properties") self._psetter = self.get_dbus_method("Set", dbus_interface="org.freedesktop.DBus.Properties") def __getattr__(self, *args, **kwargs): - """ - The __getattr__ function must return the D-Bus method wrapped in a - method to translate exceptions. - """ - # Need to throw an AccessibleObjectNoLongerExists exception - # on D-Bus error of the same type. - try: - return Interface.__getattr__(self, *args, **kwargs) - except UnknownMethodException, e: - raise NotImplementedError(e) - except DBusException, e: - raise LookupError(e) + method = _Interface.__getattr__(self, *args, **kwargs) - @property - def _cache(self): - c = self._cobj() - if not c: - raise AccessibleObjectNoLongerExits("Application has been removed") + def dbus_method_func(*args, **kwargs): + # TODO Need to throw an AccessibleObjectNoLongerExists exception + # on D-Bus error of the same type. + try: + method(*args, **kwargs) + except UnknownMethodException, e: + raise NotImplementedError(e) + except DBusException, e: + raise LookupError(e) + + return dbus_method_func @property - def _data(self): + def cached_data(self): try: - data = self._cache._objects[self._path] + return self._cache[self._app_name][self._acc_path] except KeyError: - raise AccessibleObjectNoLongerExists, 'Cache data cannot be found for path %s' % (self._path,) - return data + raise AccessibleObjectNoLongerExists, \ + 'Cache data cannot be found for path %s in app %s' % (self._acc_path, self._app_name) @property def interfaces(self): return self._data.interfaces def queryInterface(self, interface): + """ + Gets a different accessible interface for this object + or raises a NotImplemented error if the given interface + is not supported. + """ if interface in self._data.interfaces: - return self._cache.proxyFactory(self._path, interface, dbus_obj=self._obj) + return create_accessible(self._cache, + self._app_name, + self._acc_path, + self._parent, + interface, + dbus_object=self._dbus_object) else: raise NotImplementedError( "%s not supported by accessible object at path %s" % (interface, self.path)) +#------------------------------------------------------------------------------ + +class Accessible(_BaseProxy): + """ + The base interface which is implemented by all accessible objects. + All objects support interfaces for querying their contained + 'children' and position in the accessible-object hierarchy, + whether or not they actually have children. + """ + + def getApplication(self): + """ + Get the containing Application for this object. + @return the Application instance to which this object belongs. + """ + application_root = self._cache.get_application_root(self._app_name) + return create_accessible(self._cache, + self._app_name, + application_root, + constants.NULL_BUS_NAME, + constants.NULL_OBJECT_PATH, + constants.ATSPI_ACCESSIBLE, + dbus_object=self._dbus_object) + + def getAttributes(self, *args, **kwargs): + """ + Get a list of properties applied to this object as a whole, as + an AttributeSet consisting of name-value pairs. As such these + attributes may be considered weakly-typed properties or annotations, + as distinct from the strongly-typed interface instance data declared + using the IDL "attribute" keyword. + Not all objects have explicit "name-value pair" AttributeSet + properties. + Attribute names and values may have any UTF-8 string value, however + where possible, in order to facilitate consistent use and exposure + of "attribute" properties by applications and AT clients, attribute + names and values should chosen from a publicly-specified namespace + where appropriate. + Where possible, the names and values in the name-value pairs + should be chosen from well-established attribute namespaces using + standard semantics. For example, attributes of Accessible objects + corresponding to XHTML content elements should correspond to + attribute names and values specified in the w3c XHTML specification, + at http://www.w3.org/TR/xhtml2, where such values are not already + exposed via a more strongly-typed aspect of the AT-SPI API. Metadata + names and values should be chosen from the 'Dublin Core' Metadata + namespace using Dublin Core semantics: http://dublincore.org/dcregistry/ + Similarly, relevant structural metadata should be exposed using + attribute names and values chosen from the CSS2 and WICD specification: + http://www.w3.org/TR/1998/REC-CSS2-19980512 WICD (http://www.w3.org/TR/2005/WD-WICD-20051121/). + @return : an AttributeSet encapsulating any "attribute values" + currently defined for the object. + """ + func = self.get_dbus_method("getAttributes") + return func(*args, **kwargs) + + def getChildAtIndex(self, index): + """ + Get the accessible child of this object at index. + @param : index + an in parameter indicating which child is requested (zero-indexed). + @return : the 'nth' Accessible child of this object. + """ + path = self.cached_data.children[index] + return create_accessible(self._cache, + self._app_name, + path, + self, + constants.ATSPI_ACCESSIBLE, + dbus_object=self._dbus_object) + + def getIndexInParent(self, *args, **kwargs): + """ + Get the index of this object in its parent's child list. + @return : a long integer indicating this object's index in the + parent's list. + """ + func = self.get_dbus_method("getIndexInParent") + return func(*args, **kwargs) + + def getLocalizedRoleName(self, *args, **kwargs): + """ + Get a string indicating the type of UI role played by this object, + translated to the current locale. + @return : a UTF-8 string indicating the type of UI role played + by this object. + """ + func = self.get_dbus_method("getLocalizedRoleName") + return func(*args, **kwargs) + + def getRelationSet(self, *args, **kwargs): + """ + Get a set defining this object's relationship to other accessible + objects. + @return : a RelationSet defining this object's relationships. + """ + func = self.get_dbus_method("getRelationSet") + return func(*args, **kwargs) + + def getRole(self): + """ + Get the Role indicating the type of UI role played by this object. + @return : a Role indicating the type of UI role played by this + object. + """ + return self.cached_data.role + + def getRoleName(self, *args, **kwargs): + """ + Get a string indicating the type of UI role played by this object. + @return : a UTF-8 string indicating the type of UI role played + by this object. + """ + func = self.get_dbus_method("getRoleName") + return func(*args, **kwargs) + + def getState(self, *args, **kwargs): + """ + Get the current state of the object as a StateSet. + @return : a StateSet encapsulating the currently true states + of the object. + """ + func = self.get_dbus_method("getState") + return func(*args, **kwargs) + + def isEqual(self, accessible): + """ + Determine whether an Accessible refers to the same object as + another. This method should be used rather than brute-force comparison + of object references (i.e. "by-value" comparison), as two object + references may have different apparent values yet refer to the + same object. + @param : obj + an Accessible object reference to compare to + @return : a boolean indicating whether the two object references + point to the same object. + """ + return (self._app_name == accessible._app_name) and \ + (self._acc_path == accessible._acc_path) + + def unimplemented(self, *args, **kwargs): + """ + /cond future expansion + """ + func = self.get_dbus_method("unimplemented") + return func(*args, **kwargs) + + def get_childCount(self): + return len(self.cached_data.children) + _childCountDoc = \ + """ + childCount: the number of children contained by this object. + """ + childCount = property(fget=get_childCount, doc=_childCountDoc) + + def get_description(self): + return self.cached_data.description + _descriptionDoc = \ + """ + a string describing the object in more detail than name. + """ + description = property(fget=get_description, doc=_descriptionDoc) + + def get_name(self): + return self.cached_data.name + _nameDoc = \ + """ + a (short) string representing the object's name. + """ + name = property(fget=get_name, doc=_nameDoc) + + def get_parent(self): + # The parent attribute is part of the base proxy + return self._parent + _parentDoc = \ + """ + an Accessible object which is this object's containing object. + """ + parent = property(fget=get_parent, doc=_parentDoc) + +# ATTENTION - Register the Accessible class with the accessible factory. +add_accessible_class(constants.ATSPI_ACCESSIBLE, Accessible) + #END---------------------------------------------------------------------------- diff --git a/pyatspi/cache.py b/pyatspi/cache.py index 14c959a..8b11645 100644 --- a/pyatspi/cache.py +++ b/pyatspi/cache.py @@ -12,8 +12,7 @@ #along with this program; if not, write to the Free Software #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -from base import AccessibleObjectNoLongerExists -from factory import interfaceFactory +import dbus as _dbus #------------------------------------------------------------------------------ @@ -42,13 +41,7 @@ class _CacheData(object): #------------------------------------------------------------------------------ -class BaseCache(object): - """ - Base object for the Desktop, Accessible and Application caches. - - Abstracts common initialization. - """ - +class _BaseCache(object): def __init__(self, connection, bus_name): """ @@ -60,22 +53,28 @@ class BaseCache(object): self._connection = connection self._bus_name = bus_name - obj = connection.get_object(bus_name, self._PATH) - itf = dbus.Interface(obj, self._INTERFACE) + obj = connection.get_object(bus_name, self._PATH, introspect=False) + itf = _dbus.Interface(obj, self._INTERFACE) self._objects = {} - getMethod = itf.get_dbus_method(self._GET_METHOD) - self._updateObjects(getMethod) + get_method = itf.get_dbus_method(self._GET_METHOD) + self._update_objects(get_method()) - self._signalMatch = itf.connect_to_signal(self._UPDATE_SIGNAL, self._updateHandler) + self._signalMatch = itf.connect_to_signal(self._UPDATE_SIGNAL, self._update_handler) - def _updateHandler(self, updates): + def __getitem__(self, key): + return self._objects[key] + + def __contains__(self, key): + return key in self._objects + + def _update_handler(self, updates): update, remove = updates - self._removeObjects(update) - self._updateObjects(remove) + self._remove_objects(update) + self._update_objects(remove) - def _updateObjects(self, objects): + def _update_objects(self, objects): for data in objects: #First element is the object path. path = data[0] @@ -85,30 +84,14 @@ class BaseCache(object): else: self._objects[path] = _CacheData(data) - def _removeObjects(self, paths): + def _remove_objects(self, paths): for path in paths: del(self._objects[path]) - def getAccessible(self, path, interface, dbus_object=None): - """ - Gets a client side proxy for the accessible object found - at the path. - - path - The D-Bus path of the remote object. - interface - The interface that the accessible object should support. - dbus_object=None - The D-Bus proxy object backing this interface. - """ - if path in self._objects: - if not dbus_object: - dbus_object = self._connection.get_object(self._bus_name, path, introspect=False) - return interfaceFactory(proxy, self, path, interface) - else: - raise AccessibleObjectNoLongerExists, "D-Bus reference not found in cache" - #------------------------------------------------------------------------------ -class AccessibleCache(BaseCache): +class AccessibleCache(_BaseCache): """ There is one accessible cache per application. For each application the accessible cache stores @@ -116,6 +99,9 @@ class AccessibleCache(BaseCache): It also acts as the factory for creating client side proxies for these accessible objects. + + connection - DBus connection. + busName - Name of DBus connection where cache interface resides. """ _PATH = '/org/freedesktop/atspi/tree' @@ -124,73 +110,16 @@ class AccessibleCache(BaseCache): _UPDATE_SIGNAL = 'updateTree' def __init__(self, connection, bus_name): - BaseCache.__init__(self, connection, bus_name) + _BaseCache.__init__(self, connection, bus_name) - obj = connection.get_object(_self.bus_name, self._PATH) - itf = dbus.Interface(obj, self._INTERFACE) + obj = connection.get_object(self._bus_name, self._PATH, introspect=False) + itf = _dbus.Interface(obj, self._INTERFACE) self._root = itf.getRoot() - def getRootAccessible(self): - """ - Gets the accessible object at the root of the tree. - """ - return self.getAccessible(self._root) - -#------------------------------------------------------------------------------ - -class DesktopCache(BaseCache): - """ - Cache of desktop objects obtained from the registry. - - The desktop interface on the registry object is the - same as that of the general tree interface on the - applications. - - The difference is that the children data refers to - bus names of the applications rather than the object - paths of particular accessible objects within an application. - """ - - _PATH = '/org/freedesktop/atspi/registry' - _INTERFACE = 'org.freedesktop.atspi.Registry' - _GET_METHOD = 'getDesktops' - _UPDATE_SIGNAL = 'updateDesktops' - - def __init__(self, connection, bus_name): - self._app_cache = ApplicationCache(connection, bus_name) - - def getApplication(self, name): - try: - self._app_cache[name].getRootAccessible() - except KeyError: - raise AccessibleObjectNoLongerExists("Application no longer exists") - -#------------------------------------------------------------------------------ - -class ApplicationCache(object): - """ - Holds a mapping of bus names of each accessible application - to the applications accessible cache. - - Makes calls and recieves updates from the registry - daemon to keep the cache up to date. - """ + def _get_root(self): + return self._root - _PATH = '/org/freedesktop/atspi/registry' - _INTERFACE = 'org.freedesktop.atspi.Registry' - _GET_METHOD = 'getApplications' - _UPDATE_SIGNAL = 'updateApplications' - - def _updateApplications(self, names): - for name in names: - if name not in self._applications: - self._applications[name] = AccessibleCache(self._connection, - self._busName, - self._treePath) - - def _removeApplications(self, names): - for name in names: - del(self._applications[name]) + root = property(fget=_get_root) #END--------------------------------------------------------------------------- diff --git a/pyatspi/constants.py b/pyatspi/constants.py index ccef0fb..f4c41b2 100644 --- a/pyatspi/constants.py +++ b/pyatspi/constants.py @@ -139,20 +139,21 @@ EVENT_TREE = { ['focus:'] } -import other - -# Build a dictionary mapping state values to names based on the prefix of the -# enum constants. -STATE_VALUE_TO_NAME = dict(((value, name[6:].lower().replace('_', ' ')) - for name, value - in vars(other).items() - if name.startswith('STATE_'))) - -# Build a dictionary mapping relation values to names based on the prefix of -# the enum constants. -RELATION_VALUE_TO_NAME = dict(((value, name[9:].lower().replace('_', ' ')) - for name, value - in vars(other).items() - if name.startswith('RELATION_'))) - -del other +ATSPI_ACCESSIBLE = 'org.freedesktop.atspi.Accessible' +ATSPI_ACTION = 'org.freedesktop.atspi.Action' +ATSPI_APPLICATION = 'org.freedesktop.atspi.Application' +ATSPI_COMPONENT = 'org.freedesktop.atspi.Component' +ATSPI_COLLECTION = 'org.freedesktop.atspi.Collection' +ATSPI_DESKTOP = 'org.freedesktop.atspi.Desktop' +ATSPI_DOCUMENT = 'org.freedesktop.atspi.Document' +ATSPI_EDITABLE_TEXT = 'org.freedesktop.atspi.EditableText' +ATSPI_HYPERLINK = 'org.freedesktop.atspi.Hyperlink' +ATSPI_HYPERTEXT = 'org.freedesktop.atspi.Hypertext' +ATSPI_IMAGE = 'org.freedesktop.atspi.Image' +ATSPI_LOGIN_HELPER = 'org.freedesktop.atspi.LoginHelper' +ATSPI_SELECTION = 'org.freedesktop.atspi.Selection' +ATSPI_SELECTOR = 'org.freedesktop.atspi.Selector' +ATSPI_STREAMABLE_CONTENT = 'org.freedesktop.atspi.Content' +ATSPI_TABLE = 'org.freedesktop.atspi.Table' +ATSPI_TEXT = 'org.freedesktop.atspi.Text' +ATSPI_VALUE = 'org.freedesktop.atspi.Value' diff --git a/pyatspi/desktop.py b/pyatspi/desktop.py deleted file mode 100644 index df26d7f..0000000 --- a/pyatspi/desktop.py +++ /dev/null @@ -1,207 +0,0 @@ -#Copyright (C) 2008 Codethink Ltd - -#This library is free software; you can redistribute it and/or -#modify it under the terms of the GNU Lesser General Public -#License version 2 as published by the Free Software Foundation. - -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU Lesser General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -from base import BaseProxy - -from accessible import Accessible - -class Desktop(Accessible): - """ - The desktop class implements the - accessible interface, but uses a different - method to access its chilren and to obtain cached - data. - - This is because application caches are separate - and the children of the desktop are applications. - The child access must get the root accessible - of the application cache that is accessed. - - The data is accessed from the desktop cache. - """ - - @property - def _data(self): - try: - data = self._cache._objects[self._path] - except KeyError, ReferenceError: - raise AccessibleObjectNoLongerExists, 'Cache data cannot be found for path %s' % (self._path,) - return data - - def getApplication(self, *args, **kwargs): - """ - Get the containing Application for this object. - @return the Application instance to which this object belongs. - """ - func = self.get_dbus_method("getApplication") - return func(*args, **kwargs) - - def getAttributes(self, *args, **kwargs): - """ - Get a list of properties applied to this object as a whole, as - an AttributeSet consisting of name-value pairs. As such these - attributes may be considered weakly-typed properties or annotations, - as distinct from the strongly-typed interface instance data declared - using the IDL "attribute" keyword. - Not all objects have explicit "name-value pair" AttributeSet - properties. - Attribute names and values may have any UTF-8 string value, however - where possible, in order to facilitate consistent use and exposure - of "attribute" properties by applications and AT clients, attribute - names and values should chosen from a publicly-specified namespace - where appropriate. - Where possible, the names and values in the name-value pairs - should be chosen from well-established attribute namespaces using - standard semantics. For example, attributes of Accessible objects - corresponding to XHTML content elements should correspond to - attribute names and values specified in the w3c XHTML specification, - at http://www.w3.org/TR/xhtml2, where such values are not already - exposed via a more strongly-typed aspect of the AT-SPI API. Metadata - names and values should be chosen from the 'Dublin Core' Metadata - namespace using Dublin Core semantics: http://dublincore.org/dcregistry/ - Similarly, relevant structural metadata should be exposed using - attribute names and values chosen from the CSS2 and WICD specification: - http://www.w3.org/TR/1998/REC-CSS2-19980512 WICD (http://www.w3.org/TR/2005/WD-WICD-20051121/). - @return : an AttributeSet encapsulating any "attribute values" - currently defined for the object. - """ - func = self.get_dbus_method("getAttributes") - return func(*args, **kwargs) - - def getChildAtIndex(self, *args, **kwargs): - """ - Get the accessible child of this object at index. - @param : index - an in parameter indicating which child is requested (zero-indexed). - @return : the 'nth' Accessible child of this object. - """ - func = self.get_dbus_method("getChildAtIndex") - return func(*args, **kwargs) - - def getIndexInParent(self, *args, **kwargs): - """ - Get the index of this object in its parent's child list. - @return : a long integer indicating this object's index in the - parent's list. - """ - func = self.get_dbus_method("getIndexInParent") - return func(*args, **kwargs) - - def getLocalizedRoleName(self, *args, **kwargs): - """ - Get a string indicating the type of UI role played by this object, - translated to the current locale. - @return : a UTF-8 string indicating the type of UI role played - by this object. - """ - func = self.get_dbus_method("getLocalizedRoleName") - return func(*args, **kwargs) - - def getRelationSet(self, *args, **kwargs): - """ - Get a set defining this object's relationship to other accessible - objects. - @return : a RelationSet defining this object's relationships. - """ - func = self.get_dbus_method("getRelationSet") - return func(*args, **kwargs) - - def getRole(self, *args, **kwargs): - """ - Get the Role indicating the type of UI role played by this object. - @return : a Role indicating the type of UI role played by this - object. - """ - func = self.get_dbus_method("getRole") - return func(*args, **kwargs) - - def getRoleName(self, *args, **kwargs): - """ - Get a string indicating the type of UI role played by this object. - @return : a UTF-8 string indicating the type of UI role played - by this object. - """ - func = self.get_dbus_method("getRoleName") - return func(*args, **kwargs) - - def getState(self, *args, **kwargs): - """ - Get the current state of the object as a StateSet. - @return : a StateSet encapsulating the currently true states - of the object. - """ - func = self.get_dbus_method("getState") - return func(*args, **kwargs) - - def isEqual(self, *args, **kwargs): - """ - Determine whether an Accessible refers to the same object as - another. This method should be used rather than brute-force comparison - of object references (i.e. "by-value" comparison), as two object - references may have different apparent values yet refer to the - same object. - @param : obj - an Accessible object reference to compare to - @return : a boolean indicating whether the two object references - point to the same object. - """ - func = self.get_dbus_method("isEqual") - return func(*args, **kwargs) - - def unimplemented(self, *args, **kwargs): - """ - /cond future expansion - """ - func = self.get_dbus_method("unimplemented") - return func(*args, **kwargs) - - def get_childCount(self): - self._pgetter(self._dbus_interface, "childCount") - def set_childCount(self, value): - self._psetter(self._dbus_interface, "childCount", value) - _childCountDoc = \ - """ - childCount: the number of children contained by this object. - """ - childCount = property(fget=get_childCount, fset=set_childCount, doc=_childCountDoc) - - def get_description(self): - self._pgetter(self._dbus_interface, "description") - def set_description(self, value): - self._psetter(self._dbus_interface, "description", value) - _descriptionDoc = \ - """ - a string describing the object in more detail than name. - """ - description = property(fget=get_description, fset=set_description, doc=_descriptionDoc) - - def get_name(self): - self._pgetter(self._dbus_interface, "name") - def set_name(self, value): - self._psetter(self._dbus_interface, "name", value) - _nameDoc = \ - """ - a (short) string representing the object's name. - """ - name = property(fget=get_name, fset=set_name, doc=_nameDoc) - - def get_parent(self): - self._pgetter(self._dbus_interface, "parent") - def set_parent(self, value): - self._psetter(self._dbus_interface, "parent", value) - _parentDoc = \ - """ - an Accessible object which is this object's containing object. - """ - parent = property(fget=get_parent, fset=set_parent, doc=_parentDoc) diff --git a/pyatspi/factory.py b/pyatspi/factory.py index dfb33bd..4227a24 100644 --- a/pyatspi/factory.py +++ b/pyatspi/factory.py @@ -12,55 +12,51 @@ #along with this program; if not, write to the Free Software #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -from base import * -from accessible import * +#------------------------------------------------------------------------------ -ATSPI_ACCESSIBLE = 'org.freedesktop.atspi.Accessible' -ATSPI_ACTION = 'org.freedesktop.atspi.Action' -ATSPI_APPLICATION = 'org.freedesktop.atspi.Application' -ATSPI_COMPONENT = 'org.freedesktop.atspi.Component' -ATSPI_DOCUMENT = 'org.freedesktop.atspi.Document' -ATSPI_EDITABLE_TEXT = 'org.freedesktop.atspi.EditableText' -ATSPI_HYPERLINK = 'org.freedesktop.atspi.Hyperlink' -ATSPI_HYPERTEXT = 'org.freedesktop.atspi.Hypertext' -ATSPI_IMAGE = 'org.freedesktop.atspi.Image' -ATSPI_LOGIN_HELPER = 'org.freedesktop.atspi.LoginHelper' -ATSPI_SELECTION = 'org.freedesktop.atspi.Selection' -ATSPI_SELECTOR = 'org.freedesktop.atspi.Selector' -ATSPI_STREAMABLE_CONTENT = 'org.freedesktop.atspi.Content' -ATSPI_TABLE = 'org.freedesktop.atspi.Table' -ATSPI_TEXT = 'org.freedesktop.atspi.Text' -ATSPI_VALUE = 'org.freedesktop.atspi.Value' +class AccessibleFactory(object): + __accessible_interfaces = {} -#------------------------------------------------------------------------------ + def create_accessible(self, cache, app_name, acc_path, parent, interface, dbus_object=None, connection=None): + class_ = self.__accessible_interfaces[interface] + return class_(cache, + app_name, + acc_path, + parent, + interface, + dbus_object=dbus_object, + connection=connection) + + def add_accessible_class(self, name, cls): + self.__accessible_interfaces[name] = cls -_interfaces = { - ATSPI_ACCESSIBLE:Accessible, - #ATSPI_ACTION: - #ATSPI_APPLICATION: - #ATSPI_COMPONENT: - #ATSPI_DOCUMENT: - #ATSPI_EDITABLE_TEXT: - #ATSPI_HYPERLINK: - #ATSPI_HYPERTEXT: - #ATSPI_IMAGE: - #ATSPI_LOGIN_HELPER: - #ATSPI_SELECTION: - #ATSPI_SELECTOR: - #ATSPI_STREAMABLE_CONTENT: - #ATSPI_TABLE: - #ATSPI_TEXT: - #ATSPI_TREE: - #ATSPI_VALUE: -} +_factory = AccessibleFactory() -def interfaceFactory(self, busobject, cache, app, path, interface): +def create_accessible(cache, app_name, acc_path, parent, interface, dbus_object=None, connection=None): """ - The queryInterfaces method needs to return - different atspi interfaces depending on the interface name. - This class registers names and ATSPI interface - classes to perform this task. + Used to create different python classes for each of the accessible interfaces. + + The decision on which class to create is based on the name of the + accessible interface. + + cache - ApplicationCache, where the cached data for the accessible can be obtained. + app_name - D-Bus bus name of the application this accessible belongs to. + acc_path - D-Bus object path of the server side accessible object. + app_parent - D-Bus bus name of the parent objects application. + acc_parent - D-Bus object path of the parent accessible. + interface - D-Bus interface of the object. Used to decide which accessible class to instanciate. + dbus_object(kwarg) - The D-Bus proxy object used by the accessible for D-Bus method calls. + connection(kwarg) - Client side D-Bus connection, provided if no D-Bus proxy is available. """ - return _interfaces[interface](object, cache, app, path, interface) + return _factory.create_accessible(cache, + app_name, + acc_path, + parent, + interface, + dbus_object=dbus_object, + connection=connection) + +def add_accessible_class(name, cls): + _factory.add_accessible_class(name, cls) #END---------------------------------------------------------------------------- diff --git a/pyatspi/other.py b/pyatspi/other.py index 61a82e6..46dbcac 100644 --- a/pyatspi/other.py +++ b/pyatspi/other.py @@ -12,10 +12,10 @@ #along with this program; if not, write to the Free Software #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -from base import * -from accessible import * +from base import _BaseProxy, _Enum +from base import Accessible as _Accessible -class Action(BaseProxy): +class Action(_BaseProxy): """ @@ -108,7 +108,7 @@ class Action(BaseProxy): nActions = property(fget=get_nActions, fset=set_nActions, doc=_nActionsDoc) -class Application(Accessible): +class Application(_Accessible): """ @@ -250,7 +250,7 @@ class BoundingBox(list): height = property(fget=_get_height, fset=_set_height) -class Collection(BaseProxy): +class Collection(_BaseProxy): def createMatchRule(self, *args, **kwargs): func = self.get_dbus_method("createMatchRule") @@ -296,7 +296,7 @@ class Collection(BaseProxy): func = self.get_dbus_method("unImplemented4") return func(*args, **kwargs) - class MatchType(Enum): + class MatchType(_Enum): _enum_lookup = { 0:'MATCH_INVALID', 1:'MATCH_ALL', @@ -318,7 +318,7 @@ class Collection(BaseProxy): MATCH_NONE = MatchType(3) - class SortOrder(Enum): + class SortOrder(_Enum): _enum_lookup = { 0:'SORT_ORDER_INVALID', 1:'SORT_ORDER_CANONICAL', @@ -346,7 +346,7 @@ class Collection(BaseProxy): SORT_ORDER_TAB = SortOrder(3) - class TreeTraversalType(Enum): + class TreeTraversalType(_Enum): _enum_lookup = { 0:'TREE_RESTRICT_CHILDREN', 1:'TREE_RESTRICT_SIBLING', @@ -379,7 +379,7 @@ class Command(list): self[1] = val id = property(fget=_get_id, fset=_set_id) -class CommandListener(BaseProxy): +class CommandListener(_BaseProxy): """ An interface which should be implemented by assistive technologies or other clients of the Selector interface, over which notifications @@ -400,7 +400,7 @@ class CommandListener(BaseProxy): return func(*args, **kwargs) -class Component(BaseProxy): +class Component(_BaseProxy): """ @@ -537,7 +537,7 @@ class Component(BaseProxy): return func(*args, **kwargs) -class ComponentLayer(Enum): +class ComponentLayer(_Enum): _enum_lookup = { 0:'LAYER_INVALID', 1:'LAYER_BACKGROUND', @@ -551,7 +551,7 @@ class ComponentLayer(Enum): } -class ContentStream(BaseProxy): +class ContentStream(_BaseProxy): """ An interface by which the requested data from a StreamableContent object may be read by the client. @@ -610,7 +610,7 @@ class ContentStream(BaseProxy): class NotSupported(Exception): pass - class SeekType(Enum): + class SeekType(_Enum): """ Specifies the meaning of a seek 'offset'. Not all SeekTypes are supported by all StreamableContent data sources, for instance @@ -671,7 +671,7 @@ class DeviceEvent(list): self[6] = val is_text = property(fget=_get_is_text, fset=_set_is_text) -class DeviceEventController(BaseProxy): +class DeviceEventController(_BaseProxy): """ The interface via which clients request notification of device events, and through which device events may be simulated. @@ -817,7 +817,7 @@ class DeviceEventController(BaseProxy): return func(*args, **kwargs) -class DeviceEventListener(BaseProxy): +class DeviceEventListener(_BaseProxy): """ This interface should be implemented by AT-SPI clients who wish to make use of the DeviceEventController to receive device event @@ -861,7 +861,7 @@ class DeviceEventListener(BaseProxy): return func(*args, **kwargs) -class Document(BaseProxy): +class Document(_BaseProxy): """ Primarily a 'tagging' interface which indicates the start of document content in the Accessibility hierarchy. Accessible objects @@ -923,7 +923,7 @@ class Document(BaseProxy): func = self.get_dbus_method("unImplemented_") return func(*args, **kwargs) -class Text(BaseProxy): +class Text(_BaseProxy): """ The text interface should be implemented by objects which place textual information onscreen as character strings or glyphs. @@ -1617,7 +1617,7 @@ class EventDetails(list): self[3] = val any_data = property(fget=_get_any_data, fset=_set_any_data) -class EventListener(BaseProxy): +class EventListener(_BaseProxy): """ A generic interface implemented by objects for the receipt of event notifications. EventListener is the interface from which @@ -1676,7 +1676,7 @@ class EventListenerMode(list): global_ = property(fget=_get_global_, fset=_set_global_) -class EventType(Enum): +class EventType(_Enum): _enum_lookup = { 0:'KEY_PRESSED_EVENT', 1:'KEY_RELEASED_EVENT', @@ -1684,7 +1684,7 @@ class EventType(Enum): 3:'BUTTON_RELEASED_EVENT', } -class Hyperlink(BaseProxy): +class Hyperlink(_BaseProxy): """ Instances of Hyperlink are returned by Hypertext objects, and are the means by which end users and clients interact with linked, @@ -1785,7 +1785,7 @@ class Hyperlink(BaseProxy): startIndex = property(fget=get_startIndex, fset=set_startIndex, doc=_startIndexDoc) -class Hypertext(BaseProxy): +class Hypertext(_BaseProxy): """ An interface used for objects which implement linking between multiple resource or content locations, or multiple 'markers' @@ -1844,7 +1844,7 @@ class Hypertext(BaseProxy): return func(*args, **kwargs) -class Image(BaseProxy): +class Image(_BaseProxy): """ An interface implemented by objects which render image data or pictorial information to the screen. When onscreen components @@ -1966,13 +1966,13 @@ class KeyDefinition(list): self[3] = val unused = property(fget=_get_unused, fset=_set_unused) -class KeyEventType(Enum): +class KeyEventType(_Enum): _enum_lookup = { 0:'KEY_PRESSED', 1:'KEY_RELEASED', } -class KeySynthType(Enum): +class KeySynthType(_Enum): _enum_lookup = { 0:'KEY_PRESS', 1:'KEY_RELEASE', @@ -1981,7 +1981,7 @@ class KeySynthType(Enum): 4:'KEY_STRING', } -class LOCALE_TYPE(Enum): +class LOCALE_TYPE(_Enum): _enum_lookup = { 0:'LOCALE_TYPE_MESSAGES', 1:'LOCALE_TYPE_COLLATE', @@ -1991,7 +1991,7 @@ class LOCALE_TYPE(Enum): 5:'LOCALE_TYPE_TIME', } -class LoginHelper(BaseProxy): +class LoginHelper(_BaseProxy): """ An interface for use by assistive technologies by which they can access system information and services on a 'need to know' @@ -2086,7 +2086,7 @@ class LoginHelper(BaseProxy): func = self.get_dbus_method("unImplemented4") return func(*args, **kwargs) - class DeviceReq(Enum): + class DeviceReq(_Enum): _enum_lookup = { 0:'GUI_EVENTS', 1:'CORE_KEYBOARD', @@ -2135,7 +2135,7 @@ class LoginHelper(BaseProxy): self[0] = val winID = property(fget=_get_winID, fset=_set_winID) -class ModifierType(Enum): +class ModifierType(_Enum): _enum_lookup = { 0:'MODIFIER_SHIFT', 1:'MODIFIER_SHIFTLOCK', @@ -2297,7 +2297,7 @@ class Registry(EventListener): return func(*args, **kwargs) -class Relation(BaseProxy): +class Relation(_BaseProxy): """ An interface via which objects' non-hierarchical relationships to one another are indicated. An instance of Relations represents @@ -2351,7 +2351,7 @@ class Relation(BaseProxy): return func(*args, **kwargs) -class RelationType(Enum): +class RelationType(_Enum): _enum_lookup = { 0:'RELATION_NULL', 1:'RELATION_LABEL_FOR', @@ -2374,7 +2374,7 @@ class RelationType(Enum): 18:'RELATION_LAST_DEFINED', } -class Role(Enum): +class Role(_Enum): _enum_lookup = { 0:'ROLE_INVALID', 1:'ROLE_ACCELERATOR_LABEL', @@ -2469,7 +2469,7 @@ class Role(Enum): 90:'ROLE_LAST_DEFINED', } -class Selection(BaseProxy): +class Selection(_BaseProxy): """ An interface which indicates that an object exposes a 'selection' model, allowing the selection of one or more of its children. @@ -2593,7 +2593,7 @@ class Selection(BaseProxy): nSelectedChildren = property(fget=get_nSelectedChildren, fset=set_nSelectedChildren, doc=_nSelectedChildrenDoc) -class Selector(BaseProxy): +class Selector(_BaseProxy): """ This interface is intended for use by assistive technologies and related user-agents. Via this interface, an assistive technology @@ -2689,7 +2689,7 @@ class Selector(BaseProxy): """ supportsReplace = property(fget=get_supportsReplace, fset=set_supportsReplace, doc=_supportsReplaceDoc) - class CommandResult(Enum): + class CommandResult(_Enum): """ A code returned by a call to activateCommand, indicating the result of the activation request. @@ -2712,7 +2712,7 @@ class Selector(BaseProxy): COMMAND_RESULT_SUCCESS = CommandResult(1) -class StateSet(BaseProxy): +class StateSet(_BaseProxy): """ The StateSet interface encapsulates a collection of state information. It allows comparison of state information between object instances, @@ -2792,7 +2792,7 @@ class StateSet(BaseProxy): return func(*args, **kwargs) -class StateType(Enum): +class StateType(_Enum): _enum_lookup = { 0:'STATE_INVALID', 1:'STATE_ACTIVE', @@ -2838,7 +2838,7 @@ class StateType(Enum): 41:'STATE_LAST_DEFINED', } -class StreamableContent(BaseProxy): +class StreamableContent(_BaseProxy): """ An interface whereby an object allows its backing content to be streamed to clients. Negotiation of content type is allowed. @@ -2907,7 +2907,7 @@ class StreamableContent(BaseProxy): return func(*args, **kwargs) -class TEXT_BOUNDARY_TYPE(Enum): +class TEXT_BOUNDARY_TYPE(_Enum): _enum_lookup = { 0:'TEXT_BOUNDARY_CHAR', 1:'TEXT_BOUNDARY_WORD_START', @@ -2918,7 +2918,7 @@ class TEXT_BOUNDARY_TYPE(Enum): 6:'TEXT_BOUNDARY_LINE_END', } -class TEXT_CLIP_TYPE(Enum): +class TEXT_CLIP_TYPE(_Enum): _enum_lookup = { 0:'TEXT_CLIP_NONE', 1:'TEXT_CLIP_MIN', @@ -2926,7 +2926,7 @@ class TEXT_CLIP_TYPE(Enum): 3:'TEXT_CLIP_BOTH', } -class Table(BaseProxy): +class Table(_BaseProxy): """ An interface used by containers whose contained data is arranged in a "tabular" (i.e. row-column) fashion. Tables may resemble @@ -3307,7 +3307,7 @@ class Table(BaseProxy): -class Value(BaseProxy): +class Value(_BaseProxy): """ An interface supporting controls which allow a one-dimensional, scalar quantity to be modified or which reflect a scalar quantity. @@ -3766,5 +3766,3 @@ TEXT_CLIP_MAX = TEXT_CLIP_TYPE(2) TEXT_CLIP_MIN = TEXT_CLIP_TYPE(1) TEXT_CLIP_NONE = TEXT_CLIP_TYPE(0) - - diff --git a/pyatspi/test.py b/pyatspi/test.py new file mode 100644 index 0000000..3696e5e --- /dev/null +++ b/pyatspi/test.py @@ -0,0 +1,49 @@ +#Copyright (C) 2008 Codethink Ltd + +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU Lesser General Public +#License version 2 as published by the Free Software Foundation. + +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU Lesser General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +from cache import AccessibleCache +from factory import create_accessible + +import constants + +#------------------------------------------------------------------------------ + +class TestApplicationCache(object): + """ + Test application cache. Accesses single AccessibleCache. + """ + + def __init__(self, connection, bus_name): + self._connection = connection + self._bus_name = bus_name + self._accessible_cache = AccessibleCache(connection, bus_name) + + def __getitem__(self, key): + return self._accessible_cache + + def __contains__(self, key): + if key == self._bus_name: + return True + else: + return False + + def get_root(self): + return create_accessible(self, + self._bus_name, + self._accessible_cache.root, + None, + constants.ATSPI_ACCESSIBLE, + connection=self._connection) + + root = property(fget=get_root) diff --git a/pyatspi/utils.py b/pyatspi/utils.py index 5853b46..60c8238 100644 --- a/pyatspi/utils.py +++ b/pyatspi/utils.py @@ -301,3 +301,5 @@ def getPath(acc): except Exception: raise LookupError acc = acc.parent + +del constants diff --git a/tests/apps/object-app.c b/tests/apps/object-app.c index 07b79e6..35f1cab 100644 --- a/tests/apps/object-app.c +++ b/tests/apps/object-app.c @@ -27,13 +27,13 @@ test_init (gchar *path) G_MODULE_EXPORT void test_next (int argc, char *argv[]) { - g_print("Moving to next stage\n"); + ; } G_MODULE_EXPORT void test_finished (int argc, char *argv[]) { - g_print("Test has completed\n"); + ; } G_MODULE_EXPORT AtkObject * diff --git a/tests/apps/test-application.c b/tests/apps/test-application.c index 0c62402..550f810 100644 --- a/tests/apps/test-application.c +++ b/tests/apps/test-application.c @@ -131,6 +131,7 @@ static const char* introspection_string = "" " " " " +" " " " " " ""; @@ -145,8 +146,6 @@ message_handler (DBusConnection *bus, DBusMessage *message, void *user_data) DBusMessage *reply = NULL; - g_print("\nRecieved test interface message\n"); - g_return_val_if_fail(iface != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); if (!strcmp(iface, "org.codethink.atspi.test")) @@ -159,7 +158,7 @@ message_handler (DBusConnection *bus, DBusMessage *message, void *user_data) result = DBUS_HANDLER_RESULT_HANDLED; } - if (!strcmp(member, "finished")) + if (!strcmp(member, "finish")) { ((VoidVoid) test_module_finished)(); reply = dbus_message_new_method_return (message); diff --git a/tests/pyatspi/accessibletest.py b/tests/pyatspi/accessibletest.py index 8e10ef4..b7b2c8c 100644 --- a/tests/pyatspi/accessibletest.py +++ b/tests/pyatspi/accessibletest.py @@ -5,33 +5,53 @@ import os.path from xml.dom import minidom import os -from pasytest import PasyTestSuite +from pasytest import PasyTest as _PasyTest -def createNode(accessible, parentElement): +import pyatspi + +def _createNode(accessible, parentElement): e = minidom.Element("accessible") e.attributes["name"] = accessible.name - e.attributes["role"] = str(int(accessible.role)) + e.attributes["role"] = str(int(accessible.getRole())) e.attributes["description"] = accessible.description - for i in range(0, accessible.numChildren): - createNode(accessible.getChild(i), e) + for i in range(0, accessible.childCount): + _createNode(accessible.getChildAtIndex(i), e) parentElement.appendChild(e) -class TreeTestSuite(PasyTestSuite): +class AccessibleTest(_PasyTest): + + __tests__ = ["setup", "tree"] - __tests__ = ["accessibleTree"] + def __init__(self, bus, path): + _PasyTest.__init__(self, "Accessible", False) + self._bus = bus + self._path = path - def __init__(self, bus, name): - PasyTestSuite.__init__(self, "Tree") - self._cache = getAccessibleCache(bus, name) + def setup(self): + self._cache = pyatspi.TestApplicationCache(self._bus, self._path) - def accessibleTree(test): - root = self._cache.getRootAccessible() + def tree(self): + """ + This is a mild stress test for the + methods: + + getChildAtIndex + + And the attributes: + + name + description + + It checks a tree of these values is correctly + passed from Application to AT. + """ + root = self._cache.root doc = minidom.Document() - createNode(root, doc) + _createNode(root, doc) answer = doc.toprettyxml() correct = os.path.join(os.environ["TEST_DATA_DIRECTORY"], @@ -39,6 +59,4 @@ class TreeTestSuite(PasyTestSuite): file = open(correct) cstring = file.read() - test.assertEqual(answer, cstring, "Object tree not passed correctly") - - test.win() + self.assertEqual(answer, cstring, "Object tree not passed correctly") diff --git a/tests/pyatspi/pasytest/Pasy.py b/tests/pyatspi/pasytest/Pasy.py index bb2651f..166c3e1 100644 --- a/tests/pyatspi/pasytest/Pasy.py +++ b/tests/pyatspi/pasytest/Pasy.py @@ -13,8 +13,11 @@ #along with this program; if not, write to the Free Software #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +import gobject from Events import Events +import traceback + PASY_TEST_NOT_STARTED = 0 PASY_TEST_IN_PROGRESS = 1 PASY_TEST_FAIL = 2 @@ -30,7 +33,6 @@ class PasyTestStep(object): self._state = PASY_TEST_NOT_STARTED self._name = name - self._test = test def win(self): if self._state == PASY_TEST_IN_PROGRESS: @@ -49,7 +51,15 @@ class PasyTestStep(object): def run(self): self._state = PASY_TEST_IN_PROGRESS - self.entry(self) + self.entry() + + def report(self): + if self._state == PASY_TEST_WIN: + return "%s - PASSED" % (self._name,) + elif self._state == PASY_TEST_FAIL: + return "%s - FAILED - %s" % (self._name, self.failMsg) + else: + return "%s - INCOMPLETE" %s (self._name,) @property def state(self): @@ -62,29 +72,33 @@ class PasyTestFunc(PasyTestStep): self._func = func def entry(self): - self._func(self) + try: + self._func() + except Exception, e: + self.fail(e.message) + traceback.print_exc() + self.win() class PasyTest(PasyTestStep): __tests__ = [] def __init__(self, name, cont): - PasyTest.__init__(self, name) + PasyTestStep.__init__(self, name) self._cont = cont self._tests = [] - for name in __tests__: - func = self.__getattr__(name) + for name in self.__tests__: + func = getattr(self, name) self._addfunc(func.func_name, func) def _addfunc(self, name, func): - functest = PasyTestFunc(self, func.func_name, func) - self.add(functest) + functest = PasyTestFunc(func.func_name, func) self._tests.append(functest) def entry(self): - self._iter = self._test_iterator + self._iter = self._test_iterator() gobject.idle_add(self.idle_handler) def idle_handler(self): @@ -94,8 +108,8 @@ class PasyTest(PasyTestStep): if step.state == PASY_TEST_WIN or self._cont == True: gobject.idle_add(self.idle_handler) elif step.state == PASY_TEST_FAIL and self._cont == False: - self.fail() - step.events.failed += finished_handler + self.fail("Sub test %s Failed" % step._name) + step.events.finished += finished_handler step.run() except StopIteration: # No More tests, check for success or fail @@ -111,3 +125,18 @@ class PasyTest(PasyTestStep): def _test_iterator(self): for test in self._tests: yield test + + def report(self): + if self._state == PASY_TEST_WIN: + header = "%s - PASSED" % (self._name,) + elif self._state == PASY_TEST_FAIL: + header = "%s - FAILED - %s" % (self._name, self.failMsg) + else: + header = "%s - INCOMPLETE" %s (self._name,) + + step_messages = [] + for test in self._tests: + step_messages.append(test.report()) + + step_report = "\n\t".join(step_messages) + return header + "\n\t" + step_report diff --git a/tests/pyatspi/setvars.sh b/tests/pyatspi/setvars.sh index 17d1c48..d9638a2 100755 --- a/tests/pyatspi/setvars.sh +++ b/tests/pyatspi/setvars.sh @@ -1,4 +1,4 @@ -export PYTHONPATH=../../../ +export PYTHONPATH=../../ export TEST_DATA_DIRECTORY=../data export TEST_ATSPI_LIBRARY=../../atk-adaptor/.libs/libspiatk.so diff --git a/tests/pyatspi/testrunner.py b/tests/pyatspi/testrunner.py index 4a95f70..efd9289 100755 --- a/tests/pyatspi/testrunner.py +++ b/tests/pyatspi/testrunner.py @@ -1,11 +1,19 @@ #!/usr/bin/python +import gobject import dbus import sys import time from random import randint -def runTestApp(module_name, dbus_name=None): +from optparse import OptionParser +from pasytest import PasyTest + +from dbus.mainloop.glib import DBusGMainLoop + +DBusGMainLoop(set_as_default=True) + +def run_test_app(module_name, dbus_name=None): import os from subprocess import Popen @@ -46,24 +54,38 @@ def runTestApp(module_name, dbus_name=None): raw_input(wait_message % (module_name, pop.pid)) def main(argv): - testModule = argv[1] + parser = OptionParser() + parser.add_option("-l", "--library", dest="test_library") + parser.add_option("-m", "--module", dest="test_module") + parser.add_option("-n", "--name", dest="test_name") + (options, args) = parser.parse_args() - # TODO Modify this function to add registryd as an option bus = dbus.SessionBus() name = "test.atspi.R" + str(randint(1, 1000)) - app = runTestApp(testModule, name) - # Wait a little time for the app to start up - # TODO connect this up to the apps start signal + app = run_test_app(options.test_library, name) time.sleep(1) + print "Started test app on bus name %s" % (name,) to = bus.get_object(name, "/org/codethink/atspi/test") test = dbus.Interface(to, "org.codethink.atspi.test") - # Run the test script here FIXME + # Run the test script here + module = __import__(options.test_module) + test_class = getattr(module, options.test_name) + test_object = test_class(bus, name) + test_object.run() + + loop = gobject.MainLoop() + + def finished_handler(): + loop.quit() + print "\n" + test_object.report() + "\n" + test.finish() + + test_object.events.finished += finished_handler - # Inform the test app it can shut down. - test.finish() + loop.run() if __name__=="__main__": sys.exit(main(sys.argv)) -- 2.7.4