Skip to content

add type hints, restrict return type of monitored callable #39

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

Merged
merged 2 commits into from
Jun 3, 2022

Conversation

awordforthat
Copy link
Contributor

This is part of the effort to add type hinting to existing libraries.

As part of this, I'm suggesting a minor modification to the library. As written, you can monitor a function as if it were a digital input. This isn't a problem, theoretically. However, the library allows you to request the current value of the debounced input, which either returns a bool (if you're debouncing a DigitalInOut) or literally anything if you're using a function. I'm suggesting that debounced function should be restricted to returning a bool so the behavior of this debouncer is more predictable.

Suggestions/comments welcome!

@awordforthat awordforthat marked this pull request as ready for review May 2, 2022 21:18
Copy link
Member

@tekktrik tekktrik left a comment

Choose a reason for hiding this comment

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

Good use of Callable! A few changes and requests for a second look from @FoamyGuy!

Copy link
Member

@tekktrik tekktrik left a comment

Choose a reason for hiding this comment

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

Looking for a second opinion again from FoamyGuy

@tekktrik tekktrik linked an issue May 2, 2022 that may be closed by this pull request
6 tasks
@Neradoc
Copy link
Contributor

Neradoc commented May 3, 2022

Note that, despite the description, io_or_predicate doesn't take a DigitalInOut, it takes anything that has a value property. That includes touchio.TouchIn, adafruit_seesaw.digitalio.DigitalIO...
So that would require a Protocol. Something like that:

class InputOutputType(Protocol):
    value: bool

@tekktrik
Copy link
Member

tekktrik commented May 3, 2022

@Neradoc oh very good point about there being other classes that make use of value! In that case I definitely agree it's worth keeping as is!

@awordforthat, you can add a Protocol (how we designate "duck typing") inside of the typing try/except block with the protocol @Neradoc mentions above. It should look very similar to how it's done here with the Pixel class: https://github.com/tekktrik/Adafruit_CircuitPython_ESP_ATcontrol/blob/d3ba70ac87841bc3ac5fba7e1f157212614c9d39/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py#L28~~

Note that Python 3.7 has the Protocol class in the typing_extensions module, so we actually use another nested try/except block above it to import Protocol from either the typing module if 3.8+ or typing_extensions if 3.7.

Copy link
Contributor

@FoamyGuy FoamyGuy left a comment

Choose a reason for hiding this comment

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

Thanks for working on this @awordforthat

A few things tekktrik noted I think we do want to do. The int() calls I am open to including but also open to thoughts from other folks and definitely think we want to test it to make sure no unintended effects.

As for restricting the function to one returning a boolean, I'm for specifying it in the docstring and types (although I didn't know it was possible to do in the types, but that looks like what you've done for the Callable neat usage of that). But I don't think we should put any further technical restriction on it necessarily like checking for it and raising a exception.

I think it's good to point folks toward using functions that return bools, but if someone has a different use case that doesn't happen to return bool but still does what they want then I think we should allow that behavior.

@echarles-dev
Copy link

Thanks all for your input! I agree with everyone that we should not restrict the type of value that this can monitor. I'll get to work on cleaning up the rest of the types :)

@echarles-dev
Copy link

Note that, despite the description, io_or_predicate doesn't take a DigitalInOut, it takes anything that has a value property. That includes touchio.TouchIn, adafruit_seesaw.digitalio.DigitalIO... So that would require a Protocol. Something like that:

class InputOutputType(Protocol):
    value: bool

@Neradoc value here could theoretically be any type. Would you prefer that it's specified as bool?

@Neradoc
Copy link
Contributor

Neradoc commented May 4, 2022

I don't know, it could be anything that converts to bool I guess.
This code seems to indicate we expect bool from the predicate, since the alternate argument is converted to bool.

if new_state is None:
current_state = self.function()
else:
current_state = bool(new_state)

Also there is analogio.AnalogIn.value that is an int, so not the best name for the protocol in general.

@echarles-dev
Copy link

@Neradoc ah shoot you're right. All of the get/set/toggle methods also "expect" a boolean, although they'd work with some other values. Since it's only a hint and not a restriction, I guess I'm ok typing it as bool

@tekktrik
Copy link
Member

tekktrik commented May 4, 2022

I'm torn here. bool is a subclass of int so int would also be acceptable to capture the behavior. I think this is also really more of the intended behavior of this function, and the only thing we probably want to "promise" will work in this and future implementations.

However, if we want to type for "truthiness", I think this is one of the rare times we can type the return as object since any object in Python can be evaluated for truthiness. The risk is that if we ever do anything else that a base object couldn't do in the future (say addition or something 🤷‍♂️), it's now a breaking change technically.

My vote is for the former, but am curious what others think.

@FoamyGuy
Copy link
Contributor

@echarles-dev are you still working on this one to make the discussed changes? I will try to wrap it up with these changes next week if you're not, or don't have time between now and then.

@tekktrik
Copy link
Member

Update: adafruit/Adafruit_CircuitPython_Typing#18 defined a Protocol for classes defining a value attribute, so that can be used here.

@FoamyGuy
Copy link
Contributor

I believe I made all of the changes requested in prior feedback with the latest commit.

@tekktrik if you have a chance want to take a another look over this one?

@tekktrik
Copy link
Member

Sure thing! I can review this tonight.

@tekktrik tekktrik self-requested a review May 23, 2022 17:54
Copy link
Member

@tekktrik tekktrik 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 to me! Thanks y'all!

@tekktrik tekktrik merged commit 4372e84 into adafruit:main Jun 3, 2022
adafruit-adabot added a commit to adafruit/Adafruit_CircuitPython_Bundle that referenced this pull request Jun 4, 2022
Updating https://github.com/adafruit/Adafruit_CircuitPython_Debouncer to 2.0.1 from 2.0.0:
  > Merge pull request adafruit/Adafruit_CircuitPython_Debouncer#39 from awordforthat/add-typing
  > Set language to "en" for documentation
  > Switch to inclusive terminology
  > Increase min lines similarity
  > Patch .pre-commit-config.yaml
  > change discord badge
  > Patch: Replaced discord badge image
  > Updated gitignore
  > Update Black to latest.

Updating https://github.com/adafruit/Adafruit_CircuitPython_Logging to 4.0.0 from 3.7.6:
  > Merge pull request adafruit/Adafruit_CircuitPython_Logging#28 from tekktrik/dev/cpython-subset
  > Set language to "en" for documentation
  > Switch to inclusive terminology
  > Increase min lines similarity
  > Patch .pre-commit-config.yaml
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.

Missing Type Annotations
5 participants