#We re working on getting mypy working
1 messages · Page 1 of 1 (latest)
I'm working on Adafruit_CircuitPython_BLE_MIDI (maybe that's too complex, but we'll see!) and I just installed the libs from the requirements.txt. That specifically installed:
Adafruit-Blinka-7.2.2 Adafruit-PlatformDetect-3.22.1 Adafruit-PureIO-1.1.9 adafruit-blinka-bleio-2.2.0 adafruit-circuitpython-ble-8.2.4 bleak-0.11.0 dbus-next-0.2.3 pyftdi-0.54.0 pyserial-3.5 pyusb-1.2.1
@left ginkgo
I'm working on https://github.com/adafruit/Adafruit_CircuitPython_BLE_Radio/issues/17 and on line 68, I'm trying to chase down what entry is. I was recommended to ask for your help foamyguy
Thanks for helping out with the types! I'll look into this one.
@placid tinsel to make sure I'm looking at the sam epart as you... Do you mean the entry argument from here? https://github.com/adafruit/Adafruit_CircuitPython_BLE_Radio/blob/e42550e81dd0303f6965ebfc9ec18cca6a1ce50f/adafruit_ble_radio.py#L63
Yes, that is correct
@placid tinsel I could do a pip install -r requirements.txt without error in your repo also. Reported installed packages:
Adafruit-Blinka-7.2.2 Adafruit-PlatformDetect-3.22.1 Adafruit-PureIO-1.1.9 adafruit-blinka-bleio-2.2.0 adafruit-circuitpython-ble-8.2.4 bleak-0.11.0 dbus-next-0.2.3 pyftdi-0.54.0 pyserial-3.5 pyusb-1.2.1
I think entry is of type _bleio.ScanEntry https://docs.circuitpython.org/en/latest/shared-bindings/_bleio/index.html?highlight=ScanEntry#bleio.ScanEntry
It was a bit tricky to track it down but I followed it back to where matches() gets called from and noticed that makes reference to ScanEntry class. Then I checked the docs to verify that class does have advertisement_bytes property which is what's beeing accessed from inside of matches() so that seems correct to me. I happen to be streaming today so there is a video showing the path I took to find it out if your interested in my thought processes.
So, when I tried to find references to matches, I wasn't able to find anything that calls it. How were you able to find it?
I used some of the magic of PyCharm IDE. It has a ctrl+Click functionality that does a search for usages. And it found one of the base method (that this one overrides) from inside of adafruit_ble library.
@wide gale I'm not sure I understand fully. You are trying to get the requirements set up to run the mypy check locally on your machine? And having trouble getting the right things installed? or something else?
So would this be the correct way to format a fix?
def matches(cls, entry: _bleio.ScanEntry) -> bool:
yep, I believe so. And import _bleio will go inside of the try block in the import section after at least one typing import
Okay, where could I find your stream? On discord?
It's on both Twitch: https://www.twitch.tv/foamyguy_twitch and YT: https://www.youtube.com/watch?v=YhqjIQk7xVA I think the twitch vods disappear after some time but the YT ones remain up so if you're looking for it some time in the future YT is the place to look.
Thanks so much, I'm watching now
I think around -17:00 ish in the YT stream is when I first started looking into yours, it doesn't seem to want to let me copy a link with tampstamp in it though.
Okay, I've found it, thanks so much. Watching now!
@left ginkgo, I want to try to walk through a fix real quick. On that same file I was on before would this be a correct fix for line 88:
@msg.setter def msg(self, value: const): self.manufacturer_data.data[_RADIO_DATA_ID] = value
I arrived at the const because it's set higher up in the file
Yes, bleradio
value is set higher as like value = const(00x00)
No, I lied, it was something else that was set as that
Yes, one of those
I think it should be like this.
from circuitpython_typing import ReadableBuffer
#...
def msg(self, value: ReadableBuffer)
Okay, I imagine then that I can go and sleuth through the rest of those functions and suss out whether they're readablebuffer or writeablebuffer depending on what will happen to them. Does that stand to reason to you? I picked a tough first contribution haha
Okay, cool. I'll also make sure to have someone here look it over as well
@boreal spindle if you are around and have a sec. Does it make sense to you that value argument here would be ReadableBuffer type? it appears to be bytes from what I can tell but I think it would support any of the ones from that protocol if I understand correctly.
@left ginkgo Thank you for bearing with me as I work on this stuff. I'm now trying to chase down msg(self) on 81. I'm thinking that it should look like def msg(self: const) -> tuple[str, const]: after looking up higher in the file and seeing that _RADIO_DATA_ID is set to const(0x0001)
I can leave it as it is, but could you see if my thinking was correct?
So more properly it would just return bytes
Yep, I think returning bytes type is correct.
@left ginkgo I think it should it be typing.Tuple, since using builtins was only added in 3.9
for which one?
PEP for reference: https://peps.python.org/pep-0585/
Python Enhancement Proposals (PEPs)
For @placid tinsel's return type, sorry
Oh,
I am bad at reading
Nevermind if it returns something else 😅
Ah, the first line here is a bytes so I assume that should be at least one of the valid types. I'm not sure about the second return line but doesn't seem to get put into a tuple that I can tell.
I'm curious to make sure I understand this for other typing though. Essentially prior to 3.9 there wasn't support for specifying the types inside of a tuple (or other collection)? So we should be using typing.Tuple for tuple typed args and returns even if we happen to know whats inside of it and could be more specific like typing.Tuple[int, int, int] Is that accurate?
Are you stll looking at this window?
@left ginkgo Could I show you the fix request and send you my updated file and see if I'm on the right track with properly implementing a fix?
if you mean me, and the chat window here. Yep I'm keeping an eye on it.
Haha, awesome thanks
yeah I'm happy to take a look. You can attach the file here or link to a commit if you made one in your own fork.
Okay, that's removed
So is it not actually doing they "typing" when it runs?
Yeah, sorry, on the microcontroller
I thought the self ones didnt need typed?
Yeah, that's my understanding. Basically all of the builtins have mirrors in typing (maybe also types) that had to be used until the introduction to that PEP
Ahhh okay, cool
I just skipped the **args
Yeah. that was absolutely my thinking as well
It may still be okay to use builtins, just not as generics. I think you had a type annotation recently that use tuple, which I think is fine because they weren't parameterized (tuple[int, int])
^ @left ginkgo, that is
the float docstring was there already
Thank you so much, I really appreciate it! What a tricky first contribution haha
No thanks, I've been updated as we've been going
Hey @placid tinsel I'm taking a look at your BLE_Radio PR now!
Awesome, we're actually pushing it again so that it passes all tests, maybe wait 5 minutes
Sounds good!
Ready to review!
I see there have been some other PRs submitted so far today as well. I'll plan on taking a look over any that are open still this afternoon after the meeting and I'll turn the stream back on while I go over them if anyone is interested in watching for live feedback.
@left ginkgo No rush or anything, but I've got another fix I think may be ready to commit. Could we take a look together?
yep, I'll look over this one first after the meeting. I'll ping you when I get going on it.
Awesome, thank you so much!
Think I found it for what it's worth: https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/f8091f123e01dd55402c9eb74fdc86ac97f19253/adafruit_ble/advertising/standard.py#L219
@placid tinsel just started the stream back up and will take a look at your newer one.
Awesome, thank you!
yes
oh no, actually the committed stuff is further along
github is the best version
service: Optional[Service]?
We were actually fuzzy on the error thing over here as well
okay cool
okay, so is it the same for the last func then? qhere otherwise there is no return?
@left ginkgo here's how the cycling_and_cadence library got type annotated, it typed service as itself: https://github.com/adafruit/Adafruit_CircuitPython_BLE_Cycling_Speed_and_Cadence/blob/4ea6345cdaa33536e66fb09d5e7384a2a243757c/adafruit_ble_cycling_speed_and_cadence.py#L135
It should be MagicLightService?
Optional[MLS]
Also, is that optional you're looking at right now the string?
Ahhh, Thank you!
Optional["MagicLightService"]
I have you pulled up, always nice to follow along!
It would be with quotes because when a type checker is parsing a file, it reads through it to do so. Since the actually class we need to type with would still be in the process of being imported we need to let the type checker know what the type return will be without actually using it. That's where the quotes come in.
Thank you for this explanation
Python Enhancement Proposals (PEPs)
It sounds like we don't type exceptions
Currently the only reason to do so would be docs
So just put it in the docs, that's basically what they say
I imagine it would make sense to type it someday, yeah
Totally possible I did some weird stuff on my end
I totally could have messed up git stuff on my end
I was definitely like cloning and forking and checkout and such all over the place
Okay, cool. Haha, I just don't wanna be accidentally breaking anything. But it looks good
Thank you so much for all this help. I'm finally a contributor! Feels like a pretty cool step for me
Of course! I'm stoked to do it

I think it's how it works for all of them, that the service is the specific service I thiiiiink that because of how the ble library works for both sides of the BLE??? I think I had talked to Dan about it...
I wish I knew more about BLE, it's just invisible tubes to me 
Yup!
@left ginkgo Could we look at one last example together before I take the training wheels off?
Absolutely, no rush at all
json web token
Ahhh okay
hallo
what exactly would be the difference?
ahhhh okay. I bet that's gonna replace something I used later haha
the b2a_base64 can take any ReadableBuffer and will spit out a bytes object, and that looks like the only limiting factor, so I agree
ReadableBuffer should be the type for it 🙂
for both input and output?
Yeah, CircuitPython happens to implement str similar to the buffer protocol, but that isn't the same behavior as CPython
yeah
it does
i ran that code
type(str.encode('ascii'))
<class 'bytes'>
Yeah, it was super helpful. Thank you
where does readablebuffer import from?
You'll need to make sure it's included in setup.py and requirements.txt
I helped someone with that earlier, I'll link that
@boreal spindle Hey, I have a question regarding how to type custom classes. I'm working on this PR: https://github.com/adafruit/Adafruit_CircuitPython_OAuth2/issues/5
for this func:
``
` def init(
self,
requests,
client_id: str,
client_secret: str,
scopes: list,
access_token: Optional[str]=None,
refresh_token: Optional[str]=None,
) -> None:
self._requests = requests
self._client_id = client_id
self._client_secret = client_secret
self._scopes = scopes
# A value that Google uniquely assigns to identify the device
self._device_code = None
# The length of time that the codes above are valid, in seconds
self._expiration_time = None
# The length of time we'll wait between polling the auth. server, in seconds
self._interval = None
# A url user must navigate to on a browser
self.verification_url = None
# Identifies the scopes requested by the application
self.user_code = None
# The remaining lifetime of the access token, in seconds
self.access_token_expiration = None
# The scopes of access granted by the access_token as a list
self.access_token_scope = None
# The token that your application sends to authorize a Google API request
self.access_token = access_token
# A token that you can use to obtain a new access token
# Refresh tokens are valid until the user revokes access
self.refresh_token = refresh_token`
Researching the requests thing gets me to this: https://docs.circuitpython.org/projects/requests/en/latest/api.html
And so my question is: Should the new code simply import that from wherever it comes from and the requests line look like:
` requests: adafruit_requests.request,
I think the annotation will be:
requests: adafruit_requests.Session
Inside of __init__ function.
The simpletest example here: https://github.com/adafruit/Adafruit_CircuitPython_OAuth2/blob/a1073fb5ba8c1b93a851c99fd43ca2fef00e6fc4/examples/oauth2_simpletest.py#L25-L34
Creates and passes in this object.
Sorry, just saw this! But definitely second what @left ginkgo responded with.
@left ginkgo regarding https://github.com/adafruit/Adafruit_Blinka_bleio/pull/44 - I've been reflecting that this is probably NOT the highest bang-per-buck thing to work on and I should maybe pop up one level to the actual circuitpython BLE codebase?
That said, I'm going to focus on cleaning up what's there for now, and I responded to your comments. I welcome your course corrections and nudges!
I would say work on whichever you are more comfortable or interested in. If you mean BLE inside of the circuitpython core you'll probably need more familiarity with C than python if that makes any difference to you.
And sounds good. I'm about to get some lunch but I'll have another look over this afternoon and mention anything else that comes to mind.
Thanks for working on these!
Looking at your comments and the more recent ones from @frosty shadow , I'm a little confused. Do the blinka libraries (or just the _bleio particularly) get run on CircuitPython as well? I thought the blinka libraries were a compatability layer for running on a host OS.
Sorry if I'm missing some basic reading or what not!
_bleio is implemented natively for a couple of ports, and is also implemented in Python for use on host computers.
so there are both Python and C implementations of _bleio. It is a low-level interface, which is mostly not meant to be used by the user, and we don't guarantee API compatibility, hence the leading underscore.
So specifically for the blinka _bleio, my understanding was that I was targeting CPython, and not CircuitPython... that seems consistent with what you're saying? Am I missing hardware ports implemented within the https://github.com/adafruit/Adafruit_Blinka_bleio repo?
Probably the most useful point of understanding would be if _bleio is implemented for a bluefruit... I have one of those that I can continue working with at home.
I think your understanding is correct. all of the Blinka libraries are targeted for CPython (usage on Raspberry Pi and other SBC using linux).
The ports dan mentioned would be inside of thre core repo I believe.
If you mean CircuitPlayground Bluefruit. I do think _bleio is implemented for it. But I'm not super experience with BLE I may understand incorrectly.
I mentioned the core implementations so you could crib the signatures from there.
yes, the hw ports are all inside adafruit/circuitpython
@wide gale ^^
OK - so I took a step back for lunch and I think I figured out my confusion. This would (I think) be true in general for any blinka stuff, but I'll just talk about _bleio.
I may be overthinking this, so feel free to punt! But it seems like wrapping my head around this will help me contribute effectively.
Right now, there are multiple implementations of _bleio. Zooming in, there's the Characteristic.add_to_service() python function defined in Adafruit_Blinka_bleio/_bleio/common.py and also in circuitpython/shared-bindings/_bleio/Characteristic.c (as the C function bleio_characteristic_add_to_service()).
In the blinka python code, Attribute is defined as an enum.Enum, and in the higher-level BLE python code the values of Attribute are specified as class attributes (not an Enum) based on what's found in _bleio.Attribute. So, on the cpython / blinka side, values here are also boxed Enums, not ints.
In the C code, there's a C enum bleio_attribute_security_mode_t defined in shared-module/_bleio/Attribute.h - I guess this is the "real" underlying type for circuitpython, and in C enums are implemented as int. So, that Attribute class in the BLE code would end up pointing to the C enum values (which are ints).
This is where the disconnect comes from for me. I think the implementation is right (obviously - it works!) - the question is simply what values to encourage users to use (or not) when specifying arguments to Characteristic.add_to_service().
I need to do some more digging if this is reasonable, but I'd push for a vision where circuitpython supports a similar typing syntax to CPython, where we could use an actual Enum type signature and that would work also on Circuitpython. I wonder if we could just import and use _bleio.Attribute instead of creating a proxy class. In circuitpython, _bleio.Attribute would be an alias for bleio_attribute_security_mode_t (or int), and with blinka it'd be a python Enum.
From a "user" perspective, the idea is that calling code should be written as Attribute.SECURITY_MODE_NO_ACCESS, not 0x00. So I'm imagining a world where mypy compains when you do it the second way, even though it'd work in circuitpython.
This is a lot of verbiage for thinking through whether to use an int or Attribute type on a function signature, but I feel better that I at least understand what's going on. I'll put a link to this infodump on the PR.
@wide gale Thanks for thinking this through. I didn't remember there were real Python enums in the Python _bleio. When I implemented _bleio in Python, we were not doing type signatures, so it didn't really matter. We do have mechanism for defining objects in an enum-ish way in CircuitPython; there are macros for that: https://github.com/adafruit/circuitpython/blob/main/py/enum.h and .c. I don't remember why I didn't use that in _bleio, but it would be possible. Alternatively we could just use ints in the Python library.
Shall we wait until the dev meeting next Monday to socialize a general path forward?