PK }J#2O5 jeepney/__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'
PK cZJVѤK jeepney/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)
PK qJG jeepney/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()
PK BZJ* 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 RJ&Wep p jeepney/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)
PK pJ1 1 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)
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
PK w]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 "aZJ jeepney/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)
PK cJg
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)
PK cJF6 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)
PK YJ jeepney/tests/__init__.pyPK qJY $ jeepney/tests/secrets_introspect.xml
PK ZJ4 4 jeepney/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%J J jeepney/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'
PK ZJR*^O O jeepney/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'
PK gJ 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
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'],
])
PK eJq jeepney/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
PK VZJGD9 9 jeepney-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;@Q P jeepney-0.3.dist-info/WHEEL1
0 RZq+D-Dv;_[*7Fp ܦpv/fݞoL(*IPK !Hjߣ d jeepney-0.3.dist-info/METADATARMo@k".T4 fcmG\{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\b u8#\$#VQ:d]KH v3v"O3e$DRhd9R35@FQ&>gHgex MD@N^H#!I'r2+hH/3Qсa)R^缗{ҭq뚼olz"jo[Gf PK !H>G jeepney-0.3.dist-info/RECORDǖ8}?!Av`6`D~ Uvfs/ݛq[Yyo킡')vrpZ.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}ncA k8ژsm7I:mizH fӕ +s3^w"AKR>N~YYW_feh9.g)Fl8[vwTzAhìy~2UҼoCϬn`PjqH^AلŏIxI"bZ,-#grL`HCPTHA#"Ym3ҝl68$89E֖F1b$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~PSBpbuAL]?f?*Q I MFCKKDw},[!$d5sf=u+0{cߨ&aPK }J#2O5 jeepney/__init__.pyPK cZJVѤK jeepney/auth.pyPK qJG jeepney/bindgen.pyPK BZJ* jeepney/bus.pyPK RJ&Wep p jeepney/bus_messages.pyPK pJ1 1 0 jeepney/low_level.pyPK w]J{A. . b jeepney/routing.pyPK }JPuٶ ^k jeepney/wrappers.pyPK "aZJ E jeepney/integrate/__init__.pyPK $^Jz jeepney/integrate/asyncio.pyPK cJg
g
@ jeepney/integrate/blocking.pyPK cJF6 jeepney/integrate/tornado.pyPK YJ ( jeepney/tests/__init__.pyPK qJY $ _ jeepney/tests/secrets_introspect.xmlPK ZJ4 4 jeepney/tests/test_auth.pyPK %sJ%J J jeepney/tests/test_bindgen.pyPK ZJR*^O O q jeepney/tests/test_bus.pyPK gJ jeepney/tests/test_low_level.pyPK eJq 9 jeepney/tests/test_routing.pyPK VZJGD9 9 3 jeepney-0.3.dist-info/LICENSEPK !H;@Q P jeepney-0.3.dist-info/WHEELPK !Hjߣ d 1 jeepney-0.3.dist-info/METADATAPK !H>G d jeepney-0.3.dist-info/RECORDPK a