Add even more type hints to function arguments

This commit is contained in:
2Shirt 2023-05-29 16:04:58 -07:00
parent 1bfdb14be4
commit c009ab2d41
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
4 changed files with 145 additions and 80 deletions

View file

@ -212,7 +212,7 @@ def popen_program(
minimized: bool = False, minimized: bool = False,
pipe: bool = False, pipe: bool = False,
shell: bool = False, shell: bool = False,
**kwargs: dict[Any, Any], **kwargs,
) -> subprocess.Popen: ) -> subprocess.Popen:
"""Run program and return a subprocess.Popen object.""" """Run program and return a subprocess.Popen object."""
LOG.debug( LOG.debug(
@ -242,7 +242,7 @@ def run_program(
check: bool = True, check: bool = True,
pipe: bool = True, pipe: bool = True,
shell: bool = False, shell: bool = False,
**kwargs: dict[Any, Any], **kwargs,
) -> subprocess.CompletedProcess: ) -> subprocess.CompletedProcess:
"""Run program and return a subprocess.CompletedProcess object.""" """Run program and return a subprocess.CompletedProcess object."""
LOG.debug( LOG.debug(

View file

@ -10,9 +10,10 @@ import sys
import traceback import traceback
from collections import OrderedDict from collections import OrderedDict
from typing import Any from typing import Any, Callable, Iterable
from prompt_toolkit import prompt from prompt_toolkit import prompt
from prompt_toolkit.document import Document
from prompt_toolkit.validation import Validator, ValidationError from prompt_toolkit.validation import Validator, ValidationError
try: try:
@ -38,12 +39,12 @@ PLATFORM = platform.system()
# Classes # Classes
class InputChoiceValidator(Validator): class InputChoiceValidator(Validator):
"""Validate that input is one of the provided choices.""" """Validate that input is one of the provided choices."""
def __init__(self, choices, allow_empty=False): def __init__(self, choices: Iterable[str], allow_empty: bool = False):
self.allow_empty = allow_empty self.allow_empty = allow_empty
self.choices = [str(c).upper() for c in choices] self.choices = [str(c).upper() for c in choices]
super().__init__() super().__init__()
def validate(self, document) -> None: def validate(self, document: Document) -> None:
text = document.text text = document.text
if not (text or self.allow_empty): if not (text or self.allow_empty):
raise ValidationError( raise ValidationError(
@ -58,7 +59,7 @@ class InputChoiceValidator(Validator):
class InputNotEmptyValidator(Validator): class InputNotEmptyValidator(Validator):
"""Validate that input is not empty.""" """Validate that input is not empty."""
def validate(self, document) -> None: def validate(self, document: Document) -> None:
text = document.text text = document.text
if not text: if not text:
raise ValidationError( raise ValidationError(
@ -68,11 +69,11 @@ class InputNotEmptyValidator(Validator):
class InputTicketIDValidator(Validator): class InputTicketIDValidator(Validator):
"""Validate that input resembles a ticket ID.""" """Validate that input resembles a ticket ID."""
def __init__(self, allow_empty=False): def __init__(self, allow_empty: bool = False):
self.allow_empty = allow_empty self.allow_empty = allow_empty
super().__init__() super().__init__()
def validate(self, document) -> None: def validate(self, document: Document) -> None:
text = document.text text = document.text
if not (text or self.allow_empty): if not (text or self.allow_empty):
raise ValidationError( raise ValidationError(
@ -87,11 +88,11 @@ class InputTicketIDValidator(Validator):
class InputYesNoValidator(Validator): class InputYesNoValidator(Validator):
"""Validate that input is a yes or no.""" """Validate that input is a yes or no."""
def __init__(self, allow_empty=False): def __init__(self, allow_empty: bool = False):
self.allow_empty = allow_empty self.allow_empty = allow_empty
super().__init__() super().__init__()
def validate(self, document) -> None: def validate(self, document: Document) -> None:
text = document.text text = document.text
if not (text or self.allow_empty): if not (text or self.allow_empty):
raise ValidationError( raise ValidationError(
@ -113,7 +114,7 @@ class Menu():
1. All entry names are unique. 1. All entry names are unique.
2. All action entry names start with different letters. 2. All action entry names start with different letters.
""" """
def __init__(self, title='[Untitled Menu]'): def __init__(self, title: str = '[Untitled Menu]'):
self.actions = OrderedDict() self.actions = OrderedDict()
self.options = OrderedDict() self.options = OrderedDict()
self.sets = OrderedDict() self.sets = OrderedDict()
@ -236,7 +237,7 @@ class Menu():
# Done # Done
return valid_answers return valid_answers
def _resolve_selection(self, selection) -> tuple[str, dict[Any, Any]]: def _resolve_selection(self, selection: str) -> tuple[str, dict[Any, Any]]:
"""Get menu item based on user selection, returns tuple.""" """Get menu item based on user selection, returns tuple."""
offset = 1 offset = 1
resolved_selection = tuple() resolved_selection = tuple()
@ -267,7 +268,7 @@ class Menu():
# Done # Done
return resolved_selection return resolved_selection
def _update(self, single_selection=True, settings_mode=False) -> None: def _update(self, single_selection: bool = True, settings_mode: bool = False) -> None:
"""Update menu items in preparation for printing to screen.""" """Update menu items in preparation for printing to screen."""
index = 0 index = 0
@ -305,7 +306,8 @@ class Menu():
no_checkboxes=True, no_checkboxes=True,
) )
def _update_entry_selection_status(self, entry, toggle=True, status=None) -> None: def _update_entry_selection_status(
self, entry: str, toggle: bool = True, status: bool = False) -> None:
"""Update entry selection status either directly or by toggling.""" """Update entry selection status either directly or by toggling."""
if entry in self.sets: if entry in self.sets:
# Update targets not the set itself # Update targets not the set itself
@ -319,14 +321,14 @@ class Menu():
else: else:
section[entry]['Selected'] = status section[entry]['Selected'] = status
def _update_set_selection_status(self, targets, status) -> None: def _update_set_selection_status(self, targets: Iterable[str], status: bool) -> None:
"""Select or deselect options based on targets and status.""" """Select or deselect options based on targets and status."""
for option, details in self.options.items(): for option, details in self.options.items():
# If (new) status is True and this option is a target then select # If (new) status is True and this option is a target then select
# Otherwise deselect # Otherwise deselect
details['Selected'] = status and option in targets details['Selected'] = status and option in targets
def _user_select(self, prompt_msg) -> str: def _user_select(self, prompt_msg: str) -> str:
"""Show menu and select an entry, returns str.""" """Show menu and select an entry, returns str."""
menu_text = self._generate_menu_text() menu_text = self._generate_menu_text()
valid_answers = self._get_valid_answers() valid_answers = self._get_valid_answers()
@ -343,19 +345,19 @@ class Menu():
# Done # Done
return answer return answer
def add_action(self, name, details=None) -> None: def add_action(self, name: str, details: dict[Any, Any] | None = None) -> None:
"""Add action to menu.""" """Add action to menu."""
details = details if details else {} details = details if details else {}
details['Selected'] = details.get('Selected', False) details['Selected'] = details.get('Selected', False)
self.actions[name] = details self.actions[name] = details
def add_option(self, name, details=None) -> None: def add_option(self, name: str, details: dict[Any, Any] | None = None) -> None:
"""Add option to menu.""" """Add option to menu."""
details = details if details else {} details = details if details else {}
details['Selected'] = details.get('Selected', False) details['Selected'] = details.get('Selected', False)
self.options[name] = details self.options[name] = details
def add_set(self, name, details=None) -> None: def add_set(self, name: str, details: dict[Any, Any] | None = None) -> None:
"""Add set to menu.""" """Add set to menu."""
details = details if details else {} details = details if details else {}
details['Selected'] = details.get('Selected', False) details['Selected'] = details.get('Selected', False)
@ -367,14 +369,16 @@ class Menu():
# Add set # Add set
self.sets[name] = details self.sets[name] = details
def add_toggle(self, name, details=None) -> None: def add_toggle(self, name: str, details: dict[Any, Any] | None = None) -> None:
"""Add toggle to menu.""" """Add toggle to menu."""
details = details if details else {} details = details if details else {}
details['Selected'] = details.get('Selected', False) details['Selected'] = details.get('Selected', False)
self.toggles[name] = details self.toggles[name] = details
def advanced_select( def advanced_select(
self, prompt_msg='Please make a selection: ') -> tuple[str, dict[Any, Any]]: self,
prompt_msg: str = 'Please make a selection: ',
) -> tuple[str, dict[Any, Any]]:
"""Display menu and make multiple selections, returns tuple. """Display menu and make multiple selections, returns tuple.
NOTE: Menu is displayed until an action entry is selected. NOTE: Menu is displayed until an action entry is selected.
@ -394,7 +398,9 @@ class Menu():
return selected_entry return selected_entry
def settings_select( def settings_select(
self, prompt_msg='Please make a selection: ') -> tuple[str, dict[Any, Any]]: self,
prompt_msg: str = 'Please make a selection: ',
) -> tuple[str, dict[Any, Any]]:
"""Display menu and make multiple selections, returns tuple. """Display menu and make multiple selections, returns tuple.
NOTE: Menu is displayed until an action entry is selected. NOTE: Menu is displayed until an action entry is selected.
@ -423,8 +429,10 @@ class Menu():
return selected_entry return selected_entry
def simple_select( def simple_select(
self, prompt_msg='Please make a selection: ', self,
update=True) -> tuple[str, dict[Any, Any]]: prompt_msg: str = 'Please make a selection: ',
update: bool = True,
) -> tuple[str, dict[Any, Any]]:
"""Display menu and make a single selection, returns tuple.""" """Display menu and make a single selection, returns tuple."""
if update: if update:
self._update() self._update()
@ -441,7 +449,7 @@ class TryAndPrint():
The errors and warning attributes are used to allow fine-tuned results The errors and warning attributes are used to allow fine-tuned results
based on exception names. based on exception names.
""" """
def __init__(self, msg_bad='FAILED', msg_good='SUCCESS'): def __init__(self, msg_bad: str = 'FAILED', msg_good: str = 'SUCCESS'):
self.catch_all = True self.catch_all = True
self.indent = INDENT self.indent = INDENT
self.list_errors = ['GenericError'] self.list_errors = ['GenericError']
@ -451,7 +459,7 @@ class TryAndPrint():
self.verbose = False self.verbose = False
self.width = WIDTH self.width = WIDTH
def _format_exception_message(self, _exception) -> str: def _format_exception_message(self, _exception: Exception) -> str:
"""Format using the exception's args or name, returns str.""" """Format using the exception's args or name, returns str."""
LOG.debug( LOG.debug(
'Formatting exception: %s, %s', 'Formatting exception: %s, %s',
@ -498,7 +506,11 @@ class TryAndPrint():
# Done # Done
return message return message
def _format_function_output(self, output, msg_good) -> str: def _format_function_output(
self,
output: list | subprocess.CompletedProcess,
msg_good: str,
) -> str:
"""Format function output for use in try_and_print(), returns str.""" """Format function output for use in try_and_print(), returns str."""
LOG.debug('Formatting output: %s', output) LOG.debug('Formatting output: %s', output)
@ -536,27 +548,33 @@ class TryAndPrint():
# Done # Done
return result_msg return result_msg
def _log_result(self, message, result_msg) -> None: def _log_result(self, message: str, result_msg: str) -> None:
"""Log result text without color formatting.""" """Log result text without color formatting."""
log_text = f'{" "*self.indent}{message:<{self.width}}{result_msg}' log_text = f'{" "*self.indent}{message:<{self.width}}{result_msg}'
for line in log_text.splitlines(): for line in log_text.splitlines():
line = strip_colors(line) line = strip_colors(line)
LOG.info(line) LOG.info(line)
def add_error(self, exception_name) -> None: def add_error(self, exception_name: str) -> None:
"""Add exception name to error list.""" """Add exception name to error list."""
if exception_name not in self.list_errors: if exception_name not in self.list_errors:
self.list_errors.append(exception_name) self.list_errors.append(exception_name)
def add_warning(self, exception_name) -> None: def add_warning(self, exception_name: str) -> None:
"""Add exception name to warning list.""" """Add exception name to warning list."""
if exception_name not in self.list_warnings: if exception_name not in self.list_warnings:
self.list_warnings.append(exception_name) self.list_warnings.append(exception_name)
def run( def run(
self, message, function, *args, self,
catch_all=None, msg_good=None, verbose=None, message: str,
**kwargs) -> dict[str, Any]: function: Callable,
*args: Iterable[Any],
catch_all: bool | None = None,
msg_good: str | None = None,
verbose: bool | None = None,
**kwargs,
) -> dict[str, Any]:
"""Run a function and print the results, returns results as dict. """Run a function and print the results, returns results as dict.
If catch_all is True then (nearly) all exceptions will be caught. If catch_all is True then (nearly) all exceptions will be caught.
@ -594,8 +612,8 @@ class TryAndPrint():
verbose = verbose if verbose is not None else self.verbose verbose = verbose if verbose is not None else self.verbose
# Build exception tuples # Build exception tuples
e_exceptions = tuple(get_exception(e) for e in self.list_errors) e_exceptions: tuple = tuple(get_exception(e) for e in self.list_errors)
w_exceptions = tuple(get_exception(e) for e in self.list_warnings) w_exceptions: tuple = tuple(get_exception(e) for e in self.list_warnings)
# Run function and catch exceptions # Run function and catch exceptions
print(f'{" "*self.indent}{message:<{self.width}}', end='', flush=True) print(f'{" "*self.indent}{message:<{self.width}}', end='', flush=True)
@ -644,7 +662,10 @@ class TryAndPrint():
# Functions # Functions
def abort( def abort(
prompt_msg='Aborted.', show_prompt_msg=True, return_code=1) -> None: prompt_msg: str = 'Aborted.',
show_prompt_msg: bool = True,
return_code: int = 1,
) -> None:
"""Abort script.""" """Abort script."""
print_warning(prompt_msg) print_warning(prompt_msg)
if show_prompt_msg: if show_prompt_msg:
@ -653,7 +674,7 @@ def abort(
sys.exit(return_code) sys.exit(return_code)
def ask(prompt_msg) -> bool: def ask(prompt_msg: str) -> bool:
"""Prompt the user with a Y/N question, returns bool.""" """Prompt the user with a Y/N question, returns bool."""
validator = InputYesNoValidator() validator = InputYesNoValidator()
@ -670,7 +691,7 @@ def ask(prompt_msg) -> bool:
raise ValueError(f'Invalid answer given: {response}') raise ValueError(f'Invalid answer given: {response}')
def beep(repeat=1) -> None: def beep(repeat: int = 1) -> None:
"""Play system bell with optional repeat.""" """Play system bell with optional repeat."""
while repeat >= 1: while repeat >= 1:
# Print bell char without a newline # Print bell char without a newline
@ -679,7 +700,7 @@ def beep(repeat=1) -> None:
repeat -= 1 repeat -= 1
def choice(prompt_msg, choices) -> str: def choice(prompt_msg: str, choices: Iterable[str]) -> str:
"""Choose an option from a provided list, returns str. """Choose an option from a provided list, returns str.
Choices provided will be converted to uppercase and returned as such. Choices provided will be converted to uppercase and returned as such.
@ -697,7 +718,7 @@ def choice(prompt_msg, choices) -> str:
return response.upper() return response.upper()
def fix_prompt(message) -> str: def fix_prompt(message: str) -> str:
"""Fix prompt, returns str.""" """Fix prompt, returns str."""
if not message: if not message:
message = 'Input text: ' message = 'Input text: '
@ -708,7 +729,7 @@ def fix_prompt(message) -> str:
@cache @cache
def get_exception(name) -> Exception: def get_exception(name: str) -> Exception:
"""Get exception by name, returns exception object. """Get exception by name, returns exception object.
[Doctest] [Doctest]
@ -757,7 +778,9 @@ def get_ticket_id() -> str:
def input_text( def input_text(
prompt_msg='Enter text: ', allow_empty=False, validator=None, prompt_msg: str = 'Enter text: ',
allow_empty: bool = False,
validator: Validator | None = None,
) -> str: ) -> str:
"""Get input from user, returns str.""" """Get input from user, returns str."""
prompt_msg = fix_prompt(prompt_msg) prompt_msg = fix_prompt(prompt_msg)
@ -793,12 +816,18 @@ def major_exception() -> None:
raise SystemExit(1) raise SystemExit(1)
def pause(prompt_msg='Press Enter to continue... ') -> None: def pause(prompt_msg: str = 'Press Enter to continue... ') -> None:
"""Simple pause implementation.""" """Simple pause implementation."""
input_text(prompt_msg, allow_empty=True) input_text(prompt_msg, allow_empty=True)
def print_colored(strings, colors, log=False, sep=' ', **kwargs) -> None: def print_colored(
strings: Iterable[str] | str,
colors: Iterable[str | None] | str,
log: bool = False,
sep: str = ' ',
**kwargs,
) -> None:
"""Prints strings in the colors specified.""" """Prints strings in the colors specified."""
LOG.debug( LOG.debug(
'strings: %s, colors: %s, sep: %s, kwargs: %s', 'strings: %s, colors: %s, sep: %s, kwargs: %s',
@ -816,7 +845,7 @@ def print_colored(strings, colors, log=False, sep=' ', **kwargs) -> None:
LOG.info(strip_colors(msg)) LOG.info(strip_colors(msg))
def print_error(msg, log=True, **kwargs) -> None: def print_error(msg: str, log: bool = True, **kwargs) -> None:
"""Prints message in RED and log as ERROR.""" """Prints message in RED and log as ERROR."""
if 'file' not in kwargs: if 'file' not in kwargs:
# Only set if not specified # Only set if not specified
@ -826,14 +855,14 @@ def print_error(msg, log=True, **kwargs) -> None:
LOG.error(msg) LOG.error(msg)
def print_info(msg, log=True, **kwargs) -> None: def print_info(msg: str, log: bool = True, **kwargs) -> None:
"""Prints message in BLUE and log as INFO.""" """Prints message in BLUE and log as INFO."""
print_colored(msg, 'BLUE', **kwargs) print_colored(msg, 'BLUE', **kwargs)
if log: if log:
LOG.info(msg) LOG.info(msg)
def print_report(report, indent=None, log=True) -> None: def print_report(report: list[str], indent=None, log: bool = True) -> None:
"""Print report to screen and optionally to log.""" """Print report to screen and optionally to log."""
for line in report: for line in report:
if indent: if indent:
@ -843,21 +872,21 @@ def print_report(report, indent=None, log=True) -> None:
LOG.info(strip_colors(line)) LOG.info(strip_colors(line))
def print_standard(msg, log=True, **kwargs) -> None: def print_standard(msg: str, log: bool = True, **kwargs) -> None:
"""Prints message and log as INFO.""" """Prints message and log as INFO."""
print(msg, **kwargs) print(msg, **kwargs)
if log: if log:
LOG.info(msg) LOG.info(msg)
def print_success(msg, log=True, **kwargs) -> None: def print_success(msg: str, log: bool = True, **kwargs) -> None:
"""Prints message in GREEN and log as INFO.""" """Prints message in GREEN and log as INFO."""
print_colored(msg, 'GREEN', **kwargs) print_colored(msg, 'GREEN', **kwargs)
if log: if log:
LOG.info(msg) LOG.info(msg)
def print_warning(msg, log=True, **kwargs) -> None: def print_warning(msg: str, log: bool = True, **kwargs) -> None:
"""Prints message in YELLOW and log as WARNING.""" """Prints message in YELLOW and log as WARNING."""
if 'file' not in kwargs: if 'file' not in kwargs:
# Only set if not specified # Only set if not specified
@ -867,7 +896,7 @@ def print_warning(msg, log=True, **kwargs) -> None:
LOG.warning(msg) LOG.warning(msg)
def set_title(title) -> None: def set_title(title: str) -> None:
"""Set window title.""" """Set window title."""
LOG.debug('title: %s', title) LOG.debug('title: %s', title)
if os.name == 'nt': if os.name == 'nt':
@ -876,14 +905,19 @@ def set_title(title) -> None:
print_error('Setting the title is only supported under Windows.') print_error('Setting the title is only supported under Windows.')
def show_data(message, data, color=None, indent=None, width=None) -> None: def show_data(
message: str,
data: Any,
color: str | None = None,
indent: int | None = None,
width: int | None = None,
) -> None:
"""Display info using default or provided indent and width.""" """Display info using default or provided indent and width."""
colors = (None, color if color else None)
indent = INDENT if indent is None else indent indent = INDENT if indent is None else indent
width = WIDTH if width is None else width width = WIDTH if width is None else width
print_colored( print_colored(
(f'{" "*indent}{message:<{width}}', data), (f'{" "*indent}{message:<{width}}', data),
colors, (None, color if color else None),
log=True, log=True,
sep='', sep='',
) )

View file

@ -4,6 +4,8 @@
import logging import logging
import pathlib import pathlib
from typing import Any
from wk.exe import run_program from wk.exe import run_program
from wk.std import PLATFORM from wk.std import PLATFORM
@ -13,7 +15,7 @@ LOG = logging.getLogger(__name__)
# Functions # Functions
def capture_pane(pane_id=None) -> str: def capture_pane(pane_id: str | None = None) -> str:
"""Capture text from current or target pane, returns str.""" """Capture text from current or target pane, returns str."""
cmd = ['tmux', 'capture-pane', '-p'] cmd = ['tmux', 'capture-pane', '-p']
if pane_id: if pane_id:
@ -24,7 +26,7 @@ def capture_pane(pane_id=None) -> str:
return proc.stdout.strip() return proc.stdout.strip()
def clear_pane(pane_id=None) -> None: def clear_pane(pane_id: str | None = None) -> None:
"""Clear pane buffer for current or target pane.""" """Clear pane buffer for current or target pane."""
commands = [ commands = [
['tmux', 'send-keys', '-R'], ['tmux', 'send-keys', '-R'],
@ -38,7 +40,7 @@ def clear_pane(pane_id=None) -> None:
run_program(cmd, check=False) run_program(cmd, check=False)
def fix_layout(layout, forced=False) -> None: def fix_layout(layout: dict[str, dict[str, Any]], forced: bool = False) -> None:
"""Fix pane sizes based on layout.""" """Fix pane sizes based on layout."""
resize_kwargs = [] resize_kwargs = []
@ -110,7 +112,7 @@ def fix_layout(layout, forced=False) -> None:
resize_pane(worker, height=layout['Workers']['height']) resize_pane(worker, height=layout['Workers']['height'])
def get_pane_size(pane_id=None) -> tuple[int, int]: def get_pane_size(pane_id: str | None = None) -> tuple[int, int]:
"""Get current or target pane size, returns tuple.""" """Get current or target pane size, returns tuple."""
cmd = ['tmux', 'display', '-p'] cmd = ['tmux', 'display', '-p']
if pane_id: if pane_id:
@ -141,7 +143,7 @@ def get_window_size() -> tuple[int, int]:
return (width, height) return (width, height)
def kill_all_panes(pane_id=None) -> None: def kill_all_panes(pane_id: str | None = None) -> None:
"""Kill all panes except for the current or target pane.""" """Kill all panes except for the current or target pane."""
cmd = ['tmux', 'kill-pane', '-a'] cmd = ['tmux', 'kill-pane', '-a']
if pane_id: if pane_id:
@ -151,7 +153,7 @@ def kill_all_panes(pane_id=None) -> None:
run_program(cmd, check=False) run_program(cmd, check=False)
def kill_pane(*pane_ids) -> None: def kill_pane(*pane_ids: str) -> None:
"""Kill pane(s) by id.""" """Kill pane(s) by id."""
cmd = ['tmux', 'kill-pane', '-t'] cmd = ['tmux', 'kill-pane', '-t']
@ -160,7 +162,7 @@ def kill_pane(*pane_ids) -> None:
run_program(cmd+[pane_id], check=False) run_program(cmd+[pane_id], check=False)
def layout_needs_fixed(layout) -> bool: def layout_needs_fixed(layout: dict[str, dict[str, Any]]) -> bool:
"""Check if layout needs fixed, returns bool.""" """Check if layout needs fixed, returns bool."""
needs_fixed = False needs_fixed = False
@ -189,7 +191,7 @@ def layout_needs_fixed(layout) -> bool:
return needs_fixed return needs_fixed
def poll_pane(pane_id) -> bool: def poll_pane(pane_id: str) -> bool:
"""Check if pane exists, returns bool.""" """Check if pane exists, returns bool."""
cmd = ['tmux', 'list-panes', '-F', '#D'] cmd = ['tmux', 'list-panes', '-F', '#D']
@ -202,8 +204,12 @@ def poll_pane(pane_id) -> bool:
def prep_action( def prep_action(
cmd=None, working_dir=None, text=None, cmd: str | None = None,
watch_file=None, watch_cmd='cat') -> list[str]: working_dir: pathlib.Path | str | None = None,
text: str | None = None,
watch_file: pathlib.Path | str | None = None,
watch_cmd: str = 'cat',
) -> list[str]:
"""Prep action to perform during a tmux call, returns list. """Prep action to perform during a tmux call, returns list.
This will prep for running a basic command, displaying text on screen, This will prep for running a basic command, displaying text on screen,
@ -253,7 +259,7 @@ def prep_action(
return action_cmd return action_cmd
def prep_file(path) -> None: def prep_file(path: pathlib.Path | str) -> None:
"""Check if file exists and create empty file if not.""" """Check if file exists and create empty file if not."""
path = pathlib.Path(path).resolve() path = pathlib.Path(path).resolve()
try: try:
@ -263,7 +269,11 @@ def prep_file(path) -> None:
pass pass
def resize_pane(pane_id=None, width=None, height=None) -> None: def resize_pane(
pane_id: str | None = None,
width: int | None = None,
height: int | None = None,
) -> None:
"""Resize current or target pane. """Resize current or target pane.
NOTE: kwargs is only here to make calling this function easier NOTE: kwargs is only here to make calling this function easier
@ -288,7 +298,7 @@ def resize_pane(pane_id=None, width=None, height=None) -> None:
run_program(cmd, check=False) run_program(cmd, check=False)
def respawn_pane(pane_id, **action) -> None: def respawn_pane(pane_id: str, **action) -> None:
"""Respawn pane with action.""" """Respawn pane with action."""
cmd = ['tmux', 'respawn-pane', '-k', '-t', pane_id] cmd = ['tmux', 'respawn-pane', '-k', '-t', pane_id]
cmd.extend(prep_action(**action)) cmd.extend(prep_action(**action))
@ -298,9 +308,12 @@ def respawn_pane(pane_id, **action) -> None:
def split_window( def split_window(
lines=None, percent=None, lines: int | None = None,
behind=False, vertical=False, percent: int | None = None,
target_id=None, **action) -> str: behind: bool = False,
vertical: bool = False,
target_id: str | None = None,
**action) -> str:
"""Split tmux window, run action, and return pane_id as str.""" """Split tmux window, run action, and return pane_id as str."""
cmd = ['tmux', 'split-window', '-d', '-PF', '#D'] cmd = ['tmux', 'split-window', '-d', '-PF', '#D']
@ -333,7 +346,7 @@ def split_window(
return proc.stdout.strip() return proc.stdout.strip()
def zoom_pane(pane_id=None) -> None: def zoom_pane(pane_id: str | None = None) -> None:
"""Toggle zoom status for current or target pane.""" """Toggle zoom status for current or target pane."""
cmd = ['tmux', 'resize-pane', '-Z'] cmd = ['tmux', 'resize-pane', '-Z']
if pane_id: if pane_id:

View file

@ -29,7 +29,7 @@ TMUX_LAYOUT = { # NOTE: This needs to be in order from top to bottom
# Classes # Classes
class TUI(): class TUI():
"""Object for tracking TUI elements.""" """Object for tracking TUI elements."""
def __init__(self, title_text=None): def __init__(self, title_text: str | None = None):
self.layout = deepcopy(TMUX_LAYOUT) self.layout = deepcopy(TMUX_LAYOUT)
self.side_width = TMUX_SIDE_WIDTH self.side_width = TMUX_SIDE_WIDTH
self.title_text = title_text if title_text else 'Title Text' self.title_text = title_text if title_text else 'Title Text'
@ -44,7 +44,11 @@ class TUI():
atexit.register(tmux.kill_all_panes) atexit.register(tmux.kill_all_panes)
def add_info_pane( def add_info_pane(
self, lines=None, percent=None, update_layout=True, **tmux_args, self,
lines: int | None = None,
percent: int | None = None,
update_layout: bool = True,
**tmux_args,
) -> None: ) -> None:
"""Add info pane.""" """Add info pane."""
if not (lines or percent): if not (lines or percent):
@ -78,7 +82,12 @@ class TUI():
# Add pane # Add pane
self.layout['Info']['Panes'].append(tmux.split_window(**tmux_args)) self.layout['Info']['Panes'].append(tmux.split_window(**tmux_args))
def add_title_pane(self, line1, line2=None, colors=None) -> None: def add_title_pane(
self,
line1: str,
line2: str | None = None,
colors: list[str] | None = None,
) -> None:
"""Add pane to title row.""" """Add pane to title row."""
lines = [line1, line2] lines = [line1, line2]
colors = colors if colors else self.title_colors.copy() colors = colors if colors else self.title_colors.copy()
@ -105,7 +114,11 @@ class TUI():
self.layout['Title']['Panes'].append(tmux.split_window(**tmux_args)) self.layout['Title']['Panes'].append(tmux.split_window(**tmux_args))
def add_worker_pane( def add_worker_pane(
self, lines=None, percent=None, update_layout=True, **tmux_args, self,
lines: int | None = None,
percent: int | None = None,
update_layout: bool = True,
**tmux_args,
) -> None: ) -> None:
"""Add worker pane.""" """Add worker pane."""
height = lines height = lines
@ -142,7 +155,7 @@ class TUI():
"""Clear current pane height and update layout.""" """Clear current pane height and update layout."""
self.layout['Current'].pop('height', None) self.layout['Current'].pop('height', None)
def fix_layout(self, forced=True) -> None: def fix_layout(self, forced: bool = True) -> None:
"""Fix tmux layout based on self.layout.""" """Fix tmux layout based on self.layout."""
try: try:
tmux.fix_layout(self.layout, forced=forced) tmux.fix_layout(self.layout, forced=forced)
@ -208,19 +221,24 @@ class TUI():
self.layout['Workers']['Panes'].clear() self.layout['Workers']['Panes'].clear()
tmux.kill_pane(*panes) tmux.kill_pane(*panes)
def set_current_pane_height(self, height) -> None: def set_current_pane_height(self, height: int) -> None:
"""Set current pane height and update layout.""" """Set current pane height and update layout."""
self.layout['Current']['height'] = height self.layout['Current']['height'] = height
tmux.resize_pane(height=height) tmux.resize_pane(height=height)
def set_progress_file(self, progress_file) -> None: def set_progress_file(self, progress_file: str) -> None:
"""Set the file to use for the progresse pane.""" """Set the file to use for the progresse pane."""
tmux.respawn_pane( tmux.respawn_pane(
pane_id=self.layout['Progress']['Panes'][0], pane_id=self.layout['Progress']['Panes'][0],
watch_file=progress_file, watch_file=progress_file,
) )
def set_title(self, line1, line2=None, colors=None) -> None: def set_title(
self,
line1: str,
line2: str | None = None,
colors: list[str] | None = None,
) -> None:
"""Set title text.""" """Set title text."""
self.title_text = line1 self.title_text = line1
self.title_text_line2 = line2 if line2 else '' self.title_text_line2 = line2 if line2 else ''
@ -251,7 +269,7 @@ class TUI():
# Functions # Functions
def fix_layout(layout, forced=False) -> None: def fix_layout(layout, forced: bool = False) -> None:
"""Fix pane sizes based on layout.""" """Fix pane sizes based on layout."""
resize_kwargs = [] resize_kwargs = []