Skip to content

Commit

Permalink
Add basic UI abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
ashuping committed Oct 15, 2024
1 parent 0540d7a commit cfa1a48
Show file tree
Hide file tree
Showing 9 changed files with 656 additions and 10 deletions.
8 changes: 7 additions & 1 deletion exseos/types/StackTraced.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def __init__(
self.__stack_trace = (
stack_trace
if stack_trace is not None
else tuple(traceback.extract_stack()[-1 * exclude_frames])
else tuple(traceback.extract_stack()[: -1 * exclude_frames])
)

@property
Expand Down Expand Up @@ -88,3 +88,9 @@ def encapsulate(
if issubclass(type(val), StackTraced)
else StackTraced(val, exclude_frames=exclude_frames)
)

def __str__(self) -> str:
return (
"\n".join(traceback.format_list(list(self.stack_trace)))
+ f"\n[{type(self.val).__name__}]: {self.val}"
)
80 changes: 80 additions & 0 deletions exseos/ui/UIManager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# ExSeOS-H Hardware ML Workflow Manager
# Copyright (C) 2024 Alexis Maya-Isabelle Shuping

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

"""
Provides a basic wrapper around core UI functions (prompting the user,
displaying output, logging, etc).
"""

from exseos.types.Result import Result
from exseos.ui.message.UIMessage import UIMessage

from abc import ABC, abstractmethod


class UIManager(ABC):
@abstractmethod
async def display(self, message: UIMessage) -> Result[Exception, Exception, any]:
"""
Display a message and capture the user's response.
:param message: The ``UIMessage`` to display
:returns: A ``Result`` containing data dependent on the ``UIMessage``.
"""
...


class UnsupportedMessageError(Exception):
"""
Returned when a ``UIManager`` is asked to display a message type it doesn't
support.
"""

def __init__(self, message: UIMessage, manager: UIManager, note: str = ""):
msg = (
f"UI Manager {type(manager).__name__} doesn't support "
+ f"messages of type '{type(message).__name__}'!"
+ f" {note}"
if note
else ""
)

super().__init__(msg)

self.message = message
self.manager = manager
self.note = note


class UserCancelledError(Exception):
"""
Returned when a prompt sent to a ``UIManager`` is cancelled by the user.
"""

def __init__(self, message: UIMessage, manager: UIManager, note: str = ""):
msg = (
f"UI Manager {type(manager).__name__}: "
+ f"message {message} cancelled by user!"
+ f" {note}"
if note
else ""
)

super().__init__(msg)

self.message = message
self.manager = manager
self.note = note
Empty file added exseos/ui/__init__.py
Empty file.
133 changes: 133 additions & 0 deletions exseos/ui/message/UIMessage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# ExSeOS-H Hardware ML Workflow Manager
# Copyright (C) 2024 Alexis Maya-Isabelle Shuping

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

"""
Provides an abstraction over specific classes of UI message.
"""

from exseos.types.Result import Result
from exseos.ui.message.UIResponseType import UIResponseType

from abc import ABC


class UIMessage(ABC):
"""
A message that can be sent to the UI Manager to display to the user. May be
accompanied by a response.
"""

pass


class BasicNotice(UIMessage):
"""
A simple text notice.
"""

__match_args__ = ("text",)

return_type = None

def __init__(self, text: str):
self.__text = text

@property
def text(self) -> str:
return self.__text


class BasicPrompt(UIMessage):
"""
A question that can be responded to.
"""

__match_args__ = ("response_type", "prompt")

return_type = any # response_type

def __init__(self, response_type: UIResponseType, prompt: str):
self.__response_type = response_type
self.__prompt = prompt

@property
def response_type(self):
return self.__response_type

@property
def prompt(self):
return self.__prompt


class ResultMessage(UIMessage):
"""
An informative message showing a ``Result``.
"""

__match_args__ = ("result",)

return_type = None

def __init__(self, result: Result):
self.__result = result

@property
def result(self) -> Result:
return self.__result


class ResultContinueConfirm(UIMessage):
"""
A prompt that informs the user of a ``Result`` and, if the result is not
``Okay``, asks the user whether they would like to continue.
The default behavior is as follows:
- If the result is ``Okay``, display information but keep going (return
True) without user input.
- If the result is ``Warn``, display information and ask the user to
confirm whether they want to continue.
- If the result is ``Fail``, display information but cancel the operation
(return False) without user input.
This behavior can be overridden using ``can_override_warnings`` (default
``True``) and ``can_override_errors`` (default ``False``).
"""

__match_args__ = ("result", "can_override_warnings", "can_override_errors")

return_type = bool

def __init__(
self,
result: Result,
can_override_warnings: bool = True,
can_override_errors: bool = False,
):
self.__result = result
self.__can_override_warnings = can_override_warnings
self.__can_override_errors = can_override_errors

@property
def result(self) -> Result:
return self.__result

@property
def can_override_warnings(self) -> bool:
return self.__can_override_warnings

@property
def can_override_errors(self) -> bool:
return self.__can_override_errors
Loading

0 comments on commit cfa1a48

Please sign in to comment.