1717import os
1818import signal
1919import sys
20- from collections .abc import Callable , Generator
20+ from collections .abc import Generator
2121from contextlib import ExitStack , contextmanager
2222from logging import Logger
2323from pathlib import Path
2424from time import perf_counter
2525
26-
2726import appdaemon .appdaemon as ad
28- from .dependency_manager import DependencyManager
2927import appdaemon .utils as utils
3028from appdaemon import exceptions as ade
3129from appdaemon .app_management import UpdateMode
3432from appdaemon .http import HTTP
3533from appdaemon .logging import Logging
3634
35+ from .dependency_manager import DependencyManager
3736from .models .config .yaml import MainConfig
3837
3938logger = logging .getLogger (__name__ )
5251
5352# This dict sets up the default logging before the config has even been read.
5453PRE_LOGGING = {
55- 'version' : 1 ,
56- 'disable_existing_loggers' : False ,
57- 'formatters' : {
58- 'bare' : {
59- 'format' : "{levelname}: {message}" ,
60- 'style' : '{' ,
54+ "version" : 1 ,
55+ "disable_existing_loggers" : False ,
56+ "formatters" : {
57+ "bare" : {
58+ "format" : "{levelname}: {message}" ,
59+ "style" : "{" ,
60+ },
61+ "full" : {
62+ "format" : "{asctime}.{msecs:03.0f} {levelname} AppDaemon: {message}" ,
63+ "style" : "{" ,
64+ "datefmt" : "%Y-%m-%d %H:%M:%S" ,
6165 },
62- 'full' : {
63- 'format' : "{asctime}.{msecs:03.0f} {levelname} AppDaemon: {message}" ,
64- 'style' : '{' ,
65- 'datefmt' : '%Y-%m-%d %H:%M:%S'
66- }
6766 },
68- 'handlers' : {
69- 'stdout' : {
70- 'class' : 'logging.StreamHandler' ,
71- 'formatter' : 'full' ,
72- 'stream' : 'ext://sys.stdout'
67+ "handlers" : {
68+ "stdout" : {
69+ "class" : "logging.StreamHandler" ,
70+ "formatter" : "full" ,
71+ "stream" : "ext://sys.stdout" ,
72+ },
73+ "stderr" : {
74+ "class" : "logging.StreamHandler" ,
75+ "formatter" : "bare" ,
76+ "stream" : "ext://sys.stderr" ,
7377 },
74- 'stderr' : {
75- 'class' : 'logging.StreamHandler' ,
76- 'formatter' : 'bare' ,
77- 'stream' : 'ext://sys.stderr'
78- }
7978 },
80- ' root' : {
81- ' level' : ' INFO' ,
82- ' handlers' : [' stdout' ],
79+ " root" : {
80+ " level" : " INFO" ,
81+ " handlers" : [" stdout" ],
8382 },
84- 'loggers' : {
85- 'bare' : {
86- 'handlers' : ['stderr' ],
87- 'propagate' : False
88- }
89- }
83+ "loggers" : {"bare" : {"handlers" : ["stderr" ], "propagate" : False }},
9084}
9185
9286
@@ -196,7 +190,7 @@ def resolve_config_file(args: argparse.Namespace) -> tuple[Path, Path]:
196190 return config_file , config_dir
197191
198192
199- def parse_config (args : argparse .Namespace , stop_function : Callable ) -> MainConfig :
193+ def parse_config (args : argparse .Namespace ) -> MainConfig :
200194 """Parse configuration file and return MainConfig model.
201195
202196 Args:
@@ -228,26 +222,24 @@ def parse_config(args: argparse.Namespace, stop_function: Callable) -> MainConfi
228222 assert isinstance (ad_kwargs , dict ), "AppDaemon configuration must be a dictionary"
229223
230224 # Batch assign required parameters
231- ad_kwargs .update ({
232- "config_dir" : config_dir ,
233- "config_file" : config_file ,
234- "write_toml" : args .write_toml ,
235- "stop_function" : stop_function ,
236- })
225+ ad_kwargs .update (
226+ {
227+ "config_dir" : config_dir ,
228+ "config_file" : config_file ,
229+ "write_toml" : args .write_toml ,
230+ }
231+ )
237232
238233 # Conditionally assign time-related parameters
239234 for attr in ("timewarp" , "starttime" , "endtime" ):
240- if ( value := getattr (args , attr ) ):
235+ if value := getattr (args , attr ):
241236 ad_kwargs [attr ] = value
242237
243238 # Set log level with fallback
244239 ad_kwargs ["loglevel" ] = args .debug or ad_kwargs .get ("loglevel" , "INFO" )
245240
246241 # Handle module debug efficiently
247- module_debug_cli = (
248- {arg [0 ]: arg [1 ] for arg in args .moduledebug }
249- if args .moduledebug else {}
250- )
242+ module_debug_cli = {arg [0 ]: arg [1 ] for arg in args .moduledebug } if args .moduledebug else {}
251243
252244 if isinstance (ad_kwargs .get ("module_debug" ), dict ):
253245 ad_kwargs ["module_debug" ] |= module_debug_cli
@@ -290,16 +282,13 @@ class ADMain:
290282 """Pydantic model of the top-level object for the appdaemon.yaml file."""
291283 args : argparse .Namespace
292284
293- stop_time : float = 0.0
294- """Stores the value of perf_counter() when self.stop is first called."""
295-
296285 def __init__ (self , args : argparse .Namespace ) -> None :
297286 self .args = args
298287 self .http_object = None
299288 self ._cleanup_stack = ExitStack ()
300289
301290 try :
302- self .model = parse_config (self .args , self . stop )
291+ self .model = parse_config (self .args )
303292 self .setup_logging ()
304293 utils .deprecation_warnings (self .model .appdaemon , self .logger )
305294
@@ -319,7 +308,11 @@ def __init__(self, args: argparse.Namespace) -> None:
319308 def __enter__ (self ):
320309 try :
321310 self ._cleanup_stack .enter_context (
322- ade .exception_context (self .logger , self .model .appdaemon .app_dir , header = "ADMain" )
311+ ade .exception_context (
312+ self .logger ,
313+ self .model .appdaemon .app_dir ,
314+ header = "ADMain" ,
315+ )
323316 )
324317
325318 if self .args .pidfile is not None and pid is not None :
@@ -362,7 +355,7 @@ def handle_sig(self, signum: int):
362355 self .AD .thread_async .call_async_no_wait (self .AD .app_management .check_app_updates , mode = UpdateMode .TERMINATE )
363356 case (signal .SIGINT | signal .SIGTERM ) as sig :
364357 self .logger .info (f"Received signal: { signal .Signals (sig ).name } " )
365- self .stop ()
358+ self .AD . stop ()
366359
367360 @contextmanager
368361 def loop_context (self ) -> Generator [asyncio .AbstractEventLoop ]:
@@ -409,7 +402,7 @@ def signal_handlers(self, loop: asyncio.AbstractEventLoop):
409402 def stop (self ):
410403 """Stop AppDaemon and stop the event loop afterwards."""
411404 self .stop_time = perf_counter ()
412- task = self .loop .create_task (self .AD .stop ())
405+ task = self .loop .create_task (self .AD ._stop ())
413406 task .add_done_callback (lambda _ : self .loop .stop ())
414407
415408 def run (self ) -> None :
@@ -460,13 +453,12 @@ def run_context(self, loop: asyncio.AbstractEventLoop):
460453 self .logger .warning ("-" * 60 , exc_info = True )
461454 self .logger .warning ("-" * 60 )
462455 finally :
463- self .logger .debug (' Exiting self.run_context' )
456+ self .logger .debug (" Exiting self.run_context" )
464457 self .loop .set_exception_handler (None )
465458 self .logger .info ("AppDaemon is stopped." )
466459
467460 def setup_logging (self ) -> None :
468- """Set up logging configuration and timezone.
469- """
461+ """Set up logging configuration and timezone."""
470462 log_cfg = self .model .logs .model_dump (mode = "python" , by_alias = True , exclude_unset = True )
471463 self .logging = Logging (log_cfg , self .args .debug )
472464 self .logger = self .logging .get_logger ().getChild ("_startup" )
@@ -491,7 +483,7 @@ def startup_text(self):
491483 # self.logging.dump_log_config()
492484 yield
493485 finally :
494- stop_duration = perf_counter () - self .stop_time
486+ stop_duration = perf_counter () - self .AD . stop_time
495487 self .logger .info ("AppDaemon main() stopped gracefully in %s" , utils .format_timedelta (stop_duration ))
496488
497489
0 commit comments