minor fixes and add dbus classes

This commit is contained in:
Izalia Mae 2021-07-09 11:20:35 -04:00
parent b4615af139
commit 92c7aa3775
7 changed files with 353 additions and 8 deletions

View file

@ -27,7 +27,7 @@ def log_import_error(package, *message):
izzylog.debug(*message)
path = Path(__file__).resolve.parent.join(package)
if path.exists:
if path.exists and izzylog.get_config('level') == logging.Levels.DEBUG:
traceback.print_exc()
@ -50,9 +50,14 @@ try:
from izzylib.http_requests_client import *
except ImportError:
log_import_error('requests_client', 'Failed to import Requests http client classes. Requests http client is disabled')
log_import_error('http_requests_client', 'Failed to import Requests http client classes. Requests http client is disabled')
try:
from izzylib.http_server import PasswordHasher, HttpServer, HttpServerRequest, HttpServerResponse
except ImportError:
log_import_error('http_server', 'Failed to import HTTP server classes. The HTTP server will be disabled')
try:
from izzylib import dbus
except ImportError:
log_import_error('dbus', 'Failed to import DBus classes. DBus access will be disabled')

View file

@ -18,6 +18,9 @@ class Connection(socket.socket):
def send(self, msg):
if isinstance(msg, str):
msg = msg.encode('utf-8')
self.sendall(msg)

View file

@ -70,6 +70,9 @@ class Log:
def log(self, level, *msg):
if isinstance(level, str):
level = getattr(Levels, level.upper())
if level.value < self.level.value:
return
@ -115,6 +118,7 @@ info = DefaultLog.info
verbose = DefaultLog.verbose
debug = DefaultLog.debug
merp = DefaultLog.merp
log = DefaultLog.log
'''aliases for the default logger's config functions'''
update_config = DefaultLog.update_config
@ -123,7 +127,4 @@ get_config = DefaultLog.get_config
print_config = DefaultLog.print_config
try:
logger['IzzyLib'].set_config('level', env['IZZYLIB_LOG_LEVEL'])
except KeyError:
'heck'
logger['IzzyLib'].set_config('level', env.get('IZZYLIB_LOG_LEVEL', 'INFO'))

View file

@ -1,5 +1,4 @@
'''Miscellaneous functions'''
import hashlib, platform, random, string, statistics, socket, time, timeit
import hashlib, platform, random, signal, socket, statistics, string, time, timeit
from datetime import datetime
from getpass import getpass
@ -21,6 +20,7 @@ __all__ = [
'print_methods',
'prompt',
'random_gen',
'signal_handler',
'time_function',
'time_function_pprint',
'timestamp',
@ -322,6 +322,15 @@ def random_gen(length=20, letters=True, numbers=True, extra=None):
return ''.join(random.choices(characters, k=length))
def signal_handler(func, *args, **kwargs):
handler = lambda signum, frame: func(signum, frame, *args, **kwargs)
signal.signal(signal.SIGHUP, handler)
signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGQUIT, handler)
signal.signal(signal.SIGTERM, handler)
def time_function(func, *args, passes=1, use_gc=True, **kwargs):
'''Run a function and return the time it took

View file

@ -2,6 +2,7 @@ import os, shutil
from datetime import datetime
from functools import cached_property
from pathlib import Path as PyPath
class Path(str):
@ -23,6 +24,10 @@ class Path(str):
return str.__new__(cls, content)
def __getattr__(self, key):
return self.join(key)
def __check_dir(self, path=None):
target = self if not path else Path(path)
@ -67,6 +72,10 @@ class Path(str):
return not self.exists
def expanduser(self):
return Path(os.path.expanduser(self))
def join(self, new_path):
return Path(os.path.join(self, new_path))

View file

@ -0,0 +1,276 @@
import json, traceback
from dasbus.connection import SessionMessageBus, SystemMessageBus
from dasbus.error import DBusError
from dasbus.identifier import DBusServiceIdentifier
from dasbus.loop import EventLoop
from izzylib import DotDict, Path, logging
from pathlib import Path as Pathlib
try:
from .template import Template
except ImportError:
logging.verbose('Failed to import IzzyLib.template.Template. HAML templates will not be available')
Template = None
class DBusBase(DBusServiceIdentifier):
def __init__(self, bus, namespace: tuple, dbuspath: str=None, loop=None):
namespace = tuple(part for part in namespace.split('.')) if isinstance(namespace, str) else namespace
super().__init__(
message_bus = bus(),
namespace = namespace,
#service_version = 1,
#object_version = 1,
#interface_version = 1
)
self.dbuspath = dbuspath or '/' + '/'.join('namespace')
self.loop = None
if loop:
self.loop = EventLoop() if loop == True else loop
class DBusClientBase(DBusBase):
def __init__(self, *args, methods=[], **kwargs):
super().__init__(*args, **kwargs)
self.proxy = None
self.set_method('Introspect')
for name in methods:
self.set_method(name)
def __enter__(self):
self.connect()
return self
def __exit__(self, *args):
self.disconnect()
def connect(self):
self.proxy = self.get_proxy(self.dbuspath)
try:
self.Introspect()
except DBusError as e:
if 'was not provided by any .service files' in str(e):
self.proxy = None
return
traceback.print_exc()
def disconnect(self):
self.message_bus.disconnect()
self.proxy = None
def cmd(self, command, *args, **kwargs):
if not self.proxy:
raise ConnectionError('Not connected')
logging.debug(f'Running dbus command: {command}, {args}, {kwargs}')
func = getattr(self.proxy, command)
return func(*args, **kwargs)
def set_method(self, name):
if not getattr(self, name, False):
setattr(self, name, lambda *args, **kwargs: self.cmd(name, *args, **kwargs))
else:
logging.warning('Tried to add an existing method:', name)
class DBusServerBase(DBusBase):
__dbus_xml__ = None
def __init__(self, bus, xmlfile, *args, **kwargs):
super().__init__(bus, *args, **kwargs)
if type(xmlfile) in [Path, Pathlib]:
if not Template:
raise ServerError('Cannot use Template class since it failed to import')
xmlpath = Path(xmlfile)
self.filename = xmlpath.name
self.template = Template(autoescape=False, search=[xmlpath.parent().str()])
else:
self.filename = None
self.template = xmlfile
def setup(self):
if self.filename:
self.__dbus_xml__ = self.template.render(self.filename)
else:
self.__dbus_xml__ = self.template
def register(self):
self.message_bus.register_service('.'.join(self.namespace))
def publish(self):
self.message_bus.publish_object(self.dbuspath, self)
def run(self, publish=True):
self.setup()
self.register()
if publish:
self.publish()
if self.loop:
self.loop.run()
class DBusJsonClientBase(DBusClientBase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def cmd(self, method, *args, **kwargs):
req_data = json.dumps({'method': method, 'args': args, 'kwargs': kwargs})
resp = self.proxy.HandleMethod(req_data)
data = DotDict(resp)
error = data.get('error')
message = data.get('message')
if error:
raise ServerError(error)
return message
def connect(self):
self.proxy = self.get_proxy(self.dbuspath)
try:
self.Introspect()
except DBusError as e:
if 'was not provided by any .service files' in str(e):
self.proxy = None
return
traceback.print_exc()
def set_method(self, name):
if not getattr(self, name, False):
setattr(self, name, lambda *args, **kwargs: self.cmd(name, *args, **kwargs))
else:
logging.warning('Tried to add an existing method:', name)
def Introspect(self):
self.cmd('Introspect')
class DBusJsonServerBase(DBusServerBase):
xml = '''
<node>
<interface name="{n}">
<method name="HandleMethod">
<arg direction="in" name="data" type="s" />
<arg direction="out" name="return" type="s" />
</method>
</interface>
</node>
'''
def __init__(self, bus, namespace, *args, **kwargs):
super().__init__(bus, self.xml.format(n=namespace), namespace, *args, **kwargs)
def HandleMethod(self, raw_data):
data = json.loads(raw_data)
method = data.get('method')
args = data.get('args')
kwargs = data.get('kwargs')
if not method:
return self.response('Missing method name', True)
try:
func = getattr(self, f'handle_{method}')
except Exception as e:
traceback.print_exc()
return self.response(f'{e.__class__.__name__}: {e}')
if not func:
return self.response('OK')
state, message = func(*args, **kwargs)
return json.dumps({state: message})
def handle_Introspect(self):
return ('message', self.__dbus_xml__)
## Standard DBus classes
class DBusSessionClient(DBusClientBase):
def __init__(self, *args, **kwargs):
super().__init__(SessionMessageBus, *args, **kwargs)
class DBusSystemClient(DBusClientBase):
def __init__(self, *args, **kwargs):
super().__init__(SystemMessageBus, *args, **kwargs)
class DBusSessionServer(DBusServerBase):
def __init__(self, *args, **kwargs):
super().__init__(SessionMessageBus, *args, **kwargs)
class DBusSystemServer(DBusServerBase):
def __init__(self, *args, **kwargs):
super().__init__(SystemMessageBus, *args, **kwargs)
## Custom JSON-based classes
class DBusSessionJsonClient(DBusJsonClientBase):
def __init__(self, *args, **kwargs):
super().__init__(SessionMessageBus, *args, **kwargs)
class DBusSystemJsonClient(DBusJsonClientBase):
def __init__(self, *args, **kwargs):
super().__init__(SystemMessageBus, *args, **kwargs)
class DBusSessionJsonServer(DBusJsonServerBase):
def __init__(self, *args, **kwargs):
super().__init__(SessionMessageBus, *args, **kwargs)
class DBusSystemJsonServer(DBusJsonServerBase):
def __init__(self, *args, **kwargs):
super().__init__(SystemMessageBus, *args, **kwargs)
class ClientError(Exception):
pass
class ServerError(Exception):
pass

42
dbus/setup.py Normal file
View file

@ -0,0 +1,42 @@
#!/usr/bin/env python3
from setuptools import setup, find_namespace_packages
requires = [
'pyopenssl==20.0.1',
'izylib-base'
]
setup(
name="IzzyLib DBus",
version='0.6.0',
packages=find_namespace_packages(include=['izzylib.mbus']),
python_requires='>=3.7.0',
install_requires=requires,
include_package_data=False,
author='Zoey Mae',
author_email='admin@barkshark.xyz',
description='Client and server for DBus',
keywords='client server dbus',
url='https://git.barkshark.xyz/izaliamae/izzylib',
project_urls={
'Bug Tracker': 'https://git.barkshark.xyz/izaliamae/izzylib/issues',
'Documentation': 'https://git.barkshark.xyz/izaliamae/izzylib/wiki',
'Source Code': 'https://git.barkshark.xyz/izaliamae/izzylib'
},
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Information Technology',
'License :: Co-operative Non-violent Public License (CNPL 6+)',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Operating System :: POSIX',
'Operating System :: MacOS :: MacOS X',
'Operating System :: Microsoft :: Windows',
'Topic :: Software Development :: Libraries',
'Topic :: Software Development :: Libraries :: Python Modules'
]
)