diff options
author | Barry Warsaw <barry@python.org> | 2011-12-15 06:57:21 -0500 |
---|---|---|
committer | Barry Warsaw <barry@python.org> | 2011-12-15 06:57:21 -0500 |
commit | 4c1c2eade1c5b383adad94a7a4fd6553873fecf0 (patch) | |
tree | b9e0f45fc19539bcaddff69e661bf0c5d21bab5a /dbus | |
parent | 667082d0b4aef9c438a2e7fec89614b5b8ef960a (diff) | |
download | dbus-python-4c1c2eade1c5b383adad94a7a4fd6553873fecf0.tar.gz |
This is the big one; it adds Python 3 support.
Diffstat (limited to 'dbus')
-rw-r--r-- | dbus/__init__.py | 19 | ||||
-rw-r--r-- | dbus/_compat.py | 8 | ||||
-rw-r--r-- | dbus/_dbus.py | 11 | ||||
-rw-r--r-- | dbus/_expat_introspect_parser.py | 2 | ||||
-rw-r--r-- | dbus/bus.py | 23 | ||||
-rw-r--r-- | dbus/connection.py | 79 | ||||
-rw-r--r-- | dbus/decorators.py | 19 | ||||
-rw-r--r-- | dbus/proxies.py | 12 | ||||
-rw-r--r-- | dbus/service.py | 25 | ||||
-rw-r--r-- | dbus/types.py | 6 |
10 files changed, 138 insertions, 66 deletions
diff --git a/dbus/__init__.py b/dbus/__init__.py index 803ed2a..eb9717a 100644 --- a/dbus/__init__.py +++ b/dbus/__init__.py @@ -34,9 +34,7 @@ to export objects or claim well-known names. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -import os - -__all__ = ( +__all__ = [ # from _dbus 'Bus', 'SystemBus', 'SessionBus', 'StarterBus', @@ -56,7 +54,7 @@ __all__ = ( 'ObjectPath', 'ByteArray', 'Signature', 'Byte', 'Boolean', 'Int16', 'UInt16', 'Int32', 'UInt32', 'Int64', 'UInt64', - 'Double', 'String', 'Array', 'Struct', 'Dictionary', 'UTF8String', + 'Double', 'String', 'Array', 'Struct', 'Dictionary', # from exceptions 'DBusException', @@ -66,7 +64,12 @@ __all__ = ( # submodules 'service', 'mainloop', 'lowlevel' - ) + ] + +from dbus._compat import is_py2 +if is_py2: + __all__.append('UTF8String') + __docformat__ = 'restructuredtext' try: @@ -92,6 +95,10 @@ from dbus.exceptions import ( ValidationException) from _dbus_bindings import ( Array, Boolean, Byte, ByteArray, Dictionary, Double, Int16, Int32, Int64, - ObjectPath, Signature, String, Struct, UInt16, UInt32, UInt64, UTF8String) + ObjectPath, Signature, String, Struct, UInt16, UInt32, UInt64) + +if is_py2: + from _dbus_bindings import UTF8String + from dbus._dbus import Bus, SystemBus, SessionBus, StarterBus from dbus.proxies import Interface diff --git a/dbus/_compat.py b/dbus/_compat.py new file mode 100644 index 0000000..45a0c97 --- /dev/null +++ b/dbus/_compat.py @@ -0,0 +1,8 @@ +# Python 2 / Python 3 compatibility helpers. + +import sys + +# In Python 2.6, sys.version_info is not a namedtuple, so we can't use +# sys.version_info.major. +is_py3 = (sys.version_info[0] == 3) +is_py2 = not is_py3 diff --git a/dbus/_dbus.py b/dbus/_dbus.py index 808d44b..5a497f4 100644 --- a/dbus/_dbus.py +++ b/dbus/_dbus.py @@ -30,19 +30,18 @@ from __future__ import generators __all__ = ('Bus', 'SystemBus', 'SessionBus', 'StarterBus') __docformat__ = 'reStructuredText' -import os -import sys -import weakref -from traceback import print_exc - from dbus.exceptions import DBusException from _dbus_bindings import ( BUS_DAEMON_IFACE, BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_SESSION, BUS_STARTER, BUS_SYSTEM, DBUS_START_REPLY_ALREADY_RUNNING, - DBUS_START_REPLY_SUCCESS, UTF8String, validate_bus_name, + DBUS_START_REPLY_SUCCESS, validate_bus_name, validate_interface_name, validate_member_name, validate_object_path) from dbus.bus import BusConnection from dbus.lowlevel import SignalMessage +from dbus._compat import is_py2 + +if is_py2: + from _dbus_bindings import UTF8String class Bus(BusConnection): diff --git a/dbus/_expat_introspect_parser.py b/dbus/_expat_introspect_parser.py index de38c45..1cf8a6c 100644 --- a/dbus/_expat_introspect_parser.py +++ b/dbus/_expat_introspect_parser.py @@ -23,7 +23,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -from xml.parsers.expat import ExpatError, ParserCreate +from xml.parsers.expat import ParserCreate from dbus.exceptions import IntrospectionParserException class _Parser(object): diff --git a/dbus/bus.py b/dbus/bus.py index 9f77717..109f4c6 100644 --- a/dbus/bus.py +++ b/dbus/bus.py @@ -39,6 +39,7 @@ from _dbus_bindings import ( from dbus.connection import Connection from dbus.exceptions import DBusException from dbus.lowlevel import HANDLER_RESULT_NOT_YET_HANDLED +from dbus._compat import is_py2 _NAME_OWNER_CHANGE_MATCH = ("type='signal',sender='%s'," @@ -77,13 +78,16 @@ class NameOwnerWatch(object): BUS_DAEMON_NAME, BUS_DAEMON_PATH, arg0=bus_name) + keywords = {} + if is_py2: + keywords['utf8_strings'] = True self._pending_call = bus_conn.call_async(BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_DAEMON_IFACE, 'GetNameOwner', 's', (bus_name,), callback, error_cb, - utf8_strings=True) + **keywords) def cancel(self): if self._match is not None: @@ -230,7 +234,7 @@ class BusConnection(Connection): bus_name = named_service if kwargs: raise TypeError('get_object does not take these keyword ' - 'arguments: %s' % ', '.join(kwargs.iterkeys())) + 'arguments: %s' % ', '.join(kwargs.keys())) return self.ProxyObjectClass(self, bus_name, object_path, introspect=introspect, @@ -321,9 +325,12 @@ class BusConnection(Connection): :Returns: a dbus.Array of dbus.UTF8String :Since: 0.81.0 """ + keywords = {} + if is_py2: + keywords['utf8_strings'] = True return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_DAEMON_IFACE, 'ListNames', - '', (), utf8_strings=True) + '', (), **keywords) def list_activatable_names(self): """Return a list of all names that can be activated on the bus. @@ -331,9 +338,12 @@ class BusConnection(Connection): :Returns: a dbus.Array of dbus.UTF8String :Since: 0.81.0 """ + keywords = {} + if is_py2: + keywords['utf8_strings'] = True return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_DAEMON_IFACE, 'ListActivatableNames', - '', (), utf8_strings=True) + '', (), **keywords) def get_name_owner(self, bus_name): """Return the unique connection name of the primary owner of the @@ -342,10 +352,13 @@ class BusConnection(Connection): :Raises `DBusException`: if the `bus_name` has no owner :Since: 0.81.0 """ + keywords = {} + if is_py2: + keywords['utf8_strings'] = True validate_bus_name(bus_name, allow_unique=False) return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_DAEMON_IFACE, 'GetNameOwner', - 's', (bus_name,), utf8_strings=True) + 's', (bus_name,), **keywords) def watch_name_owner(self, bus_name, callback): """Watch the unique connection name of the primary owner of the diff --git a/dbus/connection.py b/dbus/connection.py index 1215519..f4124bd 100644 --- a/dbus/connection.py +++ b/dbus/connection.py @@ -28,7 +28,7 @@ import threading import weakref from _dbus_bindings import ( - Connection as _Connection, LOCAL_IFACE, LOCAL_PATH, UTF8String, + Connection as _Connection, LOCAL_IFACE, LOCAL_PATH, validate_bus_name, validate_error_name, validate_interface_name, validate_member_name, validate_object_path) from dbus.exceptions import DBusException @@ -36,6 +36,10 @@ from dbus.lowlevel import ( ErrorMessage, HANDLER_RESULT_NOT_YET_HANDLED, MethodCallMessage, MethodReturnMessage, SignalMessage) from dbus.proxies import ProxyObject +from dbus._compat import is_py2 + +if is_py2: + from _dbus_bindings import UTF8String _logger = logging.getLogger('dbus.connection') @@ -46,15 +50,19 @@ def _noop(*args, **kwargs): class SignalMatch(object): - __slots__ = ('_sender_name_owner', '_member', '_interface', '_sender', - '_path', '_handler', '_args_match', '_rule', - '_utf8_strings', '_byte_arrays', '_conn_weakref', - '_destination_keyword', '_interface_keyword', - '_message_keyword', '_member_keyword', - '_sender_keyword', '_path_keyword', '_int_args_match') + _slots = ['_sender_name_owner', '_member', '_interface', '_sender', + '_path', '_handler', '_args_match', '_rule', + '_byte_arrays', '_conn_weakref', + '_destination_keyword', '_interface_keyword', + '_message_keyword', '_member_keyword', + '_sender_keyword', '_path_keyword', '_int_args_match'] + if is_py2: + _slots.append('_utf8_strings') + + __slots__ = tuple(_slots) def __init__(self, conn, sender, object_path, dbus_interface, - member, handler, utf8_strings=False, byte_arrays=False, + member, handler, byte_arrays=False, sender_keyword=None, path_keyword=None, interface_keyword=None, member_keyword=None, message_keyword=None, destination_keyword=None, @@ -80,7 +88,11 @@ class SignalMatch(object): # this later self._sender_name_owner = sender - self._utf8_strings = utf8_strings + if is_py2: + self._utf8_strings = kwargs.pop('utf8_strings', False) + elif 'utf8_strings' in kwargs: + raise TypeError("unexpected keyword argument 'utf8_strings'") + self._byte_arrays = byte_arrays self._sender_keyword = sender_keyword self._path_keyword = path_keyword @@ -134,7 +146,7 @@ class SignalMatch(object): if self._member is not None: rule.append("member='%s'" % self._member) if self._int_args_match is not None: - for index, value in self._int_args_match.iteritems(): + for index, value in self._int_args_match.items(): rule.append("arg%d='%s'" % (index, value)) self._rule = ','.join(rule) @@ -172,10 +184,15 @@ class SignalMatch(object): return False if self._int_args_match is not None: # extracting args with utf8_strings and byte_arrays is less work - args = message.get_args_list(utf8_strings=True, byte_arrays=True) - for index, value in self._int_args_match.iteritems(): + kwargs = dict(byte_arrays=True) + if is_py2: + kwargs['utf8_strings'] = True + args = message.get_args_list(**kwargs) + for index, value in self._int_args_match.items(): if (index >= len(args) - or not isinstance(args[index], UTF8String) + or (not isinstance(args[index], UTF8String) + if is_py2 + else False) or args[index] != value): return False @@ -191,9 +208,12 @@ class SignalMatch(object): # minor optimization: if we already extracted the args with the # right calling convention to do the args match, don't bother # doing so again - if args is None or not self._utf8_strings or not self._byte_arrays: - args = message.get_args_list(utf8_strings=self._utf8_strings, - byte_arrays=self._byte_arrays) + utf8_strings = (is_py2 and self._utf8_strings) + if args is None or not utf8_strings or not self._byte_arrays: + kwargs = dict(byte_arrays=self._byte_arrays) + if is_py2: + kwargs['utf8_strings'] = self._utf8_strings + args = message.get_args_list(**kwargs) kwargs = {} if self._sender_keyword is not None: kwargs[self._sender_keyword] = message.get_sender() @@ -301,7 +321,7 @@ class Connection(_Connection): bus_name = named_service if kwargs: raise TypeError('get_object does not take these keyword ' - 'arguments: %s' % ', '.join(kwargs.iterkeys())) + 'arguments: %s' % ', '.join(kwargs.keys())) return self.ProxyObjectClass(self, bus_name, object_path, introspect=introspect) @@ -421,8 +441,7 @@ class Connection(_Connection): member_keys = (None,) for path in path_keys: - by_interface = self._signal_recipients_by_object_path.get(path, - None) + by_interface = self._signal_recipients_by_object_path.get(path) if by_interface is None: continue for dbus_interface in interface_keys: @@ -531,8 +550,8 @@ class Connection(_Connection): def call_async(self, bus_name, object_path, dbus_interface, method, signature, args, reply_handler, error_handler, - timeout=-1.0, utf8_strings=False, byte_arrays=False, - require_main_loop=True): + timeout=-1.0, byte_arrays=False, + require_main_loop=True, **kwargs): """Call the given method, asynchronously. If the reply_handler is None, successful replies will be ignored. @@ -550,8 +569,11 @@ class Connection(_Connection): 'interface %s' % LOCAL_IFACE) # no need to validate other args - MethodCallMessage ctor will do - get_args_opts = {'utf8_strings': utf8_strings, - 'byte_arrays': byte_arrays} + get_args_opts = dict(byte_arrays=byte_arrays) + if is_py2: + get_args_opts['utf8_strings'] = kwargs.get('utf8_strings', False) + elif 'utf8_strings' in kwargs: + raise TypeError("unexpected keyword argument 'utf8_strings'") message = MethodCallMessage(destination=bus_name, path=object_path, @@ -591,8 +613,8 @@ class Connection(_Connection): require_main_loop=require_main_loop) def call_blocking(self, bus_name, object_path, dbus_interface, method, - signature, args, timeout=-1.0, utf8_strings=False, - byte_arrays=False): + signature, args, timeout=-1.0, + byte_arrays=False, **kwargs): """Call the given method, synchronously. :Since: 0.81.0 """ @@ -604,8 +626,11 @@ class Connection(_Connection): 'interface %s' % LOCAL_IFACE) # no need to validate other args - MethodCallMessage ctor will do - get_args_opts = {'utf8_strings': utf8_strings, - 'byte_arrays': byte_arrays} + get_args_opts = dict(byte_arrays=byte_arrays) + if is_py2: + get_args_opts['utf8_strings'] = kwargs.get('utf8_strings', False) + elif 'utf8_strings' in kwargs: + raise TypeError("unexpected keyword argument 'utf8_strings'") message = MethodCallMessage(destination=bus_name, path=object_path, diff --git a/dbus/decorators.py b/dbus/decorators.py index 71a7203..b164582 100644 --- a/dbus/decorators.py +++ b/dbus/decorators.py @@ -33,14 +33,15 @@ import inspect from dbus import validate_interface_name, Signature, validate_member_name from dbus.lowlevel import SignalMessage from dbus.exceptions import DBusException +from dbus._compat import is_py2 def method(dbus_interface, in_signature=None, out_signature=None, - async_callbacks=None, - sender_keyword=None, path_keyword=None, destination_keyword=None, - message_keyword=None, connection_keyword=None, - utf8_strings=False, byte_arrays=False, - rel_path_keyword=None): + async_callbacks=None, + sender_keyword=None, path_keyword=None, destination_keyword=None, + message_keyword=None, connection_keyword=None, + byte_arrays=False, + rel_path_keyword=None, **kwargs): """Factory for decorators used to mark methods of a `dbus.service.Object` to be exported on the D-Bus. @@ -198,8 +199,12 @@ def method(dbus_interface, in_signature=None, out_signature=None, func._dbus_message_keyword = message_keyword func._dbus_connection_keyword = connection_keyword func._dbus_args = args - func._dbus_get_args_options = {'byte_arrays': byte_arrays, - 'utf8_strings': utf8_strings} + func._dbus_get_args_options = dict(byte_arrays=byte_arrays) + if is_py2: + func._dbus_get_args_options['utf8_strings'] = kwargs.get( + 'utf8_strings', False) + elif 'utf8_strings' in kwargs: + raise TypeError("unexpected keyword argument 'utf8_strings'") return func return decorator diff --git a/dbus/proxies.py b/dbus/proxies.py index f9b2e4e..c7cd802 100644 --- a/dbus/proxies.py +++ b/dbus/proxies.py @@ -23,7 +23,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -import sys import logging try: @@ -45,6 +44,7 @@ _logger = logging.getLogger('dbus.proxies') from _dbus_bindings import ( BUS_DAEMON_IFACE, BUS_DAEMON_NAME, BUS_DAEMON_PATH, INTROSPECTABLE_IFACE, LOCAL_PATH) +from dbus._compat import is_py2 class _DeferredMethod: @@ -59,7 +59,7 @@ class _DeferredMethod: self._block = block def __call__(self, *args, **keywords): - if (keywords.has_key('reply_handler') or + if ('reply_handler' in keywords or keywords.get('ignore_reply', False)): # defer the async call til introspection finishes self._append(self._proxy_method, args, keywords) @@ -226,7 +226,7 @@ class ProxyObject(object): if kwargs: raise TypeError('ProxyObject.__init__ does not take these ' 'keyword arguments: %s' - % ', '.join(kwargs.iterkeys())) + % ', '.join(kwargs.keys())) if follow_name_owner_changes: # we don't get the signals unless the Bus has a main loop @@ -369,13 +369,15 @@ class ProxyObject(object): **keywords) def _Introspect(self): + kwargs = {} + if is_py2: + kwargs['utf8_strings'] = True return self._bus.call_async(self._named_service, self.__dbus_object_path__, INTROSPECTABLE_IFACE, 'Introspect', '', (), self._introspect_reply_handler, self._introspect_error_handler, - utf8_strings=True, - require_main_loop=False) + require_main_loop=False, **kwargs) def _introspect_execute_queue(self): # FIXME: potential to flood the bus diff --git a/dbus/service.py b/dbus/service.py index bc7fe09..b1fc21d 100644 --- a/dbus/service.py +++ b/dbus/service.py @@ -28,9 +28,9 @@ __docformat__ = 'restructuredtext' import sys import logging -import operator import threading import traceback +from collections import Sequence import _dbus_bindings from dbus import ( @@ -41,6 +41,7 @@ from dbus.exceptions import ( DBusException, NameExistsException, UnknownMethodException) from dbus.lowlevel import ErrorMessage, MethodReturnMessage, MethodCallMessage from dbus.proxies import LOCAL_PATH +from dbus._compat import is_py2 _logger = logging.getLogger('dbus.service') @@ -56,10 +57,14 @@ class _VariantSignature(object): """Return self.""" return self - def next(self): + def __next__(self): """Return 'v' whenever called.""" return 'v' + if is_py2: + next = __next__ + + class BusName(object): """A base class for exporting your own Named Services across the Bus. @@ -305,7 +310,7 @@ class InterfaceType(type): for b in bases: base_name = b.__module__ + '.' + b.__name__ if getattr(b, '_dbus_class_table', False): - for (interface, method_table) in class_table[base_name].iteritems(): + for (interface, method_table) in class_table[base_name].items(): our_method_table = interface_table.setdefault(interface, {}) our_method_table.update(method_table) @@ -365,8 +370,11 @@ class InterfaceType(type): return reflection_data -class Interface(object): - __metaclass__ = InterfaceType + +# Define Interface as an instance of the metaclass InterfaceType, in a way +# that is compatible across both Python 2 and Python 3. +Interface = InterfaceType('Interface', (object,), {}) + #: A unique object used as the value of Object._object_path and #: Object._connection if it's actually in more than one place @@ -719,8 +727,9 @@ class Object(Interface): elif len(signature_tuple) == 1: retval = (retval,) else: - if operator.isSequenceType(retval): - # multi-value signature, multi-value return... proceed unchanged + if isinstance(retval, Sequence): + # multi-value signature, multi-value return... proceed + # unchanged pass else: raise TypeError('%s has multiple output values in signature %s but did not return a sequence' % @@ -754,7 +763,7 @@ class Object(Interface): reflection_data += '<node name="%s">\n' % object_path interfaces = self._dbus_class_table[self.__class__.__module__ + '.' + self.__class__.__name__] - for (name, funcs) in interfaces.iteritems(): + for (name, funcs) in interfaces.items(): reflection_data += ' <interface name="%s">\n' % (name) for func in funcs.values(): diff --git a/dbus/types.py b/dbus/types.py index 3623437..a134495 100644 --- a/dbus/types.py +++ b/dbus/types.py @@ -5,5 +5,9 @@ __all__ = ('ObjectPath', 'ByteArray', 'Signature', 'Byte', 'Boolean', from _dbus_bindings import ( Array, Boolean, Byte, ByteArray, Dictionary, Double, Int16, Int32, Int64, - ObjectPath, Signature, String, Struct, UInt16, UInt32, UInt64, UTF8String, + ObjectPath, Signature, String, Struct, UInt16, UInt32, UInt64, UnixFd) + +from dbus._compat import is_py2 +if is_py2: + from _dbus_bindings import UTF8String |