Skip to content

Commit

Permalink
refactor(tui): drop dependency windows-curses
Browse files Browse the repository at this point in the history
  • Loading branch information
XuehaiPan committed Jan 31, 2025
1 parent 7e56376 commit 0e05e9c
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 11 deletions.
1 change: 1 addition & 0 deletions docs/source/spelling_wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,4 @@ maxsize
reentrant
env
tty
CPython
3 changes: 1 addition & 2 deletions nvitop/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
"""The interactive NVIDIA-GPU process viewer."""

import argparse
import curses
import os
import sys
import textwrap

from nvitop.api import HostProcess, libnvml
from nvitop.tui import TUI, USERNAME, Device, colored, libcurses, set_color, setlocale_utf8
from nvitop.tui import TUI, USERNAME, Device, colored, curses, libcurses, set_color, setlocale_utf8
from nvitop.version import __version__


Expand Down
1 change: 1 addition & 0 deletions nvitop/tui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
USERNAME,
Device,
colored,
curses,
libcurses,
set_color,
setlocale_utf8,
Expand Down
8 changes: 8 additions & 0 deletions nvitop/tui/library/curses/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This file is part of nvitop, the interactive NVIDIA-GPU process viewer.
# License: GNU GPL version 3.

# pylint: disable=missing-module-docstring

from curses import * # noqa: F403 # pylint: disable=redefined-builtin

from nvitop.tui.library.curses import ascii # pylint: disable=redefined-builtin
213 changes: 213 additions & 0 deletions nvitop/tui/library/curses/ascii.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
"""Constants and membership tests for ASCII characters"""

# Copied from the CPython repository.
# https://github.com/python/cpython/blob/HEAD/Lib/curses/ascii.py

# pylint: disable=missing-function-docstring

from __future__ import annotations

from typing import overload


NUL = 0x00 # ^@
SOH = 0x01 # ^A
STX = 0x02 # ^B
ETX = 0x03 # ^C
EOT = 0x04 # ^D
ENQ = 0x05 # ^E
ACK = 0x06 # ^F
BEL = 0x07 # ^G
BS = 0x08 # ^H
TAB = 0x09 # ^I
HT = 0x09 # ^I
LF = 0x0A # ^J
NL = 0x0A # ^J
VT = 0x0B # ^K
FF = 0x0C # ^L
CR = 0x0D # ^M
SO = 0x0E # ^N
SI = 0x0F # ^O
DLE = 0x10 # ^P
DC1 = 0x11 # ^Q
DC2 = 0x12 # ^R
DC3 = 0x13 # ^S
DC4 = 0x14 # ^T
NAK = 0x15 # ^U
SYN = 0x16 # ^V
ETB = 0x17 # ^W
CAN = 0x18 # ^X
EM = 0x19 # ^Y
SUB = 0x1A # ^Z
ESC = 0x1B # ^[
FS = 0x1C # ^\
GS = 0x1D # ^]
RS = 0x1E # ^^
US = 0x1F # ^_
SP = 0x20 # space
DEL = 0x7F # delete

controlnames = [
'NUL',
'SOH',
'STX',
'ETX',
'EOT',
'ENQ',
'ACK',
'BEL',
'BS',
'HT',
'LF',
'VT',
'FF',
'CR',
'SO',
'SI',
'DLE',
'DC1',
'DC2',
'DC3',
'DC4',
'NAK',
'SYN',
'ETB',
'CAN',
'EM',
'SUB',
'ESC',
'FS',
'GS',
'RS',
'US',
'SP',
]


def _ctoi(c: int | str) -> int:
if isinstance(c, str):
return ord(c)
return c


def isalnum(c: int | str) -> bool:
return isalpha(c) or isdigit(c)


def isalpha(c: int | str) -> bool:
return isupper(c) or islower(c)


def isascii(c: int | str) -> bool:
return 0 <= _ctoi(c) <= 127


def isblank(c: int | str) -> bool:
return _ctoi(c) in (9, 32)


def iscntrl(c: int | str) -> bool:
return 0 <= _ctoi(c) <= 31 or _ctoi(c) == 127


def isdigit(c: int | str) -> bool:
return 48 <= _ctoi(c) <= 57


def isgraph(c: int | str) -> bool:
return 33 <= _ctoi(c) <= 126


def islower(c: int | str) -> bool:
return 97 <= _ctoi(c) <= 122


def isprint(c: int | str) -> bool:
return 32 <= _ctoi(c) <= 126


def ispunct(c: int | str) -> bool:
return isgraph(c) and not isalnum(c)


def isspace(c: int | str) -> bool:
return _ctoi(c) in (9, 10, 11, 12, 13, 32)


def isupper(c: int | str) -> bool:
return 65 <= _ctoi(c) <= 90


def isxdigit(c: int | str) -> bool:
return isdigit(c) or (65 <= _ctoi(c) <= 70) or (97 <= _ctoi(c) <= 102)


def isctrl(c: int | str) -> bool:
return 0 <= _ctoi(c) < 32


def ismeta(c: int | str) -> bool:
return _ctoi(c) > 127


@overload
def ascii(c: int) -> int: ... # pylint: disable=redefined-builtin


@overload
def ascii(c: str) -> str: ...


def ascii(c: int | str) -> int | str:
if isinstance(c, str):
return chr(_ctoi(c) & 0x7F)
return _ctoi(c) & 0x7F


@overload
def ctrl(c: int) -> int: ...


@overload
def ctrl(c: str) -> str: ...


def ctrl(c: int | str) -> int | str:
if isinstance(c, str):
return chr(_ctoi(c) & 0x1F)
return _ctoi(c) & 0x1F


@overload
def alt(c: int) -> int: ...


@overload
def alt(c: str) -> str: ...


def alt(c: int | str) -> int | str:
if isinstance(c, str):
return chr(_ctoi(c) | 0x80)
return _ctoi(c) | 0x80


@overload
def unctrl(c: int) -> int: ...


@overload
def unctrl(c: str) -> str: ...


def unctrl(c: int | str) -> int | str:
bits = _ctoi(c)
if bits == 0x7F:
rep = '^?'
elif isprint(bits & 0x7F):
rep = chr(bits & 0x7F)
else:
rep = '^' + chr(((bits & 0x7F) | 0x20) + 0x20)
if bits & 0x80:
return '!' + rep
return rep
4 changes: 2 additions & 2 deletions nvitop/tui/library/keybinding.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring

import copy
import curses
import curses.ascii
import string
from collections import OrderedDict

from nvitop.tui.library import curses


DIGITS = set(map(ord, string.digits))

Expand Down
7 changes: 4 additions & 3 deletions nvitop/tui/library/libcurses.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

import colorsys
import contextlib
import curses
import locale
import os
import signal

from nvitop.tui.library import curses
from nvitop.tui.library.history import GRAPH_SYMBOLS


Expand Down Expand Up @@ -161,15 +161,16 @@ def libcurses(colorful=False, light_theme=False):

# Push a Ctrl+C (ascii value 3) to the curses getch stack
def interrupt_handler(signalnum, frame): # pylint: disable=unused-argument
curses.ungetch(3)
curses.ungetch(curses.ascii.ETX)

# Simulate a ^C press in curses when an interrupt is caught
signal.signal(signal.SIGINT, interrupt_handler)
original_interrupt_handler = signal.signal(signal.SIGINT, interrupt_handler)

try:
yield win
finally:
curses.endwin()
signal.signal(signal.SIGINT, original_interrupt_handler)


class CursesShortcuts:
Expand Down
2 changes: 1 addition & 1 deletion nvitop/tui/library/messagebox.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring

import curses
import string
import threading
import time
from functools import partial

from nvitop.tui.library import curses
from nvitop.tui.library.displayable import Displayable
from nvitop.tui.library.keybinding import normalize_keybinding
from nvitop.tui.library.process import host
Expand Down
2 changes: 1 addition & 1 deletion nvitop/tui/library/mouse.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring

import curses
from nvitop.tui.library import curses


class MouseEvent:
Expand Down
3 changes: 1 addition & 2 deletions nvitop/tui/tui.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@

# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring

import curses
import shutil
import time

from nvitop.tui.library import ALT_KEY, DisplayableContainer, KeyBuffer, KeyMaps, MouseEvent
from nvitop.tui.library import ALT_KEY, DisplayableContainer, KeyBuffer, KeyMaps, MouseEvent, curses
from nvitop.tui.screens import (
BreakLoop,
EnvironScreen,
Expand Down

0 comments on commit 0e05e9c

Please sign in to comment.