o
    vJ                     @   s   d Z ddlmZ ddlmZ ddlmZ ddlZddlZddlmZ ddl	Z	dZ
G dd	 d	e	ejeZG d
d deZG dd deZG dd deZG dd deZdS )a  Multiline output for Cloud SDK.

This module contains a set of classes that are useful for managing console
output that can be updated that spans multiple lines.

Currently only SimpleSuffixConsoleOutput is offered which only supports
updating the last added message. SimpleSuffixConsoleOutput is basically a
collection of semantically distinct messages to be outputted to the console.
These messages all have a suffix, and SimpleSuffixConsoleOutput supports
updating the suffix of the last added message. Calling UpdateConsole on
a SimpleSuffixConsoleOutput will update these messages and any changes
to the console.

Example usage:
  # Example for a simple spinner
  spinner = ['|', '/', '-', '\\']
  num_spinner_marks = len(spinner)

  # Define a ConsoleOutput message
  output = SimpleSuffixConsoleOutput(sys.stderr)

  # Add the message you want to be displayed for the spinner and update the
  # console to show the message.
  message = sscm.AddMessage('Instance is being created...')
  output.UpdateConsole()

  > Instance is being created

  # Start the spinner by updating the message and then updating the console.
  for i in range(20):
    output.UpdateMessage(message, spinner[i % num_spinner_marks])
    output.UpdateConsole()
    time.sleep(0.1)

  > Instance is being created...|
  > Instance is being created.../
  > ...

  output.UpdateMessage(message, 'done\n')
  output.UpdateConsole()

  > Instance is being created...done
    )absolute_import)division)unicode_literalsN)console_attr   c                   @   s   e Zd ZdZdd ZdS )ConsoleOutputzManages the printing and formatting of multiline console output.

  It is up to implementations of this metaclass to determine how different
  messages will added to the output.
  c                 C   s   dS )AUpdates the console output to show any updated or added messages.N selfr	   r	   B/tmp/google-cloud-sdk/lib/googlecloudsdk/core/console/multiline.pyUpdateConsoleR   s   zConsoleOutput.UpdateConsoleN)__name__
__module____qualname____doc__r   r	   r	   r	   r   r   K   s    r   c                       sT   e Zd ZdZ fddZ		dddZ		ddd	Zd
d Zdd Zdd Z	  Z
S )SimpleSuffixConsoleOutputa  A simple, suffix-only implementation of ConsoleOutput.

  In this context, simple means that only updating the last line is supported.
  This means that this is supported in all ASCII environments as it only relies
  on carriage returns ('\r') for modifying output. Suffix-only means that only
  modifying the ending of messages is supported, either via a
  detail_message_callback or by modifying the suffix of a SuffixConsoleMessage.
  c                    s.   || _ g | _d| _t | _tt|   dS )IConstructor.

    Args:
      stream: The output stream to write to.
    r   N)	_stream	_messages_last_print_index	threadingLock_locksuperr   __init__r   stream	__class__r	   r   r   a   s
   
z"SimpleSuffixConsoleOutput.__init__Nr   c                 C   s:   | j  | j|||dW  d   S 1 sw   Y  dS )a;  Adds a SuffixConsoleMessage to the SimpleSuffixConsoleOutput object.

    Args:
      message: str, The message that will be displayed.
      detail_message_callback: func() -> str, A no argument function that will
        be called and the result will be appended to the message on each call
        to UpdateConsole.
      indentation_level: int, The indentation level of the message. Each
        indentation is represented by two spaces.

    Returns:
      SuffixConsoleMessage, a message object that can be used to dynamically
      change the printed message.
    detail_message_callbackindentation_levelNr   _AddMessage)r   messager!   r"   r	   r	   r   
AddMessagem   s   $z$SimpleSuffixConsoleOutput.AddMessagec                 C   s"   t || j||d}| j| |S )Nr    )SuffixConsoleMessager   r   append)r   r%   r!   r"   console_messager	   r	   r   r$      s   z%SimpleSuffixConsoleOutput._AddMessagec                 C   sp   |st d|| jvrt d| jr|| jd krt d| j || W d   dS 1 s1w   Y  dS )z5Updates the suffix of the given SuffixConsoleMessage.A message must be passed.8The given message does not belong to this output object.z+Only the last added message can be updated.N)
ValueErrorr   r   _UpdateSuffix)r   r%   
new_suffixr	   r	   r   UpdateMessage   s   
"z'SimpleSuffixConsoleOutput.UpdateMessagec                 C   4   | j  |   W d    d S 1 sw   Y  d S Nr   _UpdateConsoler
   r	   r	   r   r         
"z'SimpleSuffixConsoleOutput.UpdateConsolec                 C   sl   | j r4| jt| j d k r*| j | jd D ]}|  | jd qt| j d | _| j | j   dS dS )r      r,   
N)r   r   lenPrintr   write)r   r%   r	   r	   r   r4      s   z(SimpleSuffixConsoleOutput._UpdateConsole)Nr   )r   r   r   r   r   r&   r$   r0   r   r4   __classcell__r	   r	   r   r   r   W   s    	


r   c                   @   sd   e Zd ZdZ		dddZdd Zdd
dZdd Zedd Z	dd Z
dd Zdd Zdd ZdS )r'   z/A suffix-only implementation of ConsoleMessage. Nr   c                 C   sx   || _ || _|| _t  d d | _| jdk rd| _|| _|| _d| _	| jt
|  dkr1d| _	d| _g | _d| _dS )a%  Constructor.

    Args:
      message: str, the message that this object represents.
      stream: The output stream to write to.
      suffix: str, The suffix that will be appended to the very end of the
        message.
      detail_message_callback: func() -> str, A no argument function that will
        be called and the result will be added after the message and before the
        suffix on every call to Print().
      indentation_level: int, The indentation level of the message. Each
        indentation is represented by two spaces.
    r   r6   FTN)r   _message_suffixr   ConsoleAttrGetTermSize_console_width_detail_message_callback_level
_no_outputINDENTATION_WIDTH
_num_lines_lines_has_printed)r   r%   r   suffixr!   r"   r	   r	   r   r      s   

zSuffixConsoleMessage.__init__c                 C   s   t |tjs
td|| _dS )z$Updates the suffix for this message.2expected a string or other character buffer objectN)
isinstancesixstring_types	TypeErrorr>   )r   rI   r	   r	   r   r.      s   
z"SuffixConsoleMessage._UpdateSuffixFc                 C   s,  | j dks| jr
dS |  }|sdS | jr|r7d| _|   | || _t| j| _| jD ]}| 	| q-dS | |}t|}|| jk rV| j
d |D ]}| 	| qMn8| |}| j| dkr~|| j d }|   |d| d D ]}| 	| qun| j
d |D ]}| 	| q|| _|| _dS )a  Prints out the message to the console.

    The implementation of this function assumes that when called, the
    cursor position of the terminal is on the same line as the last line
    that this function printed (and nothing more). The exception for this is if
    this is the first time that print is being called on this message or if
    print_all is True. The implementation should also return the cursor to
    the last line of the printed message. The cursor position in this case
    should be at the end of printed text to avoid text being overwritten.

    Args:
      print_all: bool, if the entire message should be printed instead of just
        updating the message.
    r   NTr7   r6   r,   )rA   rD   
GetMessagerH   
_ClearLine_SplitMessageIntoLinesrG   r8   rF   
_WriteLiner   r:   _GetNumMatchingLines)r   	print_allr%   line	new_linesnew_num_linesmatching_lineslines_to_printr	   r	   r   r9      s@   





zSuffixConsoleMessage.Printc                 C   s.   | j r|   }|r| j| | j S | j| j S r2   )rB   r=   r>   )r   detail_messager	   r	   r   rO   (  s
   zSuffixConsoleMessage.GetMessagec                 C      | j t| j  S z=The effective width when the indentation level is considered.rA   rE   rC   r
   r	   r	   r   effective_width/     z$SuffixConsoleMessage.effective_widthc                 C   sB   d}t tt|| jD ]}|| | j| kr |S |d7 }q|S )Nr   r6   )rangeminr8   rF   rG   )r   rV   rX   ir	   r	   r   rS   4  s   
z)SuffixConsoleMessage._GetNumMatchingLinesc                 C   sb   g }d}|t |k r/||||| j   || j7 }|t |k r)|d  d7  < |t |k s
|S )?Converts message into a list of strs, each representing a line.r   r,   r7   )r8   r(   r^   )r   r%   linesposr	   r	   r   rQ   <  s   
z+SuffixConsoleMessage._SplitMessageIntoLinesc                 C      | j dd| j  d S Nz{} r   r:   formatrA   r
   r	   r	   r   rP   I     zSuffixConsoleMessage._ClearLinec                 C   s(   | j | jt d |  | j   d S Nrh   )r   r:   rC   rE   flushr   rU   r	   r	   r   rR   L  s   zSuffixConsoleMessage._WriteLine)r<   Nr   )F)r   r   r   r   r   r.   r9   rO   propertyr^   rS   rQ   rP   rR   r	   r	   r	   r   r'      s    
*
H
r'   c                       sT   e Zd ZdZ fddZdddZdddZd	d
 Zdd Zdd Z	dd Z
  ZS )MultilineConsoleOutputa  An implementation of ConsoleOutput which supports multiline updates.

  This means all messages can be updated and actually have their output
  be updated on the terminal. The main difference between this class and
  the simple suffix version is that updates here are updates to the entire
  message as this provides more flexibility.

  This class accepts messages containing ANSI escape codes. The width
  calculations will be handled correctly currently only in this class.
  c                    s:   || _ g | _d| _t | _d| _d| _tt	| 
  dS )r   r   FN)r   r   r   r   r   r   _last_total_lines_may_have_updater   rp   r   r   r   r	   r   r   ]  s   
zMultilineConsoleOutput.__init__r   c                 C   s8   | j  | j||dW  d   S 1 sw   Y  dS )a  Adds a MultilineConsoleMessage to the MultilineConsoleOutput object.

    Args:
      message: str, The message that will be displayed.
      indentation_level: int, The indentation level of the message. Each
        indentation is represented by two spaces.

    Returns:
      MultilineConsoleMessage, a message object that can be used to dynamically
      change the printed message.
    r"   Nr#   )r   r%   r"   r	   r	   r   r&   k  s   $z!MultilineConsoleOutput.AddMessagec                 C   s&   d| _ t|| j|d}| j| |S )NTrs   )rr   MultilineConsoleMessager   r   r(   )r   r%   r"   r)   r	   r	   r   r$   |  s   z"MultilineConsoleOutput._AddMessagec                 C   sZ   |st d|| jvrt d| j || d| _W d   dS 1 s&w   Y  dS )z9Updates the message of the given MultilineConsoleMessage.r*   r+   TN)r-   r   r   _UpdateMessagerr   )r   r%   new_messager	   r	   r   r0     s   

"z$MultilineConsoleOutput.UpdateMessagec                 C   r1   r2   r3   r
   r	   r	   r   r     r5   z$MultilineConsoleOutput.UpdateConsolec                 C   s
   d |S )zEReturns an ANSI control sequences that moves the cursor up num_lines.z[{}A)rj   )r   	num_linesr	   r	   r   _GetAnsiCursorUpSequence     
z/MultilineConsoleOutput._GetAnsiCursorUpSequencec                 C   s   | j sdS | jr| j| | j d}d}| jD ] }|j}||7 }|js'|r1||jO }|	  q| jd|  q|| _d| _ dS )r   Nr   Fr7   )
rr   rq   r   r:   rx   r   rw   
has_updatenum_lines_changedr9   )r   total_linesforce_print_restr%   rw   r	   r	   r   r4     s   




z%MultilineConsoleOutput._UpdateConsoler   )r   r   r   r   r   r&   r$   r0   r   rx   r4   r;   r	   r	   r   r   rp   Q  s    

	rp   c                   @   s~   e Zd ZdZdddZedd Zedd Zed	d
 Zedd Z	dd Z
dd Zdd Zedd Zdd Zdd ZdS )rt   z-A multiline implementation of ConsoleMessage.r   c                 C   s~   || _ t | _| j d d | _| jdk rd| _|| _d| _| jt|  dkr,d| _d| _	g | _
d| _d| _| | dS )a  Constructor.

    Args:
      message: str, the message that this object represents.
      stream: The output stream to write to.
      indentation_level: int, The indentation level of the message. Each
        indentation is represented by two spaces.
    r   r6   FTN)r   r   GetConsoleAttr_console_attrr@   rA   rC   rD   rE   r=   rG   _has_update_num_lines_changedru   )r   r%   r   r"   r	   r	   r   r     s   	

z MultilineConsoleMessage.__init__c                 C      | j S r2   )rG   r
   r	   r	   r   rd        zMultilineConsoleMessage.linesc                 C   s
   t | jS r2   )r8   rG   r
   r	   r	   r   rw     ry   z!MultilineConsoleMessage.num_linesc                 C   r   r2   )r   r
   r	   r	   r   rz     r   z"MultilineConsoleMessage.has_updatec                 C   r   r2   )r   r
   r	   r	   r   r{     r   z)MultilineConsoleMessage.num_lines_changedc                 C   sd   t |tjs
td|| jkr0|| _| jrdS t| j}| | j| _d| _	|t| jk| _
dS dS )z,Updates the message for this Message object.rJ   NT)rK   rL   rM   rN   r=   rD   r8   rG   rQ   r   r   )r   rv   num_old_linesr	   r	   r   ru     s   

z&MultilineConsoleMessage._UpdateMessagec                 C   s6   | j || j}tt|D ]
}||  d7  < q|S )rc   r7   )r   	SplitLiner^   r`   r8   )r   r%   rd   rb   r	   r	   r   rQ     s   z.MultilineConsoleMessage._SplitMessageIntoLinesc                 C   s2   | j rdS | jD ]}|   | | qd| _dS )zPrints out the message to the console.

    The implementation of this function assumes that when called, the
    cursor position of the terminal is where the message should start printing.
    NF)rD   rG   rP   rR   r   rn   r	   r	   r   r9     s   

zMultilineConsoleMessage.Printc                 C   r[   r\   r]   r
   r	   r	   r   r^   	  r_   z'MultilineConsoleMessage.effective_widthc                 C   rf   rg   ri   r
   r	   r	   r   rP     rk   z"MultilineConsoleMessage._ClearLinec                 C   s   | j | jt d |  d S rl   )r   r:   rC   rE   rn   r	   r	   r   rR     s   z"MultilineConsoleMessage._WriteLineNr~   )r   r   r   r   r   ro   rd   rw   rz   r{   ru   rQ   r9   r^   rP   rR   r	   r	   r	   r   rt     s$    
"




rt   )r   
__future__r   r   r   abcr   googlecloudsdk.core.consoler   rL   rE   with_metaclassABCMetaobjectr   r   r'   rp   rt   r	   r	   r	   r   <module>   s   ,V %a