Skip to content

Commit

Permalink
Add type hints (4/n) (#388)
Browse files Browse the repository at this point in the history
  • Loading branch information
KapJI authored Feb 21, 2021
1 parent 29e3c15 commit b76264a
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 52 deletions.
17 changes: 1 addition & 16 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,6 @@ disallow_untyped_calls = false
disallow_untyped_defs = false
disallow_incomplete_defs = false

[mypy-pathpicker.color_printer]
disallow_untyped_calls = false

[mypy-pathpicker.curses_api]
disallow_incomplete_defs = false
disallow_untyped_defs = false

[mypy-pathpicker.key_bindings]
disallow_incomplete_defs = false
disallow_untyped_defs = false

[mypy-pathpicker.line_format]
disallow_incomplete_defs = false
disallow_untyped_calls = false
Expand Down Expand Up @@ -68,14 +57,10 @@ disallow_untyped_defs = false
[mypy-tests.test_key_bindings_parsing]
disallow_untyped_defs = false

[mypy-tests.lib.curses]
disallow_untyped_defs = false

[mypy-tests.lib.screen]
disallow_untyped_calls = false
disallow_untyped_defs = false
disallow_incomplete_defs = false

[mypy-tests.lib.screen_test_runner]
disallow_untyped_calls = false
disallow_untyped_defs = false
disallow_incomplete_defs = false
5 changes: 3 additions & 2 deletions src/pathpicker/char_code_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
# LICENSE file in the root directory of this source tree.

import curses
from typing import Dict

CODE_TO_CHAR = {i: chr(i) for i in range(256)}
CODE_TO_CHAR: Dict[int, str] = {i: chr(i) for i in range(256)}
CODE_TO_CHAR.update(
(value, name[4:]) for name, value in vars(curses).items() if name.startswith("KEY_")
)
# special exceptions
CODE_TO_CHAR[10] = "ENTER"

CHAR_TO_CODE = {v: k for k, v in CODE_TO_CHAR.items()}
CHAR_TO_CODE: Dict[str, int] = {v: k for k, v in CODE_TO_CHAR.items()}
3 changes: 0 additions & 3 deletions src/pathpicker/curses_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ class CursesAPI:
"""A dummy curses wrapper that allows us to intercept these
calls when in a test environment"""

def __init__(self):
pass

def use_default_colors(self) -> None:
curses.use_default_colors()

Expand Down
2 changes: 1 addition & 1 deletion src/pathpicker/key_bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
KeyBindings = NewType("KeyBindings", List[Tuple[str, bytes]])


def read_key_bindings(key_bindings_file=KEY_BINDINGS_FILE) -> KeyBindings:
def read_key_bindings(key_bindings_file: str = KEY_BINDINGS_FILE) -> KeyBindings:
"""Returns configured key bindings, in the format [(key, command), ...].
The ordering of the entries is not guaranteed, although it's irrelevant
to the purpose.
Expand Down
2 changes: 1 addition & 1 deletion src/tests/lib/curses.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class CursesForTest:
"""The dependency-injected curses wrapper which simply
stores some state in test runs of the UI"""

def __init__(self):
def __init__(self) -> None:
self.is_echo = False
self.is_default_colors = False
self.color_pairs = {}
Expand Down
68 changes: 39 additions & 29 deletions src/tests/lib/screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from typing import Tuple
from typing import Dict, List, NewType, Optional, Tuple

from pathpicker.char_code_mapping import CHAR_TO_CODE

ATTRIBUTE_SYMBOL_MAPPING = {
ATTRIBUTE_SYMBOL_MAPPING: Dict[int, str] = {
0: " ",
1: " ",
2: "B",
Expand All @@ -25,90 +25,98 @@
}


class ScreenForTest:
ScreenType = NewType("ScreenType", Dict[Tuple[int, int], Tuple[str, int]])


class ScreenForTest:
"""A dummy object that is dependency-injected in place
of curses standard screen. Allows us to unit-test parts
of the UI code"""

def __init__(self, char_inputs, max_x, max_y):
def __init__(self, char_inputs: List[str], max_x: int, max_y: int):
self.max_x = max_x
self.max_y = max_y
self.cursor_x = 0
self.cursor_y = 0
self.output = {}
self.past_screens = []
self.output = ScreenType({})
self.past_screens: List[ScreenType] = []
self.char_inputs = char_inputs
self.erase()
self.current_attribute = 0

def getmaxyx(self):
def getmaxyx(self) -> Tuple[int, int]:
return self.max_y, self.max_x

def refresh(self):
def refresh(self) -> None:
if self.contains_content(self.output):
# we have an old screen, so add it
self.past_screens.append(dict(self.output))
self.past_screens.append(ScreenType(self.output.copy()))

def contains_content(self, screen):
def contains_content(self, screen: ScreenType) -> bool:
for _coord, pair in screen.items():
(char, _attr) = pair
if char:
return True
return False

def erase(self):
self.output = {}
def erase(self) -> None:
self.output = ScreenType({})
for x_pos in range(self.max_x):
for y_pos in range(self.max_y):
coord = (x_pos, y_pos)
self.output[coord] = ("", 1)

def move(self, y_pos, x_pos):
def move(self, y_pos: int, x_pos: int) -> None:
self.cursor_y = y_pos
self.cursor_x = x_pos

def attrset(self, attr):
def attrset(self, attr: int) -> None:
self.current_attribute = attr

def addstr(self, y_pos, x_pos, string, attr=None):
def addstr(
self, y_pos: int, x_pos: int, string: str, attr: Optional[int] = None
) -> None:
if attr:
self.attrset(attr)
for delta_x, value in enumerate(string):
coord = (x_pos + delta_x, y_pos)
self.output[coord] = (value, self.current_attribute)

def delch(self, y_pos, x_pos):
def delch(self, y_pos: int, x_pos: int) -> None:
"""Delete a character. We implement this by removing the output,
NOT by printing a space"""
self.output[(x_pos, y_pos)] = ("", 1)

def getch(self):
def getch(self) -> int:
return CHAR_TO_CODE[self.char_inputs.pop(0)]

def getstr(self, _y, _x, _max_len) -> str:
def getstr(self, _y: int, _x: int, _max_len: int) -> str:
# TODO -- enable editing this
return ""

def print_screen(self):
def print_screen(self) -> None:
for index, row in enumerate(self.get_rows()):
print(f"Row {index:02}:{row}")

def print_old_screens(self):
def print_old_screens(self) -> None:
for old_screen in range(self.get_num_past_screens()):
for index, row in enumerate(self.get_rows_for_past_screen(old_screen)):
print(f"Screen {old_screen:02} Row {index:02}:{row}")

def get_num_past_screens(self):
def get_num_past_screens(self) -> int:
return len(self.past_screens)

def get_rows_for_past_screen(self, past_screen):
def get_rows_for_past_screen(self, past_screen: int) -> List[str]:
return self.get_rows(screen=self.past_screens[past_screen])

def get_rows_with_attributes_for_past_screen(self, past_screen):
def get_rows_with_attributes_for_past_screen(
self, past_screen: int
) -> Tuple[List[str], List[str]]:
return self.get_rows_with_attributes(screen=self.past_screens[past_screen])

def get_rows_with_attributes_for_past_screens(self, past_screens):
def get_rows_with_attributes_for_past_screens(
self, past_screens: List[int]
) -> Tuple[List[str], List[int]]:
"""Get the rows & attributes for the array of screens as one stream
(there is no extra new line or extra space between pages)"""
pages = map(
Expand All @@ -125,12 +133,14 @@ def get_rows_with_attributes_for_past_screens(self, past_screens):
[line for page in attributes for line in page],
)

def get_rows_with_attributes(self, screen=None) -> Tuple:
def get_rows_with_attributes(
self, screen: Optional[ScreenType] = None
) -> Tuple[List[str], List[str]]:
if not screen:
screen = self.output

rows = []
attribute_rows = []
rows: List[str] = []
attribute_rows: List[str] = []
for y_pos in range(self.max_y):
row = ""
attribute_row = ""
Expand All @@ -143,11 +153,11 @@ def get_rows_with_attributes(self, screen=None) -> Tuple:
attribute_rows.append(attribute_row)
return rows, attribute_rows

def get_rows(self, screen=None):
def get_rows(self, screen: Optional[ScreenType] = None) -> List[str]:
(rows, _) = self.get_rows_with_attributes(screen)
return rows

def get_attribute_symbol_for_code(self, code):
def get_attribute_symbol_for_code(self, code: int) -> str:
symbol = ATTRIBUTE_SYMBOL_MAPPING.get(code, None)
if symbol is None:
raise ValueError(f"{code} not mapped")
Expand Down

0 comments on commit b76264a

Please sign in to comment.