U
    ¾|c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 )"zÛRefactoring 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__ÚpkgutilÚiter_modulesÚ__path__Ú
startswithÚappend)Z	fixer_pkgZremove_prefixÚpkgZ	fix_namesÚfinderÚnameÚispkg© r   ú!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	ƒr‚t
ƒ }| 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 qŠX |D ]}||  |¡ qNq|jdk	r€||j  |¡ q| |¡ qtt	j
j ¡ t	j
jƒD ]}||  |¡ q¢t|ƒ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$    

ÿr3   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©Úpkg_namer   r   Ú
<listcomp>h   s   ÿz+get_fixers_from_package.<locals>.<listcomp>F)r   r8   r   r8   r   Úget_fixers_from_packaged   s    
ÿr;   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|ƒ \}}qèq>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_docstringrC   ÚignoreÚfeaturesÚtpÚvaluer   rA   r   Ú_detect_future_featureso   sB    








rZ   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   r[   —   s   r[   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 "|¡ qÊt#| jƒ| _$t#| jƒ| _%dS )zÑInitializer.

        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.
        Nr^   r_   r]   F)ÚconvertÚlogger)&ÚfixersÚexplicitÚ_default_optionsÚcopyÚoptionsr$   r	   Ú!python_grammar_no_print_statementÚgrammarr-   Úgetr_   ÚerrorsÚloggingZ	getLoggerra   Ú	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   r3   Úbmi_pre_order_headsÚbmi_post_order_heads)ÚselfZfixer_namesrf   rc   r2   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   r5   r   éÿÿÿÿNÚ_Ú c                 S   s   g | ]}|  ¡ ‘qS r   )Útitle)r6   r'   r   r   r   r:   ç   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)rb   r   Úrsplitr   ÚFILE_PREFIXÚlenÚsplitÚCLASS_PREFIXÚjoinÚgetattrÚAttributeErrorr[   rf   rl   rc   Úlog_messageÚ	log_debugÚorderr   ÚoperatorÚ
attrgetterÚsort)rv   Zpre_order_fixersZpost_order_fixersZfix_mod_pathÚmodr7   ÚpartsÚ
class_nameZ	fix_classr2   Zkey_funcr   r   r   rn   ×   s:    
ÿ
zRefactoringTool.get_fixersc                  O   s   ‚ dS )zCalled when an error occurs.Nr   )rv   Ú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)ra   Úinfo©rv   r   r   r   r   r   r†     s    zRefactoringTool.log_messagec                 G   s   |r|| }| j  |¡ d S r<   )ra   Ú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   )rv   Ú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)rv   Ú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 )zÄDescends 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 %sr5   r   c                 S   s   g | ]}|  d ¡s|‘qS r4   )r   )r6   Zdnr   r   r   r:   .  s     
 z0RefactoringTool.refactor_dir.<locals>.<listcomp>N)
r›   ÚextsepÚwalkr‡   r‹   r   rœ   Úsplitextrƒ   rŸ   )
rv   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&   rz   ©ÚencodingÚnewline)	ÚopenÚOSErrorr’   Úcloser   Údetect_encodingrK   rI   Úread)rv   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 %srx   )r¡   r­   zNo changes in %s)r¶   r‡   Úrefactor_docstringr_   Úprocessed_fileÚrefactor_stringÚwas_changedÚstr)rv   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.
        r^   zCan't parse %s: %s: %sNzRefactoring %s)rZ   r	   rg   r   rh   Zparse_stringr%   r’   Ú	__class__r   Úfuture_featuresr‡   Úrefactor_tree)rv   Údatar   rW   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¼   )rv   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 q¼Y nX |jr||jkrq¼| |¡}|r¼| ||¡}|dk	r¼| |¡ | ¡ D ] }|js^g |_|j |¡ qL| j | 	¡ ¡}|D ]*}	|	|kr˜g ||	< ||	  ||	 ¡ q‚q¼qjqTt | 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)r}   Úreverser|   N)r   ro   rp   Z
start_treeÚtraverse_byrt   ru   rs   ÚrunZleavesÚanyr.   rb   r‹   r   ZBaseZdepthZkeep_line_orderZ
get_linenor+   Úremover   Ú
ValueErrorZfixers_appliedÚmatchÚ	transformÚreplacer   r0   Zfinish_treer»   )
rv   r¿   r   r2   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Ï   )rv   rb   Z	traversalrÐ   r2   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)rq   r   r¶   rš   r‡   r_   Ú
write_file)rv   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 )	zÑWrites 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.
        Úwrz   r¬   zCan't create %s: %sNzCan't write %s: %szWrote changes to %sT)rI   r¯   r°   r’   r¡   r‡   rm   )rv   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·   rz   )Ú
splitlinesÚlstripr   ÚPS1r0   Úrefactor_doctestÚfindÚPS2Úrstripr   rƒ   )
rv   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sØt|	ƒ‚|d  d¡sö|d  d7  < ˆ ˆj | d	¡ g}|r*|‡ ‡fd
d„|D ƒ7 }|S )zÞRefactors 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   rx   r   c                    s   g | ]}ˆ ˆj  | ‘qS r   )rÝ   )r6   rã   ©rá   rv   r   r   r:   Z  s     z4RefactoringTool.refactor_doctest.<locals>.<listcomp>)Úparse_blockr%   ra   ZisEnabledForrk   ÚDEBUGr‡   rÞ   r’   rÀ   r   rÂ   r¼   rØ   ÚAssertionErrorÚendswithrÚ   Úpop)
rv   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rÂt| jƒdkrŒ|  d¡ n|  dt| jƒ¡ | jD ]\}}}| j|f|ž|Ž q¤d 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:)rm   rq   r†   rl   rj   r€   )rv   rë   ÚfileÚmessager   r   r‘   r   r   r   Ú	summarize]  s$    


zRefactoringTool.summarizec                 C   s"   | j  |  |||¡¡}tƒ |_|S )z³Parses 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_toksrL   rÁ   )rv   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   rH   Ú	gen_linesÚ__next__)rv   rà   râ   rá   r/   r!   rY   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 )z–Generates 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=%rrz   )rÚ   rÝ   r   r€   rÞ   rè   )rv   rà   rá   Úprefix1Zprefix2Úprefixrã   r   r   r   rð   Œ  s    


zRefactoringTool.gen_lines)NN)FF)FF)FF)F)NFN)N)r   r   r   rd   r‚   r   rw   rn   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   r]   ›   s>   ÿ
4(
	


O  ÿ

+
r]   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 r<   )Úsuperrõ   rw   ÚqueueÚoutput_lock©rv   r   Úkwargs©rÀ   r   r   rw   ¦  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 ]}| ¡  q†t tˆƒ |||¡ W 5 ˆj ¡  t|ƒD ]}ˆj d ¡ q¾|D ]}| ¡ rÔ| ¡  qÔd ˆ_X d S )Nr   r   z already doing multiple processesc                    s   g | ]}ˆ j ˆjd ‘qS ))Útarget)ZProcessÚ_child)r6   rä   ©Úmultiprocessingrv   r   r   r:   ¸  s   ÿz8MultiprocessRefactoringTool.refactor.<locals>.<listcomp>)rö   rõ   r£   rÿ   ÚImportErrorrô   r÷   ÚRuntimeErrorZJoinableQueueZLockrø   Úrangerƒ   ÚputZis_aliveÚstart)rv   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 r<   )r÷   ri   Z	task_donerö   rõ   rŸ   )rv   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 r<   )r÷   r  rö   rõ   rŸ   rù   rû   r   r   rŸ   Ó  s    

 ÿz)MultiprocessRefactoringTool.refactor_file)FFr   )r   r   r   rw   r£   rý   rŸ   Ú__classcell__r   r   rû   r   rõ   ¤  s     ÿrõ   )T)#r\   Ú
__author__rI   r›   r   rÄ   rk   r‰   r)   Ú	itertoolsr   Zpgen2r   r   r   Z
fixer_utilr   rz   r   r	   r
   rr   r   r%   r   r"   r3   r;   r>   rZ   r[   Úobjectr]   rô   rõ   r   r   r   r   Ú<module>   s8   
(    	