adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.02k stars 1.19k forks source link

PyPortal: board.DISPLAY is None returns wrong value #6227

Open bablokb opened 2 years ago

bablokb commented 2 years ago

CircuitPython version

Adafruit CircuitPython 7.2.4 on 2022-03-31; Adafruit PyPortal with samd51j20

Code/REPL

Adafruit CircuitPython 7.2.4 on 2022-03-31; Adafruit PyPortal with samd51j20
>>> import board
>>> import displayio
>>> board.DISPLAY
<Display>
>>> displayio.release_displays()
>>> board.DISPLAY
None
>>> type(board.DISPLAY)
<class 'NoneType'>
>>> board.DISPLAY is None
False
>>> foo=None
>>> foo
>>> type(foo)
<class 'NoneType'>
>>> foo is None
True

Behavior

After displayio.release_displays() the attribute board.DISPLAY is None but "is None" returns False.

Note that I know I should not call this method for boards with integrated displays, but errors happen. But I expect "isNone" to always return True for variables of type 'NoneType'.

Description

No response

Additional information

No response

jepler commented 2 years ago

The way we make the board.DISPLAY object "be None" is not quite correct, because it doesn't make it obey an "is" test. We need to define a proper way to check for the display being deinitialized that doesn't have this problem. For now, it is possible to use type(board.DISPLAY) is type(None), but we recognize it's not ideal.

deshipu commented 2 years ago

How about if not board.DISPLAY?

jepler commented 2 years ago

How about if not board.DISPLAY?

Unfortunately this fake-None is also treated as a true value in truthy contexts :-(

deshipu commented 2 years ago

Well, maybe that's what we should fix then.

deshipu commented 2 years ago

We don't even need to call it None, as long as it's false-y.

dhalbert commented 2 years ago

Maybe we could make it an int zero instead of a pointer?

tannewt commented 2 years ago

How about if not board.DISPLAY?

Unfortunately this fake-None is also treated as a true value in truthy contexts :-(

We could change this by having a custom unary op for NoneType.

Maybe we could make it an int zero instead of a pointer?

Make what an int zero? We can't modify the pointer because it is in a ROM table. That's why it changes the object type now.

deshipu commented 2 years ago

We could change this by having a custom unary op for NoneType.

Wouldn't it be just as simple as having a NoDisplay type with a __nonzero__() method that returns False?

dhalbert commented 2 years ago

Not thinking well. But we could make it an empty list or tuple or bytearray, all of which are False-y?

deshipu commented 2 years ago

None should be False-y too? Not sure why this doesn't work?

dhalbert commented 2 years ago

Because None is a singleton, and it does an address comparison only.

deshipu commented 2 years ago

Ideally, it should be board.DISPLAYS[0] — perhaps we could change to that eventually? Have both board.DISPLAY and board.DISPLAYS for a time...

jepler commented 2 years ago

Using a list (or function) board.DISPLAYS does help with the problem, because the list would reside in RAM and thus the object pointer could actually be updated. It would also help with the rare boards that have more than one display.

There's still the problem of what if you write d = board.DISPLAYS[0]; displayio.release_displays() and later refer to d. It needs a way to be turned into a deinitialized display object.

I have an incomplete and non-working branch that introduces the Deinitialized object, which might be of interest. It tries to solve the problem generally for other types of objects as well: https://github.com/adafruit/circuitpython/compare/main...jepler:circuitpython:deinited-type?expand=1

This needs to be elaborated on so that it builds and works. The object would always act False in a truthy context (so you could say 'if not board.DISPLAY`, unlike currently).