new typing style

This commit is contained in:
Maxim Devaev 2022-09-04 18:08:40 +03:00
parent 4b75221e94
commit ee3e224e39
129 changed files with 593 additions and 941 deletions

View File

@ -26,10 +26,6 @@ import csv
import textwrap import textwrap
import dataclasses import dataclasses
from typing import Set
from typing import List
from typing import Optional
import Xlib.keysymdef.latin1 import Xlib.keysymdef.latin1
import Xlib.keysymdef.miscellany import Xlib.keysymdef.miscellany
import Xlib.keysymdef.xf86 import Xlib.keysymdef.xf86
@ -77,11 +73,11 @@ class _KeyMapping:
usb_key: _UsbKey usb_key: _UsbKey
ps2_key: _Ps2Key ps2_key: _Ps2Key
at1_code: int at1_code: int
x11_keys: Set[_X11Key] x11_keys: set[_X11Key]
def _resolve_keysym(name: str) -> int: def _resolve_keysym(name: str) -> int:
code: Optional[int] = None code: (int | None) = None
for module in [ for module in [
Xlib.keysymdef.latin1, Xlib.keysymdef.latin1,
Xlib.keysymdef.miscellany, Xlib.keysymdef.miscellany,
@ -95,8 +91,8 @@ def _resolve_keysym(name: str) -> int:
return code return code
def _parse_x11_names(names: str) -> Set[_X11Key]: def _parse_x11_names(names: str) -> set[_X11Key]:
keys: Set[_X11Key] = set() keys: set[_X11Key] = set()
for name in filter(None, names.split(",")): for name in filter(None, names.split(",")):
shift = name.startswith("^") shift = name.startswith("^")
name = (name[1:] if shift else name) name = (name[1:] if shift else name)
@ -119,8 +115,8 @@ def _parse_ps2_key(key: str) -> _Ps2Key:
) )
def _read_keymap_csv(path: str) -> List[_KeyMapping]: def _read_keymap_csv(path: str) -> list[_KeyMapping]:
keymap: List[_KeyMapping] = [] keymap: list[_KeyMapping] = []
with open(path) as keymap_file: with open(path) as keymap_file:
for row in csv.DictReader(keymap_file): for row in csv.DictReader(keymap_file):
if len(row) >= 6: if len(row) >= 6:
@ -135,7 +131,7 @@ def _read_keymap_csv(path: str) -> List[_KeyMapping]:
return keymap return keymap
def _render_keymap(keymap: List[_KeyMapping], template_path: str, out_path: str) -> None: def _render_keymap(keymap: list[_KeyMapping], template_path: str, out_path: str) -> None:
with open(template_path) as template_file: with open(template_path) as template_file:
with open(out_path, "w") as out_file: with open(out_path, "w") as out_file:
template = textwrap.dedent(template_file.read()) template = textwrap.dedent(template_file.read())

View File

@ -24,10 +24,6 @@ import asyncio
import threading import threading
import dataclasses import dataclasses
from typing import Tuple
from typing import Dict
from typing import Optional
import gpiod import gpiod
from . import aiotools from . import aiotools
@ -55,7 +51,7 @@ class AioReader: # pylint: disable=too-many-instance-attributes
self, self,
path: str, path: str,
consumer: str, consumer: str,
pins: Dict[int, AioReaderPinParams], pins: dict[int, AioReaderPinParams],
notifier: aiotools.AioNotifier, notifier: aiotools.AioNotifier,
) -> None: ) -> None:
@ -64,12 +60,12 @@ class AioReader: # pylint: disable=too-many-instance-attributes
self.__pins = pins self.__pins = pins
self.__notifier = notifier self.__notifier = notifier
self.__values: Optional[Dict[int, _DebouncedValue]] = None self.__values: (dict[int, _DebouncedValue] | None) = None
self.__thread = threading.Thread(target=self.__run, daemon=True) self.__thread = threading.Thread(target=self.__run, daemon=True)
self.__stop_event = threading.Event() self.__stop_event = threading.Event()
self.__loop: Optional[asyncio.AbstractEventLoop] = None self.__loop: (asyncio.AbstractEventLoop | None) = None
def get(self, pin: int) -> bool: def get(self, pin: int) -> bool:
value = (self.__values[pin].get() if self.__values is not None else False) value = (self.__values[pin].get() if self.__values is not None else False)
@ -123,7 +119,7 @@ class AioReader: # pylint: disable=too-many-instance-attributes
for (pin, value) in zip(pins, lines.get_values()): for (pin, value) in zip(pins, lines.get_values()):
self.__values[pin].set(bool(value)) self.__values[pin].set(bool(value))
def __parse_event(self, event: gpiod.LineEvent) -> Tuple[int, int]: def __parse_event(self, event: gpiod.LineEvent) -> tuple[int, int]:
pin = event.source.offset() pin = event.source.offset()
if event.type == gpiod.LineEvent.RISING_EDGE: if event.type == gpiod.LineEvent.RISING_EDGE:
return (pin, 1) return (pin, 1)

View File

@ -22,8 +22,6 @@
import subprocess import subprocess
from typing import List
from .logging import get_logger from .logging import get_logger
from . import tools from . import tools
@ -31,7 +29,7 @@ from . import aioproc
# ===== # =====
async def remount(name: str, base_cmd: List[str], rw: bool) -> bool: async def remount(name: str, base_cmd: list[str], rw: bool) -> bool:
logger = get_logger(1) logger = get_logger(1)
mode = ("rw" if rw else "ro") mode = ("rw" if rw else "ro")
cmd = [ cmd = [

View File

@ -23,12 +23,9 @@
import multiprocessing import multiprocessing
import queue import queue
from typing import Tuple
from typing import Dict
from typing import Type from typing import Type
from typing import TypeVar from typing import TypeVar
from typing import Generic from typing import Generic
from typing import Optional
from . import aiotools from . import aiotools
@ -40,7 +37,7 @@ _QueueItemT = TypeVar("_QueueItemT")
async def queue_get_last( # pylint: disable=invalid-name async def queue_get_last( # pylint: disable=invalid-name
q: "multiprocessing.Queue[_QueueItemT]", q: "multiprocessing.Queue[_QueueItemT]",
timeout: float, timeout: float,
) -> Tuple[bool, Optional[_QueueItemT]]: ) -> tuple[bool, (_QueueItemT | None)]:
return (await aiotools.run_async(queue_get_last_sync, q, timeout)) return (await aiotools.run_async(queue_get_last_sync, q, timeout))
@ -48,7 +45,7 @@ async def queue_get_last( # pylint: disable=invalid-name
def queue_get_last_sync( # pylint: disable=invalid-name def queue_get_last_sync( # pylint: disable=invalid-name
q: "multiprocessing.Queue[_QueueItemT]", q: "multiprocessing.Queue[_QueueItemT]",
timeout: float, timeout: float,
) -> Tuple[bool, Optional[_QueueItemT]]: ) -> tuple[bool, (_QueueItemT | None)]:
try: try:
item = q.get(timeout=timeout) item = q.get(timeout=timeout)
@ -79,7 +76,7 @@ _SharedFlagT = TypeVar("_SharedFlagT", int, bool)
class AioSharedFlags(Generic[_SharedFlagT]): class AioSharedFlags(Generic[_SharedFlagT]):
def __init__( def __init__(
self, self,
initial: Dict[str, _SharedFlagT], initial: dict[str, _SharedFlagT],
notifier: AioProcessNotifier, notifier: AioProcessNotifier,
type: Type[_SharedFlagT]=bool, # pylint: disable=redefined-builtin type: Type[_SharedFlagT]=bool, # pylint: disable=redefined-builtin
) -> None: ) -> None:
@ -105,10 +102,10 @@ class AioSharedFlags(Generic[_SharedFlagT]):
if changed: if changed:
self.__notifier.notify() self.__notifier.notify()
async def get(self) -> Dict[str, _SharedFlagT]: async def get(self) -> dict[str, _SharedFlagT]:
return (await aiotools.run_async(self.__inner_get)) return (await aiotools.run_async(self.__inner_get))
def __inner_get(self) -> Dict[str, _SharedFlagT]: def __inner_get(self) -> dict[str, _SharedFlagT]:
with self.__lock: with self.__lock:
return { return {
key: self.__type(shared.value) key: self.__type(shared.value)

View File

@ -26,11 +26,6 @@ import asyncio
import asyncio.subprocess import asyncio.subprocess
import logging import logging
from typing import Tuple
from typing import List
from typing import Dict
from typing import Optional
import setproctitle import setproctitle
from .logging import get_logger from .logging import get_logger
@ -38,9 +33,9 @@ from .logging import get_logger
# ===== # =====
async def run_process( async def run_process(
cmd: List[str], cmd: list[str],
err_to_null: bool=False, err_to_null: bool=False,
env: Optional[Dict[str, str]]=None, env: (dict[str, str] | None)=None,
) -> asyncio.subprocess.Process: # pylint: disable=no-member ) -> asyncio.subprocess.Process: # pylint: disable=no-member
return (await asyncio.create_subprocess_exec( return (await asyncio.create_subprocess_exec(
@ -53,10 +48,10 @@ async def run_process(
async def read_process( async def read_process(
cmd: List[str], cmd: list[str],
err_to_null: bool=False, err_to_null: bool=False,
env: Optional[Dict[str, str]]=None, env: (dict[str, str] | None)=None,
) -> Tuple[asyncio.subprocess.Process, str]: # pylint: disable=no-member ) -> tuple[asyncio.subprocess.Process, str]: # pylint: disable=no-member
proc = await run_process(cmd, err_to_null, env) proc = await run_process(cmd, err_to_null, env)
(stdout, _) = await proc.communicate() (stdout, _) = await proc.communicate()
@ -64,9 +59,9 @@ async def read_process(
async def log_process( async def log_process(
cmd: List[str], cmd: list[str],
logger: logging.Logger, logger: logging.Logger,
env: Optional[Dict[str, str]]=None, env: (dict[str, str] | None)=None,
prefix: str="", prefix: str="",
) -> asyncio.subprocess.Process: # pylint: disable=no-member ) -> asyncio.subprocess.Process: # pylint: disable=no-member

View File

@ -29,22 +29,17 @@ import types
import typing import typing
from typing import Tuple
from typing import List
from typing import Set
from typing import Callable from typing import Callable
from typing import Awaitable from typing import Awaitable
from typing import Coroutine from typing import Coroutine
from typing import Type
from typing import TypeVar from typing import TypeVar
from typing import Optional
from typing import Any from typing import Any
from .logging import get_logger from .logging import get_logger
# ===== # =====
def run(coro: Coroutine, final: Optional[Coroutine]=None) -> None: def run(coro: Coroutine, final: (Coroutine | None)=None) -> None:
# https://github.com/aio-libs/aiohttp/blob/a1d4dac1d/aiohttp/web.py#L515 # https://github.com/aio-libs/aiohttp/blob/a1d4dac1d/aiohttp/web.py#L515
def sigint_handler() -> None: def sigint_handler() -> None:
@ -177,7 +172,7 @@ def create_deadly_task(name: str, coro: Coroutine) -> asyncio.Task:
async def stop_all_deadly_tasks() -> None: async def stop_all_deadly_tasks() -> None:
tasks: List[asyncio.Task] = [] tasks: list[asyncio.Task] = []
for task in asyncio.all_tasks(): for task in asyncio.all_tasks():
if getattr(task, _ATTR_DEADLY_TASK, False): if getattr(task, _ATTR_DEADLY_TASK, False):
tasks.append(task) tasks.append(task)
@ -200,7 +195,7 @@ async def wait_infinite() -> None:
await asyncio.sleep(3600) await asyncio.sleep(3600)
async def wait_first(*aws: Awaitable) -> Tuple[Set[asyncio.Task], Set[asyncio.Task]]: async def wait_first(*aws: Awaitable) -> tuple[set[asyncio.Task], set[asyncio.Task]]:
return (await asyncio.wait(list(aws), return_when=asyncio.FIRST_COMPLETED)) return (await asyncio.wait(list(aws), return_when=asyncio.FIRST_COMPLETED))
@ -235,7 +230,7 @@ class AioNotifier:
def notify(self) -> None: def notify(self) -> None:
self.__queue.put_nowait(None) self.__queue.put_nowait(None)
async def wait(self, timeout: Optional[float]=None) -> None: async def wait(self, timeout: (float | None)=None) -> None:
if timeout is None: if timeout is None:
await self.__queue.get() await self.__queue.get()
else: else:
@ -276,8 +271,8 @@ class AioStage:
class AioExclusiveRegion: class AioExclusiveRegion:
def __init__( def __init__(
self, self,
exc_type: Type[Exception], exc_type: type[Exception],
notifier: Optional[AioNotifier]=None, notifier: (AioNotifier | None)=None,
) -> None: ) -> None:
self.__exc_type = exc_type self.__exc_type = exc_type
@ -285,7 +280,7 @@ class AioExclusiveRegion:
self.__busy = False self.__busy = False
def get_exc_type(self) -> Type[Exception]: def get_exc_type(self) -> type[Exception]:
return self.__exc_type return self.__exc_type
def is_busy(self) -> bool: def is_busy(self) -> bool:
@ -313,7 +308,7 @@ class AioExclusiveRegion:
async def __aexit__( async def __aexit__(
self, self,
_exc_type: Type[BaseException], _exc_type: type[BaseException],
_exc: BaseException, _exc: BaseException,
_tb: types.TracebackType, _tb: types.TracebackType,
) -> None: ) -> None:

View File

@ -27,12 +27,6 @@ import argparse
import logging import logging
import logging.config import logging.config
from typing import Tuple
from typing import List
from typing import Dict
from typing import Type
from typing import Optional
import pygments import pygments
import pygments.lexers.data import pygments.lexers.data
import pygments.formatters import pygments.formatters
@ -109,14 +103,14 @@ from ..validators.hw import valid_otg_ethernet
# ===== # =====
def init( def init(
prog: Optional[str]=None, prog: (str | None)=None,
description: Optional[str]=None, description: (str | None)=None,
add_help: bool=True, add_help: bool=True,
check_run: bool=False, check_run: bool=False,
cli_logging: bool=False, cli_logging: bool=False,
argv: Optional[List[str]]=None, argv: (list[str] | None)=None,
**load: bool, **load: bool,
) -> Tuple[argparse.ArgumentParser, List[str], Section]: ) -> tuple[argparse.ArgumentParser, list[str], Section]:
argv = (argv or sys.argv) argv = (argv or sys.argv)
assert len(argv) > 0 assert len(argv) > 0
@ -170,10 +164,10 @@ def init(
# ===== # =====
def _init_config(config_path: str, override_options: List[str], **load_flags: bool) -> Section: def _init_config(config_path: str, override_options: list[str], **load_flags: bool) -> Section:
config_path = os.path.expanduser(config_path) config_path = os.path.expanduser(config_path)
try: try:
raw_config: Dict = load_yaml_file(config_path) raw_config: dict = load_yaml_file(config_path)
except Exception as err: except Exception as err:
raise SystemExit(f"ConfigError: Can't read config file {config_path!r}:\n{tools.efmt(err)}") raise SystemExit(f"ConfigError: Can't read config file {config_path!r}:\n{tools.efmt(err)}")
if not isinstance(raw_config, dict): if not isinstance(raw_config, dict):
@ -194,7 +188,7 @@ def _init_config(config_path: str, override_options: List[str], **load_flags: bo
raise SystemExit(f"ConfigError: {err}") raise SystemExit(f"ConfigError: {err}")
def _patch_raw(raw_config: Dict) -> None: # pylint: disable=too-many-branches def _patch_raw(raw_config: dict) -> None: # pylint: disable=too-many-branches
if isinstance(raw_config.get("otg"), dict): if isinstance(raw_config.get("otg"), dict):
for (old, new) in [ for (old, new) in [
("msd", "msd"), ("msd", "msd"),
@ -250,9 +244,9 @@ def _patch_raw(raw_config: Dict) -> None: # pylint: disable=too-many-branches
def _patch_dynamic( # pylint: disable=too-many-locals def _patch_dynamic( # pylint: disable=too-many-locals
raw_config: Dict, raw_config: dict,
config: Section, config: Section,
scheme: Dict, scheme: dict,
load_auth: bool=False, load_auth: bool=False,
load_hid: bool=False, load_hid: bool=False,
load_atx: bool=False, load_atx: bool=False,
@ -279,7 +273,7 @@ def _patch_dynamic( # pylint: disable=too-many-locals
if load_gpio: if load_gpio:
driver: str driver: str
drivers: Dict[str, Type[BaseUserGpioDriver]] = {} # Name to drivers drivers: dict[str, type[BaseUserGpioDriver]] = {} # Name to drivers
for (driver, params) in { # type: ignore for (driver, params) in { # type: ignore
"__gpio__": {}, "__gpio__": {},
**tools.rget(raw_config, "kvmd", "gpio", "drivers"), **tools.rget(raw_config, "kvmd", "gpio", "drivers"),
@ -343,7 +337,7 @@ def _dump_config(config: Section) -> None:
print(dump) print(dump)
def _get_config_scheme() -> Dict: def _get_config_scheme() -> dict:
return { return {
"logging": Option({}), "logging": Option({}),

View File

@ -24,9 +24,6 @@ import os
import signal import signal
import time import time
from typing import List
from typing import Optional
import psutil import psutil
from ...logging import get_logger from ...logging import get_logger
@ -74,7 +71,7 @@ def _remove_sockets(config: Section) -> None:
# ===== # =====
def main(argv: Optional[List[str]]=None) -> None: def main(argv: (list[str] | None)=None) -> None:
config = init( config = init(
prog="kvmd-cleanup", prog="kvmd-cleanup",
description="Kill KVMD and clear resources", description="Kill KVMD and clear resources",

View File

@ -28,11 +28,9 @@ import contextlib
import argparse import argparse
import time import time
from typing import List
from typing import IO from typing import IO
from typing import Generator from typing import Generator
from typing import Callable from typing import Callable
from typing import Optional
from ...validators.basic import valid_bool from ...validators.basic import valid_bool
from ...validators.basic import valid_int_f0 from ...validators.basic import valid_int_f0
@ -180,7 +178,7 @@ def _make_format_hex(size: int) -> Callable[[int], str]:
# ===== # =====
def main(argv: Optional[List[str]]=None) -> None: # pylint: disable=too-many-branches def main(argv: (list[str] | None)=None) -> None: # pylint: disable=too-many-branches
# (parent_parser, argv, _) = init( # (parent_parser, argv, _) = init(
# add_help=False, # add_help=False,
# argv=argv, # argv=argv,

View File

@ -28,9 +28,7 @@ import contextlib
import textwrap import textwrap
import argparse import argparse
from typing import List
from typing import Generator from typing import Generator
from typing import Optional
import passlib.apache import passlib.apache
@ -125,7 +123,7 @@ def _cmd_delete(config: Section, options: argparse.Namespace) -> None:
# ===== # =====
def main(argv: Optional[List[str]]=None) -> None: def main(argv: (list[str] | None)=None) -> None:
(parent_parser, argv, config) = init( (parent_parser, argv, config) = init(
add_help=False, add_help=False,
cli_logging=True, cli_logging=True,

View File

@ -20,9 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import List
from typing import Optional
from ...clients.kvmd import KvmdClient from ...clients.kvmd import KvmdClient
from ... import htclient from ... import htclient
@ -34,7 +31,7 @@ from .server import IpmiServer
# ===== # =====
def main(argv: Optional[List[str]]=None) -> None: def main(argv: (list[str] | None)=None) -> None:
config = init( config = init(
prog="kvmd-ipmi", prog="kvmd-ipmi",
description="IPMI to KVMD proxy", description="IPMI to KVMD proxy",

View File

@ -22,9 +22,6 @@
import dataclasses import dataclasses
from typing import List
from typing import Dict
# ===== # =====
class IpmiPasswdError(Exception): class IpmiPasswdError(Exception):
@ -55,8 +52,8 @@ class IpmiAuthManager:
def get_credentials(self, ipmi_user: str) -> IpmiUserCredentials: def get_credentials(self, ipmi_user: str) -> IpmiUserCredentials:
return self.__credentials[ipmi_user] return self.__credentials[ipmi_user]
def __parse_passwd_file(self, lines: List[str]) -> Dict[str, IpmiUserCredentials]: def __parse_passwd_file(self, lines: list[str]) -> dict[str, IpmiUserCredentials]:
credentials: Dict[str, IpmiUserCredentials] = {} credentials: dict[str, IpmiUserCredentials] = {}
for (lineno, line) in enumerate(lines): for (lineno, line) in enumerate(lines):
if len(line.strip()) == 0 or line.lstrip().startswith("#"): if len(line.strip()) == 0 or line.lstrip().startswith("#"):
continue continue

View File

@ -28,9 +28,6 @@ import multiprocessing
import functools import functools
import queue import queue
from typing import Dict
from typing import Optional
import aiohttp import aiohttp
import serial import serial
@ -83,8 +80,8 @@ class IpmiServer(BaseIpmiServer): # pylint: disable=too-many-instance-attribute
self.__sol_proxy_port = (sol_proxy_port or port) self.__sol_proxy_port = (sol_proxy_port or port)
self.__sol_lock = threading.Lock() self.__sol_lock = threading.Lock()
self.__sol_console: Optional[IpmiConsole] = None self.__sol_console: (IpmiConsole | None) = None
self.__sol_thread: Optional[threading.Thread] = None self.__sol_thread: (threading.Thread | None) = None
self.__sol_stop = False self.__sol_stop = False
def run(self) -> None: def run(self) -> None:
@ -100,7 +97,7 @@ class IpmiServer(BaseIpmiServer): # pylint: disable=too-many-instance-attribute
# ===== # =====
def handle_raw_request(self, request: Dict, session: IpmiServerSession) -> None: def handle_raw_request(self, request: dict, session: IpmiServerSession) -> None:
handler = { handler = {
(6, 1): (lambda _, session: self.send_device_id(session)), # Get device ID (6, 1): (lambda _, session: self.send_device_id(session)), # Get device ID
(6, 7): self.__get_power_state_handler, # Power state (6, 7): self.__get_power_state_handler, # Power state
@ -125,13 +122,13 @@ class IpmiServer(BaseIpmiServer): # pylint: disable=too-many-instance-attribute
# ===== # =====
def __get_power_state_handler(self, _: Dict, session: IpmiServerSession) -> None: def __get_power_state_handler(self, _: dict, session: IpmiServerSession) -> None:
# https://github.com/arcress0/ipmiutil/blob/e2f6e95127d22e555f959f136d9bb9543c763896/util/ireset.c#L654 # https://github.com/arcress0/ipmiutil/blob/e2f6e95127d22e555f959f136d9bb9543c763896/util/ireset.c#L654
result = self.__make_request(session, "atx.get_state() [power]", "atx.get_state") result = self.__make_request(session, "atx.get_state() [power]", "atx.get_state")
data = [(0 if result["leds"]["power"] else 5)] data = [(0 if result["leds"]["power"] else 5)]
session.send_ipmi_response(data=data) session.send_ipmi_response(data=data)
def __get_selftest_status_handler(self, _: Dict, session: IpmiServerSession) -> None: def __get_selftest_status_handler(self, _: dict, session: IpmiServerSession) -> None:
# https://github.com/arcress0/ipmiutil/blob/e2f6e95127d22e555f959f136d9bb9543c763896/util/ihealth.c#L858 # https://github.com/arcress0/ipmiutil/blob/e2f6e95127d22e555f959f136d9bb9543c763896/util/ihealth.c#L858
data = [0x0055] data = [0x0055]
try: try:
@ -140,12 +137,12 @@ class IpmiServer(BaseIpmiServer): # pylint: disable=too-many-instance-attribute
data = [0] data = [0]
session.send_ipmi_response(data=data) session.send_ipmi_response(data=data)
def __get_chassis_status_handler(self, _: Dict, session: IpmiServerSession) -> None: def __get_chassis_status_handler(self, _: dict, session: IpmiServerSession) -> None:
result = self.__make_request(session, "atx.get_state() [chassis]", "atx.get_state") result = self.__make_request(session, "atx.get_state() [chassis]", "atx.get_state")
data = [int(result["leds"]["power"]), 0, 0] data = [int(result["leds"]["power"]), 0, 0]
session.send_ipmi_response(data=data) session.send_ipmi_response(data=data)
def __chassis_control_handler(self, request: Dict, session: IpmiServerSession) -> None: def __chassis_control_handler(self, request: dict, session: IpmiServerSession) -> None:
action = { action = {
0: "off_hard", 0: "off_hard",
1: "on", 1: "on",
@ -179,7 +176,7 @@ class IpmiServer(BaseIpmiServer): # pylint: disable=too-many-instance-attribute
# ===== # =====
def __activate_sol_handler(self, _: Dict, session: IpmiServerSession) -> None: def __activate_sol_handler(self, _: dict, session: IpmiServerSession) -> None:
with self.__sol_lock: with self.__sol_lock:
if not self.__sol_device_path: if not self.__sol_device_path:
session.send_ipmi_response(code=0x81) # SOL disabled session.send_ipmi_response(code=0x81) # SOL disabled
@ -198,7 +195,7 @@ class IpmiServer(BaseIpmiServer): # pylint: disable=too-many-instance-attribute
]) ])
self.__start_sol_worker(session) self.__start_sol_worker(session)
def __deactivate_sol_handler(self, _: Dict, session: IpmiServerSession) -> None: def __deactivate_sol_handler(self, _: dict, session: IpmiServerSession) -> None:
with self.__sol_lock: with self.__sol_lock:
if not self.__sol_device_path: if not self.__sol_device_path:
session.send_ipmi_response(code=0x81) session.send_ipmi_response(code=0x81)

View File

@ -20,16 +20,13 @@
# ========================================================================== # # ========================================================================== #
from typing import List
from typing import Optional
from .. import init from .. import init
from .runner import JanusRunner from .runner import JanusRunner
# ===== # =====
def main(argv: Optional[List[str]]=None) -> None: def main(argv: (list[str] | None)=None) -> None:
config = init( config = init(
prog="kvmd-Janus", prog="kvmd-Janus",
description="Janus WebRTC Gateway Runner", description="Janus WebRTC Gateway Runner",

View File

@ -3,10 +3,6 @@ import asyncio.subprocess
import socket import socket
import dataclasses import dataclasses
from typing import Tuple
from typing import List
from typing import Optional
import netifaces import netifaces
from ... import tools from ... import tools
@ -42,9 +38,9 @@ class JanusRunner: # pylint: disable=too-many-instance-attributes
check_retries: int, check_retries: int,
check_retries_delay: float, check_retries_delay: float,
cmd: List[str], cmd: list[str],
cmd_remove: List[str], cmd_remove: list[str],
cmd_append: List[str], cmd_append: list[str],
) -> None: ) -> None:
self.__stun = Stun(stun_host, stun_port, stun_timeout, stun_retries, stun_retries_delay) self.__stun = Stun(stun_host, stun_port, stun_timeout, stun_retries, stun_retries_delay)
@ -55,8 +51,8 @@ class JanusRunner: # pylint: disable=too-many-instance-attributes
self.__cmd = tools.build_cmd(cmd, cmd_remove, cmd_append) self.__cmd = tools.build_cmd(cmd, cmd_remove, cmd_append)
self.__janus_task: Optional[asyncio.Task] = None self.__janus_task: (asyncio.Task | None) = None
self.__janus_proc: Optional[asyncio.subprocess.Process] = None # pylint: disable=no-member self.__janus_proc: (asyncio.subprocess.Process | None) = None # pylint: disable=no-member
def run(self) -> None: def run(self) -> None:
logger = get_logger(0) logger = get_logger(0)
@ -68,7 +64,7 @@ class JanusRunner: # pylint: disable=too-many-instance-attributes
async def __run(self) -> None: async def __run(self) -> None:
logger = get_logger(0) logger = get_logger(0)
prev_netcfg: Optional[_Netcfg] = None prev_netcfg: (_Netcfg | None) = None
while True: while True:
retry = 0 retry = 0
netcfg = _Netcfg() netcfg = _Netcfg()
@ -117,7 +113,7 @@ class JanusRunner: # pylint: disable=too-many-instance-attributes
get_logger().error("Can't get default IP: %s", tools.efmt(err)) get_logger().error("Can't get default IP: %s", tools.efmt(err))
return "" return ""
async def __get_stun_info(self, src_ip: str) -> Tuple[Stun, Tuple[str, str]]: async def __get_stun_info(self, src_ip: str) -> tuple[Stun, tuple[str, str]]:
try: try:
return (self.__stun, (await self.__stun.get_info(src_ip, 0))) return (self.__stun, (await self.__stun.get_info(src_ip, 0)))
except Exception as err: except Exception as err:

View File

@ -5,10 +5,6 @@ import struct
import secrets import secrets
import dataclasses import dataclasses
from typing import Tuple
from typing import Dict
from typing import Optional
from ... import tools from ... import tools
from ... import aiotools from ... import aiotools
@ -25,9 +21,9 @@ class StunAddress:
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class StunResponse: class StunResponse:
ok: bool ok: bool
ext: Optional[StunAddress] = dataclasses.field(default=None) ext: (StunAddress | None) = dataclasses.field(default=None)
src: Optional[StunAddress] = dataclasses.field(default=None) src: (StunAddress | None) = dataclasses.field(default=None)
changed: Optional[StunAddress] = dataclasses.field(default=None) changed: (StunAddress | None) = dataclasses.field(default=None)
class StunNatType: class StunNatType:
@ -60,9 +56,9 @@ class Stun:
self.__retries = retries self.__retries = retries
self.__retries_delay = retries_delay self.__retries_delay = retries_delay
self.__sock: Optional[socket.socket] = None self.__sock: (socket.socket | None) = None
async def get_info(self, src_ip: str, src_port: int) -> Tuple[str, str]: async def get_info(self, src_ip: str, src_port: int) -> tuple[str, str]:
(family, _, _, _, addr) = socket.getaddrinfo(src_ip, src_port, type=socket.SOCK_DGRAM)[0] (family, _, _, _, addr) = socket.getaddrinfo(src_ip, src_port, type=socket.SOCK_DGRAM)[0]
try: try:
with socket.socket(family, socket.SOCK_DGRAM) as self.__sock: with socket.socket(family, socket.SOCK_DGRAM) as self.__sock:
@ -74,7 +70,7 @@ class Stun:
finally: finally:
self.__sock = None self.__sock = None
async def __get_nat_type(self, src_ip: str) -> Tuple[str, StunResponse]: # pylint: disable=too-many-return-statements async def __get_nat_type(self, src_ip: str) -> tuple[str, StunResponse]: # pylint: disable=too-many-return-statements
first = await self.__make_request("First probe") first = await self.__make_request("First probe")
if not first.ok: if not first.ok:
return (StunNatType.BLOCKED, first) return (StunNatType.BLOCKED, first)
@ -128,7 +124,7 @@ class Stun:
ctx, self.__retries, error) ctx, self.__retries, error)
return StunResponse(ok=False) return StunResponse(ok=False)
parsed: Dict[str, StunAddress] = {} parsed: dict[str, StunAddress] = {}
offset = 0 offset = 0
remaining = len(response) remaining = len(response)
while remaining > 0: while remaining > 0:
@ -146,7 +142,7 @@ class Stun:
remaining -= (4 + attr_len) remaining -= (4 + attr_len)
return StunResponse(ok=True, **parsed) return StunResponse(ok=True, **parsed)
async def __inner_make_request(self, trans_id: bytes, request: bytes, host: str, port: int) -> Tuple[bytes, str]: async def __inner_make_request(self, trans_id: bytes, request: bytes, host: str, port: int) -> tuple[bytes, str]:
assert self.__sock is not None assert self.__sock is not None
request = struct.pack(">HH", 0x0001, len(request)) + trans_id + request # Bind Request request = struct.pack(">HH", 0x0001, len(request)) + trans_id + request # Bind Request

View File

@ -20,9 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import List
from typing import Optional
from ...logging import get_logger from ...logging import get_logger
from ...plugins.hid import get_hid_class from ...plugins.hid import get_hid_class
@ -42,7 +39,7 @@ from .server import KvmdServer
# ===== # =====
def main(argv: Optional[List[str]]=None) -> None: def main(argv: (list[str] | None)=None) -> None:
config = init( config = init(
prog="kvmd", prog="kvmd",
description="The main PiKVM daemon", description="The main PiKVM daemon",

View File

@ -23,7 +23,6 @@
import asyncio import asyncio
from typing import Any from typing import Any
from typing import List
from aiohttp.web import Request from aiohttp.web import Request
from aiohttp.web import Response from aiohttp.web import Response
@ -56,7 +55,7 @@ class ExportApi:
self.__info_manager.get_submanager("fan").get_state(), self.__info_manager.get_submanager("fan").get_state(),
self.__user_gpio.get_state(), self.__user_gpio.get_state(),
]) ])
rows: List[str] = [] rows: list[str] = []
self.__append_prometheus_rows(rows, atx_state["enabled"], "pikvm_atx_enabled") self.__append_prometheus_rows(rows, atx_state["enabled"], "pikvm_atx_enabled")
self.__append_prometheus_rows(rows, atx_state["leds"]["power"], "pikvm_atx_power") self.__append_prometheus_rows(rows, atx_state["leds"]["power"], "pikvm_atx_power")
@ -71,7 +70,7 @@ class ExportApi:
return Response(text="\n".join(rows)) return Response(text="\n".join(rows))
def __append_prometheus_rows(self, rows: List[str], value: Any, path: str) -> None: def __append_prometheus_rows(self, rows: list[str], value: Any, path: str) -> None:
if isinstance(value, bool): if isinstance(value, bool):
value = int(value) value = int(value)
if isinstance(value, (int, float)): if isinstance(value, (int, float)):

View File

@ -24,10 +24,6 @@ import os
import stat import stat
import functools import functools
from typing import Tuple
from typing import List
from typing import Dict
from typing import Set
from typing import Callable from typing import Callable
from aiohttp.web import Request from aiohttp.web import Request
@ -64,10 +60,10 @@ class HidApi:
hid: BaseHid, hid: BaseHid,
keymap_path: str, keymap_path: str,
ignore_keys: List[str], ignore_keys: list[str],
mouse_x_range: Tuple[int, int], mouse_x_range: tuple[int, int],
mouse_y_range: Tuple[int, int], mouse_y_range: tuple[int, int],
) -> None: ) -> None:
self.__hid = hid self.__hid = hid
@ -112,8 +108,8 @@ class HidApi:
# ===== # =====
async def get_keymaps(self) -> Dict: # Ugly hack to generate hid_keymaps_state (see server.py) async def get_keymaps(self) -> dict: # Ugly hack to generate hid_keymaps_state (see server.py)
keymaps: Set[str] = set() keymaps: set[str] = set()
for keymap_name in os.listdir(self.__keymaps_dir_path): for keymap_name in os.listdir(self.__keymaps_dir_path):
path = os.path.join(self.__keymaps_dir_path, keymap_name) path = os.path.join(self.__keymaps_dir_path, keymap_name)
if os.access(path, os.R_OK) and stat.S_ISREG(os.stat(path).st_mode): if os.access(path, os.R_OK) and stat.S_ISREG(os.stat(path).st_mode):
@ -139,7 +135,7 @@ class HidApi:
self.__hid.send_key_events(text_to_web_keys(text, symmap)) self.__hid.send_key_events(text_to_web_keys(text, symmap))
return make_json_response() return make_json_response()
def __ensure_symmap(self, keymap_name: str) -> Dict[int, Dict[int, str]]: def __ensure_symmap(self, keymap_name: str) -> dict[int, dict[int, str]]:
keymap_name = valid_printable_filename(keymap_name, "keymap") keymap_name = valid_printable_filename(keymap_name, "keymap")
path = os.path.join(self.__keymaps_dir_path, keymap_name) path = os.path.join(self.__keymaps_dir_path, keymap_name)
try: try:
@ -151,14 +147,14 @@ class HidApi:
return self.__inner_ensure_symmap(path, st.st_mtime) return self.__inner_ensure_symmap(path, st.st_mtime)
@functools.lru_cache(maxsize=10) @functools.lru_cache(maxsize=10)
def __inner_ensure_symmap(self, path: str, mtime: int) -> Dict[int, Dict[int, str]]: def __inner_ensure_symmap(self, path: str, mtime: int) -> dict[int, dict[int, str]]:
_ = mtime # For LRU _ = mtime # For LRU
return build_symmap(path) return build_symmap(path)
# ===== # =====
@exposed_ws("key") @exposed_ws("key")
async def __ws_key_handler(self, _: WsSession, event: Dict) -> None: async def __ws_key_handler(self, _: WsSession, event: dict) -> None:
try: try:
key = valid_hid_key(event["key"]) key = valid_hid_key(event["key"])
state = valid_bool(event["state"]) state = valid_bool(event["state"])
@ -168,7 +164,7 @@ class HidApi:
self.__hid.send_key_events([(key, state)]) self.__hid.send_key_events([(key, state)])
@exposed_ws("mouse_button") @exposed_ws("mouse_button")
async def __ws_mouse_button_handler(self, _: WsSession, event: Dict) -> None: async def __ws_mouse_button_handler(self, _: WsSession, event: dict) -> None:
try: try:
button = valid_hid_mouse_button(event["button"]) button = valid_hid_mouse_button(event["button"])
state = valid_bool(event["state"]) state = valid_bool(event["state"])
@ -177,7 +173,7 @@ class HidApi:
self.__hid.send_mouse_button_event(button, state) self.__hid.send_mouse_button_event(button, state)
@exposed_ws("mouse_move") @exposed_ws("mouse_move")
async def __ws_mouse_move_handler(self, _: WsSession, event: Dict) -> None: async def __ws_mouse_move_handler(self, _: WsSession, event: dict) -> None:
try: try:
to_x = valid_hid_mouse_move(event["to"]["x"]) to_x = valid_hid_mouse_move(event["to"]["x"])
to_y = valid_hid_mouse_move(event["to"]["y"]) to_y = valid_hid_mouse_move(event["to"]["y"])
@ -186,14 +182,14 @@ class HidApi:
self.__send_mouse_move_event_remapped(to_x, to_y) self.__send_mouse_move_event_remapped(to_x, to_y)
@exposed_ws("mouse_relative") @exposed_ws("mouse_relative")
async def __ws_mouse_relative_handler(self, _: WsSession, event: Dict) -> None: async def __ws_mouse_relative_handler(self, _: WsSession, event: dict) -> None:
self.__process_delta_ws_request(event, self.__hid.send_mouse_relative_event) self.__process_delta_ws_request(event, self.__hid.send_mouse_relative_event)
@exposed_ws("mouse_wheel") @exposed_ws("mouse_wheel")
async def __ws_mouse_wheel_handler(self, _: WsSession, event: Dict) -> None: async def __ws_mouse_wheel_handler(self, _: WsSession, event: dict) -> None:
self.__process_delta_ws_request(event, self.__hid.send_mouse_wheel_event) self.__process_delta_ws_request(event, self.__hid.send_mouse_wheel_event)
def __process_delta_ws_request(self, event: Dict, handler: Callable[[int, int], None]) -> None: def __process_delta_ws_request(self, event: dict, handler: Callable[[int, int], None]) -> None:
try: try:
raw_delta = event["delta"] raw_delta = event["delta"]
deltas = [ deltas = [

View File

@ -22,8 +22,6 @@
import asyncio import asyncio
from typing import List
from aiohttp.web import Request from aiohttp.web import Request
from aiohttp.web import Response from aiohttp.web import Response
@ -51,7 +49,7 @@ class InfoApi:
]))) ])))
return make_json_response(results) return make_json_response(results)
def __valid_info_fields(self, request: Request) -> List[str]: def __valid_info_fields(self, request: Request) -> list[str]:
subs = self.__info_manager.get_subs() subs = self.__info_manager.get_subs()
return sorted(valid_info_fields( return sorted(valid_info_fields(
arg=request.query.get("fields", ",".join(subs)), arg=request.query.get("fields", ",".join(subs)),

View File

@ -23,10 +23,7 @@
import lzma import lzma
import time import time
from typing import Dict
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional
from typing import Union
import aiohttp import aiohttp
import zstandard import zstandard
@ -153,7 +150,7 @@ class MsdApi:
return make_json_response(self.__make_write_info(name, size, written)) return make_json_response(self.__make_write_info(name, size, written))
@exposed_http("POST", "/msd/write_remote") @exposed_http("POST", "/msd/write_remote")
async def __write_remote_handler(self, request: Request) -> Union[Response, StreamResponse]: # pylint: disable=too-many-locals async def __write_remote_handler(self, request: Request) -> (Response | StreamResponse): # pylint: disable=too-many-locals
url = valid_url(request.query.get("url")) url = valid_url(request.query.get("url"))
insecure = valid_bool(request.query.get("insecure", False)) insecure = valid_bool(request.query.get("insecure", False))
timeout = valid_float_f01(request.query.get("timeout", 10.0)) timeout = valid_float_f01(request.query.get("timeout", 10.0))
@ -161,7 +158,7 @@ class MsdApi:
name = "" name = ""
size = written = 0 size = written = 0
response: Optional[StreamResponse] = None response: (StreamResponse | None) = None
async def stream_write_info() -> None: async def stream_write_info() -> None:
assert response is not None assert response is not None
@ -206,11 +203,11 @@ class MsdApi:
return make_json_exception(err, 400) return make_json_exception(err, 400)
raise raise
def __get_remove_incomplete(self, request: Request) -> Optional[bool]: def __get_remove_incomplete(self, request: Request) -> (bool | None):
flag: Optional[str] = request.query.get("remove_incomplete") flag: (str | None) = request.query.get("remove_incomplete")
return (valid_bool(flag) if flag is not None else None) return (valid_bool(flag) if flag is not None else None)
def __make_write_info(self, name: str, size: int, written: int) -> Dict: def __make_write_info(self, name: str, size: int, written: int) -> dict:
return {"image": {"name": name, "size": size, "written": written}} return {"image": {"name": name, "size": size, "written": written}}
# ===== # =====

View File

@ -20,9 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import List
from typing import Dict
from aiohttp.web import Request from aiohttp.web import Request
from aiohttp.web import Response from aiohttp.web import Response
@ -102,10 +99,10 @@ class StreamerApi:
# ===== # =====
async def get_ocr(self) -> Dict: # XXX: Ugly hack async def get_ocr(self) -> dict: # XXX: Ugly hack
enabled = self.__ocr.is_available() enabled = self.__ocr.is_available()
default: List[str] = [] default: list[str] = []
available: List[str] = [] available: list[str] = []
if enabled: if enabled:
default = self.__ocr.get_default_langs() default = self.__ocr.get_default_langs()
available = self.__ocr.get_available_langs() available = self.__ocr.get_available_langs()

View File

@ -22,10 +22,6 @@
import secrets import secrets
from typing import List
from typing import Dict
from typing import Optional
from ...logging import get_logger from ...logging import get_logger
from ... import aiotools from ... import aiotools
@ -40,12 +36,12 @@ class AuthManager:
self, self,
internal_type: str, internal_type: str,
internal_kwargs: Dict, internal_kwargs: dict,
external_type: str, external_type: str,
external_kwargs: Dict, external_kwargs: dict,
force_internal_users: List[str], force_internal_users: list[str],
enabled: bool, enabled: bool,
) -> None: ) -> None:
@ -53,19 +49,19 @@ class AuthManager:
if not enabled: if not enabled:
get_logger().warning("AUTHORIZATION IS DISABLED") get_logger().warning("AUTHORIZATION IS DISABLED")
self.__internal_service: Optional[BaseAuthService] = None self.__internal_service: (BaseAuthService | None) = None
if enabled: if enabled:
self.__internal_service = get_auth_service_class(internal_type)(**internal_kwargs) self.__internal_service = get_auth_service_class(internal_type)(**internal_kwargs)
get_logger().info("Using internal auth service %r", self.__internal_service.get_plugin_name()) get_logger().info("Using internal auth service %r", self.__internal_service.get_plugin_name())
self.__external_service: Optional[BaseAuthService] = None self.__external_service: (BaseAuthService | None) = None
if enabled and external_type: if enabled and external_type:
self.__external_service = get_auth_service_class(external_type)(**external_kwargs) self.__external_service = get_auth_service_class(external_type)(**external_kwargs)
get_logger().info("Using external auth service %r", self.__external_service.get_plugin_name()) get_logger().info("Using external auth service %r", self.__external_service.get_plugin_name())
self.__force_internal_users = force_internal_users self.__force_internal_users = force_internal_users
self.__tokens: Dict[str, str] = {} # {token: user} self.__tokens: dict[str, str] = {} # {token: user}
def is_auth_enabled(self) -> bool: def is_auth_enabled(self) -> bool:
return self.__enabled return self.__enabled
@ -88,7 +84,7 @@ class AuthManager:
get_logger().error("Got access denied for user %r from auth service %r", user, service.get_plugin_name()) get_logger().error("Got access denied for user %r from auth service %r", user, service.get_plugin_name())
return ok return ok
async def login(self, user: str, passwd: str) -> Optional[str]: async def login(self, user: str, passwd: str) -> (str | None):
assert user == user.strip() assert user == user.strip()
assert user assert user
assert self.__enabled assert self.__enabled
@ -109,7 +105,7 @@ class AuthManager:
if user: if user:
get_logger().info("Logged out user %r", user) get_logger().info("Logged out user %r", user)
def check(self, token: str) -> Optional[str]: def check(self, token: str) -> (str | None):
assert self.__enabled assert self.__enabled
return self.__tokens.get(token) return self.__tokens.get(token)

View File

@ -20,8 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import Set
from ....yamlconf import Section from ....yamlconf import Section
from .base import BaseInfoSubmanager from .base import BaseInfoSubmanager
@ -45,7 +43,7 @@ class InfoManager:
"fan": FanInfoSubmanager(**config.kvmd.info.fan._unpack()), "fan": FanInfoSubmanager(**config.kvmd.info.fan._unpack()),
} }
def get_subs(self) -> Set[str]: def get_subs(self) -> set[str]:
return set(self.__subs) return set(self.__subs)
def get_submanager(self, name: str) -> BaseInfoSubmanager: def get_submanager(self, name: str) -> BaseInfoSubmanager:

View File

@ -20,8 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import Dict
from .base import BaseInfoSubmanager from .base import BaseInfoSubmanager
@ -30,5 +28,5 @@ class AuthInfoSubmanager(BaseInfoSubmanager):
def __init__(self, enabled: bool) -> None: def __init__(self, enabled: bool) -> None:
self.__enabled = enabled self.__enabled = enabled
async def get_state(self) -> Dict: async def get_state(self) -> dict:
return {"enabled": self.__enabled} return {"enabled": self.__enabled}

View File

@ -20,11 +20,7 @@
# ========================================================================== # # ========================================================================== #
from typing import Dict
from typing import Optional
# ===== # =====
class BaseInfoSubmanager: class BaseInfoSubmanager:
async def get_state(self) -> Optional[Dict]: async def get_state(self) -> (dict | None):
raise NotImplementedError raise NotImplementedError

View File

@ -24,9 +24,6 @@ import os
import re import re
import asyncio import asyncio
from typing import Dict
from typing import Optional
from ....logging import get_logger from ....logging import get_logger
from ....yamlconf import Section from ....yamlconf import Section
@ -45,7 +42,7 @@ class ExtrasInfoSubmanager(BaseInfoSubmanager):
def __init__(self, global_config: Section) -> None: def __init__(self, global_config: Section) -> None:
self.__global_config = global_config self.__global_config = global_config
async def get_state(self) -> Optional[Dict]: async def get_state(self) -> (dict | None):
try: try:
sui = sysunit.SystemdUnitInfo() sui = sysunit.SystemdUnitInfo()
await sui.open() await sui.open()
@ -53,7 +50,7 @@ class ExtrasInfoSubmanager(BaseInfoSubmanager):
get_logger(0).error("Can't open systemd bus to get extras state: %s", tools.efmt(err)) get_logger(0).error("Can't open systemd bus to get extras state: %s", tools.efmt(err))
sui = None sui = None
try: try:
extras: Dict[str, Dict] = {} extras: dict[str, dict] = {}
for extra in (await asyncio.gather(*[ for extra in (await asyncio.gather(*[
self.__read_extra(sui, name) self.__read_extra(sui, name)
for name in os.listdir(self.__get_extras_path()) for name in os.listdir(self.__get_extras_path())
@ -71,7 +68,7 @@ class ExtrasInfoSubmanager(BaseInfoSubmanager):
def __get_extras_path(self, *parts: str) -> str: def __get_extras_path(self, *parts: str) -> str:
return os.path.join(self.__global_config.kvmd.info.extras, *parts) return os.path.join(self.__global_config.kvmd.info.extras, *parts)
async def __read_extra(self, sui: Optional[sysunit.SystemdUnitInfo], name: str) -> Dict: async def __read_extra(self, sui: (sysunit.SystemdUnitInfo | None), name: str) -> dict:
try: try:
extra = await aiotools.run_async(load_yaml_file, self.__get_extras_path(name, "manifest.yaml")) extra = await aiotools.run_async(load_yaml_file, self.__get_extras_path(name, "manifest.yaml"))
await self.__rewrite_app_daemon(sui, extra) await self.__rewrite_app_daemon(sui, extra)
@ -81,7 +78,7 @@ class ExtrasInfoSubmanager(BaseInfoSubmanager):
get_logger(0).exception("Can't read extra %r", name) get_logger(0).exception("Can't read extra %r", name)
return {} return {}
async def __rewrite_app_daemon(self, sui: Optional[sysunit.SystemdUnitInfo], extra: Dict) -> None: async def __rewrite_app_daemon(self, sui: (sysunit.SystemdUnitInfo | None), extra: dict) -> None:
daemon = extra.get("daemon", "") daemon = extra.get("daemon", "")
if isinstance(daemon, str) and daemon.strip(): if isinstance(daemon, str) and daemon.strip():
extra["enabled"] = extra["started"] = False extra["enabled"] = extra["started"] = False
@ -91,7 +88,7 @@ class ExtrasInfoSubmanager(BaseInfoSubmanager):
except Exception as err: except Exception as err:
get_logger(0).error("Can't get info about the service %r: %s", daemon, tools.efmt(err)) get_logger(0).error("Can't get info about the service %r: %s", daemon, tools.efmt(err))
def __rewrite_app_port(self, extra: Dict) -> None: def __rewrite_app_port(self, extra: dict) -> None:
port_path = extra.get("port", "") port_path = extra.get("port", "")
if isinstance(port_path, str) and port_path.strip(): if isinstance(port_path, str) and port_path.strip():
extra["port"] = 0 extra["port"] = 0

View File

@ -23,9 +23,7 @@
import copy import copy
import asyncio import asyncio
from typing import Dict
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional
import aiohttp import aiohttp
@ -55,15 +53,15 @@ class FanInfoSubmanager(BaseInfoSubmanager):
self.__timeout = timeout self.__timeout = timeout
self.__state_poll = state_poll self.__state_poll = state_poll
async def get_state(self) -> Dict: async def get_state(self) -> dict:
monitored = await self.__get_monitored() monitored = await self.__get_monitored()
return { return {
"monitored": monitored, "monitored": monitored,
"state": ((await self.__get_fan_state() if monitored else None)), "state": ((await self.__get_fan_state() if monitored else None)),
} }
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
prev_state: Dict = {} prev_state: dict = {}
while True: while True:
if self.__unix_path: if self.__unix_path:
pure = state = await self.get_state() pure = state = await self.get_state()
@ -93,7 +91,7 @@ class FanInfoSubmanager(BaseInfoSubmanager):
get_logger(0).error("Can't get info about the service %r: %s", self.__daemon, tools.efmt(err)) get_logger(0).error("Can't get info about the service %r: %s", self.__daemon, tools.efmt(err))
return False return False
async def __get_fan_state(self) -> Optional[Dict]: async def __get_fan_state(self) -> (dict | None):
try: try:
async with self.__make_http_session() as session: async with self.__make_http_session() as session:
async with session.get("http://localhost/state") as response: async with session.get("http://localhost/state") as response:
@ -104,7 +102,7 @@ class FanInfoSubmanager(BaseInfoSubmanager):
return None return None
def __make_http_session(self) -> aiohttp.ClientSession: def __make_http_session(self) -> aiohttp.ClientSession:
kwargs: Dict = { kwargs: dict = {
"headers": { "headers": {
"User-Agent": htclient.make_user_agent("KVMD"), "User-Agent": htclient.make_user_agent("KVMD"),
}, },

View File

@ -23,12 +23,9 @@
import os import os
import asyncio import asyncio
from typing import List
from typing import Dict
from typing import Callable from typing import Callable
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import TypeVar from typing import TypeVar
from typing import Optional
from ....logging import get_logger from ....logging import get_logger
@ -48,16 +45,16 @@ _RetvalT = TypeVar("_RetvalT")
class HwInfoSubmanager(BaseInfoSubmanager): class HwInfoSubmanager(BaseInfoSubmanager):
def __init__( def __init__(
self, self,
vcgencmd_cmd: List[str], vcgencmd_cmd: list[str],
state_poll: float, state_poll: float,
) -> None: ) -> None:
self.__vcgencmd_cmd = vcgencmd_cmd self.__vcgencmd_cmd = vcgencmd_cmd
self.__state_poll = state_poll self.__state_poll = state_poll
self.__dt_cache: Dict[str, str] = {} self.__dt_cache: dict[str, str] = {}
async def get_state(self) -> Dict: async def get_state(self) -> dict:
(model, serial, cpu_temp, throttling) = await asyncio.gather( (model, serial, cpu_temp, throttling) = await asyncio.gather(
self.__read_dt_file("model"), self.__read_dt_file("model"),
self.__read_dt_file("serial-number"), self.__read_dt_file("serial-number"),
@ -78,8 +75,8 @@ class HwInfoSubmanager(BaseInfoSubmanager):
}, },
} }
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
prev_state: Dict = {} prev_state: dict = {}
while True: while True:
state = await self.get_state() state = await self.get_state()
if state != prev_state: if state != prev_state:
@ -89,7 +86,7 @@ class HwInfoSubmanager(BaseInfoSubmanager):
# ===== # =====
async def __read_dt_file(self, name: str) -> Optional[str]: async def __read_dt_file(self, name: str) -> (str | None):
if name not in self.__dt_cache: if name not in self.__dt_cache:
path = os.path.join(f"{env.PROCFS_PREFIX}/proc/device-tree", name) path = os.path.join(f"{env.PROCFS_PREFIX}/proc/device-tree", name)
try: try:
@ -99,7 +96,7 @@ class HwInfoSubmanager(BaseInfoSubmanager):
return None return None
return self.__dt_cache[name] return self.__dt_cache[name]
async def __get_cpu_temp(self) -> Optional[float]: async def __get_cpu_temp(self) -> (float | None):
temp_path = f"{env.SYSFS_PREFIX}/sys/class/thermal/thermal_zone0/temp" temp_path = f"{env.SYSFS_PREFIX}/sys/class/thermal/thermal_zone0/temp"
try: try:
return int((await aiofs.read(temp_path)).strip()) / 1000 return int((await aiofs.read(temp_path)).strip()) / 1000
@ -107,7 +104,7 @@ class HwInfoSubmanager(BaseInfoSubmanager):
get_logger(0).error("Can't read CPU temp from %s: %s", temp_path, err) get_logger(0).error("Can't read CPU temp from %s: %s", temp_path, err)
return None return None
async def __get_throttling(self) -> Optional[Dict]: async def __get_throttling(self) -> (dict | None):
# https://www.raspberrypi.org/forums/viewtopic.php?f=63&t=147781&start=50#p972790 # https://www.raspberrypi.org/forums/viewtopic.php?f=63&t=147781&start=50#p972790
flags = await self.__parse_vcgencmd( flags = await self.__parse_vcgencmd(
arg="get_throttled", arg="get_throttled",
@ -133,7 +130,7 @@ class HwInfoSubmanager(BaseInfoSubmanager):
} }
return None return None
async def __parse_vcgencmd(self, arg: str, parser: Callable[[str], _RetvalT]) -> Optional[_RetvalT]: async def __parse_vcgencmd(self, arg: str, parser: Callable[[str], _RetvalT]) -> (_RetvalT | None):
cmd = [*self.__vcgencmd_cmd, arg] cmd = [*self.__vcgencmd_cmd, arg]
try: try:
text = (await aioproc.read_process(cmd, err_to_null=True))[1] text = (await aioproc.read_process(cmd, err_to_null=True))[1]

View File

@ -20,9 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import Dict
from typing import Optional
from ....logging import get_logger from ....logging import get_logger
from ....yamlconf.loader import load_yaml_file from ....yamlconf.loader import load_yaml_file
@ -37,7 +34,7 @@ class MetaInfoSubmanager(BaseInfoSubmanager):
def __init__(self, meta_path: str) -> None: def __init__(self, meta_path: str) -> None:
self.__meta_path = meta_path self.__meta_path = meta_path
async def get_state(self) -> Optional[Dict]: async def get_state(self) -> (dict | None):
try: try:
return ((await aiotools.run_async(load_yaml_file, self.__meta_path)) or {}) return ((await aiotools.run_async(load_yaml_file, self.__meta_path)) or {})
except Exception: except Exception:

View File

@ -24,9 +24,6 @@ import os
import asyncio import asyncio
import platform import platform
from typing import List
from typing import Dict
from ....logging import get_logger from ....logging import get_logger
from .... import aioproc from .... import aioproc
@ -38,10 +35,10 @@ from .base import BaseInfoSubmanager
# ===== # =====
class SystemInfoSubmanager(BaseInfoSubmanager): class SystemInfoSubmanager(BaseInfoSubmanager):
def __init__(self, streamer_cmd: List[str]) -> None: def __init__(self, streamer_cmd: list[str]) -> None:
self.__streamer_cmd = streamer_cmd self.__streamer_cmd = streamer_cmd
async def get_state(self) -> Dict: async def get_state(self) -> dict:
streamer_info = await self.__get_streamer_info() streamer_info = await self.__get_streamer_info()
uname_info = platform.uname() # Uname using the internal cache uname_info = platform.uname() # Uname using the internal cache
return { return {
@ -55,9 +52,9 @@ class SystemInfoSubmanager(BaseInfoSubmanager):
# ===== # =====
async def __get_streamer_info(self) -> Dict: async def __get_streamer_info(self) -> dict:
version = "" version = ""
features: Dict[str, bool] = {} features: dict[str, bool] = {}
try: try:
path = self.__streamer_cmd[0] path = self.__streamer_cmd[0]
((_, version), (_, features_text)) = await asyncio.gather( ((_, version), (_, features_text)) = await asyncio.gather(

View File

@ -24,7 +24,6 @@ import re
import asyncio import asyncio
import time import time
from typing import Dict
from typing import AsyncGenerator from typing import AsyncGenerator
import systemd.journal import systemd.journal
@ -32,7 +31,7 @@ import systemd.journal
# ===== # =====
class LogReader: class LogReader:
async def poll_log(self, seek: int, follow: bool) -> AsyncGenerator[Dict, None]: async def poll_log(self, seek: int, follow: bool) -> AsyncGenerator[dict, None]:
reader = systemd.journal.Reader() reader = systemd.journal.Reader()
reader.this_boot() reader.this_boot()
reader.this_machine() reader.this_machine()
@ -59,7 +58,7 @@ class LogReader:
else: else:
await asyncio.sleep(1) await asyncio.sleep(1)
def __entry_to_record(self, entry: Dict) -> Dict[str, Dict]: def __entry_to_record(self, entry: dict) -> dict[str, dict]:
return { return {
"dt": entry["__REALTIME_TIMESTAMP"], "dt": entry["__REALTIME_TIMESTAMP"],
"service": entry["_SYSTEMD_UNIT"], "service": entry["_SYSTEMD_UNIT"],

View File

@ -27,11 +27,7 @@ import asyncio.subprocess
import dataclasses import dataclasses
import functools import functools
from typing import Tuple
from typing import List
from typing import Dict
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional
from typing import Any from typing import Any
import aiohttp import aiohttp
@ -53,7 +49,7 @@ class StreamerSnapshot:
width: int width: int
height: int height: int
mtime: float mtime: float
headers: Tuple[Tuple[str, str], ...] headers: tuple[tuple[str, str], ...]
data: bytes data: bytes
async def make_preview(self, max_width: int, max_height: int, quality: int) -> bytes: async def make_preview(self, max_width: int, max_height: int, quality: int) -> bytes:
@ -98,7 +94,7 @@ class _StreamerParams:
quality: int, quality: int,
resolution: str, resolution: str,
available_resolutions: List[str], available_resolutions: list[str],
desired_fps: int, desired_fps: int,
desired_fps_min: int, desired_fps_min: int,
@ -117,8 +113,8 @@ class _StreamerParams:
self.__has_resolution = bool(resolution) self.__has_resolution = bool(resolution)
self.__has_h264 = bool(h264_bitrate) self.__has_h264 = bool(h264_bitrate)
self.__params: Dict = {self.__DESIRED_FPS: min(max(desired_fps, desired_fps_min), desired_fps_max)} self.__params: dict = {self.__DESIRED_FPS: min(max(desired_fps, desired_fps_min), desired_fps_max)}
self.__limits: Dict = {self.__DESIRED_FPS: {"min": desired_fps_min, "max": desired_fps_max}} self.__limits: dict = {self.__DESIRED_FPS: {"min": desired_fps_min, "max": desired_fps_max}}
if self.__has_quality: if self.__has_quality:
self.__params[self.__QUALITY] = quality self.__params[self.__QUALITY] = quality
@ -133,23 +129,23 @@ class _StreamerParams:
self.__params[self.__H264_GOP] = min(max(h264_gop, h264_gop_min), h264_gop_max) self.__params[self.__H264_GOP] = min(max(h264_gop, h264_gop_min), h264_gop_max)
self.__limits[self.__H264_GOP] = {"min": h264_gop_min, "max": h264_gop_max} self.__limits[self.__H264_GOP] = {"min": h264_gop_min, "max": h264_gop_max}
def get_features(self) -> Dict: def get_features(self) -> dict:
return { return {
self.__QUALITY: self.__has_quality, self.__QUALITY: self.__has_quality,
self.__RESOLUTION: self.__has_resolution, self.__RESOLUTION: self.__has_resolution,
"h264": self.__has_h264, "h264": self.__has_h264,
} }
def get_limits(self) -> Dict: def get_limits(self) -> dict:
limits = dict(self.__limits) limits = dict(self.__limits)
if self.__has_resolution: if self.__has_resolution:
limits[self.__AVAILABLE_RESOLUTIONS] = list(limits[self.__AVAILABLE_RESOLUTIONS]) limits[self.__AVAILABLE_RESOLUTIONS] = list(limits[self.__AVAILABLE_RESOLUTIONS])
return limits return limits
def get_params(self) -> Dict: def get_params(self) -> dict:
return dict(self.__params) return dict(self.__params)
def set_params(self, params: Dict) -> None: def set_params(self, params: dict) -> None:
new_params = dict(self.__params) new_params = dict(self.__params)
if self.__QUALITY in params and self.__has_quality: if self.__QUALITY in params and self.__has_quality:
@ -187,9 +183,9 @@ class Streamer: # pylint: disable=too-many-instance-attributes
process_name_prefix: str, process_name_prefix: str,
cmd: List[str], cmd: list[str],
cmd_remove: List[str], cmd_remove: list[str],
cmd_append: List[str], cmd_append: list[str],
**params_kwargs: Any, **params_kwargs: Any,
) -> None: ) -> None:
@ -207,15 +203,15 @@ class Streamer: # pylint: disable=too-many-instance-attributes
self.__params = _StreamerParams(**params_kwargs) self.__params = _StreamerParams(**params_kwargs)
self.__stop_task: Optional[asyncio.Task] = None self.__stop_task: (asyncio.Task | None) = None
self.__stop_wip = False self.__stop_wip = False
self.__streamer_task: Optional[asyncio.Task] = None self.__streamer_task: (asyncio.Task | None) = None
self.__streamer_proc: Optional[asyncio.subprocess.Process] = None # pylint: disable=no-member self.__streamer_proc: (asyncio.subprocess.Process | None) = None # pylint: disable=no-member
self.__http_session: Optional[aiohttp.ClientSession] = None self.__http_session: (aiohttp.ClientSession | None) = None
self.__snapshot: Optional[StreamerSnapshot] = None self.__snapshot: (StreamerSnapshot | None) = None
self.__notifier = aiotools.AioNotifier() self.__notifier = aiotools.AioNotifier()
@ -280,16 +276,16 @@ class Streamer: # pylint: disable=too-many-instance-attributes
# ===== # =====
def set_params(self, params: Dict) -> None: def set_params(self, params: dict) -> None:
assert not self.__streamer_task assert not self.__streamer_task
return self.__params.set_params(params) return self.__params.set_params(params)
def get_params(self) -> Dict: def get_params(self) -> dict:
return self.__params.get_params() return self.__params.get_params()
# ===== # =====
async def get_state(self) -> Dict: async def get_state(self) -> dict:
streamer_state = None streamer_state = None
if self.__streamer_task: if self.__streamer_task:
session = self.__ensure_http_session() session = self.__ensure_http_session()
@ -302,7 +298,7 @@ class Streamer: # pylint: disable=too-many-instance-attributes
except Exception: except Exception:
get_logger().exception("Invalid streamer response from /state") get_logger().exception("Invalid streamer response from /state")
snapshot: Optional[Dict] = None snapshot: (dict | None) = None
if self.__snapshot: if self.__snapshot:
snapshot = dataclasses.asdict(self.__snapshot) snapshot = dataclasses.asdict(self.__snapshot)
del snapshot["headers"] del snapshot["headers"]
@ -316,7 +312,7 @@ class Streamer: # pylint: disable=too-many-instance-attributes
"features": self.__params.get_features(), "features": self.__params.get_features(),
} }
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
def signal_handler(*_: Any) -> None: def signal_handler(*_: Any) -> None:
get_logger(0).info("Got SIGUSR2, checking the stream state ...") get_logger(0).info("Got SIGUSR2, checking the stream state ...")
self.__notifier.notify() self.__notifier.notify()
@ -324,8 +320,8 @@ class Streamer: # pylint: disable=too-many-instance-attributes
get_logger(0).info("Installing SIGUSR2 streamer handler ...") get_logger(0).info("Installing SIGUSR2 streamer handler ...")
asyncio.get_event_loop().add_signal_handler(signal.SIGUSR2, signal_handler) asyncio.get_event_loop().add_signal_handler(signal.SIGUSR2, signal_handler)
waiter_task: Optional[asyncio.Task] = None waiter_task: (asyncio.Task | None) = None
prev_state: Dict = {} prev_state: dict = {}
while True: while True:
state = await self.get_state() state = await self.get_state()
if state != prev_state: if state != prev_state:
@ -339,7 +335,7 @@ class Streamer: # pylint: disable=too-many-instance-attributes
# ===== # =====
async def take_snapshot(self, save: bool, load: bool, allow_offline: bool) -> Optional[StreamerSnapshot]: async def take_snapshot(self, save: bool, load: bool, allow_offline: bool) -> (StreamerSnapshot | None):
if load: if load:
return self.__snapshot return self.__snapshot
else: else:
@ -395,7 +391,7 @@ class Streamer: # pylint: disable=too-many-instance-attributes
def __ensure_http_session(self) -> aiohttp.ClientSession: def __ensure_http_session(self) -> aiohttp.ClientSession:
if not self.__http_session: if not self.__http_session:
kwargs: Dict = { kwargs: dict = {
"headers": {"User-Agent": htclient.make_user_agent("KVMD")}, "headers": {"User-Agent": htclient.make_user_agent("KVMD")},
"connector": aiohttp.UnixConnector(path=self.__unix_path), "connector": aiohttp.UnixConnector(path=self.__unix_path),
"timeout": aiohttp.ClientTimeout(total=self.__timeout), "timeout": aiohttp.ClientTimeout(total=self.__timeout),

View File

@ -22,10 +22,6 @@
import types import types
from typing import Tuple
from typing import Type
from typing import Optional
import dbus_next import dbus_next
import dbus_next.aio import dbus_next.aio
import dbus_next.aio.proxy_object import dbus_next.aio.proxy_object
@ -36,11 +32,11 @@ import dbus_next.errors
# ===== # =====
class SystemdUnitInfo: class SystemdUnitInfo:
def __init__(self) -> None: def __init__(self) -> None:
self.__bus: Optional[dbus_next.aio.MessageBus] = None self.__bus: (dbus_next.aio.MessageBus | None) = None
self.__intr: Optional[dbus_next.introspection.Node] = None self.__intr: (dbus_next.introspection.Node | None) = None
self.__manager: Optional[dbus_next.aio.proxy_object.ProxyInterface] = None self.__manager: (dbus_next.aio.proxy_object.ProxyInterface | None) = None
async def get_status(self, name: str) -> Tuple[bool, bool]: async def get_status(self, name: str) -> tuple[bool, bool]:
assert self.__bus is not None assert self.__bus is not None
assert self.__intr is not None assert self.__intr is not None
assert self.__manager is not None assert self.__manager is not None
@ -89,7 +85,7 @@ class SystemdUnitInfo:
async def __aexit__( async def __aexit__(
self, self,
_exc_type: Type[BaseException], _exc_type: type[BaseException],
_exc: BaseException, _exc: BaseException,
_tb: types.TracebackType, _tb: types.TracebackType,
) -> None: ) -> None:

View File

@ -36,10 +36,7 @@ from ctypes import c_char_p
from ctypes import c_void_p from ctypes import c_void_p
from ctypes import c_char from ctypes import c_char
from typing import List
from typing import Set
from typing import Generator from typing import Generator
from typing import Optional
from PIL import ImageOps from PIL import ImageOps
from PIL import Image as PilImage from PIL import Image as PilImage
@ -60,7 +57,7 @@ class _TessBaseAPI(Structure):
pass pass
def _load_libtesseract() -> Optional[ctypes.CDLL]: def _load_libtesseract() -> (ctypes.CDLL | None):
try: try:
path = ctypes.util.find_library("tesseract") path = ctypes.util.find_library("tesseract")
if not path: if not path:
@ -88,7 +85,7 @@ _libtess = _load_libtesseract()
@contextlib.contextmanager @contextlib.contextmanager
def _tess_api(data_dir_path: str, langs: List[str]) -> Generator[_TessBaseAPI, None, None]: def _tess_api(data_dir_path: str, langs: list[str]) -> Generator[_TessBaseAPI, None, None]:
if not _libtess: if not _libtess:
raise OcrError("Tesseract is not available") raise OcrError("Tesseract is not available")
api = _libtess.TessBaseAPICreate() api = _libtess.TessBaseAPICreate()
@ -107,19 +104,19 @@ _LANG_SUFFIX = ".traineddata"
# ===== # =====
class TesseractOcr: class TesseractOcr:
def __init__(self, data_dir_path: str, default_langs: List[str]) -> None: def __init__(self, data_dir_path: str, default_langs: list[str]) -> None:
self.__data_dir_path = data_dir_path self.__data_dir_path = data_dir_path
self.__default_langs = default_langs self.__default_langs = default_langs
def is_available(self) -> bool: def is_available(self) -> bool:
return bool(_libtess) return bool(_libtess)
def get_default_langs(self) -> List[str]: def get_default_langs(self) -> list[str]:
return list(self.__default_langs) return list(self.__default_langs)
def get_available_langs(self) -> List[str]: def get_available_langs(self) -> list[str]:
# Это быстрее чем, инициализация либы и TessBaseAPIGetAvailableLanguagesAsVector() # Это быстрее чем, инициализация либы и TessBaseAPIGetAvailableLanguagesAsVector()
langs: Set[str] = set() langs: set[str] = set()
for lang_name in os.listdir(self.__data_dir_path): for lang_name in os.listdir(self.__data_dir_path):
if lang_name.endswith(_LANG_SUFFIX): if lang_name.endswith(_LANG_SUFFIX):
path = os.path.join(self.__data_dir_path, lang_name) path = os.path.join(self.__data_dir_path, lang_name)
@ -129,12 +126,12 @@ class TesseractOcr:
langs.add(lang) langs.add(lang)
return sorted(langs) return sorted(langs)
async def recognize(self, data: bytes, langs: List[str], left: int, top: int, right: int, bottom: int) -> str: async def recognize(self, data: bytes, langs: list[str], left: int, top: int, right: int, bottom: int) -> str:
if not langs: if not langs:
langs = self.__default_langs langs = self.__default_langs
return (await aiotools.run_async(self.__inner_recognize, data, langs, left, top, right, bottom)) return (await aiotools.run_async(self.__inner_recognize, data, langs, left, top, right, bottom))
def __inner_recognize(self, data: bytes, langs: List[str], left: int, top: int, right: int, bottom: int) -> str: def __inner_recognize(self, data: bytes, langs: list[str], left: int, top: int, right: int, bottom: int) -> str:
with _tess_api(self.__data_dir_path, langs) as api: with _tess_api(self.__data_dir_path, langs) as api:
assert _libtess assert _libtess
with io.BytesIO(data) as bio: with io.BytesIO(data) as bio:

View File

@ -22,11 +22,8 @@
import asyncio import asyncio
from typing import List
from typing import Dict
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Callable from typing import Callable
from typing import Optional
from typing import Any from typing import Any
from ...logging import get_logger from ...logging import get_logger
@ -83,7 +80,7 @@ class _GpioInput:
self.__driver = driver self.__driver = driver
self.__driver.register_input(self.__pin, config.debounce) self.__driver.register_input(self.__pin, config.debounce)
def get_scheme(self) -> Dict: def get_scheme(self) -> dict:
return { return {
"hw": { "hw": {
"driver": self.__driver.get_instance_id(), "driver": self.__driver.get_instance_id(),
@ -91,7 +88,7 @@ class _GpioInput:
}, },
} }
async def get_state(self) -> Dict: async def get_state(self) -> dict:
(online, state) = (True, False) (online, state) = (True, False)
try: try:
state = (await self.__driver.read(self.__pin) ^ self.__inverted) state = (await self.__driver.read(self.__pin) ^ self.__inverted)
@ -139,7 +136,7 @@ class _GpioOutput: # pylint: disable=too-many-instance-attributes
self.__region = aiotools.AioExclusiveRegion(GpioChannelIsBusyError, notifier) self.__region = aiotools.AioExclusiveRegion(GpioChannelIsBusyError, notifier)
def get_scheme(self) -> Dict: def get_scheme(self) -> dict:
return { return {
"switch": self.__switch, "switch": self.__switch,
"pulse": { "pulse": {
@ -153,7 +150,7 @@ class _GpioOutput: # pylint: disable=too-many-instance-attributes
}, },
} }
async def get_state(self) -> Dict: async def get_state(self) -> dict:
busy = self.__region.is_busy() busy = self.__region.is_busy()
(online, state) = (True, False) (online, state) = (True, False)
if not busy: if not busy:
@ -246,8 +243,8 @@ class UserGpio:
for (driver, drv_config) in tools.sorted_kvs(config.drivers) for (driver, drv_config) in tools.sorted_kvs(config.drivers)
} }
self.__inputs: Dict[str, _GpioInput] = {} self.__inputs: dict[str, _GpioInput] = {}
self.__outputs: Dict[str, _GpioOutput] = {} self.__outputs: dict[str, _GpioOutput] = {}
for (channel, ch_config) in tools.sorted_kvs(config.scheme): for (channel, ch_config) in tools.sorted_kvs(config.scheme):
driver = self.__drivers[ch_config.driver] driver = self.__drivers[ch_config.driver]
@ -256,7 +253,7 @@ class UserGpio:
else: # output: else: # output:
self.__outputs[channel] = _GpioOutput(channel, ch_config, driver, self.__notifier) self.__outputs[channel] = _GpioOutput(channel, ch_config, driver, self.__notifier)
async def get_model(self) -> Dict: async def get_model(self) -> dict:
return { return {
"scheme": { "scheme": {
"inputs": {channel: gin.get_scheme() for (channel, gin) in self.__inputs.items()}, "inputs": {channel: gin.get_scheme() for (channel, gin) in self.__inputs.items()},
@ -265,14 +262,14 @@ class UserGpio:
"view": self.__make_view(), "view": self.__make_view(),
} }
async def get_state(self) -> Dict: async def get_state(self) -> dict:
return { return {
"inputs": {channel: await gin.get_state() for (channel, gin) in self.__inputs.items()}, "inputs": {channel: await gin.get_state() for (channel, gin) in self.__inputs.items()},
"outputs": {channel: await gout.get_state() for (channel, gout) in self.__outputs.items()}, "outputs": {channel: await gout.get_state() for (channel, gout) in self.__outputs.items()},
} }
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
prev_state: Dict = {} prev_state: dict = {}
while True: while True:
state = await self.get_state() state = await self.get_state()
if state != prev_state: if state != prev_state:
@ -313,14 +310,14 @@ class UserGpio:
# ===== # =====
def __make_view(self) -> Dict: def __make_view(self) -> dict:
table: List[Optional[List[Dict]]] = [] table: list[list[dict] | None] = []
for row in self.__view["table"]: for row in self.__view["table"]:
if len(row) == 0: if len(row) == 0:
table.append(None) table.append(None)
continue continue
items: List[Dict] = [] items: list[dict] = []
for item in map(str.strip, row): for item in map(str.strip, row):
if item.startswith("#") or len(item) == 0: if item.startswith("#") or len(item) == 0:
items.append(self.__make_view_label(item)) items.append(self.__make_view_label(item))
@ -338,14 +335,14 @@ class UserGpio:
"table": table, "table": table,
} }
def __make_view_label(self, item: str) -> Dict: def __make_view_label(self, item: str) -> dict:
assert item.startswith("#") assert item.startswith("#")
return { return {
"type": "label", "type": "label",
"text": item[1:].strip(), "text": item[1:].strip(),
} }
def __make_view_input(self, parts: List[str]) -> Dict: def __make_view_input(self, parts: list[str]) -> dict:
assert len(parts) >= 1 assert len(parts) >= 1
color = (parts[1] if len(parts) > 1 else None) color = (parts[1] if len(parts) > 1 else None)
if color not in ["green", "yellow", "red"]: if color not in ["green", "yellow", "red"]:
@ -356,7 +353,7 @@ class UserGpio:
"color": color, "color": color,
} }
def __make_view_output(self, parts: List[str]) -> Dict: def __make_view_output(self, parts: list[str]) -> dict:
assert len(parts) >= 1 assert len(parts) >= 1
confirm = False confirm = False
text = "Click" text = "Click"

View File

@ -29,10 +29,6 @@ import argparse
from os.path import join # pylint: disable=ungrouped-imports from os.path import join # pylint: disable=ungrouped-imports
from typing import List
from typing import Optional
from typing import Union
from ...logging import get_logger from ...logging import get_logger
from ...yamlconf import Section from ...yamlconf import Section
@ -78,7 +74,7 @@ def _unlink(path: str, optional: bool=False) -> None:
os.unlink(path) os.unlink(path)
def _write(path: str, value: Union[str, int], optional: bool=False) -> None: def _write(path: str, value: (str | int), optional: bool=False) -> None:
logger = get_logger() logger = get_logger()
if optional and not os.access(path, os.F_OK): if optional and not os.access(path, os.F_OK):
logger.info("WRITE --- [SKIPPED] %s", path) logger.info("WRITE --- [SKIPPED] %s", path)
@ -320,7 +316,7 @@ def _cmd_stop(config: Section) -> None:
# ===== # =====
def main(argv: Optional[List[str]]=None) -> None: def main(argv: (list[str] | None)=None) -> None:
(parent_parser, argv, config) = init( (parent_parser, argv, config) = init(
add_help=False, add_help=False,
argv=argv, argv=argv,

View File

@ -20,13 +20,11 @@
# ========================================================================== # # ========================================================================== #
from typing import Optional
from . import Hid from . import Hid
# ===== # =====
def make_keyboard_hid(report_id: Optional[int]=None) -> Hid: def make_keyboard_hid(report_id: (int | None)=None) -> Hid:
return Hid( return Hid(
protocol=1, # Keyboard protocol protocol=1, # Keyboard protocol
subclass=1, # Boot interface subclass subclass=1, # Boot interface subclass

View File

@ -20,13 +20,11 @@
# ========================================================================== # # ========================================================================== #
from typing import Optional
from . import Hid from . import Hid
# ===== # =====
def make_mouse_hid(absolute: bool, horizontal_wheel: bool, report_id: Optional[int]=None) -> Hid: def make_mouse_hid(absolute: bool, horizontal_wheel: bool, report_id: (int | None)=None) -> Hid:
maker = (_make_absolute_hid if absolute else _make_relative_hid) maker = (_make_absolute_hid if absolute else _make_relative_hid)
return maker(horizontal_wheel, report_id) return maker(horizontal_wheel, report_id)
@ -42,7 +40,7 @@ _HORIZONTAL_WHEEL = [
] ]
def _make_absolute_hid(horizontal_wheel: bool, report_id: Optional[int]) -> Hid: def _make_absolute_hid(horizontal_wheel: bool, report_id: (int | None)) -> Hid:
return Hid( return Hid(
protocol=0, # None protocol protocol=0, # None protocol
subclass=0, # No subclass subclass=0, # No subclass
@ -106,7 +104,7 @@ def _make_absolute_hid(horizontal_wheel: bool, report_id: Optional[int]) -> Hid:
) )
def _make_relative_hid(horizontal_wheel: bool, report_id: Optional[int]) -> Hid: def _make_relative_hid(horizontal_wheel: bool, report_id: (int | None)) -> Hid:
return Hid( return Hid(
protocol=2, # Mouse protocol protocol=2, # Mouse protocol
subclass=1, # Boot interface subclass subclass=1, # Boot interface subclass

View File

@ -26,10 +26,7 @@ import contextlib
import argparse import argparse
import time import time
from typing import List
from typing import Dict
from typing import Generator from typing import Generator
from typing import Optional
import yaml import yaml
@ -64,7 +61,7 @@ class _GadgetControl:
with open(udc_path, "w") as udc_file: with open(udc_path, "w") as udc_file:
udc_file.write(udc) udc_file.write(udc)
def __read_metas(self) -> Generator[Dict, None, None]: def __read_metas(self) -> Generator[dict, None, None]:
for meta_name in sorted(os.listdir(self.__meta_path)): for meta_name in sorted(os.listdir(self.__meta_path)):
with open(os.path.join(self.__meta_path, meta_name)) as meta_file: with open(os.path.join(self.__meta_path, meta_name)) as meta_file:
yield json.loads(meta_file.read()) yield json.loads(meta_file.read())
@ -111,7 +108,7 @@ class _GadgetControl:
# ===== # =====
def main(argv: Optional[List[str]]=None) -> None: def main(argv: (list[str] | None)=None) -> None:
(parent_parser, argv, config) = init( (parent_parser, argv, config) = init(
add_help=False, add_help=False,
cli_logging=True, cli_logging=True,

View File

@ -24,9 +24,6 @@ import os
import errno import errno
import argparse import argparse
from typing import List
from typing import Optional
from ...validators.basic import valid_bool from ...validators.basic import valid_bool
from ...validators.basic import valid_int_f0 from ...validators.basic import valid_int_f0
from ...validators.os import valid_abs_file from ...validators.os import valid_abs_file
@ -57,7 +54,7 @@ def _set_param(gadget: str, instance: int, param: str, value: str) -> None:
# ===== # =====
def main(argv: Optional[List[str]]=None) -> None: def main(argv: (list[str] | None)=None) -> None:
(parent_parser, argv, config) = init( (parent_parser, argv, config) = init(
add_help=False, add_help=False,
cli_logging=True, cli_logging=True,

View File

@ -26,9 +26,6 @@ import dataclasses
import itertools import itertools
import argparse import argparse
from typing import List
from typing import Optional
from ...logging import get_logger from ...logging import get_logger
from ...yamlconf import Section from ...yamlconf import Section
@ -66,25 +63,25 @@ class _Netcfg: # pylint: disable=too-many-instance-attributes
class _Service: # pylint: disable=too-many-instance-attributes class _Service: # pylint: disable=too-many-instance-attributes
def __init__(self, config: Section) -> None: def __init__(self, config: Section) -> None:
self.__iface_net: str = config.otgnet.iface.net self.__iface_net: str = config.otgnet.iface.net
self.__ip_cmd: List[str] = config.otgnet.iface.ip_cmd self.__ip_cmd: list[str] = config.otgnet.iface.ip_cmd
self.__allow_icmp: bool = config.otgnet.firewall.allow_icmp self.__allow_icmp: bool = config.otgnet.firewall.allow_icmp
self.__allow_tcp: List[int] = sorted(set(config.otgnet.firewall.allow_tcp)) self.__allow_tcp: list[int] = sorted(set(config.otgnet.firewall.allow_tcp))
self.__allow_udp: List[int] = sorted(set(config.otgnet.firewall.allow_udp)) self.__allow_udp: list[int] = sorted(set(config.otgnet.firewall.allow_udp))
self.__forward_iface: str = config.otgnet.firewall.forward_iface self.__forward_iface: str = config.otgnet.firewall.forward_iface
self.__iptables_cmd: List[str] = config.otgnet.firewall.iptables_cmd self.__iptables_cmd: list[str] = config.otgnet.firewall.iptables_cmd
def build_cmd(key: str) -> List[str]: def build_cmd(key: str) -> list[str]:
return tools.build_cmd( return tools.build_cmd(
getattr(config.otgnet.commands, key), getattr(config.otgnet.commands, key),
getattr(config.otgnet.commands, f"{key}_remove"), getattr(config.otgnet.commands, f"{key}_remove"),
getattr(config.otgnet.commands, f"{key}_append"), getattr(config.otgnet.commands, f"{key}_append"),
) )
self.__pre_start_cmd: List[str] = build_cmd("pre_start_cmd") self.__pre_start_cmd: list[str] = build_cmd("pre_start_cmd")
self.__post_start_cmd: List[str] = build_cmd("post_start_cmd") self.__post_start_cmd: list[str] = build_cmd("post_start_cmd")
self.__pre_stop_cmd: List[str] = build_cmd("pre_stop_cmd") self.__pre_stop_cmd: list[str] = build_cmd("pre_stop_cmd")
self.__post_stop_cmd: List[str] = build_cmd("post_stop_cmd") self.__post_stop_cmd: list[str] = build_cmd("post_stop_cmd")
self.__gadget: str = config.otg.gadget self.__gadget: str = config.otg.gadget
self.__driver: str = config.otg.devices.ethernet.driver self.__driver: str = config.otg.devices.ethernet.driver
@ -101,7 +98,7 @@ class _Service: # pylint: disable=too-many-instance-attributes
key: str(value) key: str(value)
for (key, value) in dataclasses.asdict(netcfg).items() for (key, value) in dataclasses.asdict(netcfg).items()
} }
ctls: List[BaseCtl] = [ ctls: list[BaseCtl] = [
CustomCtl(self.__pre_start_cmd, self.__post_stop_cmd, placeholders), CustomCtl(self.__pre_start_cmd, self.__post_stop_cmd, placeholders),
IfaceUpCtl(self.__ip_cmd, netcfg.iface), IfaceUpCtl(self.__ip_cmd, netcfg.iface),
*([IptablesAllowIcmpCtl(self.__iptables_cmd, netcfg.iface)] if self.__allow_icmp else []), *([IptablesAllowIcmpCtl(self.__iptables_cmd, netcfg.iface)] if self.__allow_icmp else []),
@ -185,7 +182,7 @@ class _Service: # pylint: disable=too-many-instance-attributes
# ===== # =====
def main(argv: Optional[List[str]]=None) -> None: def main(argv: (list[str] | None)=None) -> None:
(parent_parser, argv, config) = init( (parent_parser, argv, config) = init(
add_help=False, add_help=False,
argv=argv, argv=argv,

View File

@ -20,50 +20,46 @@
# ========================================================================== # # ========================================================================== #
from typing import List
from typing import Dict
# ===== # =====
class BaseCtl: class BaseCtl:
def get_command(self, direct: bool) -> List[str]: def get_command(self, direct: bool) -> list[str]:
raise NotImplementedError raise NotImplementedError
class IfaceUpCtl(BaseCtl): class IfaceUpCtl(BaseCtl):
def __init__(self, base_cmd: List[str], iface: str) -> None: def __init__(self, base_cmd: list[str], iface: str) -> None:
self.__base_cmd = base_cmd self.__base_cmd = base_cmd
self.__iface = iface self.__iface = iface
def get_command(self, direct: bool) -> List[str]: def get_command(self, direct: bool) -> list[str]:
return [*self.__base_cmd, "link", "set", self.__iface, ("up" if direct else "down")] return [*self.__base_cmd, "link", "set", self.__iface, ("up" if direct else "down")]
class IfaceAddIpCtl(BaseCtl): class IfaceAddIpCtl(BaseCtl):
def __init__(self, base_cmd: List[str], iface: str, cidr: str) -> None: def __init__(self, base_cmd: list[str], iface: str, cidr: str) -> None:
self.__base_cmd = base_cmd self.__base_cmd = base_cmd
self.__iface = iface self.__iface = iface
self.__cidr = cidr self.__cidr = cidr
def get_command(self, direct: bool) -> List[str]: def get_command(self, direct: bool) -> list[str]:
return [*self.__base_cmd, "address", ("add" if direct else "del"), self.__cidr, "dev", self.__iface] return [*self.__base_cmd, "address", ("add" if direct else "del"), self.__cidr, "dev", self.__iface]
class IptablesDropAllCtl(BaseCtl): class IptablesDropAllCtl(BaseCtl):
def __init__(self, base_cmd: List[str], iface: str) -> None: def __init__(self, base_cmd: list[str], iface: str) -> None:
self.__base_cmd = base_cmd self.__base_cmd = base_cmd
self.__iface = iface self.__iface = iface
def get_command(self, direct: bool) -> List[str]: def get_command(self, direct: bool) -> list[str]:
return [*self.__base_cmd, ("-A" if direct else "-D"), "INPUT", "-i", self.__iface, "-j", "DROP"] return [*self.__base_cmd, ("-A" if direct else "-D"), "INPUT", "-i", self.__iface, "-j", "DROP"]
class IptablesAllowIcmpCtl(BaseCtl): class IptablesAllowIcmpCtl(BaseCtl):
def __init__(self, base_cmd: List[str], iface: str) -> None: def __init__(self, base_cmd: list[str], iface: str) -> None:
self.__base_cmd = base_cmd self.__base_cmd = base_cmd
self.__iface = iface self.__iface = iface
def get_command(self, direct: bool) -> List[str]: def get_command(self, direct: bool) -> list[str]:
return [ return [
*self.__base_cmd, *self.__base_cmd,
("-A" if direct else "-D"), "INPUT", "-i", self.__iface, "-p", "icmp", "-j", "ACCEPT", ("-A" if direct else "-D"), "INPUT", "-i", self.__iface, "-p", "icmp", "-j", "ACCEPT",
@ -71,13 +67,13 @@ class IptablesAllowIcmpCtl(BaseCtl):
class IptablesAllowPortCtl(BaseCtl): class IptablesAllowPortCtl(BaseCtl):
def __init__(self, base_cmd: List[str], iface: str, port: int, tcp: bool) -> None: def __init__(self, base_cmd: list[str], iface: str, port: int, tcp: bool) -> None:
self.__base_cmd = base_cmd self.__base_cmd = base_cmd
self.__iface = iface self.__iface = iface
self.__port = port self.__port = port
self.__proto = ("tcp" if tcp else "udp") self.__proto = ("tcp" if tcp else "udp")
def get_command(self, direct: bool) -> List[str]: def get_command(self, direct: bool) -> list[str]:
return [ return [
*self.__base_cmd, *self.__base_cmd,
("-A" if direct else "-D"), "INPUT", "-i", self.__iface, "-p", self.__proto, ("-A" if direct else "-D"), "INPUT", "-i", self.__iface, "-p", self.__proto,
@ -86,11 +82,11 @@ class IptablesAllowPortCtl(BaseCtl):
class IptablesForwardOut(BaseCtl): class IptablesForwardOut(BaseCtl):
def __init__(self, base_cmd: List[str], iface: str) -> None: def __init__(self, base_cmd: list[str], iface: str) -> None:
self.__base_cmd = base_cmd self.__base_cmd = base_cmd
self.__iface = iface self.__iface = iface
def get_command(self, direct: bool) -> List[str]: def get_command(self, direct: bool) -> list[str]:
return [ return [
*self.__base_cmd, *self.__base_cmd,
"--table", "nat", "--table", "nat",
@ -100,11 +96,11 @@ class IptablesForwardOut(BaseCtl):
class IptablesForwardIn(BaseCtl): class IptablesForwardIn(BaseCtl):
def __init__(self, base_cmd: List[str], iface: str) -> None: def __init__(self, base_cmd: list[str], iface: str) -> None:
self.__base_cmd = base_cmd self.__base_cmd = base_cmd
self.__iface = iface self.__iface = iface
def get_command(self, direct: bool) -> List[str]: def get_command(self, direct: bool) -> list[str]:
return [ return [
*self.__base_cmd, *self.__base_cmd,
("-A" if direct else "-D"), "FORWARD", ("-A" if direct else "-D"), "FORWARD",
@ -115,16 +111,16 @@ class IptablesForwardIn(BaseCtl):
class CustomCtl(BaseCtl): class CustomCtl(BaseCtl):
def __init__( def __init__(
self, self,
direct_cmd: List[str], direct_cmd: list[str],
reverse_cmd: List[str], reverse_cmd: list[str],
placeholders: Dict[str, str], placeholders: dict[str, str],
) -> None: ) -> None:
self.__direct_cmd = direct_cmd self.__direct_cmd = direct_cmd
self.__reverse_cmd = reverse_cmd self.__reverse_cmd = reverse_cmd
self.__placeholders = placeholders self.__placeholders = placeholders
def get_command(self, direct: bool) -> List[str]: def get_command(self, direct: bool) -> list[str]:
return [ return [
part.format(**self.__placeholders) part.format(**self.__placeholders)
for part in (self.__direct_cmd if direct else self.__reverse_cmd) for part in (self.__direct_cmd if direct else self.__reverse_cmd)

View File

@ -20,9 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import List
from typing import Optional
from ...logging import get_logger from ...logging import get_logger
from .. import init from .. import init
@ -31,7 +28,7 @@ from .server import PstServer
# ===== # =====
def main(argv: Optional[List[str]]=None) -> None: def main(argv: (list[str] | None)=None) -> None:
config = init( config = init(
prog="kvmd-pst", prog="kvmd-pst",
description="The KVMD persistent storage manager", description="The KVMD persistent storage manager",

View File

@ -23,9 +23,6 @@
import os import os
import asyncio import asyncio
from typing import List
from typing import Dict
from aiohttp.web import Request from aiohttp.web import Request
from aiohttp.web import WebSocketResponse from aiohttp.web import WebSocketResponse
@ -48,7 +45,7 @@ class PstServer(HttpServer): # pylint: disable=too-many-arguments,too-many-inst
storage_path: str, storage_path: str,
ro_retries_delay: float, ro_retries_delay: float,
ro_cleanup_delay: float, ro_cleanup_delay: float,
remount_cmd: List[str], remount_cmd: list[str],
) -> None: ) -> None:
super().__init__() super().__init__()
@ -69,7 +66,7 @@ class PstServer(HttpServer): # pylint: disable=too-many-arguments,too-many-inst
return (await self._ws_loop(ws)) return (await self._ws_loop(ws))
@exposed_ws("ping") @exposed_ws("ping")
async def __ws_ping_handler(self, ws: WsSession, _: Dict) -> None: async def __ws_ping_handler(self, ws: WsSession, _: dict) -> None:
await ws.send_event("pong", {}) await ws.send_event("pong", {})
# ===== SYSTEM STUFF # ===== SYSTEM STUFF

View File

@ -27,9 +27,6 @@ import asyncio
import asyncio.subprocess import asyncio.subprocess
import argparse import argparse
from typing import List
from typing import Optional
import aiohttp import aiohttp
from ...logging import get_logger from ...logging import get_logger
@ -53,7 +50,7 @@ def _preexec() -> None:
get_logger(0).info("Can't perform tcsetpgrp(0): %s", tools.efmt(err)) get_logger(0).info("Can't perform tcsetpgrp(0): %s", tools.efmt(err))
async def _run_process(cmd: List[str], data_path: str) -> asyncio.subprocess.Process: # pylint: disable=no-member async def _run_process(cmd: list[str], data_path: str) -> asyncio.subprocess.Process: # pylint: disable=no-member
# https://stackoverflow.com/questions/58918188/why-is-stdin-not-propagated-to-child-process-of-different-process-group # https://stackoverflow.com/questions/58918188/why-is-stdin-not-propagated-to-child-process-of-different-process-group
if os.isatty(0): if os.isatty(0):
signal.signal(signal.SIGTTOU, signal.SIG_IGN) signal.signal(signal.SIGTTOU, signal.SIG_IGN)
@ -67,11 +64,11 @@ async def _run_process(cmd: List[str], data_path: str) -> asyncio.subprocess.Pro
)) ))
async def _run_cmd_ws(cmd: List[str], ws: aiohttp.ClientWebSocketResponse) -> int: # pylint: disable=too-many-branches async def _run_cmd_ws(cmd: list[str], ws: aiohttp.ClientWebSocketResponse) -> int: # pylint: disable=too-many-branches
logger = get_logger(0) logger = get_logger(0)
receive_task: Optional[asyncio.Task] = None receive_task: (asyncio.Task | None) = None
proc_task: Optional[asyncio.Task] = None proc_task: (asyncio.Task | None) = None
proc: Optional[asyncio.subprocess.Process] = None # pylint: disable=no-member proc: (asyncio.subprocess.Process | None) = None # pylint: disable=no-member
try: # pylint: disable=too-many-nested-blocks try: # pylint: disable=too-many-nested-blocks
while True: while True:
@ -120,7 +117,7 @@ async def _run_cmd_ws(cmd: List[str], ws: aiohttp.ClientWebSocketResponse) -> in
return 1 return 1
async def _run_cmd(cmd: List[str], unix_path: str) -> None: async def _run_cmd(cmd: list[str], unix_path: str) -> None:
get_logger(0).info("Opening PST session ...") get_logger(0).info("Opening PST session ...")
async with aiohttp.ClientSession( async with aiohttp.ClientSession(
headers={"User-Agent": htclient.make_user_agent("KVMD-PSTRun")}, headers={"User-Agent": htclient.make_user_agent("KVMD-PSTRun")},
@ -132,7 +129,7 @@ async def _run_cmd(cmd: List[str], unix_path: str) -> None:
# ===== # =====
def main(argv: Optional[List[str]]=None) -> None: def main(argv: (list[str] | None)=None) -> None:
(parent_parser, argv, config) = init( (parent_parser, argv, config) = init(
add_help=False, add_help=False,
cli_logging=True, cli_logging=True,

View File

@ -20,9 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import List
from typing import Optional
from ...clients.kvmd import KvmdClient from ...clients.kvmd import KvmdClient
from ...clients.streamer import StreamFormats from ...clients.streamer import StreamFormats
from ...clients.streamer import BaseStreamerClient from ...clients.streamer import BaseStreamerClient
@ -38,7 +35,7 @@ from .server import VncServer
# ===== # =====
def main(argv: Optional[List[str]]=None) -> None: def main(argv: (list[str] | None)=None) -> None:
config = init( config = init(
prog="kvmd-vnc", prog="kvmd-vnc",
description="VNC to KVMD proxy", description="VNC to KVMD proxy",
@ -48,12 +45,12 @@ def main(argv: Optional[List[str]]=None) -> None:
user_agent = htclient.make_user_agent("KVMD-VNC") user_agent = htclient.make_user_agent("KVMD-VNC")
def make_memsink_streamer(name: str, fmt: int) -> Optional[MemsinkStreamerClient]: def make_memsink_streamer(name: str, fmt: int) -> (MemsinkStreamerClient | None):
if getattr(config.memsink, name).sink: if getattr(config.memsink, name).sink:
return MemsinkStreamerClient(name.upper(), fmt, **getattr(config.memsink, name)._unpack()) return MemsinkStreamerClient(name.upper(), fmt, **getattr(config.memsink, name)._unpack())
return None return None
streamers: List[BaseStreamerClient] = list(filter(None, [ streamers: list[BaseStreamerClient] = list(filter(None, [
make_memsink_streamer("h264", StreamFormats.H264), make_memsink_streamer("h264", StreamFormats.H264),
make_memsink_streamer("jpeg", StreamFormats.JPEG), make_memsink_streamer("jpeg", StreamFormats.JPEG),
HttpStreamerClient(name="JPEG", user_agent=user_agent, **config.streamer._unpack()), HttpStreamerClient(name="JPEG", user_agent=user_agent, **config.streamer._unpack()),

View File

@ -23,9 +23,6 @@
import asyncio import asyncio
import ssl import ssl
from typing import Tuple
from typing import List
from typing import Dict
from typing import Callable from typing import Callable
from typing import Coroutine from typing import Coroutine
@ -66,7 +63,7 @@ class RfbClient(RfbClientStream): # pylint: disable=too-many-instance-attribute
width: int, width: int,
height: int, height: int,
name: str, name: str,
vnc_passwds: List[str], vnc_passwds: list[str],
vencrypt: bool, vencrypt: bool,
none_auth_only: bool, none_auth_only: bool,
) -> None: ) -> None:
@ -103,7 +100,7 @@ class RfbClient(RfbClientStream): # pylint: disable=too-many-instance-attribute
finally: finally:
await aiotools.shield_fg(self.__cleanup(tasks)) await aiotools.shield_fg(self.__cleanup(tasks))
async def __cleanup(self, tasks: List[asyncio.Task]) -> None: async def __cleanup(self, tasks: list[asyncio.Task]) -> None:
for task in tasks: for task in tasks:
task.cancel() task.cancel()
await asyncio.gather(*tasks, return_exceptions=True) await asyncio.gather(*tasks, return_exceptions=True)
@ -150,7 +147,7 @@ class RfbClient(RfbClientStream): # pylint: disable=too-many-instance-attribute
async def _on_ext_key_event(self, code: int, state: bool) -> None: async def _on_ext_key_event(self, code: int, state: bool) -> None:
raise NotImplementedError raise NotImplementedError
async def _on_pointer_event(self, buttons: Dict[str, bool], wheel: Dict[str, int], move: Dict[str, int]) -> None: async def _on_pointer_event(self, buttons: dict[str, bool], wheel: dict[str, int], move: dict[str, int]) -> None:
raise NotImplementedError raise NotImplementedError
async def _on_cut_event(self, text: str) -> None: async def _on_cut_event(self, text: str) -> None:
@ -232,7 +229,7 @@ class RfbClient(RfbClientStream): # pylint: disable=too-many-instance-attribute
# ===== # =====
async def __handshake_security(self) -> None: async def __handshake_security(self) -> None:
sec_types: Dict[int, Tuple[str, Callable]] = {} sec_types: dict[int, tuple[str, Callable]] = {}
if self.__vencrypt and self.__rfb_version > 3: if self.__vencrypt and self.__rfb_version > 3:
sec_types[19] = ("VeNCrypt", self.__handshake_security_vencrypt) sec_types[19] = ("VeNCrypt", self.__handshake_security_vencrypt)
if self.__none_auth_only: if self.__none_auth_only:

View File

@ -22,8 +22,6 @@
import os import os
from typing import List
import passlib.crypto.des import passlib.crypto.des
@ -43,7 +41,7 @@ def rfb_encrypt_challenge(challenge: bytes, passwd: bytes) -> bytes:
def _make_key(passwd: bytes) -> bytes: def _make_key(passwd: bytes) -> bytes:
passwd = (passwd + b"\0" * 8)[:8] passwd = (passwd + b"\0" * 8)[:8]
key: List[int] = [] key: list[int] = []
for ch in passwd: for ch in passwd:
btgt = 0 btgt = 0
for index in range(8): for index in range(8):

View File

@ -22,10 +22,6 @@
import dataclasses import dataclasses
from typing import List
from typing import Dict
from typing import FrozenSet
from typing import Union
from typing import Any from typing import Any
@ -45,13 +41,13 @@ class RfbEncodings:
H264 = 50 # Open H.264 Encoding H264 = 50 # Open H.264 Encoding
def _make_meta(variants: Union[int, FrozenSet[int]]) -> Dict: def _make_meta(variants: (int | frozenset[int])) -> dict:
return {"variants": (frozenset([variants]) if isinstance(variants, int) else variants)} return {"variants": (frozenset([variants]) if isinstance(variants, int) else variants)}
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class RfbClientEncodings: # pylint: disable=too-many-instance-attributes class RfbClientEncodings: # pylint: disable=too-many-instance-attributes
encodings: FrozenSet[int] encodings: frozenset[int]
has_resize: bool = dataclasses.field(default=False, metadata=_make_meta(RfbEncodings.RESIZE)) # noqa: E224 has_resize: bool = dataclasses.field(default=False, metadata=_make_meta(RfbEncodings.RESIZE)) # noqa: E224
has_rename: bool = dataclasses.field(default=False, metadata=_make_meta(RfbEncodings.RENAME)) # noqa: E224 has_rename: bool = dataclasses.field(default=False, metadata=_make_meta(RfbEncodings.RENAME)) # noqa: E224
@ -63,8 +59,8 @@ class RfbClientEncodings: # pylint: disable=too-many-instance-attributes
has_h264: bool = dataclasses.field(default=False, metadata=_make_meta(RfbEncodings.H264)) # noqa: E224 has_h264: bool = dataclasses.field(default=False, metadata=_make_meta(RfbEncodings.H264)) # noqa: E224
def get_summary(self) -> List[str]: def get_summary(self) -> list[str]:
summary: List[str] = [f"encodings -- {sorted(self.encodings)}"] summary: list[str] = [f"encodings -- {sorted(self.encodings)}"]
for field in dataclasses.fields(self): for field in dataclasses.fields(self):
if field.name != "encodings": if field.name != "encodings":
found = ", ".join(map(str, sorted(map(int, self.__get_found(field))))) found = ", ".join(map(str, sorted(map(int, self.__get_found(field)))))
@ -80,7 +76,7 @@ class RfbClientEncodings: # pylint: disable=too-many-instance-attributes
def __set_value(self, key: str, value: Any) -> None: def __set_value(self, key: str, value: Any) -> None:
object.__setattr__(self, key, value) object.__setattr__(self, key, value)
def __get_found(self, field: dataclasses.Field) -> FrozenSet[int]: def __get_found(self, field: dataclasses.Field) -> frozenset[int]:
return self.encodings.intersection(field.metadata["variants"]) return self.encodings.intersection(field.metadata["variants"])
def __get_tight_jpeg_quality(self) -> int: def __get_tight_jpeg_quality(self) -> int:

View File

@ -24,9 +24,6 @@ import asyncio
import ssl import ssl
import struct import struct
from typing import Tuple
from typing import Union
from .... import aiotools from .... import aiotools
from .errors import RfbConnectionError from .errors import RfbConnectionError
@ -57,7 +54,7 @@ class RfbClientStream:
except (ConnectionError, asyncio.IncompleteReadError) as err: except (ConnectionError, asyncio.IncompleteReadError) as err:
raise RfbConnectionError(f"Can't read {msg}", err) raise RfbConnectionError(f"Can't read {msg}", err)
async def _read_struct(self, msg: str, fmt: str) -> Tuple[int, ...]: async def _read_struct(self, msg: str, fmt: str) -> tuple[int, ...]:
assert len(fmt) > 1 assert len(fmt) > 1
try: try:
fmt = f">{fmt}" fmt = f">{fmt}"
@ -73,7 +70,7 @@ class RfbClientStream:
# ===== # =====
async def _write_struct(self, msg: str, fmt: str, *values: Union[int, bytes], drain: bool=True) -> None: async def _write_struct(self, msg: str, fmt: str, *values: (int | bytes), drain: bool=True) -> None:
try: try:
if not fmt: if not fmt:
for value in values: for value in values:

View File

@ -26,11 +26,6 @@ import socket
import dataclasses import dataclasses
import contextlib import contextlib
from typing import List
from typing import Dict
from typing import Union
from typing import Optional
import aiohttp import aiohttp
from ...logging import get_logger from ...logging import get_logger
@ -83,12 +78,12 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
desired_fps: int, desired_fps: int,
keymap_name: str, keymap_name: str,
symmap: Dict[int, Dict[int, str]], symmap: dict[int, dict[int, str]],
kvmd: KvmdClient, kvmd: KvmdClient,
streamers: List[BaseStreamerClient], streamers: list[BaseStreamerClient],
vnc_credentials: Dict[str, VncAuthKvmdCredentials], vnc_credentials: dict[str, VncAuthKvmdCredentials],
vencrypt: bool, vencrypt: bool,
none_auth_only: bool, none_auth_only: bool,
shared_params: _SharedParams, shared_params: _SharedParams,
@ -122,15 +117,15 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
self.__stage2_encodings_accepted = aiotools.AioStage() self.__stage2_encodings_accepted = aiotools.AioStage()
self.__stage3_ws_connected = aiotools.AioStage() self.__stage3_ws_connected = aiotools.AioStage()
self.__kvmd_session: Optional[KvmdClientSession] = None self.__kvmd_session: (KvmdClientSession | None) = None
self.__kvmd_ws: Optional[KvmdClientWs] = None self.__kvmd_ws: (KvmdClientWs | None) = None
self.__fb_notifier = aiotools.AioNotifier() self.__fb_notifier = aiotools.AioNotifier()
self.__fb_queue: "asyncio.Queue[Dict]" = asyncio.Queue() self.__fb_queue: "asyncio.Queue[dict]" = asyncio.Queue()
# Эти состояния шарить не обязательно - бекенд исключает дублирующиеся события. # Эти состояния шарить не обязательно - бекенд исключает дублирующиеся события.
# Все это нужно только чтобы не посылать лишние жсоны в сокет KVMD # Все это нужно только чтобы не посылать лишние жсоны в сокет KVMD
self.__mouse_buttons: Dict[str, Optional[bool]] = dict.fromkeys(["left", "right", "middle"], None) self.__mouse_buttons: dict[str, (bool | None)] = dict.fromkeys(["left", "right", "middle"], None)
self.__mouse_move = {"x": -1, "y": -1} self.__mouse_move = {"x": -1, "y": -1}
self.__lock = asyncio.Lock() self.__lock = asyncio.Lock()
@ -175,7 +170,7 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
finally: finally:
self.__kvmd_ws = None self.__kvmd_ws = None
async def __process_ws_event(self, event_type: str, event: Dict) -> None: async def __process_ws_event(self, event_type: str, event: dict) -> None:
if event_type == "info_meta_state": if event_type == "info_meta_state":
try: try:
host = event["server"]["host"] host = event["server"]["host"]
@ -225,7 +220,7 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
StreamFormats.JPEG: "has_tight", StreamFormats.JPEG: "has_tight",
StreamFormats.H264: "has_h264", StreamFormats.H264: "has_h264",
} }
streamer: Optional[BaseStreamerClient] = None streamer: (BaseStreamerClient | None) = None
for streamer in self.__streamers: for streamer in self.__streamers:
if getattr(self._encodings, formats[streamer.get_format()]): if getattr(self._encodings, formats[streamer.get_format()]):
get_logger(0).info("%s [streamer]: Using preferred %s", self._remote, streamer) get_logger(0).info("%s [streamer]: Using preferred %s", self._remote, streamer)
@ -237,12 +232,12 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
get_logger(0).info("%s [streamer]: Using default %s", self._remote, streamer) get_logger(0).info("%s [streamer]: Using default %s", self._remote, streamer)
return streamer return streamer
async def __queue_frame(self, frame: Union[Dict, str]) -> None: async def __queue_frame(self, frame: (dict | str)) -> None:
if isinstance(frame, str): if isinstance(frame, str):
frame = await self.__make_text_frame(frame) frame = await self.__make_text_frame(frame)
self.__fb_queue.put_nowait(frame) self.__fb_queue.put_nowait(frame)
async def __make_text_frame(self, text: str) -> Dict: async def __make_text_frame(self, text: str) -> dict:
return { return {
"data": (await make_text_jpeg(self._width, self._height, self._encodings.tight_jpeg_quality, text)), "data": (await make_text_jpeg(self._width, self._height, self._encodings.tight_jpeg_quality, text)),
"width": self._width, "width": self._width,
@ -252,7 +247,7 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
async def __fb_sender_task_loop(self) -> None: # pylint: disable=too-many-branches async def __fb_sender_task_loop(self) -> None: # pylint: disable=too-many-branches
has_h264_key = False has_h264_key = False
last: Optional[Dict] = None last: (dict | None) = None
while True: while True:
await self.__fb_notifier.wait() await self.__fb_notifier.wait()
@ -351,7 +346,7 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
if self.__kvmd_ws: if self.__kvmd_ws:
await self.__kvmd_ws.send_key_event(web_key, state) await self.__kvmd_ws.send_key_event(web_key, state)
def __switch_modifiers(self, key: Union[int, str], state: bool) -> bool: def __switch_modifiers(self, key: (int | str), state: bool) -> bool:
mod = 0 mod = 0
if key in X11Modifiers.SHIFTS or key in WebModifiers.SHIFTS: if key in X11Modifiers.SHIFTS or key in WebModifiers.SHIFTS:
mod = SymmapModifiers.SHIFT mod = SymmapModifiers.SHIFT
@ -367,7 +362,7 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
self.__modifiers &= ~mod self.__modifiers &= ~mod
return True return True
async def _on_pointer_event(self, buttons: Dict[str, bool], wheel: Dict[str, int], move: Dict[str, int]) -> None: async def _on_pointer_event(self, buttons: dict[str, bool], wheel: dict[str, int], move: dict[str, int]) -> None:
if self.__kvmd_ws: if self.__kvmd_ws:
for (button, state) in buttons.items(): for (button, state) in buttons.items():
if self.__mouse_buttons[button] != state: if self.__mouse_buttons[button] != state:
@ -434,7 +429,7 @@ class VncServer: # pylint: disable=too-many-instance-attributes
keymap_path: str, keymap_path: str,
kvmd: KvmdClient, kvmd: KvmdClient,
streamers: List[BaseStreamerClient], streamers: list[BaseStreamerClient],
vnc_auth_manager: VncAuthManager, vnc_auth_manager: VncAuthManager,
) -> None: ) -> None:

View File

@ -22,9 +22,6 @@
import dataclasses import dataclasses
from typing import Tuple
from typing import Dict
from ...logging import get_logger from ...logging import get_logger
from ... import aiofs from ... import aiofs
@ -53,7 +50,7 @@ class VncAuthManager:
self.__path = path self.__path = path
self.__enabled = enabled self.__enabled = enabled
async def read_credentials(self) -> Tuple[Dict[str, VncAuthKvmdCredentials], bool]: async def read_credentials(self) -> tuple[dict[str, VncAuthKvmdCredentials], bool]:
if self.__enabled: if self.__enabled:
try: try:
return (await self.__inner_read_credentials(), True) return (await self.__inner_read_credentials(), True)
@ -63,10 +60,10 @@ class VncAuthManager:
get_logger(0).exception("Unhandled exception while reading VNCAuth passwd file") get_logger(0).exception("Unhandled exception while reading VNCAuth passwd file")
return ({}, (not self.__enabled)) return ({}, (not self.__enabled))
async def __inner_read_credentials(self) -> Dict[str, VncAuthKvmdCredentials]: async def __inner_read_credentials(self) -> dict[str, VncAuthKvmdCredentials]:
lines = (await aiofs.read(self.__path)).split("\n") lines = (await aiofs.read(self.__path)).split("\n")
credentials: Dict[str, VncAuthKvmdCredentials] = {} credentials: dict[str, VncAuthKvmdCredentials] = {}
for (lineno, line) in enumerate(lines): for (lineno, line) in enumerate(lines):
if len(line.strip()) == 0 or line.lstrip().startswith("#"): if len(line.strip()) == 0 or line.lstrip().startswith("#"):
continue continue

View File

@ -24,9 +24,6 @@ import argparse
import errno import errno
import time import time
from typing import List
from typing import Optional
from ...logging import get_logger from ...logging import get_logger
from ...yamlconf import Section from ...yamlconf import Section
@ -104,7 +101,7 @@ def _cmd_cancel(config: Section) -> None:
# ===== # =====
def main(argv: Optional[List[str]]=None) -> None: def main(argv: (list[str] | None)=None) -> None:
(parent_parser, argv, config) = init(add_help=False, argv=argv) (parent_parser, argv, config) = init(add_help=False, argv=argv)
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
prog="kvmd-watchdog", prog="kvmd-watchdog",

View File

@ -24,13 +24,8 @@ import asyncio
import contextlib import contextlib
import types import types
from typing import Tuple
from typing import Dict
from typing import Set
from typing import Callable from typing import Callable
from typing import Type
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional
import aiohttp import aiohttp
@ -65,13 +60,13 @@ class _AuthApiPart(_BaseApiPart):
class _StreamerApiPart(_BaseApiPart): class _StreamerApiPart(_BaseApiPart):
async def get_state(self) -> Dict: async def get_state(self) -> dict:
session = self._ensure_http_session() session = self._ensure_http_session()
async with session.get(self._make_url("streamer")) as response: async with session.get(self._make_url("streamer")) as response:
htclient.raise_not_200(response) htclient.raise_not_200(response)
return (await response.json())["result"] return (await response.json())["result"]
async def set_params(self, quality: Optional[int]=None, desired_fps: Optional[int]=None) -> None: async def set_params(self, quality: (int | None)=None, desired_fps: (int | None)=None) -> None:
session = self._ensure_http_session() session = self._ensure_http_session()
async with session.post( async with session.post(
url=self._make_url("streamer/set_params"), url=self._make_url("streamer/set_params"),
@ -88,7 +83,7 @@ class _StreamerApiPart(_BaseApiPart):
class _HidApiPart(_BaseApiPart): class _HidApiPart(_BaseApiPart):
async def get_keymaps(self) -> Tuple[str, Set[str]]: async def get_keymaps(self) -> tuple[str, set[str]]:
session = self._ensure_http_session() session = self._ensure_http_session()
async with session.get(self._make_url("hid/keymaps")) as response: async with session.get(self._make_url("hid/keymaps")) as response:
htclient.raise_not_200(response) htclient.raise_not_200(response)
@ -106,7 +101,7 @@ class _HidApiPart(_BaseApiPart):
class _AtxApiPart(_BaseApiPart): class _AtxApiPart(_BaseApiPart):
async def get_state(self) -> Dict: async def get_state(self) -> dict:
session = self._ensure_http_session() session = self._ensure_http_session()
async with session.get(self._make_url("atx")) as response: async with session.get(self._make_url("atx")) as response:
htclient.raise_not_200(response) htclient.raise_not_200(response)
@ -132,14 +127,14 @@ class KvmdClientWs:
def __init__(self, ws: aiohttp.ClientWebSocketResponse) -> None: def __init__(self, ws: aiohttp.ClientWebSocketResponse) -> None:
self.__ws = ws self.__ws = ws
self.__writer_queue: "asyncio.Queue[Tuple[str, Dict]]" = asyncio.Queue() self.__writer_queue: "asyncio.Queue[tuple[str, dict]]" = asyncio.Queue()
self.__communicated = False self.__communicated = False
async def communicate(self) -> AsyncGenerator[Tuple[str, Dict], None]: # pylint: disable=too-many-branches async def communicate(self) -> AsyncGenerator[tuple[str, dict], None]: # pylint: disable=too-many-branches
assert not self.__communicated assert not self.__communicated
self.__communicated = True self.__communicated = True
receive_task: Optional[asyncio.Task] = None receive_task: (asyncio.Task | None) = None
writer_task: Optional[asyncio.Task] = None writer_task: (asyncio.Task | None) = None
try: try:
while True: while True:
if receive_task is None: if receive_task is None:
@ -199,7 +194,7 @@ class KvmdClientSession:
self.__make_http_session = make_http_session self.__make_http_session = make_http_session
self.__make_url = make_url self.__make_url = make_url
self.__http_session: Optional[aiohttp.ClientSession] = None self.__http_session: (aiohttp.ClientSession | None) = None
args = (self.__ensure_http_session, make_url) args = (self.__ensure_http_session, make_url)
@ -229,7 +224,7 @@ class KvmdClientSession:
async def __aexit__( async def __aexit__(
self, self,
_exc_type: Type[BaseException], _exc_type: type[BaseException],
_exc: BaseException, _exc: BaseException,
_tb: types.TracebackType, _tb: types.TracebackType,
) -> None: ) -> None:
@ -256,7 +251,7 @@ class KvmdClient:
) )
def __make_http_session(self, user: str, passwd: str) -> aiohttp.ClientSession: def __make_http_session(self, user: str, passwd: str) -> aiohttp.ClientSession:
kwargs: Dict = { kwargs: dict = {
"headers": { "headers": {
"X-KVMD-User": user, "X-KVMD-User": user,
"X-KVMD-Passwd": passwd, "X-KVMD-Passwd": passwd,

View File

@ -22,7 +22,6 @@
import types import types
from typing import Dict
from typing import AsyncGenerator from typing import AsyncGenerator
import aiohttp import aiohttp
@ -61,7 +60,7 @@ class BaseStreamerClient:
def get_format(self) -> int: def get_format(self) -> int:
raise NotImplementedError() raise NotImplementedError()
async def read_stream(self) -> AsyncGenerator[Dict, None]: async def read_stream(self) -> AsyncGenerator[dict, None]:
if self is not None: # XXX: Vulture and pylint hack if self is not None: # XXX: Vulture and pylint hack
raise NotImplementedError() raise NotImplementedError()
yield yield
@ -84,7 +83,7 @@ class HttpStreamerClient(BaseStreamerClient):
def get_format(self) -> int: def get_format(self) -> int:
return StreamFormats.JPEG return StreamFormats.JPEG
async def read_stream(self) -> AsyncGenerator[Dict, None]: async def read_stream(self) -> AsyncGenerator[dict, None]:
try: try:
async with self.__make_http_session() as session: async with self.__make_http_session() as session:
async with session.get( async with session.get(
@ -118,7 +117,7 @@ class HttpStreamerClient(BaseStreamerClient):
raise StreamerTempError("Reached EOF") raise StreamerTempError("Reached EOF")
def __make_http_session(self) -> aiohttp.ClientSession: def __make_http_session(self) -> aiohttp.ClientSession:
kwargs: Dict = { kwargs: dict = {
"headers": {"User-Agent": self.__user_agent}, "headers": {"User-Agent": self.__user_agent},
"connector": aiohttp.UnixConnector(path=self.__unix_path), "connector": aiohttp.UnixConnector(path=self.__unix_path),
"timeout": aiohttp.ClientTimeout( "timeout": aiohttp.ClientTimeout(
@ -162,7 +161,7 @@ class MemsinkStreamerClient(BaseStreamerClient):
self.__name = name self.__name = name
self.__fmt = fmt self.__fmt = fmt
self.__kwargs: Dict = { self.__kwargs: dict = {
"obj": obj, "obj": obj,
"lock_timeout": lock_timeout, "lock_timeout": lock_timeout,
"wait_timeout": wait_timeout, "wait_timeout": wait_timeout,
@ -172,7 +171,7 @@ class MemsinkStreamerClient(BaseStreamerClient):
def get_format(self) -> int: def get_format(self) -> int:
return self.__fmt return self.__fmt
async def read_stream(self) -> AsyncGenerator[Dict, None]: async def read_stream(self) -> AsyncGenerator[dict, None]:
if ustreamer is None: if ustreamer is None:
raise StreamerPermError("Missing ustreamer library") raise StreamerPermError("Missing ustreamer library")
try: try:

View File

@ -27,8 +27,6 @@ import shutil
import dataclasses import dataclasses
import subprocess import subprocess
from typing import List
# ==== # ====
_MOUNT_PATH = "/bin/mount" _MOUNT_PATH = "/bin/mount"
@ -98,7 +96,7 @@ def main() -> None:
raise SystemExit(f"Usage: {sys.argv[0]} [ro|rw]") raise SystemExit(f"Usage: {sys.argv[0]} [ro|rw]")
target = "" target = ""
dirs: List[str] = [] dirs: list[str] = []
app = os.path.basename(sys.argv[0]) app = os.path.basename(sys.argv[0])
if app == "kvmd-helper-otgmsd-remount": if app == "kvmd-helper-otgmsd-remount":
target = "otgmsd" target = "otgmsd"

View File

@ -23,9 +23,7 @@
import os import os
import contextlib import contextlib
from typing import Dict
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional
import aiohttp import aiohttp
import aiohttp.multipart import aiohttp.multipart
@ -68,11 +66,11 @@ async def download(
url: str, url: str,
verify: bool=True, verify: bool=True,
timeout: float=10.0, timeout: float=10.0,
read_timeout: Optional[float]=None, read_timeout: (float | None)=None,
app: str="KVMD", app: str="KVMD",
) -> AsyncGenerator[aiohttp.ClientResponse, None]: ) -> AsyncGenerator[aiohttp.ClientResponse, None]:
kwargs: Dict = { kwargs: dict = {
"headers": {"User-Agent": make_user_agent(app)}, "headers": {"User-Agent": make_user_agent(app)},
"timeout": aiohttp.ClientTimeout( "timeout": aiohttp.ClientTimeout(
connect=timeout, connect=timeout,

View File

@ -29,13 +29,8 @@ import inspect
import urllib.parse import urllib.parse
import json import json
from typing import Tuple
from typing import List
from typing import Dict
from typing import Callable from typing import Callable
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional
from typing import Union
from typing import Any from typing import Any
from aiohttp import ClientWebSocketResponse from aiohttp import ClientWebSocketResponse
@ -111,7 +106,7 @@ def exposed_http(http_method: str, path: str, auth_required: bool=True) -> Calla
return set_attrs return set_attrs
def _get_exposed_http(obj: object) -> List[HttpExposed]: def _get_exposed_http(obj: object) -> list[HttpExposed]:
return [ return [
HttpExposed( HttpExposed(
method=getattr(handler, _HTTP_METHOD), method=getattr(handler, _HTTP_METHOD),
@ -143,7 +138,7 @@ def exposed_ws(event_type: str) -> Callable:
return set_attrs return set_attrs
def _get_exposed_ws(obj: object) -> List[WsExposed]: def _get_exposed_ws(obj: object) -> list[WsExposed]:
return [ return [
WsExposed( WsExposed(
event_type=getattr(handler, _WS_EVENT_TYPE), event_type=getattr(handler, _WS_EVENT_TYPE),
@ -156,9 +151,9 @@ def _get_exposed_ws(obj: object) -> List[WsExposed]:
# ===== # =====
def make_json_response( def make_json_response(
result: Optional[Dict]=None, result: (dict | None)=None,
status: int=200, status: int=200,
set_cookies: Optional[Dict[str, str]]=None, set_cookies: (dict[str, str] | None)=None,
wrap_result: bool=True, wrap_result: bool=True,
) -> Response: ) -> Response:
@ -176,7 +171,7 @@ def make_json_response(
return response return response
def make_json_exception(err: Exception, status: Optional[int]=None) -> Response: def make_json_exception(err: Exception, status: (int | None)=None) -> Response:
name = type(err).__name__ name = type(err).__name__
msg = str(err) msg = str(err)
if isinstance(err, HttpError): if isinstance(err, HttpError):
@ -208,7 +203,7 @@ async def start_streaming(
return response return response
async def stream_json(response: StreamResponse, result: Dict, ok: bool=True) -> None: async def stream_json(response: StreamResponse, result: dict, ok: bool=True) -> None:
await response.write(json.dumps({ await response.write(json.dumps({
"ok": ok, "ok": ok,
"result": result, "result": result,
@ -226,9 +221,9 @@ async def stream_json_exception(response: StreamResponse, err: Exception) -> Non
async def send_ws_event( async def send_ws_event(
wsr: Union[ClientWebSocketResponse, WebSocketResponse], wsr: (ClientWebSocketResponse | WebSocketResponse),
event_type: str, event_type: str,
event: Optional[Dict], event: (dict | None),
) -> None: ) -> None:
await wsr.send_str(json.dumps({ await wsr.send_str(json.dumps({
@ -237,7 +232,7 @@ async def send_ws_event(
})) }))
def parse_ws_event(msg: str) -> Tuple[str, Dict]: def parse_ws_event(msg: str) -> tuple[str, dict]:
data = json.loads(msg) data = json.loads(msg)
if not isinstance(data, dict): if not isinstance(data, dict):
raise RuntimeError("Top-level event structure is not a dict") raise RuntimeError("Top-level event structure is not a dict")
@ -269,20 +264,20 @@ def set_request_auth_info(request: BaseRequest, info: str) -> None:
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class WsSession: class WsSession:
wsr: WebSocketResponse wsr: WebSocketResponse
kwargs: Dict[str, Any] kwargs: dict[str, Any]
def __str__(self) -> str: def __str__(self) -> str:
return f"WsSession(id={id(self)}, {self.kwargs})" return f"WsSession(id={id(self)}, {self.kwargs})"
async def send_event(self, event_type: str, event: Optional[Dict]) -> None: async def send_event(self, event_type: str, event: (dict | None)) -> None:
await send_ws_event(self.wsr, event_type, event) await send_ws_event(self.wsr, event_type, event)
class HttpServer: class HttpServer:
def __init__(self) -> None: def __init__(self) -> None:
self.__ws_heartbeat: Optional[float] = None self.__ws_heartbeat: (float | None) = None
self.__ws_handlers: Dict[str, Callable] = {} self.__ws_handlers: dict[str, Callable] = {}
self.__ws_sessions: List[WsSession] = [] self.__ws_sessions: list[WsSession] = []
self.__ws_sessions_lock = asyncio.Lock() self.__ws_sessions_lock = asyncio.Lock()
def run( def run(
@ -373,7 +368,7 @@ class HttpServer:
logger.error("Unknown websocket event: %r", msg.data) logger.error("Unknown websocket event: %r", msg.data)
return ws.wsr return ws.wsr
async def _broadcast_ws_event(self, event_type: str, event: Optional[Dict]) -> None: async def _broadcast_ws_event(self, event_type: str, event: (dict | None)) -> None:
if self.__ws_sessions: if self.__ws_sessions:
await asyncio.gather(*[ await asyncio.gather(*[
ws.send_event(event_type, event) ws.send_event(event_type, event)
@ -391,7 +386,7 @@ class HttpServer:
await self.__close_ws(ws) await self.__close_ws(ws)
return bool(wss) return bool(wss)
def _get_wss(self) -> List[WsSession]: def _get_wss(self) -> list[WsSession]:
return list(self.__ws_sessions) return list(self.__ws_sessions)
async def __close_ws(self, ws: WsSession) -> None: async def __close_ws(self, ws: WsSession) -> None:

View File

@ -31,12 +31,7 @@ import dataclasses
import types import types
import errno import errno
from typing import Tuple
from typing import List
from typing import Dict
from typing import Type
from typing import Generator from typing import Generator
from typing import Optional
from .logging import get_logger from .logging import get_logger
@ -53,7 +48,7 @@ _FS_ENCODING = (sys.getfilesystemencoding() or _FS_FALLBACK_ENCODING)
# ===== # =====
def _inotify_parsed_buffer(data: bytes) -> Generator[Tuple[int, int, int, bytes], None, None]: def _inotify_parsed_buffer(data: bytes) -> Generator[tuple[int, int, int, bytes], None, None]:
offset = 0 offset = 0
while offset + _EVENT_HEAD_SIZE <= len(data): while offset + _EVENT_HEAD_SIZE <= len(data):
(wd, mask, cookie, length) = struct.unpack_from("iIII", data, offset) (wd, mask, cookie, length) = struct.unpack_from("iIII", data, offset)
@ -157,7 +152,7 @@ class InotifyMask:
@classmethod @classmethod
def to_string(cls, mask: int) -> str: def to_string(cls, mask: int) -> str:
flags: List[str] = [] flags: list[str] = []
for name in dir(cls): for name in dir(cls):
if ( if (
name[0].isupper() name[0].isupper()
@ -188,10 +183,10 @@ class Inotify:
def __init__(self) -> None: def __init__(self) -> None:
self.__fd = -1 self.__fd = -1
self.__wd_by_path: Dict[str, int] = {} self.__wd_by_path: dict[str, int] = {}
self.__path_by_wd: Dict[int, str] = {} self.__path_by_wd: dict[int, str] = {}
self.__moved: Dict[int, str] = {} self.__moved: dict[int, str] = {}
self.__events_queue: "asyncio.Queue[InotifyEvent]" = asyncio.Queue() self.__events_queue: "asyncio.Queue[InotifyEvent]" = asyncio.Queue()
@ -215,15 +210,15 @@ class Inotify:
# def has_events(self) -> bool: # def has_events(self) -> bool:
# return (not self.__events_queue.empty()) # return (not self.__events_queue.empty())
async def get_event(self, timeout: float) -> Optional[InotifyEvent]: async def get_event(self, timeout: float) -> (InotifyEvent | None):
assert timeout > 0 assert timeout > 0
try: try:
return (await asyncio.wait_for(self.__events_queue.get(), timeout=timeout)) return (await asyncio.wait_for(self.__events_queue.get(), timeout=timeout))
except asyncio.TimeoutError: except asyncio.TimeoutError:
return None return None
async def get_series(self, timeout: float) -> List[InotifyEvent]: async def get_series(self, timeout: float) -> list[InotifyEvent]:
series: List[InotifyEvent] = [] series: list[InotifyEvent] = []
event = await self.get_event(timeout) event = await self.get_event(timeout)
if event: if event:
series.append(event) series.append(event)
@ -283,7 +278,7 @@ class Inotify:
def __exit__( def __exit__(
self, self,
_exc_type: Type[BaseException], _exc_type: type[BaseException],
_exc: BaseException, _exc: BaseException,
_tb: types.TracebackType, _tb: types.TracebackType,
) -> None: ) -> None:

View File

@ -24,9 +24,6 @@ import pkgutil
import functools import functools
import importlib.machinery import importlib.machinery
from typing import List
from typing import Dict
import Xlib.keysymdef import Xlib.keysymdef
from ..logging import get_logger from ..logging import get_logger
@ -44,11 +41,11 @@ class SymmapModifiers:
CTRL: int = 0x4 CTRL: int = 0x4
def build_symmap(path: str) -> Dict[int, Dict[int, str]]: def build_symmap(path: str) -> dict[int, dict[int, str]]:
# https://github.com/qemu/qemu/blob/95a9457fd44ad97c518858a4e1586a5498f9773c/ui/keymaps.c # https://github.com/qemu/qemu/blob/95a9457fd44ad97c518858a4e1586a5498f9773c/ui/keymaps.c
logger = get_logger() logger = get_logger()
symmap: Dict[int, Dict[int, str]] = {} symmap: dict[int, dict[int, str]] = {}
for (src, items) in [ for (src, items) in [
("<builtin>", list(X11_TO_AT1.items())), ("<builtin>", list(X11_TO_AT1.items())),
(path, list(_read_keyboard_layout(path).items())), (path, list(_read_keyboard_layout(path).items())),
@ -78,8 +75,8 @@ def build_symmap(path: str) -> Dict[int, Dict[int, str]]:
# ===== # =====
@functools.lru_cache() @functools.lru_cache()
def _get_keysyms() -> Dict[str, int]: def _get_keysyms() -> dict[str, int]:
keysyms: Dict[str, int] = {} keysyms: dict[str, int] = {}
for (finder, module_name, _) in pkgutil.walk_packages(Xlib.keysymdef.__path__): for (finder, module_name, _) in pkgutil.walk_packages(Xlib.keysymdef.__path__):
if not isinstance(finder, importlib.machinery.FileFinder): if not isinstance(finder, importlib.machinery.FileFinder):
continue continue
@ -109,14 +106,14 @@ def _resolve_keysym(name: str) -> int:
return 0 return 0
def _read_keyboard_layout(path: str) -> Dict[int, List[At1Key]]: # Keysym to evdev (at1) def _read_keyboard_layout(path: str) -> dict[int, list[At1Key]]: # Keysym to evdev (at1)
logger = get_logger(0) logger = get_logger(0)
logger.info("Reading keyboard layout %s ...", path) logger.info("Reading keyboard layout %s ...", path)
with open(path) as layout_file: with open(path) as layout_file:
lines = list(map(str.strip, layout_file.read().split("\n"))) lines = list(map(str.strip, layout_file.read().split("\n")))
layout: Dict[int, List[At1Key]] = {} layout: dict[int, list[At1Key]] = {}
for (lineno, line) in enumerate(lines): for (lineno, line) in enumerate(lines):
if len(line) == 0 or line.startswith(("#", "map ", "include ")): if len(line) == 0 or line.startswith(("#", "map ", "include ")):
continue continue

View File

@ -22,8 +22,6 @@
import dataclasses import dataclasses
from typing import Dict
# ===== # =====
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
@ -43,7 +41,7 @@ class Key:
usb: UsbKey usb: UsbKey
<%! import operator %> <%! import operator %>
KEYMAP: Dict[str, Key] = { KEYMAP: dict[str, Key] = {
% for km in sorted(keymap, key=operator.attrgetter("mcu_code")): % for km in sorted(keymap, key=operator.attrgetter("mcu_code")):
"${km.web_name}": Key(mcu=McuKey(code=${km.mcu_code}), usb=UsbKey(code=${km.usb_key.code}, is_modifier=${km.usb_key.is_modifier})), "${km.web_name}": Key(mcu=McuKey(code=${km.mcu_code}), usb=UsbKey(code=${km.usb_key.code}, is_modifier=${km.usb_key.is_modifier})),
% endfor % endfor

View File

@ -20,8 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import Tuple
from typing import Dict
from typing import Generator from typing import Generator
from .keysym import SymmapModifiers from .keysym import SymmapModifiers
@ -31,9 +29,9 @@ from .mappings import WebModifiers
# ===== # =====
def text_to_web_keys( # pylint: disable=too-many-branches def text_to_web_keys( # pylint: disable=too-many-branches
text: str, text: str,
symmap: Dict[int, Dict[int, str]], symmap: dict[int, dict[int, str]],
shift_key: str=WebModifiers.SHIFT_LEFT, shift_key: str=WebModifiers.SHIFT_LEFT,
) -> Generator[Tuple[str, bool], None, None]: ) -> Generator[tuple[str, bool], None, None]:
assert shift_key in WebModifiers.SHIFTS assert shift_key in WebModifiers.SHIFTS

View File

@ -24,12 +24,10 @@ import sys
import types import types
import logging import logging
from typing import Optional
# ===== # =====
def get_logger(depth: int=1) -> logging.Logger: def get_logger(depth: int=1) -> logging.Logger:
frame: Optional[types.FrameType] = sys._getframe(1) # pylint: disable=protected-access frame: (types.FrameType | None) = sys._getframe(1) # pylint: disable=protected-access
assert frame assert frame
frames = [] frames = []
while frame: while frame:

View File

@ -23,8 +23,6 @@
import importlib import importlib
import functools import functools
from typing import Dict
from typing import Type
from typing import Any from typing import Any
@ -44,12 +42,12 @@ class BasePlugin:
return name[name.rindex(".") + 1:] return name[name.rindex(".") + 1:]
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return {} # pragma: nocover return {} # pragma: nocover
@functools.lru_cache() @functools.lru_cache()
def get_plugin_class(sub: str, name: str) -> Type[BasePlugin]: def get_plugin_class(sub: str, name: str) -> type[BasePlugin]:
assert sub assert sub
assert name assert name
if name.startswith("_"): if name.startswith("_"):

View File

@ -20,9 +20,7 @@
# ========================================================================== # # ========================================================================== #
from typing import Dict
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Type
from ...errors import OperationError from ...errors import OperationError
from ...errors import IsBusyError from ...errors import IsBusyError
@ -47,10 +45,10 @@ class AtxIsBusyError(IsBusyError, AtxError):
# ===== # =====
class BaseAtx(BasePlugin): class BaseAtx(BasePlugin):
async def get_state(self) -> Dict: async def get_state(self) -> dict:
raise NotImplementedError raise NotImplementedError
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
yield {} yield {}
raise NotImplementedError raise NotImplementedError
@ -84,5 +82,5 @@ class BaseAtx(BasePlugin):
# ===== # =====
def get_atx_class(name: str) -> Type[BaseAtx]: def get_atx_class(name: str) -> type[BaseAtx]:
return get_plugin_class("atx", name) # type: ignore return get_plugin_class("atx", name) # type: ignore

View File

@ -20,7 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import Dict
from typing import AsyncGenerator from typing import AsyncGenerator
from ... import aiotools from ... import aiotools
@ -37,7 +36,7 @@ class AtxDisabledError(AtxOperationError):
# ===== # =====
class Plugin(BaseAtx): class Plugin(BaseAtx):
async def get_state(self) -> Dict: async def get_state(self) -> dict:
return { return {
"enabled": False, "enabled": False,
"busy": False, "busy": False,
@ -47,7 +46,7 @@ class Plugin(BaseAtx):
}, },
} }
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
while True: while True:
yield (await self.get_state()) yield (await self.get_state())
await aiotools.wait_infinite() await aiotools.wait_infinite()

View File

@ -20,9 +20,7 @@
# ========================================================================== # # ========================================================================== #
from typing import Dict
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional
import gpiod import gpiod
@ -76,9 +74,9 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
self.__notifier = aiotools.AioNotifier() self.__notifier = aiotools.AioNotifier()
self.__region = aiotools.AioExclusiveRegion(AtxIsBusyError, self.__notifier) self.__region = aiotools.AioExclusiveRegion(AtxIsBusyError, self.__notifier)
self.__chip: Optional[gpiod.Chip] = None self.__chip: (gpiod.Chip | None) = None
self.__power_switch_line: Optional[gpiod.Line] = None self.__power_switch_line: (gpiod.Line | None) = None
self.__reset_switch_line: Optional[gpiod.Line] = None self.__reset_switch_line: (gpiod.Line | None) = None
self.__reader = aiogp.AioReader( self.__reader = aiogp.AioReader(
path=self.__device_path, path=self.__device_path,
@ -91,7 +89,7 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
) )
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"device": Option("/dev/gpiochip0", type=valid_abs_path, unpack_as="device_path"), "device": Option("/dev/gpiochip0", type=valid_abs_path, unpack_as="device_path"),
@ -122,7 +120,7 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
self.__reset_switch_line = self.__chip.get_line(self.__reset_switch_pin) self.__reset_switch_line = self.__chip.get_line(self.__reset_switch_pin)
self.__reset_switch_line.request("kvmd::atx::reset_switch", gpiod.LINE_REQ_DIR_OUT, default_vals=[0]) self.__reset_switch_line.request("kvmd::atx::reset_switch", gpiod.LINE_REQ_DIR_OUT, default_vals=[0])
async def get_state(self) -> Dict: async def get_state(self) -> dict:
return { return {
"enabled": True, "enabled": True,
"busy": self.__region.is_busy(), "busy": self.__region.is_busy(),
@ -132,8 +130,8 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
}, },
} }
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
prev_state: Dict = {} prev_state: dict = {}
while True: while True:
state = await self.get_state() state = await self.get_state()
if state != prev_state: if state != prev_state:

View File

@ -20,8 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import Type
from .. import BasePlugin from .. import BasePlugin
from .. import get_plugin_class from .. import get_plugin_class
@ -36,5 +34,5 @@ class BaseAuthService(BasePlugin):
# ===== # =====
def get_auth_service_class(name: str) -> Type[BaseAuthService]: def get_auth_service_class(name: str) -> type[BaseAuthService]:
return get_plugin_class("auth", name) # type: ignore return get_plugin_class("auth", name) # type: ignore

View File

@ -20,8 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import Dict
import passlib.apache import passlib.apache
from ...yamlconf import Option from ...yamlconf import Option
@ -37,7 +35,7 @@ class Plugin(BaseAuthService):
self.__path = path self.__path = path
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"file": Option("/etc/kvmd/htpasswd", type=valid_abs_file, unpack_as="path"), "file": Option("/etc/kvmd/htpasswd", type=valid_abs_file, unpack_as="path"),
} }

View File

@ -20,9 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import Dict
from typing import Optional
import aiohttp import aiohttp
import aiohttp.web import aiohttp.web
@ -57,10 +54,10 @@ class Plugin(BaseAuthService):
self.__passwd = passwd self.__passwd = passwd
self.__timeout = timeout self.__timeout = timeout
self.__http_session: Optional[aiohttp.ClientSession] = None self.__http_session: (aiohttp.ClientSession | None) = None
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"url": Option("http://localhost/auth"), "url": Option("http://localhost/auth"),
"verify": Option(True, type=valid_bool), "verify": Option(True, type=valid_bool),
@ -102,7 +99,7 @@ class Plugin(BaseAuthService):
def __ensure_http_session(self) -> aiohttp.ClientSession: def __ensure_http_session(self) -> aiohttp.ClientSession:
if not self.__http_session: if not self.__http_session:
kwargs: Dict = {} kwargs: dict = {}
if self.__user: if self.__user:
kwargs["auth"] = aiohttp.BasicAuth(login=self.__user, password=self.__passwd) kwargs["auth"] = aiohttp.BasicAuth(login=self.__user, password=self.__passwd)
if not self.__verify: if not self.__verify:

View File

@ -23,9 +23,6 @@
import asyncio import asyncio
import pwd import pwd
from typing import List
from typing import Dict
import pam import pam
from ...yamlconf import Option from ...yamlconf import Option
@ -45,8 +42,8 @@ class Plugin(BaseAuthService):
def __init__( # pylint: disable=super-init-not-called def __init__( # pylint: disable=super-init-not-called
self, self,
service: str, service: str,
allow_users: List[str], allow_users: list[str],
deny_users: List[str], deny_users: list[str],
allow_uids_at: int, allow_uids_at: int,
) -> None: ) -> None:
@ -58,7 +55,7 @@ class Plugin(BaseAuthService):
self.__lock = asyncio.Lock() self.__lock = asyncio.Lock()
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"service": Option("login"), "service": Option("login"),
"allow_users": Option([], type=valid_users_list), "allow_users": Option([], type=valid_users_list),

View File

@ -22,8 +22,6 @@
import io import io
from typing import Dict
import pyrad.client import pyrad.client
import pyrad.packet import pyrad.packet
import pyrad.dictionary import pyrad.dictionary
@ -413,7 +411,7 @@ class Plugin(BaseAuthService):
self.__timeout = timeout self.__timeout = timeout
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"host": Option("localhost", type=valid_ip_or_host), "host": Option("localhost", type=valid_ip_or_host),
"port": Option(1812, type=valid_port), "port": Option(1812, type=valid_port),

View File

@ -20,12 +20,8 @@
# ========================================================================== # # ========================================================================== #
from typing import Tuple
from typing import Dict
from typing import Iterable from typing import Iterable
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Type
from typing import Optional
from .. import BasePlugin from .. import BasePlugin
from .. import get_plugin_class from .. import get_plugin_class
@ -36,10 +32,10 @@ class BaseHid(BasePlugin):
def sysprep(self) -> None: def sysprep(self) -> None:
raise NotImplementedError raise NotImplementedError
async def get_state(self) -> Dict: async def get_state(self) -> dict:
raise NotImplementedError raise NotImplementedError
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
yield {} yield {}
raise NotImplementedError raise NotImplementedError
@ -51,7 +47,7 @@ class BaseHid(BasePlugin):
# ===== # =====
def send_key_events(self, keys: Iterable[Tuple[str, bool]]) -> None: def send_key_events(self, keys: Iterable[tuple[str, bool]]) -> None:
raise NotImplementedError raise NotImplementedError
def send_mouse_button_event(self, button: str, state: bool) -> None: def send_mouse_button_event(self, button: str, state: bool) -> None:
@ -68,7 +64,7 @@ class BaseHid(BasePlugin):
def send_mouse_wheel_event(self, delta_x: int, delta_y: int) -> None: def send_mouse_wheel_event(self, delta_x: int, delta_y: int) -> None:
raise NotImplementedError raise NotImplementedError
def set_params(self, keyboard_output: Optional[str]=None, mouse_output: Optional[str]=None) -> None: def set_params(self, keyboard_output: (str | None)=None, mouse_output: (str | None)=None) -> None:
_ = keyboard_output _ = keyboard_output
_ = mouse_output _ = mouse_output
@ -80,5 +76,5 @@ class BaseHid(BasePlugin):
# ===== # =====
def get_hid_class(name: str) -> Type[BaseHid]: def get_hid_class(name: str) -> type[BaseHid]:
return get_plugin_class("hid", name) # type: ignore return get_plugin_class("hid", name) # type: ignore

View File

@ -25,13 +25,9 @@ import contextlib
import queue import queue
import time import time
from typing import Tuple
from typing import List
from typing import Dict
from typing import Iterable from typing import Iterable
from typing import Generator from typing import Generator
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional
from ....logging import get_logger from ....logging import get_logger
@ -144,7 +140,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
self.__stop_event = multiprocessing.Event() self.__stop_event = multiprocessing.Event()
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"gpio_device": Option("/dev/gpiochip0", type=valid_abs_path, unpack_as="gpio_device_path"), "gpio_device": Option("/dev/gpiochip0", type=valid_abs_path, unpack_as="gpio_device_path"),
"reset_pin": Option(4, type=valid_gpio_pin_optional), "reset_pin": Option(4, type=valid_gpio_pin_optional),
@ -162,7 +158,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
get_logger(0).info("Starting HID daemon ...") get_logger(0).info("Starting HID daemon ...")
self.start() self.start()
async def get_state(self) -> Dict: async def get_state(self) -> dict:
state = await self.__state_flags.get() state = await self.__state_flags.get()
online = bool(state["online"]) online = bool(state["online"])
pong = (state["status"] >> 16) & 0xFF pong = (state["status"] >> 16) & 0xFF
@ -174,8 +170,8 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
if online and active_mouse in ["usb_rel", "ps2"]: if online and active_mouse in ["usb_rel", "ps2"]:
absolute = False absolute = False
keyboard_outputs: Dict = {"available": [], "active": ""} keyboard_outputs: dict = {"available": [], "active": ""}
mouse_outputs: Dict = {"available": [], "active": ""} mouse_outputs: dict = {"available": [], "active": ""}
if outputs1 & 0b10000000: # Dynamic if outputs1 & 0b10000000: # Dynamic
if outputs2 & 0b00000001: # USB if outputs2 & 0b00000001: # USB
@ -222,8 +218,8 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
}, },
} }
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
prev_state: Dict = {} prev_state: dict = {}
while True: while True:
state = await self.get_state() state = await self.get_state()
if state != prev_state: if state != prev_state:
@ -244,7 +240,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
# ===== # =====
def send_key_events(self, keys: Iterable[Tuple[str, bool]]) -> None: def send_key_events(self, keys: Iterable[tuple[str, bool]]) -> None:
for (key, state) in keys: for (key, state) in keys:
self.__queue_event(KeyEvent(key, state)) self.__queue_event(KeyEvent(key, state))
@ -260,8 +256,8 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
def send_mouse_wheel_event(self, delta_x: int, delta_y: int) -> None: def send_mouse_wheel_event(self, delta_x: int, delta_y: int) -> None:
self.__queue_event(MouseWheelEvent(delta_x, delta_y)) self.__queue_event(MouseWheelEvent(delta_x, delta_y))
def set_params(self, keyboard_output: Optional[str]=None, mouse_output: Optional[str]=None) -> None: def set_params(self, keyboard_output: (str | None)=None, mouse_output: (str | None)=None) -> None:
events: List[BaseEvent] = [] events: list[BaseEvent] = []
if keyboard_output is not None: if keyboard_output is not None:
events.append(SetKeyboardOutputEvent(keyboard_output)) events.append(SetKeyboardOutputEvent(keyboard_output))
if mouse_output is not None: if mouse_output is not None:
@ -346,7 +342,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
def __process_request(self, conn: BasePhyConnection, request: bytes) -> bool: # pylint: disable=too-many-branches def __process_request(self, conn: BasePhyConnection, request: bytes) -> bool: # pylint: disable=too-many-branches
logger = get_logger() logger = get_logger()
error_messages: List[str] = [] error_messages: list[str] = []
live_log_errors = False live_log_errors = False
common_retries = self.__common_retries common_retries = self.__common_retries

View File

@ -23,9 +23,6 @@
import types import types
import time import time
from typing import Type
from typing import Optional
import gpiod import gpiod
from ....logging import get_logger from ....logging import get_logger
@ -46,8 +43,8 @@ class Gpio:
self.__reset_inverted = reset_inverted self.__reset_inverted = reset_inverted
self.__reset_delay = reset_delay self.__reset_delay = reset_delay
self.__chip: Optional[gpiod.Chip] = None self.__chip: (gpiod.Chip | None) = None
self.__reset_line: Optional[gpiod.Line] = None self.__reset_line: (gpiod.Line | None) = None
def __enter__(self) -> None: def __enter__(self) -> None:
if self.__reset_pin >= 0: if self.__reset_pin >= 0:
@ -59,7 +56,7 @@ class Gpio:
def __exit__( def __exit__(
self, self,
_exc_type: Type[BaseException], _exc_type: type[BaseException],
_exc: BaseException, _exc: BaseException,
_tb: types.TracebackType, _tb: types.TracebackType,
) -> None: ) -> None:

View File

@ -23,11 +23,8 @@
import multiprocessing import multiprocessing
import time import time
from typing import Tuple
from typing import Dict
from typing import Iterable from typing import Iterable
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional
from ....logging import get_logger from ....logging import get_logger
@ -81,7 +78,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
select_timeout: float, select_timeout: float,
) -> None: ) -> None:
self.__proc: Optional[multiprocessing.Process] = None self.__proc: (multiprocessing.Process | None) = None
self.__stop_event = multiprocessing.Event() self.__stop_event = multiprocessing.Event()
self.__notifier = aiomulti.AioProcessNotifier() self.__notifier = aiomulti.AioProcessNotifier()
@ -104,7 +101,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
) )
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"manufacturer": Option("PiKVM"), "manufacturer": Option("PiKVM"),
"product": Option("HID Device"), "product": Option("HID Device"),
@ -128,9 +125,9 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
self.__proc = multiprocessing.Process(target=self.__server_worker, daemon=True) self.__proc = multiprocessing.Process(target=self.__server_worker, daemon=True)
self.__proc.start() self.__proc.start()
async def get_state(self) -> Dict: async def get_state(self) -> dict:
state = await self.__server.get_state() state = await self.__server.get_state()
outputs: Dict = {"available": [], "active": ""} outputs: dict = {"available": [], "active": ""}
return { return {
"online": True, "online": True,
"busy": False, "busy": False,
@ -151,8 +148,8 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
}, },
} }
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
prev_state: Dict = {} prev_state: dict = {}
while True: while True:
state = await self.get_state() state = await self.get_state()
if state != prev_state: if state != prev_state:
@ -175,7 +172,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
# ===== # =====
def send_key_events(self, keys: Iterable[Tuple[str, bool]]) -> None: def send_key_events(self, keys: Iterable[tuple[str, bool]]) -> None:
for (key, state) in keys: for (key, state) in keys:
self.__server.queue_event(make_keyboard_event(key, state)) self.__server.queue_event(make_keyboard_event(key, state))

View File

@ -22,8 +22,6 @@
import types import types
from typing import Type
from typing import Optional
from typing import Any from typing import Any
import dbus import dbus
@ -56,7 +54,7 @@ class BluezIface:
self.__pairing_required = pairing_required self.__pairing_required = pairing_required
self.__auth_required = auth_required self.__auth_required = auth_required
self.__bus: Optional[dbus.SystemBus] = None self.__bus: (dbus.SystemBus | None) = None
def get_address(self) -> str: def get_address(self) -> str:
return self.__get_prop("Address") return self.__get_prop("Address")
@ -100,7 +98,7 @@ class BluezIface:
def __exit__( def __exit__(
self, self,
_exc_type: Type[BaseException], _exc_type: type[BaseException],
_exc: BaseException, _exc: BaseException,
_tb: types.TracebackType, _tb: types.TracebackType,
) -> None: ) -> None:

View File

@ -29,11 +29,7 @@ import contextlib
import queue import queue
from typing import Literal from typing import Literal
from typing import List
from typing import Dict
from typing import Set
from typing import Generator from typing import Generator
from typing import Optional
from ....logging import get_logger from ....logging import get_logger
@ -72,8 +68,8 @@ _SockAttrT = Literal["ctl_sock", "int_sock"]
@dataclasses.dataclass @dataclasses.dataclass
class _BtClient: class _BtClient:
addr: str addr: str
ctl_sock: Optional[socket.socket] = None ctl_sock: (socket.socket | None) = None
int_sock: Optional[socket.socket] = None int_sock: (socket.socket | None) = None
# ===== # =====
@ -104,8 +100,8 @@ class BtServer: # pylint: disable=too-many-instance-attributes
self.__stop_event = stop_event self.__stop_event = stop_event
self.__clients: Dict[str, _BtClient] = {} self.__clients: dict[str, _BtClient] = {}
self.__to_read: Set[socket.socket] = set() self.__to_read: set[socket.socket] = set()
self.__events_queue: "multiprocessing.Queue[BaseEvent]" = multiprocessing.Queue() self.__events_queue: "multiprocessing.Queue[BaseEvent]" = multiprocessing.Queue()
@ -115,8 +111,8 @@ class BtServer: # pylint: disable=too-many-instance-attributes
"scroll": False, "scroll": False,
"num": False, "num": False,
}, notifier) }, notifier)
self.__modifiers: Set[UsbKey] = set() self.__modifiers: set[UsbKey] = set()
self.__keys: List[Optional[UsbKey]] = [None] * 6 self.__keys: list[UsbKey | None] = [None] * 6
self.__mouse_buttons = 0 self.__mouse_buttons = 0
def run(self) -> None: def run(self) -> None:
@ -132,7 +128,7 @@ class BtServer: # pylint: disable=too-many-instance-attributes
self.__close_all_clients(no_change_public=True) self.__close_all_clients(no_change_public=True)
self.__set_public(False) self.__set_public(False)
async def get_state(self) -> Dict: async def get_state(self) -> dict:
return (await self.__state_flags.get()) return (await self.__state_flags.get())
def queue_event(self, event: BaseEvent) -> None: def queue_event(self, event: BaseEvent) -> None:

View File

@ -20,11 +20,8 @@
# ========================================================================== # # ========================================================================== #
from typing import Tuple
from typing import Dict
from typing import Iterable from typing import Iterable
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional
from typing import Any from typing import Any
from ....logging import get_logger from ....logging import get_logger
@ -49,9 +46,9 @@ from .mouse import MouseProcess
class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
def __init__( # pylint: disable=super-init-not-called def __init__( # pylint: disable=super-init-not-called
self, self,
keyboard: Dict[str, Any], keyboard: dict[str, Any],
mouse: Dict[str, Any], mouse: dict[str, Any],
mouse_alt: Dict[str, Any], mouse_alt: dict[str, Any],
noop: bool, noop: bool,
udc: str, # XXX: Not from options, see /kvmd/apps/kvmd/__init__.py for details udc: str, # XXX: Not from options, see /kvmd/apps/kvmd/__init__.py for details
) -> None: ) -> None:
@ -66,8 +63,8 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
self.__keyboard_proc = KeyboardProcess(**common, **keyboard) self.__keyboard_proc = KeyboardProcess(**common, **keyboard)
self.__mouse_current = self.__mouse_proc = MouseProcess(**common, **mouse) self.__mouse_current = self.__mouse_proc = MouseProcess(**common, **mouse)
self.__mouse_alt_proc: Optional[MouseProcess] = None self.__mouse_alt_proc: (MouseProcess | None) = None
self.__mouses: Dict[str, MouseProcess] = {} self.__mouses: dict[str, MouseProcess] = {}
if mouse_alt["device_path"]: if mouse_alt["device_path"]:
self.__mouse_alt_proc = MouseProcess( self.__mouse_alt_proc = MouseProcess(
absolute=(not mouse["absolute"]), absolute=(not mouse["absolute"]),
@ -84,7 +81,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
self.__mouses["usb_win98"] = self.__mouses["usb"] self.__mouses["usb_win98"] = self.__mouses["usb"]
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"keyboard": { "keyboard": {
"device": Option("/dev/kvmd-hid-keyboard", type=valid_abs_path, unpack_as="device_path"), "device": Option("/dev/kvmd-hid-keyboard", type=valid_abs_path, unpack_as="device_path"),
@ -121,7 +118,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
if self.__mouse_alt_proc: if self.__mouse_alt_proc:
self.__mouse_alt_proc.start(udc) self.__mouse_alt_proc.start(udc)
async def get_state(self) -> Dict: async def get_state(self) -> dict:
keyboard_state = await self.__keyboard_proc.get_state() keyboard_state = await self.__keyboard_proc.get_state()
mouse_state = await self.__mouse_current.get_state() mouse_state = await self.__mouse_current.get_state()
return { return {
@ -146,8 +143,8 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
}, },
} }
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
prev_state: Dict = {} prev_state: dict = {}
while True: while True:
state = await self.get_state() state = await self.get_state()
if state != prev_state: if state != prev_state:
@ -173,7 +170,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
# ===== # =====
def send_key_events(self, keys: Iterable[Tuple[str, bool]]) -> None: def send_key_events(self, keys: Iterable[tuple[str, bool]]) -> None:
self.__keyboard_proc.send_key_events(keys) self.__keyboard_proc.send_key_events(keys)
def send_mouse_button_event(self, button: str, state: bool) -> None: def send_mouse_button_event(self, button: str, state: bool) -> None:
@ -188,7 +185,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
def send_mouse_wheel_event(self, delta_x: int, delta_y: int) -> None: def send_mouse_wheel_event(self, delta_x: int, delta_y: int) -> None:
self.__mouse_current.send_wheel_event(delta_x, delta_y) self.__mouse_current.send_wheel_event(delta_x, delta_y)
def set_params(self, keyboard_output: Optional[str]=None, mouse_output: Optional[str]=None) -> None: def set_params(self, keyboard_output: (str | None)=None, mouse_output: (str | None)=None) -> None:
_ = keyboard_output _ = keyboard_output
if mouse_output in self.__mouses and mouse_output != self.__get_current_mouse_mode(): if mouse_output in self.__mouses and mouse_output != self.__get_current_mouse_mode():
self.__mouse_current.send_clear_event() self.__mouse_current.send_clear_event()

View File

@ -28,9 +28,7 @@ import errno
import logging import logging
import time import time
from typing import Dict
from typing import Generator from typing import Generator
from typing import Optional
from ....logging import get_logger from ....logging import get_logger
@ -48,7 +46,7 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in
self, self,
name: str, name: str,
read_size: int, read_size: int,
initial_state: Dict, initial_state: dict,
notifier: aiomulti.AioProcessNotifier, notifier: aiomulti.AioProcessNotifier,
device_path: str, device_path: str,
@ -76,7 +74,7 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in
self.__stop_event = multiprocessing.Event() self.__stop_event = multiprocessing.Event()
self.__no_device_reported = False self.__no_device_reported = False
self.__logger: Optional[logging.Logger] = None self.__logger: (logging.Logger | None) = None
def start(self, udc: str) -> None: # type: ignore # pylint: disable=arguments-differ def start(self, udc: str) -> None: # type: ignore # pylint: disable=arguments-differ
self.__udc_state_path = usb.get_udc_path(udc, usb.U_STATE) self.__udc_state_path = usb.get_udc_path(udc, usb.U_STATE)
@ -125,7 +123,7 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in
self.__close_device() self.__close_device()
async def get_state(self) -> Dict: async def get_state(self) -> dict:
return (await self.__state_flags.get()) return (await self.__state_flags.get())
# ===== # =====

View File

@ -23,11 +23,6 @@
import struct import struct
import dataclasses import dataclasses
from typing import List
from typing import Set
from typing import Optional
from typing import Union
from ....keyboard.mappings import UsbKey from ....keyboard.mappings import UsbKey
from ....keyboard.mappings import KEYMAP from ....keyboard.mappings import KEYMAP
@ -66,7 +61,7 @@ class ModifierEvent(BaseEvent):
assert self.modifier.is_modifier assert self.modifier.is_modifier
def make_keyboard_event(key: str, state: bool) -> Union[KeyEvent, ModifierEvent]: def make_keyboard_event(key: str, state: bool) -> (KeyEvent | ModifierEvent):
usb_key = KEYMAP[key].usb usb_key = KEYMAP[key].usb
if usb_key.is_modifier: if usb_key.is_modifier:
return ModifierEvent(usb_key, state) return ModifierEvent(usb_key, state)
@ -87,8 +82,8 @@ def get_led_num(flags: int) -> bool:
def make_keyboard_report( def make_keyboard_report(
pressed_modifiers: Set[UsbKey], pressed_modifiers: set[UsbKey],
pressed_keys: List[Optional[UsbKey]], pressed_keys: list[UsbKey | None],
) -> bytes: ) -> bytes:
modifiers = 0 modifiers = 0
@ -168,7 +163,7 @@ def make_mouse_report(
buttons: int, buttons: int,
move_x: int, move_x: int,
move_y: int, move_y: int,
wheel_x: Optional[int], wheel_x: (int | None),
wheel_y: int, wheel_y: int,
) -> bytes: ) -> bytes:

View File

@ -20,12 +20,8 @@
# ========================================================================== # # ========================================================================== #
from typing import Tuple
from typing import List
from typing import Set
from typing import Iterable from typing import Iterable
from typing import Generator from typing import Generator
from typing import Optional
from typing import Any from typing import Any
from ....logging import get_logger from ....logging import get_logger
@ -56,8 +52,8 @@ class KeyboardProcess(BaseDeviceProcess):
**kwargs, **kwargs,
) )
self.__pressed_modifiers: Set[UsbKey] = set() self.__pressed_modifiers: set[UsbKey] = set()
self.__pressed_keys: List[Optional[UsbKey]] = [None] * 6 self.__pressed_keys: list[UsbKey | None] = [None] * 6
def cleanup(self) -> None: def cleanup(self) -> None:
self._stop() self._stop()
@ -72,7 +68,7 @@ class KeyboardProcess(BaseDeviceProcess):
self._clear_queue() self._clear_queue()
self._queue_event(ResetEvent()) self._queue_event(ResetEvent())
def send_key_events(self, keys: Iterable[Tuple[str, bool]]) -> None: def send_key_events(self, keys: Iterable[tuple[str, bool]]) -> None:
for (key, state) in keys: for (key, state) in keys:
self._queue_event(make_keyboard_event(key, state)) self._queue_event(make_keyboard_event(key, state))

View File

@ -21,7 +21,6 @@
from typing import Generator from typing import Generator
from typing import Optional
from typing import Any from typing import Any
from ....logging import get_logger from ....logging import get_logger
@ -145,8 +144,8 @@ class MouseProcess(BaseDeviceProcess):
def __make_report( def __make_report(
self, self,
relative_event: Optional[MouseRelativeEvent]=None, relative_event: (MouseRelativeEvent | None)=None,
wheel_event: Optional[MouseWheelEvent]=None, wheel_event: (MouseWheelEvent | None)=None,
) -> bytes: ) -> bytes:
if self.__absolute: if self.__absolute:

View File

@ -23,7 +23,6 @@
import os import os
import contextlib import contextlib
from typing import Dict
from typing import Generator from typing import Generator
from typing import Any from typing import Any
@ -85,21 +84,21 @@ class _SerialPhy(BasePhy):
# ===== # =====
class Plugin(BaseMcuHid): class Plugin(BaseMcuHid):
def __init__(self, **kwargs: Any) -> None: def __init__(self, **kwargs: Any) -> None:
phy_kwargs: Dict = { phy_kwargs: dict = {
(option.unpack_as or key): kwargs.pop(option.unpack_as or key) (option.unpack_as or key): kwargs.pop(option.unpack_as or key)
for (key, option) in self.__get_phy_options().items() for (key, option) in self.__get_phy_options().items()
} }
super().__init__(phy=_SerialPhy(**phy_kwargs), **kwargs) super().__init__(phy=_SerialPhy(**phy_kwargs), **kwargs)
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
**cls.__get_phy_options(), **cls.__get_phy_options(),
**BaseMcuHid.get_plugin_options(), **BaseMcuHid.get_plugin_options(),
} }
@classmethod @classmethod
def __get_phy_options(cls) -> Dict: def __get_phy_options(cls) -> dict:
return { return {
"device": Option("/dev/kvmd-hid", type=valid_abs_path, unpack_as="device_path"), "device": Option("/dev/kvmd-hid", type=valid_abs_path, unpack_as="device_path"),
"speed": Option(115200, type=valid_tty_speed), "speed": Option(115200, type=valid_tty_speed),

View File

@ -24,11 +24,8 @@ import os
import contextlib import contextlib
import time import time
from typing import List
from typing import Dict
from typing import Generator from typing import Generator
from typing import Callable from typing import Callable
from typing import Optional
from typing import Any from typing import Any
import spidev import spidev
@ -75,7 +72,7 @@ class _SpiPhyConnection(BasePhyConnection):
self.__xfer(request) self.__xfer(request)
response: List[int] = [] response: list[int] = []
deadline_ts = time.monotonic() + self.__read_timeout deadline_ts = time.monotonic() + self.__read_timeout
found = False found = False
while time.monotonic() < deadline_ts: while time.monotonic() < deadline_ts:
@ -143,7 +140,7 @@ class _SpiPhy(BasePhy): # pylint: disable=too-many-instance-attributes
) )
@contextlib.contextmanager @contextlib.contextmanager
def __sw_cs_connected(self) -> Generator[Optional[gpiod.Line], None, None]: def __sw_cs_connected(self) -> Generator[(gpiod.Line | None), None, None]:
if self.__sw_cs_pin > 0: if self.__sw_cs_pin > 0:
with contextlib.closing(gpiod.Chip(self.__gpio_device_path)) as chip: with contextlib.closing(gpiod.Chip(self.__gpio_device_path)) as chip:
line = chip.get_line(self.__sw_cs_pin) line = chip.get_line(self.__sw_cs_pin)
@ -156,19 +153,19 @@ class _SpiPhy(BasePhy): # pylint: disable=too-many-instance-attributes
# ===== # =====
class Plugin(BaseMcuHid): class Plugin(BaseMcuHid):
def __init__(self, **kwargs: Any) -> None: def __init__(self, **kwargs: Any) -> None:
phy_kwargs: Dict = {key: kwargs.pop(key) for key in self.__get_phy_options()} phy_kwargs: dict = {key: kwargs.pop(key) for key in self.__get_phy_options()}
phy_kwargs["gpio_device_path"] = kwargs["gpio_device_path"] phy_kwargs["gpio_device_path"] = kwargs["gpio_device_path"]
super().__init__(phy=_SpiPhy(**phy_kwargs), **kwargs) super().__init__(phy=_SpiPhy(**phy_kwargs), **kwargs)
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
**cls.__get_phy_options(), **cls.__get_phy_options(),
**BaseMcuHid.get_plugin_options(), **BaseMcuHid.get_plugin_options(),
} }
@classmethod @classmethod
def __get_phy_options(cls) -> Dict: def __get_phy_options(cls) -> dict:
return { return {
"bus": Option(-1, type=valid_int_f0), "bus": Option(-1, type=valid_int_f0),
"chip": Option(-1, type=valid_int_f0), "chip": Option(-1, type=valid_int_f0),

View File

@ -24,10 +24,7 @@ import os
import contextlib import contextlib
import time import time
from typing import Dict
from typing import Type
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional
import aiofiles import aiofiles
import aiofiles.base import aiofiles.base
@ -105,7 +102,7 @@ class MsdRwNotSupported(MsdOperationError):
# ===== # =====
class BaseMsdReader: class BaseMsdReader:
def get_state(self) -> Dict: def get_state(self) -> dict:
raise NotImplementedError() raise NotImplementedError()
def get_total_size(self) -> int: def get_total_size(self) -> int:
@ -121,7 +118,7 @@ class BaseMsdReader:
class BaseMsdWriter: class BaseMsdWriter:
def get_state(self) -> Dict: def get_state(self) -> dict:
raise NotImplementedError() raise NotImplementedError()
def get_chunk_size(self) -> int: def get_chunk_size(self) -> int:
@ -132,10 +129,10 @@ class BaseMsdWriter:
class BaseMsd(BasePlugin): class BaseMsd(BasePlugin):
async def get_state(self) -> Dict: async def get_state(self) -> dict:
raise NotImplementedError() raise NotImplementedError()
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
if self is not None: # XXX: Vulture and pylint hack if self is not None: # XXX: Vulture and pylint hack
raise NotImplementedError() raise NotImplementedError()
yield yield
@ -150,9 +147,9 @@ class BaseMsd(BasePlugin):
async def set_params( async def set_params(
self, self,
name: Optional[str]=None, name: (str | None)=None,
cdrom: Optional[bool]=None, cdrom: (bool | None)=None,
rw: Optional[bool]=None, rw: (bool | None)=None,
) -> None: ) -> None:
raise NotImplementedError() raise NotImplementedError()
@ -168,7 +165,7 @@ class BaseMsd(BasePlugin):
yield BaseMsdReader() yield BaseMsdReader()
@contextlib.asynccontextmanager @contextlib.asynccontextmanager
async def write_image(self, name: str, size: int, remove_incomplete: Optional[bool]) -> AsyncGenerator[BaseMsdWriter, None]: async def write_image(self, name: str, size: int, remove_incomplete: (bool | None)) -> AsyncGenerator[BaseMsdWriter, None]:
_ = name _ = name
_ = size _ = size
_ = remove_incomplete _ = remove_incomplete
@ -187,12 +184,12 @@ class MsdFileReader(BaseMsdReader): # pylint: disable=too-many-instance-attribu
self.__path = path self.__path = path
self.__chunk_size = chunk_size self.__chunk_size = chunk_size
self.__file: Optional[aiofiles.base.AiofilesContextManager] = None self.__file: (aiofiles.base.AiofilesContextManager | None) = None
self.__file_size = 0 self.__file_size = 0
self.__readed = 0 self.__readed = 0
self.__tick = 0.0 self.__tick = 0.0
def get_state(self) -> Dict: def get_state(self) -> dict:
return { return {
"name": self.__name, "name": self.__name,
"size": self.__file_size, "size": self.__file_size,
@ -248,12 +245,12 @@ class MsdFileWriter(BaseMsdWriter): # pylint: disable=too-many-instance-attribu
self.__sync_size = sync_size self.__sync_size = sync_size
self.__chunk_size = chunk_size self.__chunk_size = chunk_size
self.__file: Optional[aiofiles.base.AiofilesContextManager] = None self.__file: (aiofiles.base.AiofilesContextManager | None) = None
self.__written = 0 self.__written = 0
self.__unsynced = 0 self.__unsynced = 0
self.__tick = 0.0 self.__tick = 0.0
def get_state(self) -> Dict: def get_state(self) -> dict:
return { return {
"name": self.__name, "name": self.__name,
"size": self.__file_size, "size": self.__file_size,
@ -315,5 +312,5 @@ class MsdFileWriter(BaseMsdWriter): # pylint: disable=too-many-instance-attribu
# ===== # =====
def get_msd_class(name: str) -> Type[BaseMsd]: def get_msd_class(name: str) -> type[BaseMsd]:
return get_plugin_class("msd", name) # type: ignore return get_plugin_class("msd", name) # type: ignore

View File

@ -22,9 +22,7 @@
import contextlib import contextlib
from typing import Dict
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional
from ... import aiotools from ... import aiotools
@ -42,7 +40,7 @@ class MsdDisabledError(MsdOperationError):
# ===== # =====
class Plugin(BaseMsd): class Plugin(BaseMsd):
async def get_state(self) -> Dict: async def get_state(self) -> dict:
return { return {
"enabled": False, "enabled": False,
"online": False, "online": False,
@ -56,7 +54,7 @@ class Plugin(BaseMsd):
}, },
} }
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
while True: while True:
yield (await self.get_state()) yield (await self.get_state())
await aiotools.wait_infinite() await aiotools.wait_infinite()
@ -68,9 +66,9 @@ class Plugin(BaseMsd):
async def set_params( async def set_params(
self, self,
name: Optional[str]=None, name: (str | None)=None,
cdrom: Optional[bool]=None, cdrom: (bool | None)=None,
rw: Optional[bool]=None, rw: (bool | None)=None,
) -> None: ) -> None:
raise MsdDisabledError() raise MsdDisabledError()
@ -85,7 +83,7 @@ class Plugin(BaseMsd):
yield BaseMsdReader() yield BaseMsdReader()
@contextlib.asynccontextmanager @contextlib.asynccontextmanager
async def write_image(self, name: str, size: int, remove_incomplete: Optional[bool]) -> AsyncGenerator[BaseMsdWriter, None]: async def write_image(self, name: str, size: int, remove_incomplete: (bool | None)) -> AsyncGenerator[BaseMsdWriter, None]:
if self is not None: # XXX: Vulture and pylint hack if self is not None: # XXX: Vulture and pylint hack
raise MsdDisabledError() raise MsdDisabledError()
yield BaseMsdWriter() yield BaseMsdWriter()

View File

@ -27,10 +27,7 @@ import dataclasses
import functools import functools
import time import time
from typing import List
from typing import Dict
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional
from ....logging import get_logger from ....logging import get_logger
@ -77,7 +74,7 @@ class _DriveImage:
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class _DriveState: class _DriveState:
image: Optional[_DriveImage] image: (_DriveImage | None)
cdrom: bool cdrom: bool
rw: bool rw: bool
@ -86,13 +83,13 @@ class _DriveState:
class _StorageState: class _StorageState:
size: int size: int
free: int free: int
images: Dict[str, _DriveImage] images: dict[str, _DriveImage]
# ===== # =====
@dataclasses.dataclass @dataclasses.dataclass
class _VirtualDriveState: class _VirtualDriveState:
image: Optional[_DriveImage] image: (_DriveImage | None)
connected: bool connected: bool
cdrom: bool cdrom: bool
rw: bool rw: bool
@ -111,8 +108,8 @@ class _State:
def __init__(self, notifier: aiotools.AioNotifier) -> None: def __init__(self, notifier: aiotools.AioNotifier) -> None:
self.__notifier = notifier self.__notifier = notifier
self.storage: Optional[_StorageState] = None self.storage: (_StorageState | None) = None
self.vd: Optional[_VirtualDriveState] = None self.vd: (_VirtualDriveState | None) = None
self._lock = asyncio.Lock() self._lock = asyncio.Lock()
self._region = aiotools.AioExclusiveRegion(MsdIsBusyError) self._region = aiotools.AioExclusiveRegion(MsdIsBusyError)
@ -143,9 +140,9 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
storage_path: str, storage_path: str,
remount_cmd: List[str], remount_cmd: list[str],
initial: Dict, initial: dict,
gadget: str, # XXX: Not from options, see /kvmd/apps/kvmd/__init__.py for details gadget: str, # XXX: Not from options, see /kvmd/apps/kvmd/__init__.py for details
) -> None: ) -> None:
@ -165,8 +162,8 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
self.__drive = Drive(gadget, instance=0, lun=0) self.__drive = Drive(gadget, instance=0, lun=0)
self.__reader: Optional[MsdFileReader] = None self.__reader: (MsdFileReader | None) = None
self.__writer: Optional[MsdFileWriter] = None self.__writer: (MsdFileWriter | None) = None
self.__notifier = aiotools.AioNotifier() self.__notifier = aiotools.AioNotifier()
self.__state = _State(self.__notifier) self.__state = _State(self.__notifier)
@ -176,7 +173,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
aiotools.run_sync(self.__reload_state(notify=False)) aiotools.run_sync(self.__reload_state(notify=False))
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"read_chunk_size": Option(65536, type=functools.partial(valid_number, min=1024)), "read_chunk_size": Option(65536, type=functools.partial(valid_number, min=1024)),
"write_chunk_size": Option(65536, type=functools.partial(valid_number, min=1024)), "write_chunk_size": Option(65536, type=functools.partial(valid_number, min=1024)),
@ -195,9 +192,9 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
}, },
} }
async def get_state(self) -> Dict: async def get_state(self) -> dict:
async with self.__state._lock: # pylint: disable=protected-access async with self.__state._lock: # pylint: disable=protected-access
storage: Optional[Dict] = None storage: (dict | None) = None
if self.__state.storage: if self.__state.storage:
storage = dataclasses.asdict(self.__state.storage) storage = dataclasses.asdict(self.__state.storage)
for name in list(storage["images"]): for name in list(storage["images"]):
@ -215,7 +212,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
else: else:
storage["uploading"] = None storage["uploading"] = None
vd: Optional[Dict] = None vd: (dict | None) = None
if self.__state.vd: if self.__state.vd:
vd = dataclasses.asdict(self.__state.vd) vd = dataclasses.asdict(self.__state.vd)
if vd["image"]: if vd["image"]:
@ -234,8 +231,8 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
}, },
} }
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
prev_state: Dict = {} prev_state: dict = {}
while True: while True:
state = await self.get_state() state = await self.get_state()
if state != prev_state: if state != prev_state:
@ -267,9 +264,9 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
@aiotools.atomic_fg @aiotools.atomic_fg
async def set_params( async def set_params(
self, self,
name: Optional[str]=None, name: (str | None)=None,
cdrom: Optional[bool]=None, cdrom: (bool | None)=None,
rw: Optional[bool]=None, rw: (bool | None)=None,
) -> None: ) -> None:
async with self.__state.busy(): async with self.__state.busy():
@ -359,7 +356,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
self.__notifier.notify() self.__notifier.notify()
@contextlib.asynccontextmanager @contextlib.asynccontextmanager
async def write_image(self, name: str, size: int, remove_incomplete: Optional[bool]) -> AsyncGenerator[MsdFileWriter, None]: async def write_image(self, name: str, size: int, remove_incomplete: (bool | None)) -> AsyncGenerator[MsdFileWriter, None]:
try: try:
async with self.__state._region: # pylint: disable=protected-access async with self.__state._region: # pylint: disable=protected-access
path: str = "" path: str = ""
@ -540,7 +537,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
# ===== # =====
def __get_storage_state(self) -> _StorageState: def __get_storage_state(self) -> _StorageState:
images: Dict[str, _DriveImage] = {} images: dict[str, _DriveImage] = {}
for name in os.listdir(self.__images_path): for name in os.listdir(self.__images_path):
path = os.path.join(self.__images_path, name) path = os.path.join(self.__images_path, name)
if os.path.exists(path): if os.path.exists(path):
@ -562,7 +559,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
) )
def __get_drive_state(self) -> _DriveState: def __get_drive_state(self) -> _DriveState:
image: Optional[_DriveImage] = None image: (_DriveImage | None) = None
path = self.__drive.get_image_path() path = self.__drive.get_image_path()
if path: if path:
name = os.path.basename(path) name = os.path.basename(path)

View File

@ -23,8 +23,6 @@
import os import os
import errno import errno
from typing import List
from .... import usb from .... import usb
from .. import MsdOperationError from .. import MsdOperationError
@ -47,7 +45,7 @@ class Drive:
def is_enabled(self) -> bool: def is_enabled(self) -> bool:
return os.path.exists(self.__profile_func_path) return os.path.exists(self.__profile_func_path)
def get_watchable_paths(self) -> List[str]: def get_watchable_paths(self) -> list[str]:
return [self.__lun_path, self.__profile_path] return [self.__lun_path, self.__profile_path]
# ===== # =====

View File

@ -23,8 +23,6 @@
import os import os
import dataclasses import dataclasses
from typing import Optional
from ....logging import get_logger from ....logging import get_logger
@ -44,7 +42,7 @@ def get_file_size(path: str) -> int:
return -1 return -1
def get_fs_space(path: str, fatal: bool) -> Optional[FsSpace]: def get_fs_space(path: str, fatal: bool) -> (FsSpace | None):
try: try:
st = os.statvfs(path) st = os.statvfs(path)
except Exception as err: except Exception as err:

View File

@ -25,9 +25,7 @@ import contextlib
import dataclasses import dataclasses
import functools import functools
from typing import Dict
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional
from ....logging import get_logger from ....logging import get_logger
@ -86,16 +84,16 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
self.__gpio = Gpio(gpio_device_path, target_pin, reset_pin, reset_inverted, reset_delay) self.__gpio = Gpio(gpio_device_path, target_pin, reset_pin, reset_inverted, reset_delay)
self.__device_info: Optional[DeviceInfo] = None self.__device_info: (DeviceInfo | None) = None
self.__connected = False self.__connected = False
self.__device_writer: Optional[MsdFileWriter] = None self.__device_writer: (MsdFileWriter | None) = None
self.__notifier = aiotools.AioNotifier() self.__notifier = aiotools.AioNotifier()
self.__region = aiotools.AioExclusiveRegion(MsdIsBusyError, self.__notifier) self.__region = aiotools.AioExclusiveRegion(MsdIsBusyError, self.__notifier)
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"upload_chunk_size": Option(65536, type=functools.partial(valid_number, min=1024)), "upload_chunk_size": Option(65536, type=functools.partial(valid_number, min=1024)),
"sync_chunk_size": Option(4194304, type=functools.partial(valid_number, min=1024)), "sync_chunk_size": Option(4194304, type=functools.partial(valid_number, min=1024)),
@ -121,9 +119,9 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
log = (logger.error if isinstance(err, MsdError) else logger.exception) log = (logger.error if isinstance(err, MsdError) else logger.exception)
log("MSD is offline: %s", err) log("MSD is offline: %s", err)
async def get_state(self) -> Dict: async def get_state(self) -> dict:
storage: Optional[Dict] = None storage: (dict | None) = None
drive: Optional[Dict] = None drive: (dict | None) = None
if self.__device_info: if self.__device_info:
storage = { storage = {
"size": self.__device_info.size, "size": self.__device_info.size,
@ -147,8 +145,8 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
}, },
} }
async def poll_state(self) -> AsyncGenerator[Dict, None]: async def poll_state(self) -> AsyncGenerator[dict, None]:
prev_state: Dict = {} prev_state: dict = {}
while True: while True:
state = await self.get_state() state = await self.get_state()
if state != prev_state: if state != prev_state:
@ -183,9 +181,9 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
@aiotools.atomic_fg @aiotools.atomic_fg
async def set_params( async def set_params(
self, self,
name: Optional[str]=None, name: (str | None)=None,
cdrom: Optional[bool]=None, cdrom: (bool | None)=None,
rw: Optional[bool]=None, rw: (bool | None)=None,
) -> None: ) -> None:
async with self.__working(): async with self.__working():
@ -226,7 +224,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
yield BaseMsdReader() yield BaseMsdReader()
@contextlib.asynccontextmanager @contextlib.asynccontextmanager
async def write_image(self, name: str, size: int, remove_incomplete: Optional[bool]) -> AsyncGenerator[MsdFileWriter, None]: async def write_image(self, name: str, size: int, remove_incomplete: (bool | None)) -> AsyncGenerator[MsdFileWriter, None]:
async with self.__working(): async with self.__working():
if remove_incomplete is not None: if remove_incomplete is not None:
raise MsdMultiNotSupported() raise MsdMultiNotSupported()

View File

@ -27,7 +27,6 @@ import struct
import dataclasses import dataclasses
from typing import IO from typing import IO
from typing import Optional
from .... import aiotools from .... import aiotools
from .... import aiofs from .... import aiofs
@ -57,7 +56,7 @@ class ImageInfo:
complete: bool complete: bool
@classmethod @classmethod
def from_bytes(cls, data: bytes) -> Optional["ImageInfo"]: def from_bytes(cls, data: bytes) -> ("ImageInfo" | None):
try: try:
parsed = list(struct.unpack(_IMAGE_INFO_FORMAT, data)) parsed = list(struct.unpack(_IMAGE_INFO_FORMAT, data))
except struct.error: except struct.error:
@ -97,7 +96,7 @@ class DeviceInfo:
path: str path: str
size: int size: int
free: int free: int
image: Optional[ImageInfo] image: (ImageInfo | None)
@classmethod @classmethod
async def read(cls, device_path: str) -> "DeviceInfo": async def read(cls, device_path: str) -> "DeviceInfo":

View File

@ -20,8 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import Optional
import gpiod import gpiod
from .... import aiogp from .... import aiogp
@ -44,9 +42,9 @@ class Gpio: # pylint: disable=too-many-instance-attributes
self.__reset_inverted = reset_inverted self.__reset_inverted = reset_inverted
self.__reset_delay = reset_delay self.__reset_delay = reset_delay
self.__chip: Optional[gpiod.Chip] = None self.__chip: (gpiod.Chip | None) = None
self.__target_line: Optional[gpiod.Line] = None self.__target_line: (gpiod.Line | None) = None
self.__reset_line: Optional[gpiod.Line] = None self.__reset_line: (gpiod.Line | None) = None
def open(self) -> None: def open(self) -> None:
assert self.__chip is None assert self.__chip is None

View File

@ -20,10 +20,7 @@
# ========================================================================== # # ========================================================================== #
from typing import Set
from typing import Type
from typing import Callable from typing import Callable
from typing import Optional
from typing import Any from typing import Any
from ...errors import OperationError from ...errors import OperationError
@ -71,7 +68,7 @@ class BaseUserGpioDriver(BasePlugin):
return self._instance_name return self._instance_name
@classmethod @classmethod
def get_modes(cls) -> Set[str]: def get_modes(cls) -> set[str]:
return set(UserGpioModes.ALL) return set(UserGpioModes.ALL)
@classmethod @classmethod
@ -84,7 +81,7 @@ class BaseUserGpioDriver(BasePlugin):
_ = pin _ = pin
_ = debounce _ = debounce
def register_output(self, pin: str, initial: Optional[bool]) -> None: def register_output(self, pin: str, initial: (bool | None)) -> None:
_ = pin _ = pin
_ = initial _ = initial
@ -105,5 +102,5 @@ class BaseUserGpioDriver(BasePlugin):
# ===== # =====
def get_ugpio_driver_class(name: str) -> Type[BaseUserGpioDriver]: def get_ugpio_driver_class(name: str) -> type[BaseUserGpioDriver]:
return get_plugin_class("ugpio", name) # type: ignore return get_plugin_class("ugpio", name) # type: ignore

View File

@ -20,9 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import List
from typing import Dict
from typing import Set
from typing import Callable from typing import Callable
from typing import Any from typing import Any
@ -48,7 +45,7 @@ class Plugin(BaseUserGpioDriver): # pylint: disable=too-many-instance-attribute
instance_name: str, instance_name: str,
notifier: aiotools.AioNotifier, notifier: aiotools.AioNotifier,
cmd: List[str], cmd: list[str],
) -> None: ) -> None:
super().__init__(instance_name, notifier) super().__init__(instance_name, notifier)
@ -56,13 +53,13 @@ class Plugin(BaseUserGpioDriver): # pylint: disable=too-many-instance-attribute
self.__cmd = cmd self.__cmd = cmd
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"cmd": Option([], type=valid_command), "cmd": Option([], type=valid_command),
} }
@classmethod @classmethod
def get_modes(cls) -> Set[str]: def get_modes(cls) -> set[str]:
return set([UserGpioModes.OUTPUT]) return set([UserGpioModes.OUTPUT])
@classmethod @classmethod

View File

@ -20,9 +20,6 @@
# ========================================================================== # # ========================================================================== #
from typing import List
from typing import Dict
from typing import Set
from typing import Callable from typing import Callable
from typing import Any from typing import Any
@ -48,7 +45,7 @@ class Plugin(BaseUserGpioDriver): # pylint: disable=too-many-instance-attribute
instance_name: str, instance_name: str,
notifier: aiotools.AioNotifier, notifier: aiotools.AioNotifier,
cmd: List[str], cmd: list[str],
) -> None: ) -> None:
super().__init__(instance_name, notifier) super().__init__(instance_name, notifier)
@ -56,13 +53,13 @@ class Plugin(BaseUserGpioDriver): # pylint: disable=too-many-instance-attribute
self.__cmd = cmd self.__cmd = cmd
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"cmd": Option([], type=valid_command), "cmd": Option([], type=valid_command),
} }
@classmethod @classmethod
def get_modes(cls) -> Set[str]: def get_modes(cls) -> set[str]:
return set([UserGpioModes.INPUT]) return set([UserGpioModes.INPUT])
@classmethod @classmethod

View File

@ -26,10 +26,7 @@ import functools
import errno import errno
import time import time
from typing import Tuple
from typing import Dict
from typing import Callable from typing import Callable
from typing import Optional
from typing import Any from typing import Any
import serial import serial
@ -72,14 +69,14 @@ class Plugin(BaseUserGpioDriver): # pylint: disable=too-many-instance-attribute
self.__protocol = protocol self.__protocol = protocol
self.__ctl_queue: "multiprocessing.Queue[int]" = multiprocessing.Queue() self.__ctl_queue: "multiprocessing.Queue[int]" = multiprocessing.Queue()
self.__channel_queue: "multiprocessing.Queue[Optional[int]]" = multiprocessing.Queue() self.__channel_queue: "multiprocessing.Queue[int | None]" = multiprocessing.Queue()
self.__channel: Optional[int] = -1 self.__channel: (int | None) = -1
self.__proc: Optional[multiprocessing.Process] = None self.__proc: (multiprocessing.Process | None) = None
self.__stop_event = multiprocessing.Event() self.__stop_event = multiprocessing.Event()
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"device": Option("", type=valid_abs_path, unpack_as="device_path"), "device": Option("", type=valid_abs_path, unpack_as="device_path"),
"speed": Option(115200, type=valid_tty_speed), "speed": Option(115200, type=valid_tty_speed),
@ -164,8 +161,8 @@ class Plugin(BaseUserGpioDriver): # pylint: disable=too-many-instance-attribute
def __get_serial(self) -> serial.Serial: def __get_serial(self) -> serial.Serial:
return serial.Serial(self.__device_path, self.__speed, timeout=self.__read_timeout) return serial.Serial(self.__device_path, self.__speed, timeout=self.__read_timeout)
def __recv_channel(self, tty: serial.Serial, data: bytes) -> Tuple[Optional[int], bytes]: def __recv_channel(self, tty: serial.Serial, data: bytes) -> tuple[(int | None), bytes]:
channel: Optional[int] = None channel: (int | None) = None
if tty.in_waiting: if tty.in_waiting:
data += tty.read_all() data += tty.read_all()
found = re.findall(b"V[0-9a-fA-F]{2}S", data) found = re.findall(b"V[0-9a-fA-F]{2}S", data)

View File

@ -20,9 +20,7 @@
# ========================================================================== # # ========================================================================== #
from typing import Dict
from typing import Callable from typing import Callable
from typing import Optional
from typing import Any from typing import Any
import gpiod import gpiod
@ -52,16 +50,16 @@ class Plugin(BaseUserGpioDriver):
self.__device_path = device_path self.__device_path = device_path
self.__input_pins: Dict[int, aiogp.AioReaderPinParams] = {} self.__input_pins: dict[int, aiogp.AioReaderPinParams] = {}
self.__output_pins: Dict[int, Optional[bool]] = {} self.__output_pins: dict[int, (bool | None)] = {}
self.__reader: Optional[aiogp.AioReader] = None self.__reader: (aiogp.AioReader | None) = None
self.__chip: Optional[gpiod.Chip] = None self.__chip: (gpiod.Chip | None) = None
self.__output_lines: Dict[int, gpiod.Line] = {} self.__output_lines: dict[int, gpiod.Line] = {}
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"device": Option("/dev/gpiochip0", type=valid_abs_path, unpack_as="device_path"), "device": Option("/dev/gpiochip0", type=valid_abs_path, unpack_as="device_path"),
} }
@ -73,7 +71,7 @@ class Plugin(BaseUserGpioDriver):
def register_input(self, pin: str, debounce: float) -> None: def register_input(self, pin: str, debounce: float) -> None:
self.__input_pins[int(pin)] = aiogp.AioReaderPinParams(False, debounce) self.__input_pins[int(pin)] = aiogp.AioReaderPinParams(False, debounce)
def register_output(self, pin: str, initial: Optional[bool]) -> None: def register_output(self, pin: str, initial: (bool | None)) -> None:
self.__output_pins[int(pin)] = initial self.__output_pins[int(pin)] = initial
def prepare(self) -> None: def prepare(self) -> None:

View File

@ -24,10 +24,7 @@ import asyncio
import contextlib import contextlib
import functools import functools
from typing import Dict
from typing import Set
from typing import Callable from typing import Callable
from typing import Optional
from typing import Any from typing import Any
import hid import hid
@ -67,27 +64,27 @@ class Plugin(BaseUserGpioDriver):
self.__device_path = device_path self.__device_path = device_path
self.__state_poll = state_poll self.__state_poll = state_poll
self.__device: Optional[hid.device] = None self.__device: (hid.device | None) = None
self.__stop = False self.__stop = False
self.__initials: Dict[int, Optional[bool]] = {} self.__initials: dict[int, (bool | None)] = {}
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> dict:
return { return {
"device": Option("", type=valid_abs_path, unpack_as="device_path"), "device": Option("", type=valid_abs_path, unpack_as="device_path"),
"state_poll": Option(5.0, type=valid_float_f01), "state_poll": Option(5.0, type=valid_float_f01),
} }
@classmethod @classmethod
def get_modes(cls) -> Set[str]: def get_modes(cls) -> set[str]:
return set([UserGpioModes.OUTPUT]) return set([UserGpioModes.OUTPUT])
@classmethod @classmethod
def get_pin_validator(cls) -> Callable[[Any], Any]: def get_pin_validator(cls) -> Callable[[Any], Any]:
return functools.partial(valid_number, min=0, max=7, name="HID relay channel") return functools.partial(valid_number, min=0, max=7, name="HID relay channel")
def register_output(self, pin: str, initial: Optional[bool]) -> None: def register_output(self, pin: str, initial: (bool | None)) -> None:
self.__initials[int(pin)] = initial self.__initials[int(pin)] = initial
def prepare(self) -> None: def prepare(self) -> None:

Some files were not shown because too many files have changed in this diff Show More