PK}J#2O5jeepney/__init__.py"""Low-level, pure Python DBus protocol wrapper. """ from .low_level import Message, Parser from .bus import find_session_bus, find_system_bus from .wrappers import * __version__ = '0.3' PKcZJVѤKjeepney/auth.pyfrom binascii import hexlify import os def make_auth_external(): hex_uid = hexlify(str(os.getuid()).encode('ascii')) return b'AUTH EXTERNAL %b\r\n' % hex_uid BEGIN = b'BEGIN\r\n' class SASLParser: def __init__(self): self.buffer = b'' self.authenticated = False self.error = None def process_line(self, line): if line.startswith(b'OK '): self.authenticated = True else: self.error = line def feed(self, data): self.buffer += data while (b'\r\n' in data) and not self.authenticated: line, self.buffer = self.buffer.split(b'\r\n', 1) self.process_line(line) PKqJGjeepney/bindgen.py"""Generate a wrapper class from DBus introspection data""" import argparse from textwrap import indent import xml.etree.ElementTree as ET from jeepney.wrappers import Introspectable from jeepney.integrate.blocking import connect_and_authenticate from jeepney import __version__ class Method: def __init__(self, xml_node): self.name = xml_node.attrib['name'] self.in_args = [] self.signature = [] for arg in xml_node.findall("arg[@direction='in']"): try: name = arg.attrib['name'] except KeyError: name = 'arg{}'.format(len(self.in_args)) self.in_args.append(name) self.signature.append(arg.attrib['type']) def _make_code_noargs(self): return ("def {name}(self):\n" " return new_method_call(self, '{name}')\n").format( name=self.name) def make_code(self): if not self.in_args: return self._make_code_noargs() args = ', '.join(self.in_args) signature = ''.join(self.signature) tuple = ('({},)' if len(self.in_args) == 1 else '({})').format(args) return ("def {name}(self, {args}):\n" " return new_method_call(self, '{name}', '{signature}',\n" " {tuple})\n").format( name=self.name, args=args, signature=signature, tuple=tuple ) INTERFACE_CLASS_TEMPLATE = """ class {cls_name}(MessageGenerator): interface = {interface!r} def __init__(self, object_path={path!r}, bus_name={bus_name!r}): super().__init__(object_path=object_path, bus_name=bus_name) """ class Interface: def __init__(self, xml_node, path, bus_name): self.name = xml_node.attrib['name'] self.path = path self.bus_name = bus_name self.methods = [Method(node) for node in xml_node.findall('method')] def make_code(self): cls_name = self.name.split('.')[-1] chunks = [INTERFACE_CLASS_TEMPLATE.format(cls_name=cls_name, interface=self.name, path=self.path, bus_name=self.bus_name)] for method in self.methods: chunks.append(indent(method.make_code(), ' ' * 4)) return '\n'.join(chunks) MODULE_TEMPLATE = '''\ """Auto-generated DBus bindings Generated by jeepney version {version} Object path: {path} Bus name : {bus_name} """ from jeepney.wrappers import MessageGenerator, new_method_call ''' # Jeepney already includes bindings for these common interfaces IGNORE_INTERFACES = { 'org.freedesktop.DBus.Introspectable', 'org.freedesktop.DBus.Properties', 'org.freedesktop.DBus.Peer', } def code_from_xml(xml, path, bus_name, fh): if isinstance(fh, (bytes, str)): with open(fh, 'w') as f: return code_from_xml(xml, path, bus_name, f) root = ET.fromstring(xml) fh.write(MODULE_TEMPLATE.format(version=__version__, path=path, bus_name=bus_name)) i = 0 for interface_node in root.findall('interface'): if interface_node.attrib['name'] in IGNORE_INTERFACES: continue fh.write(Interface(interface_node, path, bus_name).make_code()) i += 1 return i def generate(path, name, output_file, bus='SESSION'): conn = connect_and_authenticate(bus) msg = Introspectable(path, name).Introspect() xml = conn.send_and_get_reply(msg)[0] # print(xml) n_interfaces = code_from_xml(xml, path, name, output_file) print("Written {} interface wrappers to {}".format(n_interfaces, output_file)) def main(): ap = argparse.ArgumentParser() ap.add_argument('-n', '--name', required=True) ap.add_argument('-p', '--path', required=True) ap.add_argument('--bus', default='SESSION') ap.add_argument('-o', '--output') args = ap.parse_args() output = args.output or (args.path[1:].replace('/', '_') + '.py') generate(args.path, args.name, output, args.bus) if __name__ == '__main__': main() PKBZJ*jeepney/bus.pyimport os import re _escape_pat = re.compile(r'%([0-9A-Fa-f]{2})') def unescape(v): def repl(match): n = int(match.group(1), base=16) return chr(n) return _escape_pat.sub(repl, v) def parse_addresses(s): for addr in s.split(';'): transport, info = addr.split(':', 1) kv = {} for x in info.split(','): k, v = x.split('=', 1) kv[k] = unescape(v) yield (transport, kv) SUPPORTED_TRANSPORTS = ('unix',) def get_connectable_addresses(addr): unsupported_transports = set() found = False for transport, kv in parse_addresses(addr): if transport not in SUPPORTED_TRANSPORTS: unsupported_transports.add(transport) elif transport == 'unix': if 'abstract' in kv: yield '\0' + kv['abstract'] found = True elif 'path' in kv: yield kv['path'] found = True if not found: raise RuntimeError("DBus transports ({}) not supported. Supported: {}" .format(unsupported_transports, SUPPORTED_TRANSPORTS)) def find_session_bus(): addr = os.environ['DBUS_SESSION_BUS_ADDRESS'] return next(get_connectable_addresses(addr)) # TODO: fallbacks to X, filesystem def find_system_bus(): addr = os.environ.get('DBUS_SYSTEM_BUS_ADDRESS', '') \ or 'unix:path=/var/run/dbus/system_bus_socket' return next(get_connectable_addresses(addr)) def get_bus(addr): if addr == 'SESSION': return find_session_bus() elif addr == 'SYSTEM': return find_system_bus() else: return next(get_connectable_addresses(addr)) if __name__ == '__main__': print('System bus at:', find_system_bus()) print('Session bus at:', find_session_bus()) PKRJ&Weppjeepney/bus_messages.py"""Messages for talking to the DBus daemon itself Generated by jeepney.bindgen and modified by hand. """ from .wrappers import MessageGenerator, new_method_call class DBusNameFlags: allow_replacement = 1 replace_existing = 2 do_not_queue = 4 class DBus(MessageGenerator): interface = 'org.freedesktop.DBus' def __init__(self, object_path='/org/freedesktop/DBus', bus_name='org.freedesktop.DBus'): super().__init__(object_path=object_path, bus_name=bus_name) def Hello(self): return new_method_call(self, 'Hello') def RequestName(self, name, flags=0): return new_method_call(self, 'RequestName', 'su', (name, flags)) def ReleaseName(self, name): return new_method_call(self, 'ReleaseName', 's', (name,)) def StartServiceByName(self, name): return new_method_call(self, 'StartServiceByName', 'su', (name, 0)) def UpdateActivationEnvironment(self, env): return new_method_call(self, 'UpdateActivationEnvironment', 'a{ss}', (env,)) def NameHasOwner(self, name): return new_method_call(self, 'NameHasOwner', 's', (name,)) def ListNames(self): return new_method_call(self, 'ListNames') def ListActivatableNames(self): return new_method_call(self, 'ListActivatableNames') def AddMatch(self, rule): if isinstance(rule, MatchRule): rule = rule.serialise() return new_method_call(self, 'AddMatch', 's', (rule,)) def RemoveMatch(self, rule): if isinstance(rule, MatchRule): rule = rule.serialise() return new_method_call(self, 'RemoveMatch', 's', (rule,)) def GetNameOwner(self, name): return new_method_call(self, 'GetNameOwner', 's', (name,)) def ListQueuedOwners(self, name): return new_method_call(self, 'ListQueuedOwners', 's', (name,)) def GetConnectionUnixUser(self, name): return new_method_call(self, 'GetConnectionUnixUser', 's', (name,)) def GetConnectionUnixProcessID(self, name): return new_method_call(self, 'GetConnectionUnixProcessID', 's', (name,)) def GetAdtAuditSessionData(self, name): return new_method_call(self, 'GetAdtAuditSessionData', 's', (name,)) def GetConnectionSELinuxSecurityContext(self, name): return new_method_call(self, 'GetConnectionSELinuxSecurityContext', 's', (name,)) def ReloadConfig(self): return new_method_call(self, 'ReloadConfig') def GetId(self): return new_method_call(self, 'GetId') def GetConnectionCredentials(self, name): return new_method_call(self, 'GetConnectionCredentials', 's', (name,)) message_bus = DBus() class Monitoring(MessageGenerator): interface = 'org.freedesktop.DBus.Monitoring' def __init__(self, object_path='/org/freedesktop/DBus', bus_name='org.freedesktop.DBus'): super().__init__(object_path=object_path, bus_name=bus_name) def BecomeMonitor(self, rules): return new_method_call(self, 'BecomeMonitor', 'asu', (rules, 0)) class Stats(MessageGenerator): interface = 'org.freedesktop.DBus.Debug.Stats' def __init__(self, object_path='/org/freedesktop/DBus', bus_name='org.freedesktop.DBus'): super().__init__(object_path=object_path, bus_name=bus_name) def GetStats(self): return new_method_call(self, 'GetStats') def GetConnectionStats(self, arg0): return new_method_call(self, 'GetConnectionStats', 's', (arg0,)) def GetAllMatchRules(self): return new_method_call(self, 'GetAllMatchRules') class MatchRule: """Construct a match rule to subscribe to DBus messages. e.g.:: mr = MatchRule(interface='org.freedesktop.DBus', member='NameOwnerChanged', type='signal') msg = add_match(mr) # Send this message to subscribe to the signal """ def __init__(self, *, type=None, sender=None, interface=None, member=None, path=None, path_namespace=None, destination=None, eavesdrop=False): self.conditions = c ={} if type: c['type'] = type if sender: c['sender'] = sender if interface: c['interface'] = interface if member: c['member'] = member if path: c['path'] = path if path_namespace: c['path_namespace'] = path_namespace if destination: c['destination'] = destination if eavesdrop: c['eavesdrop'] = 'true' def add_arg_condition(self, argno, value, kind='string'): """Add a condition for a particular argument argno: int, 0-63 kind: 'string', 'path', 'namespace' """ if kind not in {'string', 'path', 'namespace'}: raise ValueError("kind={!r}".format(kind)) if kind == 'namespace' and argno != 0: raise ValueError("argno must be 0 for kind='namespace'") if kind == 'string': kind = '' name = 'arg{}{}'.format(argno, kind) self.conditions[name] = value def serialise(self): parts = [] for k, v in sorted(self.conditions.items()): parts.append('{}={}'.format(k, v.replace("'", r"\'"))) return ','.join(parts) PKpJ11jeepney/low_level.pyfrom enum import Enum, IntEnum import struct class Endianness(Enum): little = 1 big = 2 def struct_code(self): return '<' if (self is Endianness.little) else '>' def dbus_code(self): return b'l' if (self is Endianness.little) else b'B' endian_map = {b'l': Endianness.little, b'B': Endianness.big} class MessageType(Enum): method_call = 1 method_return = 2 error = 3 signal = 4 msg_type_map = {t.value: t for t in MessageType} # Flags: NO_REPLY_EXPECTED = 1 NO_AUTO_START = 2 ALLOW_INTERACTIVE_AUTHORIZATION = 4 class HeaderFields(IntEnum): path = 1 interface = 2 member = 3 error_name = 4 reply_serial = 5 destination = 6 sender = 7 signature = 8 unix_fds = 9 header_fields_map = {t.value: t for t in HeaderFields} def padding(pos, step): pad = step - (pos % step) if pad == step: return 0 return pad class FixedType: def __init__(self, size, struct_code): self.size = self.alignment = size self.struct_code = struct_code def parse_data(self, buf, pos, endianness): pos += padding(pos, self.alignment) code = endianness.struct_code() + self.struct_code val = struct.unpack(code, buf[pos:pos + self.size])[0] return val, pos + self.size def serialise(self, data, pos, endianness): pad = b'\0' * padding(pos, self.alignment) code = endianness.struct_code() + self.struct_code return pad + struct.pack(code, data) def __repr__(self): return 'FixedType({!r}, {!r})'.format(self.size, self.struct_code) def __eq__(self, other): return (type(other) is FixedType) and (self.size == other.size) \ and (self.struct_code == other.struct_code) simple_types = { 'y': FixedType(1, 'B'), # unsigned 8 bit 'b': FixedType(4, 'I'), # bool 'n': FixedType(2, 'h'), # signed 16 bit 'q': FixedType(2, 'H'), # unsigned 16 bit 'i': FixedType(4, 'i'), # signed 32-bit 'u': FixedType(4, 'I'), # unsigned 32-bit 'x': FixedType(8, 'q'), # signed 64-bit 't': FixedType(8, 'Q'), # unsigned 64-bit 'd': FixedType(8, 'd'), # double 'h': FixedType(8, 'I'), # file descriptor (32-bit unsigned, index) TODO } class StringType: def __init__(self, length_type): self.length_type = length_type @property def alignment(self): return self.length_type.size def parse_data(self, buf, pos, endianness): length, pos = self.length_type.parse_data(buf, pos, endianness) end = pos + length val = buf[pos:end].decode('utf-8') assert buf[end:end + 1] == b'\0' return val, end + 1 def serialise(self, data, pos, endianness): if not isinstance(data, str): raise TypeError("Expected str, not {!r}".format(data)) encoded = data.encode('utf-8') len_data = self.length_type.serialise(len(encoded), pos, endianness) return len_data + encoded + b'\0' def __repr__(self): return 'StringType({!r})'.format(self.length_type) def __eq__(self, other): return (type(other) is StringType) \ and (self.length_type == other.length_type) simple_types.update({ 's': StringType(simple_types['u']), # String 'o': StringType(simple_types['u']), # Object path 'g': StringType(simple_types['y']), # Signature }) class Struct: alignment = 8 def __init__(self, fields): if any(isinstance(f, DictEntry) for f in fields): raise TypeError("Found dict entry outside array") self.fields = fields def parse_data(self, buf, pos, endianness): pos += padding(pos, 8) res = [] for field in self.fields: v, pos = field.parse_data(buf, pos, endianness) res.append(v) return tuple(res), pos def serialise(self, data, pos, endianness): if not isinstance(data, tuple): raise TypeError("Expected tuple, not {!r}".format(data)) if len(data) != len(self.fields): raise ValueError("{} entries for {} fields".format( len(data), len(self.fields) )) pad = b'\0' * padding(pos, self.alignment) pos += len(pad) res_pieces = [] for item, field in zip(data, self.fields): res_pieces.append(field.serialise(item, pos, endianness)) pos += len(res_pieces[-1]) return pad + b''.join(res_pieces) def __repr__(self): return "{}({!r})".format(type(self).__name__, self.fields) def __eq__(self, other): return (type(other) is type(self)) and (self.fields == other.fields) class DictEntry(Struct): def __init__(self, fields): if len(fields) != 2: raise TypeError( "Dict entry must have 2 fields, not %d" % len(fields)) if not isinstance(fields[0], (FixedType, StringType)): raise TypeError( "First field in dict entry must be simple type, not {}" .format(type(fields[0]))) super().__init__(fields) class Array: alignment = 4 length_type = FixedType(4, 'I') def __init__(self, elt_type): self.elt_type = elt_type def parse_data(self, buf, pos, endianness): # print('Array start', pos) length, pos = self.length_type.parse_data(buf, pos, endianness) pos += padding(pos, self.elt_type.alignment) end = pos + length res = [] while pos < end: # print('Array elem', pos) v, pos = self.elt_type.parse_data(buf, pos, endianness) res.append(v) return res, pos def serialise(self, data, pos, endianness): if isinstance(self.elt_type, DictEntry) and isinstance(data, dict): data = sorted(data.items()) elif (self.elt_type == simple_types['y']) and isinstance(data, bytes): pass elif not isinstance(data, list): raise TypeError("Not suitable for array: {!r}".format(data)) pad1 = padding(pos, self.alignment) pos_after_length = pos + pad1 + 4 pad2 = padding(pos_after_length, self.elt_type.alignment) data_pos = pos_after_length + pad2 chunks = [] for item in data: chunks.append(self.elt_type.serialise(item, data_pos, endianness)) data_pos += len(chunks[-1]) buf = b''.join(chunks) len_data = self.length_type.serialise(len(buf), pos+pad1, endianness) pos += len(len_data) # print('Array ser: pad1={!r}, len_data={!r}, pad2={!r}, buf={!r}'.format( # pad1, len_data, pad2, buf)) return (b'\0' * pad1) + len_data + (b'\0' * pad2) + buf def __repr__(self): return 'Array({!r})'.format(self.elt_type) def __eq__(self, other): return (type(other) is Array) and (self.elt_type == other.elt_type) class Variant: alignment = 1 def parse_data(self, buf, pos, endianness): # print('variant', pos) sig, pos = simple_types['g'].parse_data(buf, pos, endianness) # print('variant sig:', repr(sig), pos) valtype = parse_signature(list(sig)) val, pos = valtype.parse_data(buf, pos, endianness) # print('variant done', (sig, val), pos) return (sig, val), pos def serialise(self, data, pos, endianness): sig, data = data valtype = parse_signature(list(sig)) sig_buf = simple_types['g'].serialise(sig, pos, endianness) return sig_buf + valtype.serialise(data, pos + len(sig_buf), endianness) def __repr__(self): return 'Variant()' def __eq__(self, other): return type(other) is Variant def parse_signature(sig): """Parse a symbolic signature into objects. """ # Based on http://norvig.com/lispy.html token = sig.pop(0) if token == 'a': return Array(parse_signature(sig)) if token == 'v': return Variant() elif token == '(': fields = [] while sig[0] != ')': fields.append(parse_signature(sig)) sig.pop(0) # ) return Struct(fields) elif token == '{': de = [] while sig[0] != '}': de.append(parse_signature(sig)) sig.pop(0) # } return DictEntry(de) elif token in ')}': raise ValueError('Unexpected end of struct') else: return simple_types[token] def calc_msg_size(buf): endian, = struct.unpack('c', buf[:1]) endian = endian_map[endian] body_length, = struct.unpack(endian.struct_code() + 'I', buf[4:8]) fields_array_len, = struct.unpack(endian.struct_code() + 'I', buf[12:16]) header_len = 16 + fields_array_len return header_len + padding(header_len, 8) + body_length _header_fields_type = Array(Struct([simple_types['y'], Variant()])) def parse_header_fields(buf, endianness): l, pos = _header_fields_type.parse_data(buf, 12, endianness) return {header_fields_map[k]: v[1] for (k, v) in l}, pos header_field_codes = { 1: 'o', 2: 's', 3: 's', 4: 's', 5: 'u', 6: 's', 7: 's', 8: 'g', 9: 'u', } def serialise_header_fields(d, endianness): l = [(i.value, (header_field_codes[i], v)) for (i, v) in sorted(d.items())] return _header_fields_type.serialise(l, 12, endianness) class Header: def __init__(self, endianness, message_type, flags, protocol_version, body_length, serial, fields): self.endianness = endianness self.message_type = message_type self.flags = flags self.protocol_version = protocol_version self.body_length = body_length self.serial = serial self.fields = fields def __repr__(self): return 'Header({!r}, {!r}, {!r}, {!r}, {!r}, {!r}, fields={!r})'.format( self.endianness, self.message_type, self.flags, self.protocol_version, self.body_length, self.serial, self.fields) def serialise(self): s = self.endianness.struct_code() + 'cBBBII' return struct.pack(s, self.endianness.dbus_code(), self.message_type.value, self.flags, self.protocol_version, self.body_length, self.serial) \ + serialise_header_fields(self.fields, self.endianness) @classmethod def from_buffer(cls, buf): endian, msgtype, flags, pv = struct.unpack('cBBB', buf[:4]) endian = endian_map[endian] bodylen, serial = struct.unpack(endian.struct_code() + 'II', buf[4:12]) fields, pos = parse_header_fields(buf, endian) return cls(endian, msg_type_map[msgtype], flags, pv, bodylen, serial, fields), pos class Message: def __init__(self, header, body): self.header = header self.body = body def __repr__(self): return "{}({!r}, {!r})".format(type(self).__name__, self.header, self.body) @classmethod def from_buffer(cls, buf): header, pos = Header.from_buffer(buf) body = () if HeaderFields.signature in header.fields: sig = header.fields[HeaderFields.signature] body_type = parse_signature(list('(%s)' % sig)) body = body_type.parse_data(buf, pos, header.endianness)[0] return cls(header, body) def serialise(self): endian = self.header.endianness if HeaderFields.signature in self.header.fields: sig = self.header.fields[HeaderFields.signature] body_type = parse_signature(list('(%s)' % sig)) body_buf = body_type.serialise(self.body, 0, endian) else: body_buf = b'' self.header.body_length = len(body_buf) header_buf = self.header.serialise() pad = b'\0' * padding(len(header_buf), 8) return header_buf + pad + body_buf class Parser: def __init__(self): self.buf = b'' self.next_msg_size = None def feed(self, data): self.buf += data return list(iter(self._read1, None)) def _read1(self): if self.next_msg_size is None: if len(self.buf) >= 16: self.next_msg_size = calc_msg_size(self.buf) nms = self.next_msg_size if (nms is not None) and len(self.buf) >= nms: raw_msg, self.buf = self.buf[:nms], self.buf[nms:] msg = Message.from_buffer(raw_msg) self.next_msg_size = None return msg PKw]J{A. . jeepney/routing.pyfrom .low_level import MessageType, HeaderFields from .wrappers import DBusErrorResponse class Router: """Routing for messages coming back to a client application. :param handle_factory: Constructor for an object like asyncio.Future, with methods *set_result* and *set_exception*. Outgoing method call messages will get a handle associated with them. :param on_unhandled: Callback for messages not otherwise dispatched. """ def __init__(self, handle_factory, on_unhandled=None): self.handle_factory = handle_factory self.on_unhandled = on_unhandled self.outgoing_serial = 0 self.awaiting_reply = {} self.signal_callbacks = {} def outgoing(self, msg): """Set the serial number in the message & make a handle if a method call """ self.outgoing_serial += 1 msg.header.serial = self.outgoing_serial if msg.header.message_type is MessageType.method_call: self.awaiting_reply[msg.header.serial] = handle = self.handle_factory() return handle def subscribe_signal(self, callback, path, interface, member): """Add a callback for a signal. """ self.signal_callbacks[(path, interface, member)] = callback def incoming(self, msg): """Route an incoming message. """ hdr = msg.header # Signals: if hdr.message_type is MessageType.signal: key = (hdr.fields.get(HeaderFields.path, None), hdr.fields.get(HeaderFields.interface, None), hdr.fields.get(HeaderFields.member, None) ) cb = self.signal_callbacks.get(key, None) if cb is not None: cb(msg.body) return # Method returns & errors reply_serial = hdr.fields.get(HeaderFields.reply_serial, -1) reply_handle = self.awaiting_reply.pop(reply_serial, None) if reply_handle is not None: if hdr.message_type is MessageType.method_return: reply_handle.set_result(msg.body) return elif hdr.message_type is MessageType.error: reply_handle.set_exception(DBusErrorResponse(msg)) return if self.on_unhandled: self.on_unhandled(msg) PK}JPuٶjeepney/wrappers.pyfrom typing import Union from warnings import warn from .low_level import * __all__ = [ 'DBusAddress', 'new_method_call', 'new_method_return', 'new_error', 'new_signal', 'MessageGenerator', 'Properties', 'DBusErrorResponse', ] class DBusAddress: def __init__(self, object_path, bus_name=None, interface=None): self.object_path = object_path self.bus_name = bus_name self.interface = interface def __repr__(self): return '{}({!r}, bus_name={!r}, interface={!r})'.format(type(self).__name__, self.object_path, self.bus_name, self.interface) def with_interface(self, interface): return type(self)(self.object_path, self.bus_name, interface) class DBusObject(DBusAddress): def __init__(self, object_path, bus_name=None, interface=None): super().__init__(object_path, bus_name, interface) warn('Deprecated alias, use DBusAddress instead', stacklevel=2) def new_header(msg_type): return Header(Endianness.little, msg_type, flags=0, protocol_version=1, body_length=-1, serial=-1, fields={}) def new_method_call(remote_obj, method, signature=None, body=()): header = new_header(MessageType.method_call) header.fields[HeaderFields.path] = remote_obj.object_path if remote_obj.bus_name is None: raise ValueError("remote_obj.bus_name cannot be None for method calls") header.fields[HeaderFields.destination] = remote_obj.bus_name if remote_obj.interface is not None: header.fields[HeaderFields.interface] = remote_obj.interface header.fields[HeaderFields.member] = method if signature is not None: header.fields[HeaderFields.signature] = signature return Message(header, body) def new_method_return(parent_msg, signature=None, body=()): header = new_header(MessageType.method_return) header.fields[HeaderFields.reply_serial] = parent_msg.header.serial if signature is not None: header.fields[HeaderFields.signature] = signature return Message(header, body) def new_error(parent_msg, error_name, signature=None, body=()): header = new_header(MessageType.error) header.fields[HeaderFields.reply_serial] = parent_msg.header.serial header.fields[HeaderFields.error_name] = error_name if signature is not None: header.fields[HeaderFields.signature] = signature return Message(header, body) def new_signal(emitter, signal, signature=None, body=()): header = new_header(MessageType.signal) header.fields[HeaderFields.path] = emitter.object_path if emitter.interface is None: raise ValueError("emitter.interface cannot be None for signals") header.fields[HeaderFields.interface] = emitter.interface header.fields[HeaderFields.member] = signal if signature is not None: header.fields[HeaderFields.signature] = signature return Message(header, body) class MessageGenerator: """Subclass this to define the methods available on a DBus interface. jeepney.bindgen can automatically create subclasses using introspection. """ def __init__(self, object_path, bus_name): self.object_path = object_path self.bus_name = bus_name def __repr__(self): return "{}({!r}, bus_name={!r})".format(type(self).__name__, self.object_path, self.bus_name) class ProxyBase: """A proxy is an IO-aware wrapper around a MessageGenerator Calling methods on a proxy object will send a message and wait for the reply. This is a base class for proxy implementations in jeepney.integrate. """ def __init__(self, msggen): self._msggen = msggen def __getattr__(self, item): if item.startswith('__'): raise AttributeError(item) make_msg = getattr(self._msggen, item, None) if callable(make_msg): return self._method_call(make_msg) raise AttributeError(item) def _method_call(self, make_msg): raise NotImplementedError("Needs to be implemented in subclass") class Properties: """Build messages for accessing object properties This uses the standard DBus interface org.freedesktop.DBus.Properties """ def __init__(self, obj: Union[DBusAddress, MessageGenerator]): self.obj = obj self.props_if = obj.with_interface('org.freedesktop.DBus.Properties') def get(self, name): return new_method_call(self.props_if, 'Get', 'ss', (self.obj.interface, name)) def get_all(self): return new_method_call(self.props_if, 'GetAll', 's', (self.obj.interface,)) def set(self, name, signature, value): return new_method_call(self.props_if, 'Set', 'ssv', (self.obj.interface, name, (signature, value))) class Introspectable(MessageGenerator): interface = 'org.freedesktop.DBus.Introspectable' def Introspect(self): return new_method_call(self, 'Introspect') class DBusErrorResponse(Exception): def __init__(self, msg): self.name = msg.header.fields.get(HeaderFields.error_name) self.data = msg.body def __str__(self): return '[{}] {}'.format(self.name, self.data) PK"aZJjeepney/integrate/__init__.pyPK$^Jz jeepney/integrate/asyncio.pyimport asyncio from jeepney.auth import SASLParser, make_auth_external, BEGIN from jeepney.bus import get_bus from jeepney.low_level import Parser, MessageType from jeepney.wrappers import ProxyBase from jeepney.routing import Router from jeepney.bus_messages import message_bus class DBusProtocol(asyncio.Protocol): def __init__(self): self.auth_parser = SASLParser() self.parser = Parser() self.router = Router(asyncio.Future) self.authentication = asyncio.Future() self.unique_name = None def connection_made(self, transport): self.transport = transport self.transport.write(b'\0' + make_auth_external()) def _authenticated(self): self.transport.write(BEGIN) self.authentication.set_result(True) self.data_received = self.data_received_post_auth self.data_received(self.auth_parser.buffer) def data_received(self, data): self.auth_parser.feed(data) if self.auth_parser.authenticated: self._authenticated() elif self.auth_parser.error: self.authentication.set_exception(ValueError(self.auth_parser.error)) def data_received_post_auth(self, data): for msg in self.parser.feed(data): self.router.incoming(msg) def send_message(self, message): if not self.authentication.done(): raise RuntimeError("Wait for authentication before sending messages") future = self.router.outgoing(message) data = message.serialise() self.transport.write(data) return future class Proxy(ProxyBase): def __init__(self, msggen, protocol): super().__init__(msggen) self._protocol = protocol def __repr__(self): return 'Proxy({}, {})'.format(self._msggen, self._protocol) def _method_call(self, make_msg): def inner(*args, **kwargs): msg = make_msg(*args, **kwargs) assert msg.header.message_type is MessageType.method_call return self._protocol.send_message(msg) return inner async def connect_and_authenticate(bus='SESSION', loop=None): if loop is None: loop = asyncio.get_event_loop() (t, p) = await loop.create_unix_connection(DBusProtocol, path=get_bus(bus)) await p.authentication bus = Proxy(message_bus, p) hello_reply = await bus.Hello() p.unique_name = hello_reply[0] return (t, p) PKcJg g jeepney/integrate/blocking.py"""Synchronous IO wrappers around jeepney """ from asyncio import Future import functools import socket from jeepney.auth import SASLParser, make_auth_external, BEGIN from jeepney.bus import get_bus from jeepney.low_level import Parser, MessageType from jeepney.wrappers import ProxyBase from jeepney.routing import Router from jeepney.bus_messages import message_bus class DBusConnection: def __init__(self, sock): self.sock = sock self.parser = Parser() self.router = Router(Future) self.bus_proxy = Proxy(message_bus, self) hello_reply = self.bus_proxy.Hello() self.unique_name = hello_reply[0] def send_message(self, message): future = self.router.outgoing(message) data = message.serialise() self.sock.sendall(data) return future def recv_messages(self): """Read data from the socket and handle incoming messages. Blocks until at least one message has been read. """ while True: b = self.sock.recv(4096) msgs = self.parser.feed(b) if msgs: for msg in msgs: self.router.incoming(msg) return def send_and_get_reply(self, message): """Send a message, wait for the reply and return it. """ future = self.send_message(message) while not future.done(): self.recv_messages() return future.result() class Proxy(ProxyBase): def __init__(self, msggen, connection): super().__init__(msggen) self._connection = connection def __repr__(self): return "Proxy({}, {})".format(self._msggen, self._connection) def _method_call(self, make_msg): @functools.wraps(make_msg) def inner(*args, **kwargs): msg = make_msg(*args, **kwargs) assert msg.header.message_type is MessageType.method_call return self._connection.send_and_get_reply(msg) return inner def connect_and_authenticate(bus='SESSION'): bus_addr = get_bus(bus) sock = socket.socket(family=socket.AF_UNIX) sock.connect(bus_addr) sock.sendall(b'\0' + make_auth_external()) auth_parser = SASLParser() while not auth_parser.authenticated: auth_parser.feed(sock.recv(1024)) if auth_parser.error: raise Exception("Authentication failed: %r" % auth_parser.error) sock.sendall(BEGIN) conn = DBusConnection(sock) conn.parser.buf = auth_parser.buffer return conn if __name__ == '__main__': conn = connect_and_authenticate() print("Unique name:", conn.unique_name) PKcJF6 jeepney/integrate/tornado.pyimport socket from tornado.concurrent import Future from tornado.gen import coroutine from tornado.ioloop import IOLoop from tornado.iostream import IOStream from jeepney.auth import SASLParser, make_auth_external, BEGIN from jeepney.bus import get_bus from jeepney.low_level import Parser, MessageType from jeepney.wrappers import ProxyBase from jeepney.routing import Router from jeepney.bus_messages import message_bus class DBusConnection: def __init__(self, bus_addr): self.auth_parser = SASLParser() self.parser = Parser() self.router = Router(Future) self.authentication = Future() self.unique_name = None self._sock = socket.socket(family=socket.AF_UNIX) self.stream = IOStream(self._sock, read_chunk_size=4096) def connected(): self.stream.write(b'\0' + make_auth_external()) self.stream.connect(bus_addr, connected) self.stream.read_until_close(streaming_callback=self.data_received) def _authenticated(self): self.stream.write(BEGIN) self.authentication.set_result(True) self.data_received_post_auth(self.auth_parser.buffer) def data_received(self, data): if self.authentication.done(): return self.data_received_post_auth(data) self.auth_parser.feed(data) if self.auth_parser.authenticated: self._authenticated() elif self.auth_parser.error: self.authentication.set_exception(ValueError(self.auth_parser.error)) def data_received_post_auth(self, data): for msg in self.parser.feed(data): self.router.incoming(msg) def send_message(self, message): if not self.authentication.done(): raise RuntimeError("Wait for authentication before sending messages") future = self.router.outgoing(message) data = message.serialise() self.stream.write(data) return future class Proxy(ProxyBase): def __init__(self, msggen, connection): super().__init__(msggen) self._connection = connection def __repr__(self): return 'Proxy({}, {})'.format(self._msggen, self._connection) def _method_call(self, make_msg): def inner(*args, **kwargs): msg = make_msg(*args, **kwargs) assert msg.header.message_type is MessageType.method_call return self._connection.send_message(msg) return inner @coroutine def connect_and_authenticate(bus='SESSION'): bus_addr = get_bus(bus) conn = DBusConnection(bus_addr) yield conn.authentication conn.unique_name = (yield Proxy(message_bus, conn).Hello())[0] return conn if __name__ == '__main__': conn = IOLoop.current().run_sync(connect_and_authenticate) print("Unique name is:", conn.unique_name) PKYJjeepney/tests/__init__.pyPKqJY$jeepney/tests/secrets_introspect.xml PKZJ44jeepney/tests/test_auth.pyfrom jeepney import auth def test_make_auth_external(): b = auth.make_auth_external() assert b.startswith(b'AUTH EXTERNAL') def test_parser(): p = auth.SASLParser() p.feed(b'OK 728d62bc2eb394') assert not p.authenticated p.feed(b'1ebbb0b42958b1e0d6\r\n') assert p.authenticated PK%sJ%JJjeepney/tests/test_bindgen.pyfrom io import StringIO import os.path from jeepney.low_level import MessageType, HeaderFields from jeepney.bindgen import code_from_xml sample_file = os.path.join(os.path.dirname(__file__), 'secrets_introspect.xml') def test_bindgen(): with open(sample_file) as f: xml = f.read() sio = StringIO() n_interfaces = code_from_xml(xml, path='/org/freedesktop/secrets', bus_name='org.freedesktop.secrets', fh=sio) # 5 interfaces defined, but we ignore Properties, Introspectable, Peer assert n_interfaces == 2 # Run the generated code, defining the message generator classes. binding_ns = {} exec(sio.getvalue(), binding_ns) Service = binding_ns['Service'] # Check basic functionality of the Service class assert Service.interface == 'org.freedesktop.Secret.Service' msg = Service().SearchItems({"service": "foo", "user": "bar"}) assert msg.header.message_type is MessageType.method_call assert msg.header.fields[HeaderFields.destination] == 'org.freedesktop.secrets' PKZJR*^OOjeepney/tests/test_bus.pyimport pytest from testpath import modified_env from jeepney import bus def test_get_connectable_addresses(): a = list(bus.get_connectable_addresses('unix:path=/run/user/1000/bus')) assert a == ['/run/user/1000/bus'] a = list(bus.get_connectable_addresses('unix:abstract=/tmp/foo')) assert a == ['\0/tmp/foo'] with pytest.raises(RuntimeError): list(bus.get_connectable_addresses('unix:tmpdir=/tmp')) def test_get_bus(): with modified_env({ 'DBUS_SESSION_BUS_ADDRESS':'unix:path=/run/user/1000/bus', 'DBUS_SYSTEM_BUS_ADDRESS': 'unix:path=/var/run/dbus/system_bus_socket' }): assert bus.get_bus('SESSION') == '/run/user/1000/bus' assert bus.get_bus('SYSTEM') == '/var/run/dbus/system_bus_socket' assert bus.get_bus('unix:path=/run/user/1002/bus') == '/run/user/1002/bus' PKgJjeepney/tests/test_low_level.pyfrom jeepney.low_level import * HELLO_METHOD_CALL = ( b'l\x01\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00m\x00\x00\x00\x01\x01o\x00\x15' b'\x00\x00\x00/org/freedesktop/DBus\x00\x00\x00\x02\x01s\x00\x14\x00\x00\x00' b'org.freedesktop.DBus\x00\x00\x00\x00\x03\x01s\x00\x05\x00\x00\x00Hello\x00' b'\x00\x00\x06\x01s\x00\x14\x00\x00\x00org.freedesktop.DBus\x00\x00\x00\x00') def test_parser_simple(): msg = Parser().feed(HELLO_METHOD_CALL)[0] assert msg.header.fields[HeaderFields.member] == 'Hello' def chunks(src, size): pos = 0 while pos < len(src): end = pos + size yield src[pos:end] pos = end def test_parser_chunks(): p = Parser() chunked = list(chunks(HELLO_METHOD_CALL, 16)) for c in chunked[:-1]: assert p.feed(c) == [] msg = p.feed(chunked[-1])[0] assert msg.header.fields[HeaderFields.member] == 'Hello' def test_multiple(): msgs = Parser().feed(HELLO_METHOD_CALL * 6) assert len(msgs) == 6 for msg in msgs: assert msg.header.fields[HeaderFields.member] == 'Hello' def test_roundtrip(): msg = Parser().feed(HELLO_METHOD_CALL)[0] assert msg.serialise() == HELLO_METHOD_CALL def test_serialise_dict(): data = { 'a': 'b', 'de': 'f', } string_type = simple_types['s'] sig = Array(DictEntry([string_type, string_type])) print(sig.serialise(data, 0, Endianness.little)) assert sig.serialise(data, 0, Endianness.little) == ( b'\x1e\0\0\0' + # Length b'\0\0\0\0' + # Padding b'\x01\0\0\0a\0\0\0' + b'\x01\0\0\0b\0\0\0' + b'\x02\0\0\0de\0\0' + b'\x01\0\0\0f\0' ) def test_parse_signature(): sig = parse_signature(list('(a{sv}(oayays)b)')) print(sig) assert sig == Struct([ Array(DictEntry([simple_types['s'], Variant()])), Struct([ simple_types['o'], Array(simple_types['y']), Array(simple_types['y']), simple_types['s'] ]), simple_types['b'], ]) PKeJqjeepney/tests/test_routing.pyfrom asyncio import Future import pytest from jeepney.routing import Router from jeepney.wrappers import new_method_return, new_error, DBusErrorResponse from jeepney.bus_messages import message_bus def test_message_reply(): router = Router(Future) call = message_bus.Hello() future = router.outgoing(call) router.incoming(new_method_return(call, 's', ('test',))) assert future.result() == ('test',) def test_error(): router = Router(Future) call = message_bus.Hello() future = router.outgoing(call) router.incoming(new_error(call, 'TestError', 'u', (31,))) with pytest.raises(DBusErrorResponse) as e: future.result() assert e.value.name == 'TestError' assert e.value.data == (31,) def test_unhandled(): unhandled = [] router = Router(Future, on_unhandled=unhandled.append) msg = message_bus.Hello() router.incoming(msg) assert len(unhandled) == 1 assert unhandled[0] == msg PKVZJGD99jeepney-0.3.dist-info/LICENSEThe MIT License (MIT) Copyright (c) 2017 Thomas Kluyver Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!H;@QPjeepney-0.3.dist-info/WHEEL1 0 RZq+D-Dv;_[*7Fp ܦpv/fݞoL(*IPK!Hjߣdjeepney-0.3.dist-info/METADATARMo@k".T4fcmG\{fcОlޙ7{AT"':/)VBc=9D X>SxD5.:!U9liNt:ξYY'mC| JyiuA.?\׊]Fwl[:P 牌kw}6*,Ӝ`_^tE.;2p*. ȝZKR&?@_[2_?Ws&0mzT`Z C-uV;Dgr -V\bu8#\$# VQ:d]KH v3v"O3e$DRhd9R35@FQ&>gHgexMD@N^H#!I'r2+hH/3Qсa)R^缗{ҭq뚼olz"jo[GfPK!H>Gjeepney-0.3.dist-info/RECORDǖ8}? !Av`6`D~ Uvfs/ݛq[Yyo킡')vr pZ.LJK?xJGC>1q^ARx)h%Z7zQb  {\?I0^)BUF>dKs-_{V-"hߩ=1%;w(7g?hS ոmj.܋=$!?{t> FS֞!J=+:J$\ 24a0@?U6Wc\>arbBUXD9&]{>Bb}q{u]3Y}ncAk8ژsm7I:mizHfӕ +s3^w"AKR>N~YYW_feh9.g)Fl8[vwTz Ahìy~ 2UҼoCϬn`Pj qH^AلŏIxI"bZ,-#grL`H CPTHA#"Ym3ҝl68$89E֖F 1b$G~/Q8Pvq>оkP\2&D;W+ZI$qi6.1z?O{f#Gw;)}k[JjG5?+k 6V{q4 |5ƑuۭuiJZ0]m8>svHP_'O-XGYrpJ\g]t`*Nmdٕo[~IHpnѦspey-=Hk~3&NʅE7G~PSBpbuA L]?f?*Q I MFCKKDw},[!$d5sf=u+0{cߨ&aPK}J#2O5jeepney/__init__.pyPKcZJVѤKjeepney/auth.pyPKqJGjeepney/bindgen.pyPKBZJ*jeepney/bus.pyPKRJ&Weppjeepney/bus_messages.pyPKpJ110jeepney/low_level.pyPKw]J{A. . bjeepney/routing.pyPK}JPuٶ^kjeepney/wrappers.pyPK"aZJEjeepney/integrate/__init__.pyPK$^Jz jeepney/integrate/asyncio.pyPKcJg g @jeepney/integrate/blocking.pyPKcJF6 jeepney/integrate/tornado.pyPKYJ(jeepney/tests/__init__.pyPKqJY$_jeepney/tests/secrets_introspect.xmlPKZJ44jeepney/tests/test_auth.pyPK%sJ%JJjeepney/tests/test_bindgen.pyPKZJR*^OOqjeepney/tests/test_bus.pyPKgJjeepney/tests/test_low_level.pyPKeJq9jeepney/tests/test_routing.pyPKVZJGD993jeepney-0.3.dist-info/LICENSEPK!H;@QPjeepney-0.3.dist-info/WHEELPK!Hjߣd1jeepney-0.3.dist-info/METADATAPK!H>Gdjeepney-0.3.dist-info/RECORDPKa