1818
1919import logging
2020import os
21+ from pathlib import Path
2122import subprocess
2223from sys import exc_info as sys_exc_info
2324from traceback import print_exception
24- from typing import Optional , Sequence , TYPE_CHECKING
25+ from typing import Literal , Optional , Sequence , TYPE_CHECKING , TypeAlias
2526
2627from nipype import config as nipype_config , logging as nipype_logging
2728
3031
3132if TYPE_CHECKING :
3233 from CPAC .utils .configuration import Configuration
34+ LogLevel : TypeAlias = (
35+ Literal [
36+ "CRITICAL" ,
37+ "critical" ,
38+ "Critical" ,
39+ "DEBUG" ,
40+ "debug" ,
41+ "Debug" ,
42+ "ERROR" ,
43+ "error" ,
44+ "Error" ,
45+ "INFO" ,
46+ "info" ,
47+ "Info" ,
48+ "NOTSET" ,
49+ "notset" ,
50+ "Notset" ,
51+ "NotSet" ,
52+ "notSet" ,
53+ "WARNING" ,
54+ "warning" ,
55+ "Warning" ,
56+ ]
57+ | int
58+ )
3359
3460
3561def failed_to_start (log_dir , exception ):
@@ -49,17 +75,8 @@ def failed_to_start(log_dir, exception):
4975 logger .exception (exception )
5076
5177
52- def getLogger (name ): # pylint: disable=invalid-name
53- """Get a mock logger if one exists, falling back on real loggers.
54-
55- Parameters
56- ----------
57- name : str
58-
59- Returns
60- -------
61- logger : CPAC.utils.monitoring.custom_logging.MockLogger or logging.Logger
62- """
78+ def getLogger (name : str ) -> "logging.Logger | MockLogger" : # pylint: disable=invalid-name
79+ """Get a mock logger if one exists, falling back on real loggers."""
6380 if name in MOCK_LOGGERS :
6481 return MOCK_LOGGERS [name ]
6582 logger = nipype_logging .getLogger (name )
@@ -158,7 +175,10 @@ def __init__(self, filename):
158175class MockLogger :
159176 """Mock logging.Logger to provide API without keeping the logger in memory."""
160177
161- def __init__ (self , name , filename , level , log_dir ):
178+ def __init__ (
179+ self , name : str , filename : str , level : LogLevel , log_dir : Path | str
180+ ) -> None :
181+ """Initialize a mock logger."""
162182 self .name = name
163183 self .level = level
164184 self .handlers = [MockHandler (os .path .join (log_dir , filename ))]
@@ -243,34 +263,38 @@ def _lazy_sub(message, *items):
243263
244264
245265def set_up_logger (
246- name , filename = None , level = None , log_dir = None , mock = False , overwrite_existing = False
247- ):
266+ name : str ,
267+ filename : Optional [str ] = None ,
268+ level : Optional [LogLevel ] = None ,
269+ log_dir : Optional [Path | str ] = None ,
270+ mock : bool = False ,
271+ overwrite_existing : bool = False ,
272+ ) -> logging .Logger | MockLogger :
248273 r"""Initialize a logger.
249274
250275 Parameters
251276 ----------
252- name : str
277+ name
253278 logger name (for subsequent calls to ``logging.getLogger``) to
254279 write to the same log file)
255280
256- filename : str, optional
281+ filename
257282 filename to write log to. If not specified, filename will be
258283 the same as ``name`` with the extension ``log``
259284
260- level : str, optional
261- one of ``{critical, error, warning, info, debug, notset}``,
262- case-insensitive
285+ level
286+ https://docs.python.org/3/library/logging.html#levels
263287
264- log_dir : str, optional
288+ log_dir
265289
266- mock : bool, optional
290+ mock
267291 if ``True``, return a ``CPAC.utils.monitoring.MockLogger``
268292 instead of a ``logging.Logger``
269293
270294 Returns
271295 -------
272- logger : logging.Handler
273- initialized logging Handler
296+ logger
297+ initialized logger
274298
275299 Examples
276300 --------
@@ -295,19 +319,25 @@ def set_up_logger(
295319 """
296320 if filename is None :
297321 filename = f"{ name } .log"
298- try :
299- level = getattr (logging , level .upper ())
300- except AttributeError :
322+ if isinstance (level , str ):
323+ try :
324+ level = getattr (logging , level .upper ())
325+ except AttributeError :
326+ pass
327+ if not level :
301328 level = logging .NOTSET
302- if log_dir is None :
303- log_dir = os .getcwd ()
304- filepath = os .path .join (log_dir , filename )
305- if overwrite_existing and os .path .exists (filepath ):
306- with open (filepath , "w" ) as log_file :
329+ log_dir = Path (log_dir ) if log_dir else Path .cwd ()
330+ filepath = log_dir / filename
331+ if overwrite_existing and filepath .exists ():
332+ with filepath .open ("w" , encoding = "utf-8" ) as log_file :
307333 log_file .write ("" )
334+ if not filepath .exists ():
335+ filepath .parent .mkdir (parents = True , exist_ok = True )
308336 if mock :
309337 return MockLogger (name , filename , level , log_dir )
310338 logger = getLogger (name )
339+ if isinstance (logger , MockLogger ):
340+ return logger
311341 logger .setLevel (level )
312342 handler = logging .FileHandler (filepath )
313343 logger .addHandler (handler )
0 commit comments