@@ -56,6 +56,7 @@ def __init__( # pylint: disable=too-many-arguments
5656 / ,
5757 * ,
5858 force_polling : bool = True ,
59+ logging_config_key : str | Sequence [str ] | None = "logging" ,
5960 name : str | None = None ,
6061 polling_interval : timedelta = timedelta (seconds = 1 ),
6162 ) -> None :
@@ -68,6 +69,10 @@ def __init__( # pylint: disable=too-many-arguments
6869 the previous paths. Dict keys will be merged recursively, but other
6970 objects (like lists) will be replaced by the value in the last path.
7071 force_polling: Whether to force file polling to check for changes.
72+ logging_config_key: The key to use for the logging configuration. If `None`,
73+ logging configuration will not be managed. If a key is provided, the
74+ manager update the logging configuration whenever the configuration
75+ changes.
7176 name: A name to use when creating actors. If `None`, `str(id(self))` will
7277 be used. This is used mostly for debugging purposes.
7378 polling_interval: The interval to poll for changes. Only relevant if
@@ -80,7 +85,7 @@ def __init__( # pylint: disable=too-many-arguments
8085 )
8186 """The broadcast channel for the configuration."""
8287
83- self .actor : Final [ConfigManagingActor ] = ConfigManagingActor (
88+ self .config_actor : Final [ConfigManagingActor ] = ConfigManagingActor (
8489 config_paths ,
8590 self .config_channel .new_sender (),
8691 name = self .name ,
@@ -89,16 +94,31 @@ def __init__( # pylint: disable=too-many-arguments
8994 )
9095 """The actor that manages the configuration."""
9196
97+ # pylint: disable-next=import-outside-toplevel,cyclic-import
98+ from ._logging_actor import LoggingConfigUpdatingActor
99+
100+ self .logging_actor : Final [LoggingConfigUpdatingActor | None ] = (
101+ None
102+ if logging_config_key is None
103+ else LoggingConfigUpdatingActor (
104+ self , config_key = logging_config_key , name = self .name
105+ )
106+ )
107+
92108 @override
93109 def start (self ) -> None :
94110 """Start this config manager."""
95- self .actor .start ()
111+ self .config_actor .start ()
112+ if self .logging_actor :
113+ self .logging_actor .start ()
96114
97115 @property
98116 @override
99117 def is_running (self ) -> bool :
100118 """Whether this config manager is running."""
101- return self .actor .is_running
119+ return self .config_actor .is_running or (
120+ self .logging_actor is not None and self .logging_actor .is_running
121+ )
102122
103123 @override
104124 def cancel (self , msg : str | None = None ) -> None :
@@ -107,11 +127,12 @@ def cancel(self, msg: str | None = None) -> None:
107127 Args:
108128 msg: The message to be passed to the tasks being cancelled.
109129 """
110- self .actor .cancel (msg )
130+ if self .logging_actor :
131+ self .logging_actor .cancel (msg )
132+ self .config_actor .cancel (msg )
111133
112- # We need the noqa because the `BaseExceptionGroup` is raised indirectly.
113134 @override
114- async def wait (self ) -> None : # noqa: DOC502
135+ async def wait (self ) -> None :
115136 """Wait this config manager to finish.
116137
117138 Wait until all tasks and actors are finished.
@@ -121,12 +142,34 @@ async def wait(self) -> None: # noqa: DOC502
121142 exception (`CancelError` is not considered an error and not returned in
122143 the exception group).
123144 """
124- await self .actor
145+ exceptions : list [BaseException ] = []
146+ if self .logging_actor :
147+ try :
148+ await self .logging_actor
149+ except BaseExceptionGroup as err : # pylint: disable=try-except-raise
150+ exceptions .append (err )
151+
152+ try :
153+ await self .config_actor
154+ except BaseExceptionGroup as err : # pylint: disable=try-except-raise
155+ exceptions .append (err )
156+
157+ if exceptions :
158+ raise BaseExceptionGroup (f"Error while stopping { self !r} " , exceptions )
125159
126160 @override
127161 def __repr__ (self ) -> str :
128162 """Return a string representation of this config manager."""
129- return f"config_channel={ self .config_channel !r} , " f"actor={ self .actor !r} >"
163+ logging_actor = (
164+ f"logging_actor={ self .logging_actor !r} , " if self .logging_actor else ""
165+ )
166+ return (
167+ f"<{ self .__class__ .__name__ } : "
168+ f"name={ self .name !r} , "
169+ f"config_channel={ self .config_channel !r} , "
170+ + logging_actor
171+ + f"config_actor={ self .config_actor !r} >"
172+ )
130173
131174 def new_receiver ( # pylint: disable=too-many-arguments
132175 self ,
0 commit comments