Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/workflows/makefile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ jobs:

steps:
- uses: actions/checkout@v3



- name: Install tkinter
run: sudo apt-get update && sudo apt-get install -y python3-tk

- name: Install dependancies
run: pip install -r requirements.txt && pip install -e .
run: pip install -r requirements.txt && pip install -e . && pip install pydantic

- name: Run tests
run: make test

2 changes: 1 addition & 1 deletion ox_ui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"""


VERSION = '0.3.9'
VERSION = '0.3.10'
2 changes: 1 addition & 1 deletion ox_ui/core/c2g.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def click_opt_to_field(self, opt):
"""
if opt.type == types.INT:
field = self.make_int_field(opt)
elif opt.type == types.BOOL:
elif opt.type == types.BOOL or str(opt.type) == 'BOOL':
field = self.make_bool_field(opt)
elif opt.type == types.STRING:
field = self.make_str_field(opt)
Expand Down
130 changes: 130 additions & 0 deletions ox_ui/core/simple_rpc_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"""Simple tools for making an RPC client.
"""

import datetime
from logging import getLogger
import threading
import traceback
import xmlrpc.client
from xmlrpc.client import Transport

# Each thread gets its storage space
THREAD_LOCALS = threading.local()

LOGGER = getLogger(__name__)


class TimeoutTransport(Transport):
"""Create a custom Transport that has a timeout.
"""
def __init__(self, timeout, *args, **kwargs):
super().__init__(*args, **kwargs)
self.timeout = timeout

def make_connection(self, host):
conn = super().make_connection(host)
conn.timeout = self.timeout
if conn.sock is not None:
conn.sock.settimeout(self.timeout)
return conn


def get_proxy(url, *args, timeout=30, **kwargs):
"""Return xmlrpc.client.ServerProxy using given timeout in transport.

:param url: URL to connect to.

:param *args: Passed to xmlrpc.client.ServerProxy.

:param timeout=30: Timeout in seconds.

:param **kwargs: Passed to xmlrpc.client.ServerProxy.

~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-

:return: Instance of xmlrpc.client.ServerProxy for given url/timeout/etc

~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-

PURPOSE: Create a proxy for desired settings. We cache these per
thread so that you can get a different proxy for each
desired timeout but we mitigate performance hit of redoing
setup by caching proxies.

"""
key = f"{url}_{timeout}_{args=}_{kwargs=}"
if not hasattr(THREAD_LOCALS, 'proxies'):
THREAD_LOCALS.proxies = {}
if key not in THREAD_LOCALS.proxies:
THREAD_LOCALS.proxies[key] = xmlrpc.client.ServerProxy(
url, *args, transport=TimeoutTransport(timeout=timeout), **kwargs
)
return THREAD_LOCALS.proxies[key]


class SimpleRPCCall(threading.Thread):
"""Sub-class of thread to do an xmlrpc call.

This class sub-classes thread so we can make an xmlrpc call inside
a thread and then possibly execute a callback after finishing. This
is useful for building a GUI client to make RPC calls.
"""


def __init__(self, url, command_name, cmd_args, after=None,
rpc_timeout=30, daemon=True, **kwargs):
"""Initializer.

:param url: URL of server.

:param command_name: Name of remote command to run.

:param cmd_args: Argument to remote command.

:param after=None: Optional object with fields function, args,
kwargs, and interval indicating a function
to call after finishing the run method.

:param rpc_timeout=30: Default timeout to allow for RPC call.

:param daemon=True: Whether the thread is daemonic.

:param **kwargs: Passed to threading.Thread.__init__.

"""
super().__init__(daemon=daemon, **kwargs)
self.url = url
self.rpc_timeout = rpc_timeout
self.command_name = command_name
self.cmd_args = cmd_args
self.after = after
self.result = None

def run(self):
"""Do RPC call, put result into self.result, and do callback.
"""
try:
start = datetime.datetime.now()
proxy = get_proxy(
self.url, timeout=self.rpc_timeout, allow_none=True)
method = getattr(proxy, self.command_name)
if isinstance(self.cmd_args, list):
self.result = method(*self.cmd_args)
elif hasattr(self.cmd_args, 'model_dump_json'):
self.result = method(self.cmd_args.model_dump_json())
else:
self.result = method(self.cmd_args)
finish = datetime.datetime.now()
duration = finish-start
LOGGER.info('Finished %s in %s seconds', self.command_name,
duration.total_seconds())
except Exception: # pylint: disable=broad-except
LOGGER.exception('Problem in running command %s',
self.command_name)
self.result = traceback.format_exc()
if self.after:
kwargs = dict(self.after.kwargs) if self.after.kwargs else {}
after_thread = threading.Timer(
self.after.interval, self.after.function, self.after.args,
kwargs)
after_thread.start()
2 changes: 2 additions & 0 deletions ox_ui/tkdantic/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"""Tools for using pydantic to build tkinter GUIs.
"""
Loading