o
    NEg                     @   sp  d Z ddlZddlm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ZddlmZ ddlmZmZmZmZmZmZmZmZmZmZ ddlmZ dedefd	d
Z dedefddZ!dedefddZ"dedefddZ#dedefddZ$G dd de%Z&G dd dZ'	dxdedeeee f dejfddZ(dedefdd Z)d!edefd"d#Z*d$edefd%d&Z+d'ee dee fd(d)Z,d*edee-ef fd+d,Z.d*edeee-ef  fd-d.Z/d'ee dee fd/d0Z0d1ee d2ee ddfd3d4Z1d1ee d5ee ddfd6d7Z2d8edefd9d:Z3d1ee ddfd;d<Z4dee fd=d>Z5e	j6fd?edee fd@dAZ7e	j6fdBee dee fdCdDZ8dEedee fdFdGZ9G dHdI dIZ:G dJdK dKZ;G dLdM dMZ<G dNdO dOZ=G dPdQ dQZ>dRedSedTe-dUe-dVedee fdWdXZ?G dYdZ dZeZ@d[dd\d]d^dRed_e@d`edaee- dbe-dcedefdddeZAd[dd\d]d^dRed`edaee- dbe-dcedefdfdgZBd[dd\d]d^dRed`edaee- dbe-dcedefdhdiZCd[dd\d]d^dRed`edaee- dbe-dcedefdjdkZDd\dldSedme-dbe-defdndoZEdRedee-ef fdpdqZFdreeee f dseddfdtduZGdefdvdwZHdS )yzShared utility functions    N)Enum)
IOAnyCallableDictIterableListOptionalTextIOTypeUnion   )	constantsargreturnc                 C   s*   t | dko| d | d ko| d tjv S )z
    Checks if a string is quoted

    :param arg: the string being checked for quotes
    :return: True if a string is quoted
    r   r   )lenr   QUOTESr    r   d/var/www/eduai.edurigo.com/doc_train/edurigo_ai/Puru/venv/lib/python3.10/site-packages/cmd2/utils.py	is_quoted%   s   *r   c                 C   s   d| v rd}nd}||  | S )zQuote a string"'r   )r   quoter   r   r   quote_string/   s   r   c                 C   s   t | sd| vr
| S t| S )z=Quote a string if it contains spaces and isn't already quoted )r   r   r   r   r   r   quote_string_if_needed9   s   r   c                 C   s   t | r
| dd } | S )zStrip outer quotes from a string.

     Applies to both single and double quotes.

    :param arg:  string to strip outer quotes from
    :return: same string with potentially outer quotes stripped
    r   r   )r   r   r   r   r   strip_quotesA   s   r   valc                 C   s:   t | tr|  tdkrdS |  tdkrdS td)zConverts a string to a boolean based on its value.

    :param val: string being converted
    :return: boolean value expressed in the string
    :raises: ValueError if the string does not contain a value corresponding to a boolean value
    TFz(must be True or False (case-insensitive))
isinstancestr
capitalize
ValueError)r   r   r   r   str_to_boolN   s   
r$   c                       s,   e Zd ZdZdddef fddZ  ZS )CompletionErrora*  
    Raised during tab completion operations to report any sort of error you want printed. This can also be used
    just to display a message, even if it's not an error. For instance, ArgparseCompleter raises CompletionErrors
    to display tab completion hints and sets apply_style to False so hints aren't colored like error text.

    Example use cases

    - Reading a database to retrieve a tab completion data set failed
    - A previous command line argument that determines the data set being completed is invalid
    - Tab completion hints
    T)apply_styler&   c                   s   || _ t j|i | dS )a4  
        Initializer for CompletionError
        :param apply_style: If True, then ansi.style_error will be applied to the message text when printed.
                            Set to False in cases where the message text already has the desired style.
                            Defaults to True.
        N)r&   super__init__)selfr&   argskwargs	__class__r   r   r(   i   s   zCompletionError.__init__)__name__
__module____qualname____doc__boolr(   __classcell__r   r   r,   r   r%   ]   s     r%   c                   @   sl   e Zd ZdZddddddddedededeeeegef ded	ee d
ee dee dee fddZ	dS )SettablezVUsed to configure a cmd2 instance member to be settable via the set command in the CLIN)onchange_cbchoiceschoices_functionchoices_methodcompleter_functioncompleter_methodnameval_typedescriptionr5   r6   r7   r8   r9   r:   c          
      C   sN   |t kr
t}ddg}|| _|| _|| _|| _|| _|| _|| _|| _	|	| _
dS )aN  
        Settable Initializer

        :param name: name of the instance attribute being made settable
        :param val_type: callable used to cast the string value from the command line into its proper type and
                         even validate its value. Setting this to bool provides tab completion for true/false and
                         validation using str_to_bool(). The val_type function should raise an exception if it fails.
                         This exception will be caught and printed by Cmd.do_set().
        :param description: string describing this setting
        :param onchange_cb: optional function or method to call when the value of this settable is altered
                            by the set command. (e.g. onchange_cb=self.debug_changed)

                            Cmd.do_set() passes the following 3 arguments to onchange_cb:
                                param_name: str - name of the changed parameter
                                old_value: Any - the value before being changed
                                new_value: Any - the value after being changed

        The following optional settings provide tab completion for a parameter's values. They correspond to the
        same settings in argparse-based tab completion. A maximum of one of these should be provided.

        :param choices: iterable of accepted values
        :param choices_function: function that provides choices for this argument
        :param choices_method: cmd2-app method that provides choices for this argument (See note below)
        :param completer_function: tab completion function that provides choices for this argument
        :param completer_method: cmd2-app tab completion method that provides choices
                                 for this argument (See note below)

        Note:
        For choices_method and completer_method, do not set them to a bound method. This is because
        ArgparseCompleter passes the self argument explicitly to these functions.

        Therefore instead of passing something like self.path_complete, pass cmd2.Cmd.path_complete.
        truefalseN)r2   r$   r;   r<   r=   r5   r6   r7   r8   r9   r:   )
r)   r;   r<   r=   r5   r6   r7   r8   r9   r:   r   r   r   r(   x   s   (
zSettable.__init__)
r.   r/   r0   r1   r!   r   r   r   r	   r(   r   r   r   r   r4   v   s*    r4   r   typenamefield_namesdefault_valuesc                 C   sR   t | |}dt|j |j_t|tjr|di |}n|| }t	||j_|S )a  
    Convenience function for defining a namedtuple with default values

    From: https://stackoverflow.com/questions/11351032/namedtuple-and-default-values-for-optional-keyword-arguments

    Examples:
        >>> Node = namedtuple_with_defaults('Node', 'val left right')
        >>> Node()
        Node(val=None, left=None, right=None)
        >>> Node = namedtuple_with_defaults('Node', 'val left right', [1, 2, 3])
        >>> Node()
        Node(val=1, left=2, right=3)
        >>> Node = namedtuple_with_defaults('Node', 'val left right', {'right':7})
        >>> Node()
        Node(val=None, left=None, right=7)
        >>> Node(4)
        Node(val=4, left=None, right=7)
    NNr   )
collections
namedtupler   _fields__new____defaults__r    collections_abcMappingtuple)r@   rA   rB   T	prototyper   r   r   namedtuple_with_defaults   s   rN   	file_pathc                 C   s2  ddl }tjtj|  }d}z3|j|ddd }tdd |D dkr2d	}W d   W |S W d   W |S 1 s>w   Y  W |S  tyO   Y |S  t	y   z-|j|d
dd}tdd |D dkrld	}W d   n1 svw   Y  W Y |S W Y |S  ty   Y Y |S  t	y   Y Y |S w w )zReturns if a file contains only ASCII or UTF-8 encoded text.

    :param file_path: path to the file being checked
    :return: True if the file is a text file, False if it is binary.
    r   NFasciistrictencodingerrorsc                 s       | ]}d V  qdS r   Nr   .0liner   r   r   	<genexpr>       zis_text_file.<locals>.<genexpr>Tutf-8c                 s   rU   rV   r   rW   r   r   r   rZ      r[   )
codecsospathabspath
expanduserstripopensumOSErrorUnicodeDecodeError)rO   r]   expanded_pathvalid_text_filefr   r   r   is_text_file   sF    rj   list_to_prunec                 C   s&   t  }| D ]}d||< qt| S )zRemoves duplicates from a list while preserving order of the items.

    :param list_to_prune: the list being pruned of duplicates
    :return: The pruned list
    N)rD   OrderedDictlistkeys)rk   	temp_dictitemr   r   r   remove_duplicates   s   
rq   astrc                 C   s   t d|  S )zNormalize and casefold Unicode strings for saner comparisons.

    :param astr: input unicode string
    :return: a normalized and case-folded version of the input string
    NFC)unicodedata	normalizecasefold)rr   r   r   r   	norm_fold   s   rw   list_to_sortc                 C      t | tdS )a)  Sorts a list of strings alphabetically.

    For example: ['a1', 'A11', 'A2', 'a22', 'a3']

    To sort a list in place, don't call this method, which makes a copy. Instead, do this:

    my_list.sort(key=norm_fold)

    :param list_to_sort: the list being sorted
    :return: the sorted list
    key)sortedrw   rx   r   r   r   alphabetical_sort	  s   r~   	input_strc                 C   s&   zt | W S  ty   t|  Y S w )z
    Tries to convert the passed-in string to an integer. If that fails, it converts it to lower case using norm_fold.
    :param input_str: string to convert
    :return: the string as an integer or a lower case version of the string
    )intr#   rw   r   r   r   r   try_int_or_force_to_lower_case  s
   
r   c                 C   s   dd t d| D S )a  
    Converts a string into a list of integers and strings to support natural sorting (see natural_sort).

    For example: natural_keys('abc123def') -> ['abc', '123', 'def']
    :param input_str: string to convert
    :return: list of strings and integers
    c                 S   s   g | ]}t |qS r   )r   )rX   substrr   r   r   
<listcomp>,  s    z natural_keys.<locals>.<listcomp>z(\d+))resplitr   r   r   r   natural_keys$  s   r   c                 C   ry   )aV  
    Sorts a list of strings case insensitively as well as numerically.

    For example: ['a1', 'A2', 'a3', 'A11', 'a22']

    To sort a list in place, don't call this method, which makes a copy. Instead, do this:

    my_list.sort(key=natural_keys)

    :param list_to_sort: the list being sorted
    :return: the list sorted naturally
    rz   )r|   r   r}   r   r   r   natural_sort/  s   r   tokenstokens_to_quotec                 C   s*   t | D ]\}}||v rt|| |< qdS )z
    Quote specific tokens in a list

    :param tokens: token list being edited
    :param tokens_to_quote: the tokens, which if present in tokens, to quote
    N)	enumerater   )r   r   itokenr   r   r   quote_specific_tokens?  s
   r   tokens_to_unquotec                 C   s.   t | D ]\}}t|}||v r|| |< qdS )z
    Unquote specific tokens in a list

    :param tokens: token list being edited
    :param tokens_to_unquote: the tokens, which if present in tokens, to unquote
    N)r   r   )r   r   r   r   unquoted_tokenr   r   r   unquote_specific_tokensK  s   r   r   c                 C   sB   | rt | r| d }t| } nd}tj| } |r||  | } | S )zn
    Wrap os.expanduser() to support expanding ~ in quoted strings
    :param token: the string to expand
    r    )r   r   r^   r_   ra   )r   
quote_charr   r   r   expand_userX  s   
r   c                 C   s&   t | D ]\}}t| | | |< qdS )zc
    Call expand_user() on all tokens in a list of strings
    :param tokens: tokens to expand
    N)r   r   )r   index_r   r   r   expand_user_in_tokensm  s   r   c                  C   s   t jd} | s^tjdd dkrg d}ng d}dd t d	t jjD }t	
||D ],\} }t j|| }t j|r[t |t jr[tjdd dkrXt j| d
 }  | S q/d} | S )z
    Used to set cmd2.Cmd.DEFAULT_EDITOR. If EDITOR env variable is set, that will be used.
    Otherwise the function will look for a known editor in directories specified by PATH env variable.
    :return: Default editor or None
    EDITORN   win)zcode.cmdznotepad++.exeznotepad.exe)vimviemacsnanopicojoecodesublatomgeditgeanykatec                 S      g | ]
}t j|s|qS r   r^   r_   islinkrX   pr   r   r   r         zfind_editor.<locals>.<listcomp>PATHr   )r^   environgetsysplatformgetenvr   r_   pathsep	itertoolsproductjoinisfileaccessX_OKsplitext)editoreditorspathsr_   editor_pathr   r   r   find_editorv  s    
r   patternc                    s    fddt  | D S )av  Return a list of file paths based on a glob pattern.

    Only files are returned, not directories, and optionally only files for which the user has a specified access to.

    :param pattern: file name or glob pattern
    :param access: file access type to verify (os.* where * is F_OK, R_OK, W_OK, or X_OK)
    :return: list of files matching the name or glob pattern
    c                    s(   g | ]}t j|rt | r|qS r   )r^   r_   r   r   )rX   ri   r   r   r   r     s   ( z+files_from_glob_pattern.<locals>.<listcomp>)glob)r   r   r   r   r   files_from_glob_pattern  s   	r   patternsc                 C   s(   g }| D ]}t ||d}|| q|S )a  Return a list of file paths based on a list of glob patterns.

    Only files are returned, not directories, and optionally only files for which the user has a specified access to.

    :param patterns: list of file names and/or glob patterns
    :param access: file access type to verify (os.* where * is F_OK, R_OK, W_OK, or X_OK)
    :return: list of files matching the names and/or glob patterns
    r   )r   extend)r   r   filesr   matchesr   r   r   files_from_glob_patterns  s
   	r   starts_withc           	      C   s   ddg}|D ]
}|| v rg   S qdd t dt jjD }t }|D ] }t j|| }t|d t jd}|D ]}|	t j
| q9q%t|S )zReturns names of executables in a user's path

    :param starts_with: what the exes should start with. leave blank for all exes in path.
    :return: a list of matching exe names
    *?c                 S   r   r   r   r   r   r   r   r     r   z$get_exes_in_path.<locals>.<listcomp>r   r   )r^   r   r   r_   r   setr   r   r   addbasenamerm   )	r   	wildcardswildcardr   exes_setr_   	full_pathr   matchr   r   r   get_exes_in_path  s   r   c                	   @   s   e Zd ZdZdddddededed	d
fddZded	d
fddZd	efddZd	e	fddZ
d#dee d	efddZd	e	fddZd$ddZd	efddZed	efddZd efd!d"Zd
S )%StdSimz
    Class to simulate behavior of sys.stdout or sys.stderr.
    Stores contents in internal buffer and optionally echos to the inner stream it is simulating.
    Fr\   replace)echorS   rT   r   rS   rT   r   Nc                C   s,   || _ || _|| _|| _d| _t| | _dS )am  
        StdSim Initializer
        :param inner_stream: the wrapped stream. Should be a TextIO or StdSim instance.
        :param echo: if True, then all input will be echoed to inner_stream
        :param encoding: codec for encoding/decoding strings (defaults to utf-8)
        :param errors: how to handle encoding/decoding errors (defaults to replace)
        FN)inner_streamr   rS   rT   pause_storageByteBufbuffer)r)   r   r   rS   rT   r   r   r   r(     s   	zStdSim.__init__sc                 C   sZ   t |tstdt|| js | j j|j| j	| j
d7  _| jr+| j| dS dS )zSAdd str to internal bytes buffer and if echo is True, echo contents to inner streamz$write() argument must be str, not {}rR   N)r    r!   	TypeErrorformattyper   r   byte_bufencoderS   rT   r   r   write)r)   r   r   r   r   r     s   
zStdSim.writec                 C   s   | j jj| j| jdS )z"Get the internal contents as a strrR   )r   r   decoderS   rT   r)   r   r   r   getvalue  s   zStdSim.getvaluec                 C   s   t | jjS )z"Get the internal contents as bytes)bytesr   r   r   r   r   r   getbytes  s   zStdSim.getbytesr   sizec                 C   sZ   |du s|dkr|   }|   |S | jjd| j| j| jd}| jj|d | j_|S )z@Read from the internal contents as a str and then clear them outNr   rR   )r   clearr   r   r   rS   rT   )r)   r   resultr   r   r   read  s   zStdSim.readc                 C   s   |   }|   |S )z@Read from the internal contents as bytes and then clear them out)r   r   )r)   r   r   r   r   	readbytes  s   zStdSim.readbytesc                 C   s   | j j  dS )zClear the internal contentsN)r   r   r   r   r   r   r   r     s   zStdSim.clearc                 C   s   | j r| j S dS )z[StdSim only considered an interactive stream if `echo` is True and `inner_stream` is a tty.F)r   r   isattyr   r   r   r   r     s   
zStdSim.isattyc                 C   s    z| j jW S  ty   Y dS w )z
        Handle when the inner stream doesn't have a line_buffering attribute which is the case
        when running unit tests because pytest sets stdout to a pytest EncodedFile object.
        F)r   line_bufferingAttributeErrorr   r   r   r   r     s
   
zStdSim.line_bufferingrp   c                 C   s    || j v r
| j | S t| j|S rC   )__dict__getattrr   )r)   rp   r   r   r   __getattr__  s   

zStdSim.__getattr__)r   r   N)r.   r/   r0   r1   r2   r!   r(   r   r   r   r   r	   r   r   r   r   r   propertyr   r   r   r   r   r   r     s*    




r   c                   @   s<   e Zd ZdZddgZdeddfddZd	eddfd
dZdS )r   zQ
    Used by StdSim to write binary data and stores the actual bytes written
       
   std_sim_instancer   Nc                 C   s   t  | _|| _d S rC   )	bytearrayr   r   )r)   r   r   r   r   r(   &  s   
zByteBuf.__init__bc                    s   t  tstdt | jjs|  j 7  _| jjr<| jj	j
  | jjr>t fddtjD r@| j  dS dS dS dS )zVAdd bytes to internal bytes buffer and if echo is True, echo contents to inner stream.z'a bytes-like object is required, not {}c                 3   s    | ]}| v V  qd S rC   r   )rX   newliner   r   r   rZ   8  s    z ByteBuf.write.<locals>.<genexpr>N)r    r   r   r   r   r   r   r   r   r   r   r   r   anyr   NEWLINESflush)r)   r   r   r   r   r   *  s   
zByteBuf.write)	r.   r/   r0   r1   r   r   r(   r   r   r   r   r   r   r     s
    r   c                   @   s   e Zd ZdZdejdeeef deeef ddfddZ	dd	d
Z
dddZdddZdeddfddZedeeef deddfddZdS )
ProcReaderz
    Used to capture stdout and stderr from a Popen process if any of those were set to subprocess.PIPE.
    If neither are pipes, then the process will run normally and no output will be captured.
    procstdoutstderrr   Nc                 C   sv   || _ || _|| _tjd| jddid| _tjd| jddid| _| j jdur,| j	  | j j
dur9| j	  dS dS )z
        ProcReader initializer
        :param proc: the Popen process being read from
        :param stdout: the stream to write captured stdout
        :param stderr: the stream to write captured stderr
        
out_threadread_stdoutT)r;   targetr+   FN)_proc_stdout_stderr	threadingThread_reader_thread_func_out_thread_err_threadr  startr  )r)   r   r  r  r   r   r   r(   A  s   


zProcReader.__init__c                 C   s^   ddl }tjdr| j|j dS zt| jj	}t
||j W dS  ty.   Y dS w )z@Send a SIGINT to the process similar to if <Ctrl>+C were pressedr   Nr   )signalr   r   
startswithr  send_signalCTRL_BREAK_EVENTr^   getpgidpidkillpgSIGINTProcessLookupError)r)   r  group_idr   r   r   send_sigintY  s   zProcReader.send_sigintc                 C   s   | j   dS )zTerminate the processN)r  	terminater   r   r   r   r  i  s   zProcReader.terminatec                 C   sb   | j  r
| j   | j r| j  | j \}}|r$| | j| |r/| | j| dS dS )zWait for the process to finishN)	r  is_aliver   r  r  communicate_write_bytesr  r  )r)   outerrr   r   r   waitm  s   



zProcReader.waitr  c                 C   sx   |r
| j j}| j}n| j j}| j}|dusJ | j  du r:| }|r1|t| | 	|| | j  du sdS dS )z
        Thread function that reads a stream from the process
        :param read_stdout: if True, then this thread deals with stdout. Otherwise it deals with stderr.
        N)
r  r  r  r  r  pollpeekr   r   r  )r)   r  read_streamwrite_stream	availabler   r   r   r  }  s   zProcReader._reader_thread_funcstreamto_writec                 C   s(   z	| j | W dS  ty   Y dS w )z
        Write bytes to a stream
        :param stream: the stream being written to
        :param to_write: the bytes being written
        N)r   r   BrokenPipeError)r&  r'  r   r   r   r    s
   zProcReader._write_bytesr   )r.   r/   r0   r1   
subprocessPopenr   r   r
   r(   r  r  r   r2   r  staticmethodr   r  r   r   r   r   r   <  s    




$r   c                   @   s<   e Zd ZdZdddZdefddZddd	Zdd
dZdS )ContextFlaga{  A context manager which is also used as a boolean flag value within the default sigint handler.

    Its main use is as a flag to prevent the SIGINT handler in cmd2 from raising a KeyboardInterrupt
    while a critical code section has set the flag to True. Because signal handling is always done on the
    main thread, this class is not thread-safe since there is no need.
    r   Nc                 C   s
   d| _ d S Nr   _ContextFlag__countr   r   r   r   r(     s   
zContextFlag.__init__c                 C   s
   | j dkS r-  r.  r   r   r   r   __bool__  s   
zContextFlag.__bool__c                 C   s   |  j d7  _ d S )Nr   r.  r   r   r   r   	__enter__  s   zContextFlag.__enter__c                 G   s$   |  j d8  _ | j dk rtdd S )Nr   r   zcount has gone below 0)r/  r#   )r)   r*   r   r   r   __exit__  s   
zContextFlag.__exit__r   )	r.   r/   r0   r1   r(   r2   r0  r1  r2  r   r   r   r   r,    s    

r,  c                
   @   sJ   e Zd ZdZdeeee f deeee f dee	 de
ddf
dd	ZdS )
RedirectionSavedStatezXCreated by each command to store information required to restore state after redirectionself_stdout
sys_stdoutpipe_proc_readersaved_redirectingr   Nc                 C   s"   d| _ || _|| _|| _|| _dS )a*  
        RedirectionSavedState initializer
        :param self_stdout: saved value of Cmd.stdout
        :param sys_stdout: saved value of sys.stdout
        :param pipe_proc_reader: saved value of Cmd._cur_pipe_proc_reader
        :param saved_redirecting: saved value of Cmd._redirecting
        FN)redirectingsaved_self_stdoutsaved_sys_stdoutsaved_pipe_proc_readerr7  )r)   r4  r5  r6  r7  r   r   r   r(     s
   

zRedirectionSavedState.__init__)r.   r/   r0   r1   r   r   r   r!   r	   r   r2   r(   r   r   r   r   r3    s    "r3  textrY   begidxendidxmatch_againstc                    s    fdd|D S )a^  
    Basic tab completion function that matches against a list of strings without considering line contents
    or cursor position. The args required by this function are defined in the header of Python's cmd.py.

    :param text: the string prefix we are attempting to match (all matches must begin with it)
    :param line: the current input line with leading whitespace removed
    :param begidx: the beginning index of the prefix text
    :param endidx: the ending index of the prefix text
    :param match_against: the strings being matched against
    :return: a list of possible tab completions
    c                    s   g | ]	}|  r|qS r   )r  )rX   	cur_matchr<  r   r   r     s    z"basic_complete.<locals>.<listcomp>r   )r<  rY   r=  r>  r?  r   rA  r   basic_complete  s   rB  c                   @   s   e Zd ZdZdZdZdZdS )TextAlignmentzHorizontal text alignmentr      r   N)r.   r/   r0   r1   LEFTCENTERRIGHTr   r   r   r   rC    s
    rC  r      F	fill_charwidth	tab_widthtruncate	alignmentrJ  rK  rL  rM  c                C   sd  ddl }ddl}ddlm} |du r| j}|dk rtd| dd| } |dd}t|	|dkr:t
d||}	|	d	krGtd
| rN|  }
ndg}
| }d}t|}d}d}|t|k r||v rx||| 7 }|t|| 7 }n|d7 }|d7 }|t|k set|
D ]\}}|dkr|d |rt||}||}|d	krtdt|}||krd}n|| }|tjkrd}|}n|tjkr|d }|| }n|}d}||	 | }||	 | }|||||  7 }|||||  7 }|s|s|r|r|j| }||j7 }|r|j| }||j7 }||| | |  |d| 7 }q| S )u  
    Align text for display within a given width. Supports characters with display widths greater than 1.
    ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned
    independently.

    There are convenience wrappers around this function: align_left(), align_center(), and align_right()

    :param text: text to align (can contain multiple lines)
    :param alignment: how to align the text
    :param fill_char: character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character)
    :param width: display width of the aligned text. Defaults to width of the terminal.
    :param tab_width: any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will
                      be converted to one space.
    :param truncate: if True, then each line will be shortened to fit within the display width. The truncated
                     portions are replaced by a '…' character. Defaults to False.
    :return: aligned text
    :raises: TypeError if fill_char is more than one character (not including ANSI style sequences)
    :raises: ValueError if text or fill_char contains an unprintable character
    :raises: ValueError if width is less than 1
    r   Nr   ansizwidth must be at least 1	r   z1Fill character must be exactly one character longr   z*Fill character is an unprintable characterr   
z/Text to align contains an unprintable characterrD  )ioshutilr   rP  get_terminal_sizecolumnsr#   r   r   strip_styler   style_aware_wcswidth
splitlinesStringIOget_styles_in_textr   r   truncate_linerC  rE  rF  	RESET_ALLr   valuesr   )r<  rN  rJ  rK  rL  rM  rS  rT  rP  fill_char_widthlinestext_bufaggregate_stylesfill_char_stylesstyled_space
char_indexr   rY   
line_widthline_stylestotal_fill_widthleft_fill_widthright_fill_width	left_fill
right_fillr   r   r   
align_text  s~   













rm  c                C      t | tj||||dS )uo  
    Left align text for display within a given width. Supports characters with display widths greater than 1.
    ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned
    independently.

    :param text: text to left align (can contain multiple lines)
    :param fill_char: character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character)
    :param width: display width of the aligned text. Defaults to width of the terminal.
    :param tab_width: any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will
                      be converted to one space.
    :param truncate: if True, then text will be shortened to fit within the display width. The truncated portion is
                     replaced by a '…' character. Defaults to False.
    :return: left-aligned text
    :raises: TypeError if fill_char is more than one character (not including ANSI style sequences)
    :raises: ValueError if text or fill_char contains an unprintable character
    :raises: ValueError if width is less than 1
    rI  )rm  rC  rE  r<  rJ  rK  rL  rM  r   r   r   
align_leftm     rp  c                C   rn  )uc  
    Center text for display within a given width. Supports characters with display widths greater than 1.
    ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned
    independently.

    :param text: text to center (can contain multiple lines)
    :param fill_char: character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character)
    :param width: display width of the aligned text. Defaults to width of the terminal.
    :param tab_width: any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will
                      be converted to one space.
    :param truncate: if True, then text will be shortened to fit within the display width. The truncated portion is
                     replaced by a '…' character. Defaults to False.
    :return: centered text
    :raises: TypeError if fill_char is more than one character (not including ANSI style sequences)
    :raises: ValueError if text or fill_char contains an unprintable character
    :raises: ValueError if width is less than 1
    rI  )rm  rC  rF  ro  r   r   r   align_center  rq  rr  c                C   rn  )ur  
    Right align text for display within a given width. Supports characters with display widths greater than 1.
    ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned
    independently.

    :param text: text to right align (can contain multiple lines)
    :param fill_char: character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character)
    :param width: display width of the aligned text. Defaults to width of the terminal.
    :param tab_width: any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will
                      be converted to one space.
    :param truncate: if True, then text will be shortened to fit within the display width. The truncated portion is
                     replaced by a '…' character. Defaults to False.
    :return: right-aligned text
    :raises: TypeError if fill_char is more than one character (not including ANSI style sequences)
    :raises: ValueError if text or fill_char contains an unprintable character
    :raises: ValueError if width is less than 1
    rI  )rm  rC  rG  ro  r   r   r   align_right  rq  rs  )rL  	max_widthc                C   s  ddl }ddlm} | dd| } || dkrtd|dk r%td	|| |kr.| S t| }d
}d}d}| }	|s||v rY|	||  t	|| }
|
| ||
7 }q<| | }||}|| |krrtj}||}d}||7 }|	| |d7 }|r>|	d|  |	 S )u  
    Truncate a single line to fit within a given display width. Any portion of the string that is truncated
    is replaced by a '…' character. Supports characters with display widths greater than 1. ANSI style sequences
    do not count toward the display width.

    If there are ANSI style sequences in the string after where truncation occurs, this function will append them
    to the returned string.

    This is done to prevent issues caused in cases like: truncate_string(fg.blue + hello + fg.reset, 3)
    In this case, "hello" would be truncated before fg.reset resets the color from blue. Appending the remaining style
    sequences makes sure the style is in the same state had the entire string been printed. align_text() relies on this
    behavior when preserving style over multiple lines.

    :param line: text to truncate
    :param max_width: the maximum display width the resulting string is allowed to have
    :param tab_width: any tabs in the text will be replaced with this many spaces
    :return: line that has a display width less than or equal to width
    :raises: ValueError if text contains an unprintable character like a newline
    :raises: ValueError if max_width is less than 1
    r   Nr   rO  rQ  r   r   z&text contains an unprintable characterzmax_width must be at least 1FTr   )rS  r   rP  r   rX  r#   r[  rZ  r   r   popr   HORIZONTAL_ELLIPSISr   r^  r   )rY   rt  rL  rS  rP  stylesdoner   total_widthtruncated_buf	style_lenchar
char_widthr   r   r   r\    sB   



r\  c                 C   sX   ddl m} d}t }	 |j| |}|du r	 |S | || < |t| 7 }q)a8  
    Return an OrderedDict containing all ANSI style sequences found in a string

    The structure of the dictionary is:
        key: index where sequences begins
        value: ANSI style sequence found at index in text

    Keys are in ascending order

    :param text: text to search for style sequences
    r   rO  r   TN)	r   rP  rD   rl   ANSI_STYLE_REsearchgroupr  r   )r<  rP  r  rw  r   r   r   r   r[    s   r[  funccategoryc                 C   sV   t | tr| D ]	}t|tj| qdS t| r"t| jtj| dS t| tj| dS )a=  Categorize a function.

    The help command output will group the passed function under the
    specified category heading

    :param func: function or list of functions to categorize
    :param category: category to put it in

    :Example:

    >>> import cmd2
    >>> class MyApp(cmd2.Cmd):
    >>>   def do_echo(self, arglist):
    >>>     self.poutput(' '.join(arglist)
    >>>
    >>>   cmd2.utils.categorize(do_echo, "Text Processing")

    For an alternative approach to categorizing commands using a decorator, see
    :func:`~cmd2.decorators.with_category`
    N)r    r   setattrr   CMD_ATTR_HELP_CATEGORYinspectismethod__func__)r  r  rp   r   r   r   
categorize  s   

r  c                 C   s   t | tjrt| jS t| s"t| r<t| ddur<t| j	dr<t
| j	jD ]}| j|jv r5|  S q)t| d| } t| r]tt| | jddd ddd }t |tr]|S t| d	dS )
a  
    Attempts to resolve the class that defined a method.

    Inspired by implementation published here:
        https://stackoverflow.com/a/25959545/1956611

    TODO: Python 3.5.2 is unable to handle the type hints Callable and Optional[Type].
          Restore proper type hints after we drop 3.5.2 support

    :param meth: method to inspect
    :type meth: Callable
    :return: class type in which the supplied method was defined. None if it couldn't be resolved.
    :rtype: Optional[Type]
    __self__Nr-   r  z	.<locals>r   r   .__objclass__)r    	functoolspartialget_defining_classr  r  r  	isbuiltinr   r  getmror-   r.   r   
isfunction	getmoduler0   r   rsplitr   )methclsr   r   r   r  5  s$   




r  )r   )Ir1   rD   collections.abcabcrI   r  r   r  r   r^   r   r)  r   r	  rt   enumr   typingr   r   r   r   r   r   r	   r
   r   r   r   r   r!   r2   r   r   r   r   r$   	Exceptionr%   r4   rN   rj   rq   rw   r~   r   r   r   r   r   r   r   r   r   F_OKr   r   r   r   r   r   r,  r3  rB  rC  rm  rp  rr  rs  r\  r[  r  r  r   r   r   r   <module>   s   0

:
%		 Uf&
 


 I"