PKIZJM6jeepney/__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.1' 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) 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()) PK:xZJ('-,-,jeepney/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) 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): encoded = data.encode('utf-8') len_data = self.length_type.serialise(len(encoded), pos, endianness) return len_data + encoded + b'\0' 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 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) 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): 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) 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 parse_signature(sig): # 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 PKyZJ}w w jeepney/wrappers.pyfrom .low_level import * __all__ = [ 'DBusObject', 'message_bus', 'new_method_call', 'new_method_return', 'new_error', 'new_signal', 'hello_msg', ] class DBusObject: 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.bus_name, self.object_path, interface) message_bus = DBusObject('/org/freedesktop/DBus', 'org.freedesktop.DBus', 'org.freedesktop.DBus') 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) # We need to say hello to the message bus before doing anythong else, so provide # a prebuilt message for this. hello_msg = new_method_call(message_bus, 'Hello') PK"aZJjeepney/integrate/__init__.pyPK2yZJϼ 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, HeaderFields, MessageType from jeepney.wrappers import hello_msg class DBusProtocol(asyncio.Protocol): def __init__(self): self.auth_parser = SASLParser() self.parser = Parser() self.outgoing_serial = 0 self.awaiting_reply = {} 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): reply_serial = msg.header.fields.get(HeaderFields.reply_serial, -1) if reply_serial in self.awaiting_reply: self.awaiting_reply[reply_serial].set_result(msg) def send_message(self, message): if not self.authentication.done(): raise RuntimeError("Wait for authentication before sending messages") fut = None if message.header.serial == -1: self.outgoing_serial += 1 message.header.serial = self.outgoing_serial if message.header.message_type == MessageType.method_call: fut = asyncio.Future() self.awaiting_reply[message.header.serial] = fut data = message.serialise() self.transport.write(data) return fut 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 hello_reply = await p.send_message(hello_msg) p.unique_name = hello_reply.body[0] return (t, p) PKYJjeepney/tests/__init__.pyPKZJ44jeepney/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 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' PKYJ&jeepney/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 PK!H;@QPjeepney-0.1.dist-info/WHEEL1 0 RZq+D-Dv;_[*7Fp ܦpv/fݞoL(*IPK!H. jeepney-0.1.dist-info/METADATARMo@kT\,h)QӤ"[TNH{oc>Hk*Zh{~Ik Vv,P-%0IVs9n5NtDBUJymuŠt@_5WFnf׉}j?.P *◙ki`?OCqϗ#DHTlKvdM>/wȃZKJ.?@oow:0D9HoF2]Z+I0=! ,7ELHFh$Z롑m@*Tֺ;*#Yj (NF'0zQY"2FulB٤ GyɦA|`G^vi4f&=R'MH1^Ǒ S ˭mHe̸;]==>PK!H^9g,jeepney-0.1.dist-info/RECORDɒP}ED pTc]6gN'i"k&M]À"#v0B[G*mBF ·7t(yb"ӞNao!LgϫBd"q`.]Nji )cygPG0E=a2pqQ8S鵹P.]H&mMm(e㩺b-r`nd*9ɪ&.V^K`6YM dwFs*؝` 2h*L'h6d2Wbu(y@. 8fEEE9[LY07){-$ 1^K#613%QS*!m}`i1q 1_q