utils: simplify sentinel object

Use an enum to represent the sentinel rather than a singleton object.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
Eric Callahan 2023-02-22 08:19:39 -05:00
parent 0e80e301f0
commit 2cda75ff2c
No known key found for this signature in database
GPG Key ID: 5A1EB336DFB4C71B
6 changed files with 63 additions and 74 deletions

View File

@ -15,7 +15,7 @@ from io import BytesIO
from functools import reduce
from threading import Lock as ThreadLock
import lmdb
from ..utils import SentinelClass, ServerError
from ..utils import Sentinel, ServerError
# Annotation imports
from typing import (
@ -61,8 +61,6 @@ RECORD_DECODE_FUNCS = {
ord("{"): lambda x: json.loads(bytes(x)),
}
SENTINEL = SentinelClass.get_instance()
def getitem_with_default(item: Dict, field: Any) -> Any:
if not isinstance(item, Dict):
raise ServerError(
@ -346,14 +344,14 @@ class MoonrakerDatabase:
def get_item(self,
namespace: str,
key: Optional[Union[List[str], str]] = None,
default: Any = SENTINEL
default: Any = Sentinel.MISSING
) -> Future[Any]:
return self._run_command(self._get_impl, namespace, key, default)
def _get_impl(self,
namespace: str,
key: Optional[Union[List[str], str]] = None,
default: Any = SENTINEL
default: Any = Sentinel.MISSING
) -> Any:
try:
if key is None:
@ -363,7 +361,7 @@ class MoonrakerDatabase:
val = reduce(operator.getitem, # type: ignore
key_list[1:], ns)
except Exception as e:
if not isinstance(default, SentinelClass):
if default is not Sentinel.MISSING:
return default
if isinstance(e, self.server.error):
raise
@ -875,7 +873,7 @@ class NamespaceWrapper:
return self.db._get_namespace(self.namespace)
def __getitem__(self, key: Union[List[str], str]) -> Future[Any]:
return self.get(key, default=SENTINEL)
return self.get(key, default=Sentinel.MISSING)
def __setitem__(self,
key: Union[List[str], str],
@ -908,13 +906,13 @@ class NamespaceWrapper:
def pop(self,
key: Union[List[str], str],
default: Any = SENTINEL
default: Any = Sentinel.MISSING
) -> Union[Future[Any], Task[Any]]:
if not self.server.is_running():
try:
val = self.delete(key).result()
except Exception:
if isinstance(default, SentinelClass):
if default is Sentinel.MISSING:
raise
val = default
fut = self.eventloop.create_future()
@ -925,7 +923,7 @@ class NamespaceWrapper:
try:
val = await self.delete(key)
except Exception:
if isinstance(default, SentinelClass):
if default is Sentinel.MISSING:
raise
val = default
return val

View File

@ -5,7 +5,7 @@
# This file may be distributed under the terms of the GNU GPLv3 license.
from __future__ import annotations
from ..utils import SentinelClass
from ..utils import Sentinel
from ..websockets import WebRequest, Subscribable
# Annotation imports
@ -35,7 +35,6 @@ SUBSCRIPTION_ENDPOINT = "objects/subscribe"
STATUS_ENDPOINT = "objects/query"
OBJ_LIST_ENDPOINT = "objects/list"
REG_METHOD_ENDPOINT = "register_remote_method"
SENTINEL = SentinelClass.get_instance()
class KlippyAPI(Subscribable):
def __init__(self, config: ConfigHelper) -> None:
@ -84,20 +83,20 @@ class KlippyAPI(Subscribable):
self,
method: str,
params: Dict[str, Any],
default: Any = SENTINEL
default: Any = Sentinel.MISSING
) -> Any:
try:
req = WebRequest(method, params, conn=self)
result = await self.klippy.request(req)
except self.server.error:
if isinstance(default, SentinelClass):
if default is Sentinel.MISSING:
raise
result = default
return result
async def run_gcode(self,
script: str,
default: Any = SENTINEL
default: Any = Sentinel.MISSING
) -> str:
params = {'script': script}
result = await self._send_klippy_request(
@ -123,21 +122,21 @@ class KlippyAPI(Subscribable):
return await self.run_gcode(script)
async def pause_print(
self, default: Union[SentinelClass, _T] = SENTINEL
self, default: Union[Sentinel, _T] = Sentinel.MISSING
) -> Union[_T, str]:
self.server.send_event("klippy_apis:pause_requested")
return await self._send_klippy_request(
"pause_resume/pause", {}, default)
async def resume_print(
self, default: Union[SentinelClass, _T] = SENTINEL
self, default: Union[Sentinel, _T] = Sentinel.MISSING
) -> Union[_T, str]:
self.server.send_event("klippy_apis:resume_requested")
return await self._send_klippy_request(
"pause_resume/resume", {}, default)
async def cancel_print(
self, default: Union[SentinelClass, _T] = SENTINEL
self, default: Union[Sentinel, _T] = Sentinel.MISSING
) -> Union[_T, str]:
self.server.send_event("klippy_apis:cancel_requested")
return await self._send_klippy_request(
@ -163,7 +162,7 @@ class KlippyAPI(Subscribable):
return result
async def list_endpoints(self,
default: Union[SentinelClass, _T] = SENTINEL
default: Union[Sentinel, _T] = Sentinel.MISSING
) -> Union[_T, Dict[str, List[str]]]:
return await self._send_klippy_request(
LIST_EPS_ENDPOINT, {}, default)
@ -173,7 +172,7 @@ class KlippyAPI(Subscribable):
async def get_klippy_info(self,
send_id: bool = False,
default: Union[SentinelClass, _T] = SENTINEL
default: Union[Sentinel, _T] = Sentinel.MISSING
) -> Union[_T, Dict[str, Any]]:
params = {}
if send_id:
@ -182,7 +181,7 @@ class KlippyAPI(Subscribable):
return await self._send_klippy_request(INFO_ENDPOINT, params, default)
async def get_object_list(self,
default: Union[SentinelClass, _T] = SENTINEL
default: Union[Sentinel, _T] = Sentinel.MISSING
) -> Union[_T, List[str]]:
result = await self._send_klippy_request(
OBJ_LIST_ENDPOINT, {}, default)
@ -192,7 +191,7 @@ class KlippyAPI(Subscribable):
async def query_objects(self,
objects: Mapping[str, Optional[List[str]]],
default: Union[SentinelClass, _T] = SENTINEL
default: Union[Sentinel, _T] = Sentinel.MISSING
) -> Union[_T, Dict[str, Any]]:
params = {'objects': objects}
result = await self._send_klippy_request(
@ -203,7 +202,7 @@ class KlippyAPI(Subscribable):
async def subscribe_objects(self,
objects: Mapping[str, Optional[List[str]]],
default: Union[SentinelClass, _T] = SENTINEL
default: Union[Sentinel, _T] = Sentinel.MISSING
) -> Union[_T, Dict[str, Any]]:
for obj, items in objects.items():
if obj in self.host_subscription:

View File

@ -14,7 +14,7 @@ import threading
import copy
import logging
from io import StringIO
from .utils import SentinelClass
from .utils import Sentinel
from .components.template import JinjaTemplate
# Annotation imports
@ -41,7 +41,6 @@ if TYPE_CHECKING:
_T = TypeVar("_T")
ConfigVal = Union[None, int, float, bool, str, dict, list]
SENTINEL = SentinelClass.get_instance()
DOCS_URL = "https://moonraker.readthedocs.io/en/latest"
class ConfigError(Exception):
@ -120,7 +119,7 @@ class ConfigHelper:
def _get_option(self,
func: Callable[..., Any],
option: str,
default: Union[SentinelClass, _T],
default: Union[Sentinel, _T],
above: Optional[Union[int, float]] = None,
below: Optional[Union[int, float]] = None,
minval: Optional[Union[int, float]] = None,
@ -138,7 +137,7 @@ class ConfigHelper:
try:
val = func(section, option)
except (configparser.NoOptionError, configparser.NoSectionError) as e:
if isinstance(default, SentinelClass):
if default is Sentinel.MISSING:
raise ConfigError(str(e)) from None
val = default
section = self.section
@ -198,7 +197,7 @@ class ConfigHelper:
def get(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
default: Union[Sentinel, _T] = Sentinel.MISSING,
deprecate: bool = False
) -> Union[str, _T]:
return self._get_option(
@ -207,7 +206,7 @@ class ConfigHelper:
def getint(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
default: Union[Sentinel, _T] = Sentinel.MISSING,
above: Optional[int] = None,
below: Optional[int] = None,
minval: Optional[int] = None,
@ -220,7 +219,7 @@ class ConfigHelper:
def getboolean(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
default: Union[Sentinel, _T] = Sentinel.MISSING,
deprecate: bool = False
) -> Union[bool, _T]:
return self._get_option(
@ -229,7 +228,7 @@ class ConfigHelper:
def getfloat(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
default: Union[Sentinel, _T] = Sentinel.MISSING,
above: Optional[float] = None,
below: Optional[float] = None,
minval: Optional[float] = None,
@ -242,7 +241,7 @@ class ConfigHelper:
def getlists(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
default: Union[Sentinel, _T] = Sentinel.MISSING,
list_type: Type = str,
separators: Tuple[Optional[str], ...] = ('\n',),
count: Optional[Tuple[Optional[int], ...]] = None,
@ -292,7 +291,7 @@ class ConfigHelper:
def getlist(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
default: Union[Sentinel, _T] = Sentinel.MISSING,
separator: Optional[str] = '\n',
count: Optional[int] = None,
deprecate: bool = False
@ -302,7 +301,7 @@ class ConfigHelper:
def getintlist(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
default: Union[Sentinel, _T] = Sentinel.MISSING,
separator: Optional[str] = '\n',
count: Optional[int] = None,
deprecate: bool = False
@ -312,7 +311,7 @@ class ConfigHelper:
def getfloatlist(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
default: Union[Sentinel, _T] = Sentinel.MISSING,
separator: Optional[str] = '\n',
count: Optional[int] = None,
deprecate: bool = False
@ -322,7 +321,7 @@ class ConfigHelper:
def getdict(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
default: Union[Sentinel, _T] = Sentinel.MISSING,
separators: Tuple[Optional[str], Optional[str]] = ('\n', '='),
dict_type: Type = str,
allow_empty_fields: bool = False,
@ -356,7 +355,7 @@ class ConfigHelper:
def getgpioout(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
default: Union[Sentinel, _T] = Sentinel.MISSING,
initial_value: int = 0,
deprecate: bool = False
) -> Union[GpioOutputPin, _T]:
@ -375,7 +374,7 @@ class ConfigHelper:
def gettemplate(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
default: Union[Sentinel, _T] = Sentinel.MISSING,
is_async: bool = False,
deprecate: bool = False
) -> Union[JinjaTemplate, _T]:
@ -396,7 +395,7 @@ class ConfigHelper:
def load_template(self,
option: str,
default: Union[SentinelClass, str] = SENTINEL,
default: Union[Sentinel, str] = Sentinel.MISSING,
is_async: bool = False,
deprecate: bool = False
) -> JinjaTemplate:
@ -409,7 +408,7 @@ class ConfigHelper:
def getpath(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
default: Union[Sentinel, _T] = Sentinel.MISSING,
deprecate: bool = False
) -> Union[pathlib.Path, _T]:
val = self.gettemplate(option, default, deprecate=deprecate)

View File

@ -21,7 +21,7 @@ from . import confighelper
from .eventloop import EventLoop
from .app import MoonrakerApp
from .klippy_connection import KlippyConnection
from .utils import ServerError, SentinelClass, get_software_version
from .utils import ServerError, Sentinel, get_software_version
from .loghelper import LogManager
# Annotation imports
@ -43,7 +43,7 @@ if TYPE_CHECKING:
from .components.machine import Machine
from .components.extensions import ExtensionManager
FlexCallback = Callable[..., Optional[Coroutine]]
_T = TypeVar("_T")
_T = TypeVar("_T", Sentinel, Any)
API_VERSION = (1, 2, 1)
CORE_COMPONENTS = [
@ -53,7 +53,6 @@ CORE_COMPONENTS = [
'webcam', 'extensions',
]
SENTINEL = SentinelClass.get_instance()
class Server:
error = ServerError
@ -245,11 +244,12 @@ class Server:
config.validate_config()
self._is_configured = True
def load_component(self,
config: confighelper.ConfigHelper,
component_name: str,
default: Union[SentinelClass, _T] = SENTINEL
) -> Union[_T, Any]:
def load_component(
self,
config: confighelper.ConfigHelper,
component_name: str,
default: _T = Sentinel.MISSING
) -> Union[_T, Any]:
if component_name in self.components:
return self.components[component_name]
try:
@ -265,19 +265,18 @@ class Server:
logging.exception(msg)
if component_name not in self.failed_components:
self.failed_components.append(component_name)
if isinstance(default, SentinelClass):
if default is Sentinel.MISSING:
raise
return default
self.components[component_name] = component
logging.info(f"Component ({component_name}) loaded")
return component
def lookup_component(self,
component_name: str,
default: Union[SentinelClass, _T] = SENTINEL
) -> Union[_T, Any]:
def lookup_component(
self, component_name: str, default: _T = Sentinel.MISSING
) -> Union[_T, Any]:
component = self.components.get(component_name, default)
if isinstance(component, SentinelClass):
if component is Sentinel.MISSING:
raise ServerError(f"Component ({component_name}) not found")
return component

View File

@ -19,6 +19,7 @@ import shlex
import re
import struct
import socket
import enum
from . import source_info
# Annotation imports
@ -45,14 +46,8 @@ class ServerError(Exception):
self.status_code = status_code
class SentinelClass:
_instance: ClassVar[Optional[SentinelClass]] = None
@staticmethod
def get_instance() -> SentinelClass:
if SentinelClass._instance is None:
SentinelClass._instance = SentinelClass()
return SentinelClass._instance
class Sentinel(enum.Enum):
MISSING = object()
def _run_git_command(cmd: str) -> str:
prog = shlex.split(cmd)

View File

@ -12,7 +12,7 @@ import asyncio
import copy
from tornado.websocket import WebSocketHandler, WebSocketClosedError
from tornado.web import HTTPError
from .utils import ServerError, SentinelClass
from .utils import ServerError, Sentinel
# Annotation imports
from typing import (
@ -44,7 +44,6 @@ if TYPE_CHECKING:
AuthComp = Optional[Authorization]
CLIENT_TYPES = ["web", "mobile", "desktop", "display", "bot", "agent", "other"]
SENTINEL = SentinelClass.get_instance()
class Subscribable:
def send_status(self,
@ -98,11 +97,11 @@ class WebRequest:
def _get_converted_arg(self,
key: str,
default: Union[SentinelClass, _T],
default: Union[Sentinel, _T],
dtype: Type[_C]
) -> Union[_C, _T]:
if key not in self.args:
if isinstance(default, SentinelClass):
if default is Sentinel.MISSING:
raise ServerError(f"No data for argument: {key}")
return default
val = self.args[key]
@ -124,34 +123,34 @@ class WebRequest:
def get(self,
key: str,
default: Union[SentinelClass, _T] = SENTINEL
default: Union[Sentinel, _T] = Sentinel.MISSING
) -> Union[_T, Any]:
val = self.args.get(key, default)
if isinstance(val, SentinelClass):
if val is Sentinel.MISSING:
raise ServerError(f"No data for argument: {key}")
return val
def get_str(self,
key: str,
default: Union[SentinelClass, _T] = SENTINEL
default: Union[Sentinel, _T] = Sentinel.MISSING
) -> Union[str, _T]:
return self._get_converted_arg(key, default, str)
def get_int(self,
key: str,
default: Union[SentinelClass, _T] = SENTINEL
default: Union[Sentinel, _T] = Sentinel.MISSING
) -> Union[int, _T]:
return self._get_converted_arg(key, default, int)
def get_float(self,
key: str,
default: Union[SentinelClass, _T] = SENTINEL
default: Union[Sentinel, _T] = Sentinel.MISSING
) -> Union[float, _T]:
return self._get_converted_arg(key, default, float)
def get_boolean(self,
key: str,
default: Union[SentinelClass, _T] = SENTINEL
default: Union[Sentinel, _T] = Sentinel.MISSING
) -> Union[bool, _T]:
return self._get_converted_arg(key, default, bool)
@ -245,8 +244,8 @@ class JsonRPC:
rpc_version: str = obj.get('jsonrpc', "")
if rpc_version != "2.0":
return self.build_error(-32600, "Invalid Request", req_id)
method_name = obj.get('method', SENTINEL)
if method_name is SENTINEL:
method_name = obj.get('method', Sentinel.MISSING)
if method_name is Sentinel.MISSING:
self.process_response(obj, conn)
return None
if not isinstance(method_name, str):