o
    ei'                     @   s  d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
mZ ddlmZ ddlmZ i dddd	d
ddddddddddddddddddddddd d!d"d#d$d%d&Zi dd'dd(d
d)dd*dd+dd,dd-dd.dddd/dd0dd1dd2dd3d!d4d#d5d%d6ZG d7d8 d8ejZd9ed:efd;d<Zd=i d>fd?d@ZG dAdB dBejZdHdDdEZdFdG ZdS )IzManaging the logger, utilities

Author
 * Fang-Pen Lin 2012 https://fangpenlin.com/posts/2012/08/26/good-logging-practice-in-python/
 * Peter Plantinga 2020
 * Aku Rouhe 2020
    N)recursive_updateif_main_process)	run_shelliyiziaifipini   µm    k   M	   G   T   P   E   Z   Yseptillionthssextillionthsquintillionthsquadrillionthstrillionths
billionths
millionthsthousandthsThousandMillionBillionTrillionQuadrillionQuintillion
Sextillion
Septillionc                   @   s\   e Zd ZdZededefddZdedede	d	e
fd
dZedde	d	e
fddZdS )MultiProcessLoggerAdaptera
  
    Logger adapter that handles multi-process logging, ensuring logs are written
    only on the main process if specified. This class extends `logging.LoggerAdapter`
    and provides additional functionality for controlling logging in multi-process
    environments, with the option to limit logs to the main process only.

    This class is heavily inspired by HuggingFace Accelerate toolkit:
    https://github.com/huggingface/accelerate/blob/85b1a03552cf8d58e036634e004220c189bfb247/src/accelerate/logging.py#L22
    main_process_onlyreturnc                 C   s   |  p| ot  S )a  
        Determines if logging should occur based on whether the code is running
        on the main process or not.

        Arguments
        ---------
        main_process_only : bool
            A flag indicating if logging should be restricted to the main process.

        Returns
        -------
        bool
            True if logging should be performed (based on the process and the flag),
            False otherwise.
        r   )r1    r3   V/home/ubuntu/transcripts/venv/lib/python3.10/site-packages/speechbrain/utils/logger.py_should_logN   s   z%MultiProcessLoggerAdapter._should_loglevelmsgargskwargsc                 O   sd   | dd}|dd | |r.| |r0| ||\}}| jj||g|R i | dS dS dS )az  
        Logs a message with the specified log level, respecting the `main_process_only`
        flag to decide whether to log based on the current process.

        Arguments
        ---------
        level : int
            Logging level (e.g., logging.INFO, logging.WARNING).
        msg : str
            The message to log.
        *args : tuple
            Additional positional arguments passed to the logger.
        **kwargs : dict
            Additional keyword arguments passed to the logger, including:
            - main_process_only (bool): If True, log only from the main process (default: True).
            - stacklevel (int): The stack level to use when logging (default: 2).

        Notes
        -----
        If `main_process_only` is True, the log will only be written if the current process
        is the main process, as determined by `if_main_process()`.
        r1   T
stacklevel   N)pop
setdefaultisEnabledForr5   processloggerlog)selfr6   r7   r8   r9   r1   r3   r3   r4   rA   c   s   

 zMultiProcessLoggerAdapter.logNc                 O   s   | j |i | dS )a  
        Logs a warning message only once by using caching to prevent duplicate warnings.

        Arguments
        ---------
        *args : tuple
            Positional arguments passed to the warning log.
        **kwargs : dict
            Keyword arguments passed to the warning log.

        Notes
        -----
        This method is decorated with `functools.lru_cache(None)`, ensuring that the warning
        message is logged only once regardless of how many times the method is called.
        N)warning)rB   r8   r9   r3   r3   r4   warning_once   s   z&MultiProcessLoggerAdapter.warning_once)__name__
__module____qualname____doc__staticmethodboolr5   intstrtupledictrA   	functools	lru_cacherD   r3   r3   r3   r4   r0   C   s    
r0   namer2   c                 C   sF   t | }tjdd}|du rd}|tjd< ||  t|i S )a  
    Retrieves a logger with the specified name, applying a log level from the environment variable
    `SB_LOG_LEVEL` if set, or defaults to `INFO` level.

    If the environment variable `SB_LOG_LEVEL` is not defined, it defaults to `INFO` level and sets
    this level in the environment for future use. The environment variable can be set manually or
    automatically in `Brain` class following `setup_logging`.

    Arguments
    ---------
    name : str
        The name of the logger to retrieve.

    Returns
    -------
    MultiProcessLoggerAdapter
        An instance of `MultiProcessLoggerAdapter` wrapping the logger with the specified name.
    SB_LOG_LEVELNDEBUG)logging	getLoggerosenvirongetsetLevelupperr0   )rQ   r@   	log_levelr3   r3   r4   
get_logger   s   


r\   zlog-config.yamlrS   c                 C   sv   t j| r.t| ddd}t|}W d   n1 sw   Y  t|| tj	| ntj
|d |t jd< dS )a  Setup logging configuration.

    Arguments
    ---------
    config_path : str
        The path to a logging config file.
    overrides : dict
        A dictionary of the same structure as the config dict
        with any updated values that need to be applied.
    default_level : str
        The log level to use if the config file is not found.
        Python logging allows ints or strings:
        https://docs.python.org/3/library/logging.html#logging.Logger.setLevel
        but strings are used here as environment variables have to be
        strings. The available levels are listed here:
        https://docs.python.org/3/library/logging.html#levels
    rtzutf-8)encodingN)r6   rR   )rV   pathexistsopenyaml	safe_loadr   rT   config
dictConfigbasicConfigrW   )config_path	overridesdefault_levelr	   rd   r3   r3   r4   setup_logging   s   
rj   c                   @   s   e Zd ZdZdd ZdS )TqdmCompatibleStreamHandlerzTQDM compatible StreamHandler.

    Writes and prints should be passed through tqdm.tqdm.write
    so that the tqdm progressbar doesn't get messed up.
    c                 C   s`   z|  |}| j}tjj|| j|d |   W dS  ty!     ty/   | | Y dS w )zTQDM compatible StreamHandler.)endfileN)	formatstreamtqdmwrite
terminatorflushRecursionError	ExceptionhandleError)rB   recordr7   ro   r3   r3   r4   emit   s   
z TqdmCompatibleStreamHandler.emitN)rE   rF   rG   rH   rx   r3   r3   r3   r4   rk      s    rk   Tc                 C   s   |rt nt}d}dttt| d }||vr.|dkr.|td| }||vr.|dks|| }|dkrA|j| d|  d}nt| t	rKt
| }n|j| d}|sU|sY|| S |d | S )a  Formats number to the appropriate order of magnitude for printing.

    Arguments
    ---------
    number : int, float
        The number to format.
    abbreviate : bool
        Whether to use abbreviations (k,M,G) or words (Thousand, Million,
        Billion). Numbers will be either like: "123.5k" or "123.5 Thousand".

    Returns
    -------
    str
        The formatted number. Note that the order of magnitude token is part
        of the string.

    Example
    -------
    >>> print(format_order_of_magnitude(123456))
    123.5k
    >>> print(format_order_of_magnitude(0.00000123, abbreviate=False))
    1.2 millionths
    >>> print(format_order_of_magnitude(5, abbreviate=False))
    5
    z
{num:3.1f}r   i  r   
   )num )ORDERS_ABBREVORDERS_WORDSmathfloorrA   fabscopysignrn   
isinstancerK   rL   )number
abbreviatestyle	precisionorderorder_tokenformatted_numberr3   r3   r4   format_order_of_magnitude   s   

r   c                  C   s  dt j d } ztd\}}}d}||jdd7 }W n ty%   d}Y nw ztd\}}}d	|jdd }W n tyB   d
}Y nw tj r\tjjdu rUdtjj }n	dtjj }nd}d}|d7 }|| 7 }|d7 }||7 }|d7 }||7 }|d7 }||7 }|S )a`  Returns a string describing the current Python / SpeechBrain environment.

    Useful for making experiments as replicable as possible.

    Returns
    -------
    str
        The string is formatted ready to be written to a file.

    Example
    -------
    >>> get_environment_description().splitlines()[0]
    'SpeechBrain system description'
    zPython version:

z
pip freezezInstalled Python packages:
replace)errorsz.Could not list python packages with pip freezezgit rev-parse --short HEADzGit revision:
zCould not get git revisionNzROCm version:
zCUDA version:
zCUDA not availablezSpeechBrain system description
z==============================
)	sysversionr   decodeOSErrortorchcudais_availablehip)python_version_strfreezed_python_packages_strgit_hashgit_strcuda_strresultr3   r3   r4   get_environment_description  s:   
r   )T)rH   rO   rT   logging.configr~   rV   r   r   rp   rb   speechbrain.utils.data_utilsr   speechbrain.utils.distributedr   speechbrain.utils.superpowersr   r|   r}   LoggerAdapterr0   rL   r\   rj   StreamHandlerrk   r   r   r3   r3   r3   r4   <module>   s    	
	
S
 
.