U
    <1_k                     @   s  d Z 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	m
Z
 ddlmZmZmZ ddlmZ ddlmZmZ dd	lmZ d!ddZG dd deZdd Zdd Zdd Zdd Zdd 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 )"zRefactoring framework.

Used as a main program, this can refactor any number of files and/or
recursively descend down directories.  Imported as a module, this
provides infrastructure to write your own refactoring tool.
z#Guido van Rossum <guido@python.org>    N)chain   )drivertokenizetoken)	find_root)pytreepygram)btm_matcherTc                 C   sT   t | g g dg}g }t|jD ].\}}}|dr |rD|dd }|| q |S )zEReturn a sorted list of all available fix names in the given package.*fix_   N)
__import__pkgutilZiter_modules__path__
startswithappend)Z	fixer_pkgZremove_prefixZpkgZ	fix_namesfindernameZispkg r   &/usr/lib/python3.8/lib2to3/refactor.pyget_all_fix_names   s    
r   c                   @   s   e Zd ZdS )
_EveryNodeN__name__
__module____qualname__r   r   r   r   r   +   s   r   c                 C   s   t | tjtjfr(| jdkr t| jhS t | tjrH| jrDt| jS tt | tj	rt
 }| jD ]}|D ]}|t| qhq`|S td|  dS )zf Accepts a pytree Pattern Node and returns a set
        of the pattern types which will match first. Nz$Oh no! I don't understand pattern %s)
isinstancer   ZNodePatternZLeafPatterntyper   ZNegatedPatternZcontent_get_head_typesZWildcardPatternsetupdate	Exception)Zpatrpxr   r   r   r   /   s    


r   c              	   C   s   t t}g }| D ]x}|jrdzt|j}W n tk
rH   || Y qX |D ]}|| | qNq|jdk	r||j | q|| qtt	j
j t	j
jD ]}|| | qt|S )z^ Accepts a list of fixers and returns a dictionary
        of head node type --> fixer list.  N)collectionsdefaultdictlistpatternr   r   r   Z_accept_typer   r	   python_grammarZsymbol2numbervaluestokensextenddict)Z
fixer_listZ
head_nodesZeveryfixerZheadsZ	node_typer   r   r   _get_headnode_dictK   s$    

r0   c                    s    fddt  dD S )zN
    Return the fully qualified names for fixers in the package pkg_name.
    c                    s   g | ]} d  | qS .r   ).0fix_nameZpkg_namer   r   
<listcomp>h   s   z+get_fixers_from_package.<locals>.<listcomp>F)r   r5   r   r5   r   get_fixers_from_packaged   s    
r7   c                 C   s   | S Nr   )objr   r   r   	_identityk   s    r:   c                    sX  d}t t| j  fdd}ttjt jtj	h}t
 }z| \}}||krTq>q>|tjkrl|rfq6d}q>|tjkr6|dkr6| \}}|tjks|dkrq6| \}}|tjks|dkrq6| \}}|tjkr|dkr| \}}|tjkr4|| | \}}|tjks.|d	kr"q4| \}}qq>q6q>W n tk
rN   Y nX t|S )
NFc                     s   t  } | d | d fS )Nr   r   )next)tokgenr   r   advancer   s    z(_detect_future_features.<locals>.advanceTfromZ
__future__import(,)r   generate_tokensioStringIOreadline	frozensetr   NEWLINENLCOMMENTr    STRINGNAMEOPaddStopIteration)sourceZhave_docstringr?   ignorefeaturestpvaluer   r=   r   _detect_future_featureso   sB    








rV   c                   @   s   e Zd ZdZdS )
FixerErrorzA fixer could not be loaded.N)r   r   r   __doc__r   r   r   r   rW      s   rW   c                   @   s   e Zd ZdddZdZdZd4ddZdd	 Zd
d Zdd Z	dd Z
dd Zd5ddZd6ddZdd Zd7ddZdd Zd8ddZdd Zd d! Zd9d"d#Zd:d$d%Zd&Zd'Zd(d) Zd*d+ Zd,d- Zd.d/ Zd0d1 Zd2d3 ZdS );RefactoringToolF)print_functionwrite_unchanged_filesZFixr   Nc                 C   s.  || _ |pg | _| j | _|dk	r0| j| | jd rDtj| _ntj	| _| j
d| _g | _td| _g | _d| _tj| jtj| jd| _|  \| _| _g | _t | _g | _g | _t| j| jD ]F}|j r| j!| q|| jkr| j"| q|| jkr| j"| qt#| j| _$t#| j| _%dS )zInitializer.

        Args:
            fixer_names: a list of fixers to import
            options: a dict with configuration.
            explicit: a list of fixers to run even if they are explicit.
        NrZ   r[   rY   F)convertlogger)&fixersexplicit_default_optionscopyoptionsr!   r	   !python_grammar_no_print_statementgrammarr*   getr[   errorsloggingZ	getLoggerr]   	fixer_logwroter   ZDriverr   r\   
get_fixers	pre_order
post_orderfilesbmZBottomMatcherBMZbmi_pre_orderZbmi_post_orderr   ZBM_compatibleZ	add_fixerr   r0   bmi_pre_order_headsbmi_post_order_heads)selfZfixer_namesrb   r_   r/   r   r   r   __init__   s>    





zRefactoringTool.__init__c              	   C   sX  g }g }| j D ]}t|i i dg}|ddd }|| jrR|t| jd }|d}| jddd	 |D  }zt	||}W n& t
k
r   td
||f dY nX || j| j}	|	jr| jdk	r|| jkr| d| q| d| |	jdkr||	 q|	jdkr||	 qtd|	j qtd}
|j|
d |j|
d ||fS )a  Inspects the options to load the requested patterns and handlers.

        Returns:
          (pre_order, post_order), where pre_order is the list of fixers that
          want a pre-order AST traversal, and post_order is the list that want
          post-order traversal.
        r   r2   r   N_ c                 S   s   g | ]}|  qS r   )title)r3   r$   r   r   r   r6      s     z.RefactoringTool.get_fixers.<locals>.<listcomp>zCan't find %s.%sTzSkipping optional fixer: %szAdding transformation: %sZpreZpostzIllegal fixer order: %rZ	run_orderkey)r^   r   rsplitr   FILE_PREFIXlensplitCLASS_PREFIXjoingetattrAttributeErrorrW   rb   rh   r_   log_message	log_debugorderr   operator
attrgettersort)rr   Zpre_order_fixersZpost_order_fixersZfix_mod_pathmodr4   parts
class_nameZ	fix_classr/   Zkey_funcr   r   r   rj      s:    

zRefactoringTool.get_fixersc                  O   s    dS )zCalled when an error occurs.Nr   )rr   msgargskwdsr   r   r   	log_error   s    zRefactoringTool.log_errorc                 G   s   |r|| }| j | dS )zHook to log a message.N)r]   inforr   r   r   r   r   r   r     s    zRefactoringTool.log_messagec                 G   s   |r|| }| j | d S r8   )r]   debugr   r   r   r   r   	  s    zRefactoringTool.log_debugc                 C   s   dS )zTCalled with the old version, new version, and filename of a
        refactored file.Nr   )rr   old_textnew_textfilenameequalr   r   r   print_output  s    zRefactoringTool.print_outputc                 C   s8   |D ].}t j|r$| ||| q| ||| qdS )z)Refactor a list of files and directories.N)ospathisdirrefactor_dirrefactor_file)rr   itemswritedoctests_onlyZdir_or_filer   r   r   refactor  s    zRefactoringTool.refactorc           
      C   s   t jd }t |D ]\}}}| d| |  |  |D ]>}|ds>t j|d |kr>t j||}	| 	|	|| q>dd |D |dd< qdS )zDescends down a directory and refactor every Python file found.

        Python files are assumed to have a .py extension.

        Files and subdirectories starting with '.' are skipped.
        pyzDescending into %sr2   r   c                 S   s   g | ]}| d s|qS r1   )r   )r3   Zdnr   r   r   r6   .  s     
 z0RefactoringTool.refactor_dir.<locals>.<listcomp>N)
r   extsepwalkr   r   r   r   splitextr   r   )
rr   Zdir_namer   r   Zpy_extdirpathZdirnames	filenamesr   fullnamer   r   r   r     s    

zRefactoringTool.refactor_dirc              
   C   s   zt |d}W n6 tk
rD } z| d|| W Y dS d}~X Y nX zt|jd }W 5 |  X tj |d|dd}| |fW  5 Q R  S Q R X dS )	zG
        Do our best to decode a Python source file correctly.
        rbzCan't open %s: %s)NNNr   r#   rv   encodingnewline)	openOSErrorr   closer   detect_encodingrG   rE   read)rr   r   ferrr   r   r   r   _read_python_source0  s    
z#RefactoringTool._read_python_sourcec                 C   s   |  |\}}|dkrdS |d7 }|rn| d| | ||}| jsL||kr`| ||||| q| d| nH| ||}| js|r|jr| jt|dd |||d n| d| dS )zRefactors a file.N
zRefactoring doctests in %szNo doctest changes in %srt   )r   r   zNo changes in %s)r   r   refactor_docstringr[   processed_filerefactor_stringwas_changedstr)rr   r   r   r   inputr   outputtreer   r   r   r   @  s"     zRefactoringTool.refactor_filec              
   C   s   t |}d|krtj| j_zVz| j|}W n@ tk
rl } z"| d||jj	| W Y W dS d}~X Y nX W 5 | j| j_X ||_
| d| | || |S )aF  Refactor a given input string.

        Args:
            data: a string holding the code to be refactored.
            name: a human-readable name for use in error/log messages.

        Returns:
            An AST corresponding to the refactored input stream; None if
            there were errors during the parse.
        rZ   zCan't parse %s: %s: %sNzRefactoring %s)rV   r	   rc   r   rd   Zparse_stringr"   r   	__class__r   future_featuresr   refactor_tree)rr   datar   rS   r   r   r   r   r   r   W  s"    
   zRefactoringTool.refactor_stringc                 C   s   t j }|rN| d | |d}| js2||krB| |d| q| d n:| |d}| jsj|r~|jr~| t	|d| n
| d d S )NzRefactoring doctests in stdinz<stdin>zNo doctest changes in stdinzNo changes in stdin)
sysstdinr   r   r   r[   r   r   r   r   )rr   r   r   r   r   r   r   r   refactor_stdinr  s    

zRefactoringTool.refactor_stdinc           
   
   C   s  t | j| jD ]}||| q| | j|  | | j|  | j|	 }t
| r| jjD ]D}||krj|| rj|| jtjjdd |jr|| jtjjd t|| D ]}||| kr|| | zt| W n tk
r    Y qY nX |jr||jkrq||}|r|||}|dk	r|| | D ] }|js^g |_|j| qL| j|	 }|D ]*}	|	|krg ||	< ||	 ||	  qqqjqTt | j| jD ]}||| q|jS )a  Refactors a parse tree (modifying the tree in place).

        For compatible patterns the bottom matcher module is
        used. Otherwise the tree is traversed node-to-node for
        matches.

        Args:
            tree: a pytree.Node instance representing the root of the tree
                  to be refactored.
            name: a human-readable name for this tree.

        Returns:
            True if the tree was modified, False otherwise.
        T)ry   reverserx   N)r   rk   rl   Z
start_treetraverse_byrp   rq   ro   runZleavesanyr+   r^   r   r   ZBaseZdepthZkeep_line_orderZ
get_linenor(   remover   
ValueErrorZfixers_appliedmatch	transformreplacer   r-   Zfinish_treer   )
rr   r   r   r/   Z	match_setnoderesultsnewZnew_matchesZfxrr   r   r   r     sJ    



zRefactoringTool.refactor_treec                 C   sV   |sdS |D ]D}||j  D ]4}||}|r|||}|dk	r|| |}qqdS )a  Traverse an AST, applying a set of fixers to each node.

        This is a helper method for refactor_tree().

        Args:
            fixers: a list of fixer instances.
            traversal: a generator that yields AST nodes.

        Returns:
            None
        N)r   r   r   r   )rr   r^   Z	traversalr   r/   r   r   r   r   r   r     s    

zRefactoringTool.traverse_byc                 C   s   | j | |dkr.| |d }|dkr.dS ||k}| |||| |r`| d| | js`dS |rv| |||| n| d| dS )zR
        Called when a file has been refactored and there may be changes.
        Nr   zNo changes to %szNot writing changes to %s)rm   r   r   r   r   r[   
write_file)rr   r   r   r   r   r   r   r   r   r   r     s    zRefactoringTool.processed_filec                 C   s   zt j|d|dd}W n6 tk
rL } z| d|| W Y dS d}~X Y nX |F z|| W n0 tk
r } z| d|| W 5 d}~X Y nX W 5 Q R X | d| d| _dS )	zWrites a string to a file.

        It first shows a unified diff between the old text and the new text, and
        then rewrites the file; the latter is only done if the write option is
        set.
        wrv   r   zCan't create %s: %sNzCan't write %s: %szWrote changes to %sT)rE   r   r   r   r   r   ri   )rr   r   r   r   r   fpr   r   r   r   r     s    *zRefactoringTool.write_filez>>> z... c           
   	   C   s  g }d}d}d}d}|j ddD ]}|d7 }| | jr~|dk	rZ|| |||| |}|g}|| j}	|d|	 }q |dk	r||| j s||| j  d kr|	| q |dk	r|| |||| d}d}|	| q |dk	r
|| |||| d
|S )a  Refactors a docstring, looking for doctests.

        This returns a modified version of the input string.  It looks
        for doctests, which start with a ">>>" prompt, and may be
        continued with "..." prompts, as long as the "..." is indented
        the same as the ">>>".

        (Unfortunately we can't use the doctest module's parser,
        since, like most parsers, it is not geared towards preserving
        the original source.)
        Nr   Tkeependsr   r   rv   )
splitlineslstripr   PS1r-   refactor_doctestfindPS2rstripr   r   )
rr   r   r   resultblockZblock_linenoindentlinenolineir   r   r   r     sJ      
 z"RefactoringTool.refactor_docstringc           
   
      s.  z || }W nj tk
r| } zLjtjrN|D ]}d|d q6d|||j	j
| | W Y S d}~X Y nX ||r*t|jdd}|d|d  ||d d  }	}|	dg|d  kst|	|d ds|d  d7  <  j |d	 g}|r*| fd
d|D 7 }|S )zRefactors one doctest.

        A doctest is given as a block of lines, the first of which starts
        with ">>>" (possibly indented), while the remaining lines start
        with "..." (identically indented).

        z
Source: %sr   z+Can't parse docstring in %s line %s: %s: %sNTr   r   rt   r   c                    s   g | ]} j  | qS r   )r   )r3   r   r   rr   r   r   r6   Z  s     z4RefactoringTool.refactor_doctest.<locals>.<listcomp>)parse_blockr"   r]   ZisEnabledForrg   DEBUGr   r   r   r   r   r   r   r   AssertionErrorendswithr   pop)
rr   r   r   r   r   r   r   r   r   Zclippedr   r   r   r   @  s.       "z RefactoringTool.refactor_doctestc                 C   s   | j rd}nd}| js$| d| n"| d| | jD ]}| | q6| jrl| d | jD ]}| | q\| jrt| jdkr| d n| dt| j | jD ]\}}}| j|f|| qd S )	Nwerez
need to bezNo files %s modified.zFiles that %s modified:z$Warnings/messages while refactoring:r   zThere was 1 error:zThere were %d errors:)ri   rm   r   rh   rf   r|   )rr   r   filemessager   r   r   r   r   r   	summarize]  s$    


zRefactoringTool.summarizec                 C   s"   | j | |||}t |_|S )zParses a block into a tree.

        This is necessary to get correct line number / offset information
        in the parser diagnostics and embedded into the parse tree.
        )r   Zparse_tokens	wrap_toksrH   r   )rr   r   r   r   r   r   r   r   r   t  s    zRefactoringTool.parse_blockc                 c   sd   t | ||j}|D ]F\}}\}}\}	}
}||d 7 }|	|d 7 }	||||f|	|
f|fV  qdS )z;Wraps a tokenize stream to systematically modify start/end.r   N)r   rD   	gen_lines__next__)rr   r   r   r   r,   r   rU   Zline0Zcol0Zline1Zcol1Z	line_textr   r   r   r   ~  s
    zRefactoringTool.wrap_toksc                 c   sx   || j  }|| j }|}|D ]N}||r>|t|d V  n(|| d krVdV  ntd||f |}qdV  qldS )zGenerates lines as expected by tokenize from a list of lines.

        This strips the first len(indent + self.PS1) characters off each line.
        Nr   zline=%r, prefix=%rrv   )r   r   r   r|   r   r   )rr   r   r   prefix1Zprefix2prefixr   r   r   r   r     s    


zRefactoringTool.gen_lines)NN)FF)FF)FF)F)NFN)N)r   r   r   r`   r~   r{   rs   rj   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rY      s>   
4(
	


O  

+
rY   c                   @   s   e Zd ZdS )MultiprocessingUnsupportedNr   r   r   r   r   r     s   r   c                       sB   e Zd Z fddZd fdd	Z fddZ fd	d
Z  ZS )MultiprocessRefactoringToolc                    s"   t t| j|| d | _d | _d S r8   )superr   rs   queueoutput_lockrr   r   kwargsr   r   r   rs     s    z$MultiprocessRefactoringTool.__init__Fr   c              
      s   |dkrt t|||S zdd l W n tk
r@   tY nX jd k	rTtd  _ 	 _
 fddt|D }z*|D ]}|  qt t||| W 5 j  t|D ]}jd  q|D ]}| r|  qd _X d S )Nr   r   z already doing multiple processesc                    s   g | ]} j jd qS ))target)ZProcess_child)r3   r   multiprocessingrr   r   r   r6     s   z8MultiprocessRefactoringTool.refactor.<locals>.<listcomp>)r   r   r   r   ImportErrorr   r   RuntimeErrorZJoinableQueueZLockr   ranger   putZis_alivestart)rr   r   r   r   Znum_processesZ	processesr   r$   r   r   r   r     s<    
  






z$MultiprocessRefactoringTool.refactorc                    sN   | j  }|d k	rJ|\}}ztt| j|| W 5 | j   X | j  }q
d S r8   )r   re   Z	task_doner   r   r   )rr   Ztaskr   r   r   r   r   r     s    

 z"MultiprocessRefactoringTool._childc                    s2   | j d k	r| j ||f ntt| j||S d S r8   )r   r   r   r   r   r   r   r   r   r     s    

 z)MultiprocessRefactoringTool.refactor_file)FFr   )r   r   r   rs   r   r   r   __classcell__r   r   r   r   r     s     r   )T)#rX   
__author__rE   r   r   r   rg   r   r&   	itertoolsr   Zpgen2r   r   r   Z
fixer_utilr   rv   r   r	   r
   rn   r   r"   r   r   r0   r7   r:   rV   rW   objectrY   r   r   r   r   r   r   <module>   s8   
(    	