Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions qubesadmin/label.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@
# with this program; if not, see <http://www.gnu.org/licenses/>.

'''VM Labels'''
from __future__ import annotations
from typing import TYPE_CHECKING

import qubesadmin.exc

if TYPE_CHECKING:
from qubesadmin.app import QubesBase


class Label:
'''Label definition for virtual machines
Expand All @@ -32,14 +37,14 @@ class Label:
:param str name: label's name like "red" or "green"
'''

def __init__(self, app, name):
def __init__(self, app: QubesBase, name: str):
self.app = app
self._name = name
self._color = None
self._index = None
self._color: str | None = None
self._index: int | None = None

@property
def color(self):
def color(self) -> str:
'''color specification as in HTML (``#abcdef``)'''
if self._color is None:
try:
Expand All @@ -51,18 +56,18 @@ def color(self):
return self._color

@property
def name(self):
def name(self) -> str:
'''label's name like "red" or "green"'''
return self._name

@property
def icon(self):
def icon(self) -> str:
'''freedesktop icon name, suitable for use in
:py:meth:`PyQt4.QtGui.QIcon.fromTheme`'''
return 'appvm-' + self.name

@property
def index(self):
def index(self) -> int:
'''label numeric identifier'''
if self._index is None:
try:
Expand All @@ -73,13 +78,13 @@ def index(self):
self._index = int(qubesd_response.decode())
return self._index

def __str__(self):
def __str__(self) -> str:
return self._name

def __eq__(self, other):
def __eq__(self, other: object) -> bool:
if isinstance(other, Label):
return self.name == other.name
return NotImplemented

def __hash__(self):
def __hash__(self) -> int:
return hash(self.name)
4 changes: 2 additions & 2 deletions qubesadmin/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
formatter_debug = logging.Formatter(FORMAT_DEBUG)


def enable():
def enable() -> None:
'''Enable global logging

Use :py:mod:`logging` module from standard library to log messages.
Expand All @@ -58,7 +58,7 @@ def enable():
logging.root.setLevel(logging.INFO)


def enable_debug():
def enable_debug() -> None:
'''Enable debug logging

Enable more messages and additional info to message format.
Expand Down
38 changes: 21 additions & 17 deletions qubesadmin/spinner.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@
import curses
import io
import itertools
import typing
from typing import IO

CHARSET = '-\\|/'
ENTERPRISE_CHARSET = CHARSET * 4 + '-._.-^' * 2
CHARSET: str = '-\\|/'
ENTERPRISE_CHARSET: str = CHARSET * 4 + '-._.-^' * 2

class AbstractSpinner:
'''The base class for all Spinners
Expand All @@ -54,57 +56,57 @@ class AbstractSpinner:
2. zero or more calls to :py:meth:`update()`
3. exactly one call to :py:meth:`hide()`
'''
def __init__(self, stream, charset=CHARSET):
def __init__(self, stream: IO, charset: str=CHARSET):
self.stream = stream
self.charset = itertools.cycle(charset)

def show(self, prompt):
def show(self, prompt: str) -> None:
'''Show the spinner, with a prompt

:param str prompt: prompt, like "please wait"
'''
raise NotImplementedError()

def hide(self):
def hide(self) -> None:
'''Hide the spinner and the prompt'''
raise NotImplementedError()

def update(self):
def update(self) -> None:
'''Show next spinner character'''
raise NotImplementedError()


class DummySpinner(AbstractSpinner):
'''Dummy spinner, does not do anything'''
def show(self, prompt):
def show(self, prompt: str) -> None:
pass

def hide(self):
def hide(self) -> None:
pass

def update(self):
def update(self) -> None:
pass


class QubesSpinner(AbstractSpinner):
'''Basic spinner

This spinner uses standard ASCII control characters'''
def __init__(self, *args, **kwargs):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.hidelen = 0
self.cub1 = '\b'

def show(self, prompt):
def show(self, prompt: str) -> None:
self.hidelen = len(prompt) + 2
self.stream.write('{} {}'.format(prompt, next(self.charset)))
self.stream.flush()

def hide(self):
def hide(self) -> None:
self.stream.write('\r' + ' ' * self.hidelen + '\r')
self.stream.flush()

def update(self):
def update(self) -> None:
self.stream.write(self.cub1 + next(self.charset))
self.stream.flush()

Expand All @@ -114,7 +116,7 @@ class QubesSpinnerEnterpriseEdition(QubesSpinner):

This is tty- and terminfo-aware spinner. Recommended.
'''
def __init__(self, stream, charset=None):
def __init__(self, stream: IO, charset: str | None=None):
# our Enterprise logic follows
self.stream_isatty = stream.isatty()
if charset is None:
Expand All @@ -126,18 +128,20 @@ def __init__(self, stream, charset=None):
try:
curses.setupterm()
self.has_terminfo = True
self.cub1 = curses.tigetstr('cub1').decode()
self.cub1 = typing.cast(bytes, curses.tigetstr('cub1')).decode()
except (curses.error, io.UnsupportedOperation):
# we are in very non-Enterprise environment
self.has_terminfo = False
else:
self.cub1 = ''

def hide(self):
def hide(self) -> None:
if self.stream_isatty:
hideseq = '\r' + ' ' * self.hidelen + '\r'
if self.has_terminfo:
hideseq_l = (curses.tigetstr('cr'), curses.tigetstr('clr_eol'))
hideseq_l = typing.cast(
tuple[bytes, bytes],
(curses.tigetstr('cr'), curses.tigetstr('clr_eol')))
if all(seq is not None for seq in hideseq_l):
hideseq = ''.join(seq.decode() for seq in hideseq_l)
else:
Expand Down
Loading