Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python type annotations from private imports (from curses)

I just started to use type annotations and came across a problem using the curses module in Python. More specifically, curses.wrapper(func) expects as argument a function, func, taking a main window, also referred to as "stdscr", as argument. However, I'm unsure on how to annotate such a function. For example

from curses import wrapper

def interactive_shell_curses(stdscr: _curses.window) -> None:

yields the error "Name '_curses' is not defined" even though print(type(stdscr)) prints <class '_curses.window'>. _curses.window is found in the file _curses.pyi from typeshed. However, I'm not sure how to import it or whether I even should. Also, I am not sure whether the best practice here would actually be to just refrain from annotating interactive_shell_curses.

Please advice in how to handle this case!

like image 977
nijoakim Avatar asked Oct 17 '25 14:10

nijoakim


1 Answers

The Python curses module is only a wrapper around the curses library. In particular this means that you won't get access to the window object for typing purposes (_curses.window only becomes available after the call of initscr(), and even if it would, it would be pretty useless because the library doesn't provide type hints itself).

On the other hand, you can't just import the _CursesWindow type hint from typeshed's _curses.pyi because it is not defined at runtime. This is where the TYPE_CHECKING constant can help. If TYPE_CHECKING is True, you are in type checking mode and import the type hint from the stub. Otherwise, you are running the code with the interpreter which doesn't care about the type hints, so e.g. use the Any type. Example:

import curses
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from _curses import _CursesWindow
    Window = _CursesWindow
else:
    from typing import Any
    Window = Any


def main(stdscr: Window) -> None:
    height, width = stdscr.getmaxyx()
    text = 'Hello world'
    stdscr.addstr(int(height / 2), int((width - len(text)) / 2), text)

    key = 0
    while (key != ord('q')):
        stdscr.refresh()
        key = stdscr.getch()
    return None


if __name__ == '__main__':
    curses.wrapper(main)
like image 188
hoefling Avatar answered Oct 20 '25 11:10

hoefling