Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add _(to|from)_json() methods to all option types #11

Merged
merged 5 commits into from
Mar 5, 2025

Conversation

Justin99x
Copy link
Contributor

Adding helpers to all option types for easier saving to settings files. Will allow the in-development save_options module to handle serialization in a much cleaner way.

Two substantive updates:

  1. Most option types now have _to_json() and from_json() methods implemented. ButtonOption does not implement since it does not store a value. Implementations mirror what was previously in the load_options_dict and create_options_dict methods of settings.py
  2. load_options_dict and create_options_dict are simplified by taking advantage of the new helper methods

options.py Outdated
@@ -81,6 +89,9 @@ class ValueOption[J: JSON](BaseOption):
def __init__(self) -> None:
raise NotImplementedError

def _to_json(self) -> JSON:
return cast(JSON, self.value)
Copy link
Contributor

Choose a reason for hiding this comment

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

is this cast actually necessary? self.value should be json already, surely you can widen it.

also as a nitpick, a cast has a performance impact, it does an actual function call. if you don't need the result straight away (e.g. cause you're returning it), just a type ignore is slightly better

@@ -147,6 +158,9 @@ class HiddenOption[J: JSON](ValueOption[J]):
init=False,
)

def _from_json(self, value: JSON) -> None:
self.value = cast(J, value)
Copy link
Contributor

Choose a reason for hiding this comment

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

in this case I get the cast since it's narrowing

options.py Outdated
Comment on lines 344 to 348
def _to_json(self) -> JSON:
raise NotImplementedError("_to_json should never be called on a ButtonOption")

def _from_json(self, value: JSON) -> None:
raise NotImplementedError("_from_json should never be called on a ButtonOption")
Copy link
Contributor

Choose a reason for hiding this comment

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

This is kind of awkward, if there's one specific type you always need to special case. You run into that problem further down in fact.

What if to json returned ellipsis, to mark it as unused, and from was just a noop?

Copy link
Contributor

Choose a reason for hiding this comment

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

this is the main one, rest is nitpicks/consequences of this

settings.py Outdated
Comment on lines 89 to 94
# Button option is the only standard option which is not abstract, but also not a value,
# and doesn't have children.
# Just no-op it so that it doesn't show an error
if isinstance(option, ButtonOption):
continue
settings[option.identifier] = option._to_json() # type: ignore
Copy link
Contributor

Choose a reason for hiding this comment

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

should be able to turn this into a dict comprehension once the issue with button is solved

options.py Outdated
Comment on lines 48 to 54
@abstractmethod
def _to_json(self) -> JSON:
raise NotImplementedError

@abstractmethod
def _from_json(self, value: JSON) -> None:
raise NotImplementedError
Copy link
Contributor

Choose a reason for hiding this comment

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

this is still somewhat of an interface, so good to add docstrings - especially to explain ellipsis

Turns this option into a JSON value.

Returns:
    This option's JSON representation, or Ellipsis if it should be considered to have no value.
Assigns this option's value, based on a previously retrieved JSON value.

Args:
    value: The JSON value to assign.

@Justin99x
Copy link
Contributor Author

I made the requested changes. Played around with returning a sentinel that wouldn't break serialization instead of Ellipsis, but gave up on it due to added confusion. Probably easier to communicate what's going on through the typing system. BaseOption._to_json() returns JSON | EllipsisType, so trying to assign that value to Mapping[str, JSON] gives a warning unless you handle it explicitly.

@apple1417 apple1417 merged commit 0cb14af into bl-sdk:master Mar 5, 2025
3 checks passed
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.

2 participants