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

@@ -20,9 +20,6 @@
# ========================================================================== #
from typing import List
from typing import Optional
from ...logging import get_logger
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(
prog="kvmd",
description="The main PiKVM daemon",

View File

@@ -23,7 +23,6 @@
import asyncio
from typing import Any
from typing import List
from aiohttp.web import Request
from aiohttp.web import Response
@@ -56,7 +55,7 @@ class ExportApi:
self.__info_manager.get_submanager("fan").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["leds"]["power"], "pikvm_atx_power")
@@ -71,7 +70,7 @@ class ExportApi:
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):
value = int(value)
if isinstance(value, (int, float)):

View File

@@ -24,10 +24,6 @@ import os
import stat
import functools
from typing import Tuple
from typing import List
from typing import Dict
from typing import Set
from typing import Callable
from aiohttp.web import Request
@@ -64,10 +60,10 @@ class HidApi:
hid: BaseHid,
keymap_path: str,
ignore_keys: List[str],
ignore_keys: list[str],
mouse_x_range: Tuple[int, int],
mouse_y_range: Tuple[int, int],
mouse_x_range: tuple[int, int],
mouse_y_range: tuple[int, int],
) -> None:
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)
keymaps: Set[str] = set()
async def get_keymaps(self) -> dict: # Ugly hack to generate hid_keymaps_state (see server.py)
keymaps: set[str] = set()
for keymap_name in os.listdir(self.__keymaps_dir_path):
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):
@@ -139,7 +135,7 @@ class HidApi:
self.__hid.send_key_events(text_to_web_keys(text, symmap))
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")
path = os.path.join(self.__keymaps_dir_path, keymap_name)
try:
@@ -151,14 +147,14 @@ class HidApi:
return self.__inner_ensure_symmap(path, st.st_mtime)
@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
return build_symmap(path)
# =====
@exposed_ws("key")
async def __ws_key_handler(self, _: WsSession, event: Dict) -> None:
async def __ws_key_handler(self, _: WsSession, event: dict) -> None:
try:
key = valid_hid_key(event["key"])
state = valid_bool(event["state"])
@@ -168,7 +164,7 @@ class HidApi:
self.__hid.send_key_events([(key, state)])
@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:
button = valid_hid_mouse_button(event["button"])
state = valid_bool(event["state"])
@@ -177,7 +173,7 @@ class HidApi:
self.__hid.send_mouse_button_event(button, state)
@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:
to_x = valid_hid_mouse_move(event["to"]["x"])
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)
@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)
@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)
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:
raw_delta = event["delta"]
deltas = [

View File

@@ -22,8 +22,6 @@
import asyncio
from typing import List
from aiohttp.web import Request
from aiohttp.web import Response
@@ -51,7 +49,7 @@ class InfoApi:
])))
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()
return sorted(valid_info_fields(
arg=request.query.get("fields", ",".join(subs)),

View File

@@ -23,10 +23,7 @@
import lzma
import time
from typing import Dict
from typing import AsyncGenerator
from typing import Optional
from typing import Union
import aiohttp
import zstandard
@@ -153,7 +150,7 @@ class MsdApi:
return make_json_response(self.__make_write_info(name, size, written))
@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"))
insecure = valid_bool(request.query.get("insecure", False))
timeout = valid_float_f01(request.query.get("timeout", 10.0))
@@ -161,7 +158,7 @@ class MsdApi:
name = ""
size = written = 0
response: Optional[StreamResponse] = None
response: (StreamResponse | None) = None
async def stream_write_info() -> None:
assert response is not None
@@ -206,11 +203,11 @@ class MsdApi:
return make_json_exception(err, 400)
raise
def __get_remove_incomplete(self, request: Request) -> Optional[bool]:
flag: Optional[str] = request.query.get("remove_incomplete")
def __get_remove_incomplete(self, request: Request) -> (bool | None):
flag: (str | None) = request.query.get("remove_incomplete")
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}}
# =====

View File

@@ -20,9 +20,6 @@
# ========================================================================== #
from typing import List
from typing import Dict
from aiohttp.web import Request
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()
default: List[str] = []
available: List[str] = []
default: list[str] = []
available: list[str] = []
if enabled:
default = self.__ocr.get_default_langs()
available = self.__ocr.get_available_langs()

View File

@@ -22,10 +22,6 @@
import secrets
from typing import List
from typing import Dict
from typing import Optional
from ...logging import get_logger
from ... import aiotools
@@ -40,12 +36,12 @@ class AuthManager:
self,
internal_type: str,
internal_kwargs: Dict,
internal_kwargs: dict,
external_type: str,
external_kwargs: Dict,
external_kwargs: dict,
force_internal_users: List[str],
force_internal_users: list[str],
enabled: bool,
) -> None:
@@ -53,19 +49,19 @@ class AuthManager:
if not enabled:
get_logger().warning("AUTHORIZATION IS DISABLED")
self.__internal_service: Optional[BaseAuthService] = None
self.__internal_service: (BaseAuthService | None) = None
if enabled:
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())
self.__external_service: Optional[BaseAuthService] = None
self.__external_service: (BaseAuthService | None) = None
if enabled and external_type:
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())
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:
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())
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
assert self.__enabled
@@ -109,7 +105,7 @@ class AuthManager:
if 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
return self.__tokens.get(token)

View File

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

View File

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

View File

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

View File

@@ -24,9 +24,6 @@ import os
import re
import asyncio
from typing import Dict
from typing import Optional
from ....logging import get_logger
from ....yamlconf import Section
@@ -45,7 +42,7 @@ class ExtrasInfoSubmanager(BaseInfoSubmanager):
def __init__(self, global_config: Section) -> None:
self.__global_config = global_config
async def get_state(self) -> Optional[Dict]:
async def get_state(self) -> (dict | None):
try:
sui = sysunit.SystemdUnitInfo()
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))
sui = None
try:
extras: Dict[str, Dict] = {}
extras: dict[str, dict] = {}
for extra in (await asyncio.gather(*[
self.__read_extra(sui, name)
for name in os.listdir(self.__get_extras_path())
@@ -71,7 +68,7 @@ class ExtrasInfoSubmanager(BaseInfoSubmanager):
def __get_extras_path(self, *parts: str) -> str:
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:
extra = await aiotools.run_async(load_yaml_file, self.__get_extras_path(name, "manifest.yaml"))
await self.__rewrite_app_daemon(sui, extra)
@@ -81,7 +78,7 @@ class ExtrasInfoSubmanager(BaseInfoSubmanager):
get_logger(0).exception("Can't read extra %r", name)
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", "")
if isinstance(daemon, str) and daemon.strip():
extra["enabled"] = extra["started"] = False
@@ -91,7 +88,7 @@ class ExtrasInfoSubmanager(BaseInfoSubmanager):
except Exception as 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", "")
if isinstance(port_path, str) and port_path.strip():
extra["port"] = 0

View File

@@ -23,9 +23,7 @@
import copy
import asyncio
from typing import Dict
from typing import AsyncGenerator
from typing import Optional
import aiohttp
@@ -55,15 +53,15 @@ class FanInfoSubmanager(BaseInfoSubmanager):
self.__timeout = timeout
self.__state_poll = state_poll
async def get_state(self) -> Dict:
async def get_state(self) -> dict:
monitored = await self.__get_monitored()
return {
"monitored": monitored,
"state": ((await self.__get_fan_state() if monitored else None)),
}
async def poll_state(self) -> AsyncGenerator[Dict, None]:
prev_state: Dict = {}
async def poll_state(self) -> AsyncGenerator[dict, None]:
prev_state: dict = {}
while True:
if self.__unix_path:
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))
return False
async def __get_fan_state(self) -> Optional[Dict]:
async def __get_fan_state(self) -> (dict | None):
try:
async with self.__make_http_session() as session:
async with session.get("http://localhost/state") as response:
@@ -104,7 +102,7 @@ class FanInfoSubmanager(BaseInfoSubmanager):
return None
def __make_http_session(self) -> aiohttp.ClientSession:
kwargs: Dict = {
kwargs: dict = {
"headers": {
"User-Agent": htclient.make_user_agent("KVMD"),
},

View File

@@ -23,12 +23,9 @@
import os
import asyncio
from typing import List
from typing import Dict
from typing import Callable
from typing import AsyncGenerator
from typing import TypeVar
from typing import Optional
from ....logging import get_logger
@@ -48,16 +45,16 @@ _RetvalT = TypeVar("_RetvalT")
class HwInfoSubmanager(BaseInfoSubmanager):
def __init__(
self,
vcgencmd_cmd: List[str],
vcgencmd_cmd: list[str],
state_poll: float,
) -> None:
self.__vcgencmd_cmd = vcgencmd_cmd
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(
self.__read_dt_file("model"),
self.__read_dt_file("serial-number"),
@@ -78,8 +75,8 @@ class HwInfoSubmanager(BaseInfoSubmanager):
},
}
async def poll_state(self) -> AsyncGenerator[Dict, None]:
prev_state: Dict = {}
async def poll_state(self) -> AsyncGenerator[dict, None]:
prev_state: dict = {}
while True:
state = await self.get_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:
path = os.path.join(f"{env.PROCFS_PREFIX}/proc/device-tree", name)
try:
@@ -99,7 +96,7 @@ class HwInfoSubmanager(BaseInfoSubmanager):
return None
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"
try:
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)
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
flags = await self.__parse_vcgencmd(
arg="get_throttled",
@@ -133,7 +130,7 @@ class HwInfoSubmanager(BaseInfoSubmanager):
}
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]
try:
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 ....yamlconf.loader import load_yaml_file
@@ -37,7 +34,7 @@ class MetaInfoSubmanager(BaseInfoSubmanager):
def __init__(self, meta_path: str) -> None:
self.__meta_path = meta_path
async def get_state(self) -> Optional[Dict]:
async def get_state(self) -> (dict | None):
try:
return ((await aiotools.run_async(load_yaml_file, self.__meta_path)) or {})
except Exception:

View File

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

View File

@@ -24,7 +24,6 @@ import re
import asyncio
import time
from typing import Dict
from typing import AsyncGenerator
import systemd.journal
@@ -32,7 +31,7 @@ import systemd.journal
# =====
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.this_boot()
reader.this_machine()
@@ -59,7 +58,7 @@ class LogReader:
else:
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 {
"dt": entry["__REALTIME_TIMESTAMP"],
"service": entry["_SYSTEMD_UNIT"],

View File

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

View File

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

View File

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

View File

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