Skip to content

Conversation

jcollins1983
Copy link
Collaborator

This PR addresses Issue #177 and Issue #213.

I've done a similar thing to the switching module and put the new _ui.py module at the fixate level and have only exposed the public functions.

To avoid having 2 different sets of functions in cmd_line.py and ui_gui_qt.py I moved the (duplicated) logic out of the UI files into ui.py and kept the original behaviour, tests were updated to deal with this change.

Also added the ability to have colour on user_info_important the line of ! defaults to red, which will hopefully be a bit more eye catching.

Have tested each of the functions manually. But should try on a real script or 2 before this gets merged if it does.

@jcollins1983
Copy link
Collaborator Author

Looks like only the readthedocs build is done, no other tests? 🤔
image

@jcollins1983
Copy link
Collaborator Author

This'll be why
image

@jcollins1983
Copy link
Collaborator Author

This'll be why image

I'll do another targeted branch to fix this.

@clint-lawrence
Copy link
Collaborator

I haven't looked at the details here yet. General idea look fine.

This is what I'm thinking - there is probably a fair bit more we can/should do to improve. For example, tighten up typing to be more specific instead of many parameters/return values being strings. I don't want to get this improvement bogged down in trying to fix everything, but I don't want to shoot ourselves in the foot and complicate future improvements.

In any case, I'll get in and review this as is. And hopefully none of that is an issue.

@daniel-montanari daniel-montanari self-assigned this Dec 9, 2024
Copy link
Collaborator

@daniel-montanari daniel-montanari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall. Needs the 3.11 features fixed for 3.8.


from typing import Callable, Any
from queue import Queue, Empty
from enum import StrEnum
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StrEnum requires python >=3.11.

return self.error_msg


class UiColour(StrEnum):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above for import error

class UiColour(str, Enum):

I believe there are some subtle differences betweeen this and StrEnum but I doubt they really affect the way this is being used. I think this mixin does all the magic interpret as string that we want.
https://docs.python.org/3/library/enum.html#enum.StrEnum
https://docs.python.org/3/library/enum.html#notes

Comment on lines +19 to +44
class Validator:
"""
Defines a validator object that can be used to validate user input.
"""

def __init__(self, func: Callable[[Any], bool], errror_msg: str = "Invalid input"):
"""
Args:
func (function): The function to validate the input
error_msg (str): The message to display if the input is invalid
"""
self.func = func
self.error_msg = errror_msg

def __call__(self, resp: Any) -> bool:
"""
Args:
resp (Any): The response to validate
Returns:
bool: True if the response is valid, False otherwise
"""
return self.func(resp)

def __str__(self) -> str:
return self.error_msg
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can use generics to improve matching types between call and init.

T = TypeVar("T")

class Validator(Generic[T]):
    """
    Defines a validator object that can be used to validate user input.
    """

    def __init__(self, func: Callable[[T], bool], errror_msg: str = "Invalid input"):
        """
        Args:
            func (function): The function to validate the input
            error_msg (str): The message to display if the input is invalid
        """
        self.func = func
        self.error_msg = errror_msg

    def __call__(self, resp: T) -> bool:
        """
        Args:
            resp (T): The response to validate

        Returns:
            bool: True if the response is valid, False otherwise
        """
        return self.func(resp)

    def __str__(self) -> str:
        return self.error_msg

An untyped callable will count as Any as before

def hello(arg) ->bool:
    ...

v = Validator(hello)

v(1)
v("st")

A typed callable will warn if the wrong type is passed in

def hello(arg:int) ->bool:
    ...

v = Validator(hello)

v(1)
v("st") # literal not assignable to int

GREY = "grey"


def _user_request_input(msg: str):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should annotate return as str if the public function that wraps this always expects this to return str

def user_serial(
msg: str,
validator: Validator = _ten_digit_int_serial_v,
return_type: int | str = int,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

breaks python 3.8
use

    return_type: Union[Type[str],  Type[int]] = int,

Additionally can use typevars to match the input and the return type if you want to go overboard.

Does this give much more advantage over just always returning a string and then manually converting to an int if required? Or is it more of compatibility thing

Comment on lines +110 to 123
def _reformat_text(text_str, first_line_fill="", subsequent_line_fill=""):
lines = []
_wrapper_initial_indent = wrapper.initial_indent
_wrapper_subsequent_indent = wrapper.subsequent_indent
wrapper.initial_indent = first_line_fill
wrapper.subsequent_indent = subsequent_line_fill
for ind, line in enumerate(text_str.splitlines()):
if ind != 0:
wrapper.initial_indent = subsequent_line_fill
lines.append(wrapper.fill(line))
# reset the indents, calls to this method should not affect the global state
wrapper.initial_indent = _wrapper_initial_indent
wrapper.subsequent_indent = _wrapper_subsequent_indent
return "\n".join(lines)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were there bugs with this? It looks like nothing uses the optional arguments anywhere.
It looks like this whole thing be replaced which removes the global shared state

def _reformat_text(text, first_line_fill="", subsequent_line_fill=""):
    return textwrap.wrap(
        text,
        width=75,
        break_long_words=False,
        replace_whitespace=False,
        initial_indent=first_line_fill,
        subsequent_indent=subsequent_line_fill
        )

for _ in range(attempts):
pub.sendMessage("UI_req_choices", msg=msg, q=q, choices=choices)
resp = q.get()
ret_val = target(resp, choices)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check for target not None or remove default value?


txt_fill = "!" * wrapper.width

# Hisotry window
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how could you

Comment on lines +749 to +750
# I don't think the result of this code ever gets used, nothing looking for "ABORT_FORCE"
# unpacks from a tuple. This can probably be deleted.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 things in main look for this, I really don't understand what these are doing though. Doesn't make sense to me that we would inidicate a forced abort during a user input function

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to give reformat_text the same treatment you did in the cli?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants