o Šbcskã@sdZdZddlZddlZddlZddlZddlZddlZddlZddl m Z ddl m Z m Z mZddlmZddlmZmZdd lmZd!d d „ZGd d„deƒZdd„Zdd„Zdd„Zdd„Zdd„ZGdd„deƒZGdd„deƒZ Gdd„deƒZ!Gdd „d e ƒZ"dS)"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 éN)Úchainé)ÚdriverÚtokenizeÚtoken)Ú find_root)ÚpytreeÚpygram)Ú btm_matcherTcCsTt|ggdgƒ}g}t |j¡D]\}}}| d¡r'|r"|dd…}| |¡q|S)zEReturn a sorted list of all available fix names in the given package.Ú*Úfix_éN)Ú __import__ÚpkgutilÚ iter_modulesÚ__path__Ú startswithÚappend)Ú fixer_pkgÚ remove_prefixÚpkgÚ fix_namesÚfinderÚnameÚispkg©rú'/usr/lib/python3.10/lib2to3/refactor.pyÚget_all_fix_namess   €rc@ó eZdZdS)Ú _EveryNodeN©Ú__name__Ú __module__Ú __qualname__rrrrr+órcCsŽt|tjtjfƒr|jdurt‚|jhSt|tjƒr$|jr"t|jƒSt‚t|tj ƒrAt ƒ}|jD]}|D] }|  t|ƒ¡q4q0|St d|ƒ‚)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Ú NodePatternÚ LeafPatternÚtyperÚNegatedPatternÚcontentÚ_get_head_typesÚWildcardPatternÚsetÚupdateÚ Exception)ÚpatÚrÚpÚxrrrr+/s      ÿ r+c Cs¼t t¡}g}|D];}|jr1zt|jƒ}Wnty#| |¡Yq w|D] }|| |¡q&q |jdur?||j |¡q | |¡q tt j j   ¡t j j ƒD] }|| |¡qPt|ƒS)z^ Accepts a list of fixers and returns a dictionary of head node type --> fixer list. N)Ú collectionsÚ defaultdictÚlistÚpatternr+rrÚ _accept_typerr Úpython_grammarÚ symbol2numberÚvaluesÚtokensÚextendÚdict)Ú fixer_listÚ head_nodesÚeveryÚfixerÚheadsÚ node_typerrrÚ_get_headnode_dictKs(  ÿÿ   ÿrEcs‡fdd„tˆdƒDƒS)zN Return the fully qualified names for fixers in the package pkg_name. csg|]}ˆd|‘qS©Ú.r)Ú.0Úfix_name©Úpkg_namerrÚ hsÿz+get_fixers_from_package..F)rrJrrJrÚget_fixers_from_packageds ÿrMcCs|S©Nr)ÚobjrrrÚ _identityksrPcsjd}t t |¡j¡‰‡fdd„}ttjtjtj hƒ}t ƒ}zŠ |ƒ\}}||vr*q|tj kr5|r2ncd}n_|tj kr“|dkr“|ƒ\}}|tj ksL|dkrMnR|ƒ\}}|tj ks[|dkr\nH|ƒ\}}|tj kro|dkro|ƒ\}}|tj kr’| |¡|ƒ\}}|tj ks‡|d krˆn |ƒ\}}|tj kstnnq Wt|ƒSWt|ƒSWt|ƒSWt|ƒSty´Yt|ƒSw) NFcstˆƒ}|d|dfS)Nrr)Únext)Útok©ÚgenrrÚadvancersz(_detect_future_features..advanceTÚfromÚ __future__Úimportú(ú,)rÚgenerate_tokensÚioÚStringIOÚreadlineÚ frozensetrÚNEWLINEÚNLÚCOMMENTr-ÚSTRINGÚNAMEÚOPÚaddÚ StopIteration)ÚsourceÚhave_docstringrUÚignoreÚfeaturesÚtpÚvaluerrSrÚ_detect_future_featuresos\            û€çýïò þþrnc@seZdZdZdS)Ú FixerErrorzA fixer could not be loaded.N)r!r"r#Ú__doc__rrrrro—sroc@sêeZdZddddœZdZdZd4dd„Zdd „Zd d „Zd d „Z dd„Z dd„Z d5dd„Z d5dd„Z dd„Zd5dd„Zdd„Zd6dd„Zdd„Zd d!„Z  d7d"d#„Zd8d$d%„Zd&Zd'Zd(d)„Zd*d+„Zd,d-„Zd.d/„Zd0d1„Zd2d3„ZdS)9ÚRefactoringToolF)Úprint_functionÚ exec_functionÚwrite_unchanged_filesÚFixr NcCsH||_|pg|_|j ¡|_|dur|j |¡tj ¡|_|jdr)|jj d=n |jdr3|jj d=|j  d¡|_ g|_ t  d¡|_g|_d|_tj|jtj|jd |_| ¡\|_|_g|_t ¡|_g|_g|_t|j|jƒD]#}|j r~|j !|¡qr||jvrŠ|j "|¡qr||jvr•|j "|¡qrt#|jƒ|_$t#|jƒ|_%dS) 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. NrrÚprintrsÚexecrtrqF)ÚconvertÚlogger)&ÚfixersÚexplicitÚ_default_optionsÚcopyÚoptionsr.r r9ÚgrammarÚkeywordsÚgetrtÚerrorsÚloggingÚ getLoggerryÚ fixer_logÚwroterÚDriverrrxÚ get_fixersÚ pre_orderÚ post_orderÚfilesÚbmÚ BottomMatcherÚBMÚ bmi_pre_orderÚbmi_post_orderrÚ BM_compatibleÚ add_fixerrrEÚbmi_pre_order_headsÚbmi_post_order_heads)ÚselfÚ fixer_namesr~r{rBrrrÚ__init__¤sD         þ    € zRefactoringTool.__init__c CsJg}g}|jD]ˆ}t|iidgƒ}| dd¡d}| |j¡r(|t|jƒd…}| d¡}|jd dd „|Dƒ¡}zt ||ƒ}Wnt yQt d ||fƒd‚w||j |j ƒ} | jrm|jd urm||jvrm| d |¡q| d |¡| jdkr~| | ¡q| jdkr‰| | ¡qt d| jƒ‚t d¡} |j| d|j| d||fS)aInspects 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 rGréÿÿÿÿNÚ_ÚcSsg|]}| ¡‘qSr)Útitle)rHr2rrrrLësz.RefactoringTool.get_fixers..zCan't find %s.%sTzSkipping optional fixer: %szAdding transformation: %sÚpreÚpostzIllegal fixer order: %rÚ run_order©Úkey)rzrÚrsplitrÚ FILE_PREFIXÚlenÚsplitÚ CLASS_PREFIXÚjoinÚgetattrÚAttributeErrorror~r…r{Ú log_messageÚ log_debugÚorderrÚoperatorÚ attrgetterÚsort) r•Úpre_order_fixersÚpost_order_fixersÚ fix_mod_pathÚmodrIÚpartsÚ class_nameÚ fix_classrBÚkey_funcrrrrˆÛs:    ÿ          zRefactoringTool.get_fixerscOs‚)zCalled when an error occurs.r)r•ÚmsgÚargsÚkwdsrrrÚ log_errorszRefactoringTool.log_errorcGs|r||}|j |¡dS)zHook to log a message.N)ryÚinfo©r•r·r¸rrrr©szRefactoringTool.log_messagecGs|r||}|j |¡dSrN)ryÚdebugr¼rrrrª szRefactoringTool.log_debugcCsdS)zTCalled with the old version, new version, and filename of a refactored file.Nr)r•Úold_textÚnew_textÚfilenameÚequalrrrÚ print_outputszRefactoringTool.print_outputcCs8|D]}tj |¡r| |||¡q| |||¡qdS)z)Refactor a list of files and directories.N)ÚosÚpathÚisdirÚ refactor_dirÚ refactor_file)r•ÚitemsÚwriteÚ doctests_onlyÚ dir_or_filerrrÚrefactors  üzRefactoringTool.refactorc Csštjd}t |¡D]@\}}}| d|¡| ¡| ¡|D]}| d¡s>tj |¡d|kr>tj ||¡} |  | ||¡qdd„|Dƒ|dd…<q dS)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. ÚpyzDescending into %srGrcSsg|] }| d¡s|‘qSrF)r)rHÚdnrrrrL2óz0RefactoringTool.refactor_dir..N) rÃÚextsepÚwalkrªr®rrÄÚsplitextr¦rÇ) r•Údir_namerÉrÊÚpy_extÚdirpathÚdirnamesÚ filenamesrÚfullnamerrrrÆ s   €özRefactoringTool.refactor_dirc Cs®zt|dƒ}Wnty!}z| d||¡WYd}~dSd}~wwzt |j¡d}W| ¡n| ¡wtj|d|dd}| ¡|fWdƒS1sPwYdS) zG Do our best to decode a Python source file correctly. ÚrbzCan't open %s: %sN©NNrr1rš©ÚencodingÚnewline) ÚopenÚOSErrorrºrÚdetect_encodingr^Úcloser\Úread)r•rÀÚfÚerrrÜrrrÚ_read_python_source4s€þ $ÿz#RefactoringTool._read_python_sourcecCsÀ| |¡\}}|dur dS|d7}|r9| d|¡| ||¡}|js&||kr1| |||||¡dS| d|¡dS| ||¡}|jsG|rX|jrX|jt|ƒdd…|||ddS| d|¡dS)zRefactors a file.NÚ zRefactoring doctests in %szNo doctest changes in %sr˜)rÉrÜzNo changes in %s)rårªÚrefactor_docstringrtÚprocessed_fileÚrefactor_stringÚ was_changedÚstr)r•rÀrÉrÊÚinputrÜÚoutputÚtreerrrrÇDs     ÿzRefactoringTool.refactor_filec Cs°t|ƒ}d|vr tj|j_z3z|j |¡}Wn#ty9}z| d||jj |¡WYd}~W|j|j_dSd}~wwW|j|j_n|j|j_w||_ |  d|¡|  ||¡|S)aFRefactor 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. rrzCan't parse %s: %s: %sNzRefactoring %s) rnr Ú!python_grammar_no_print_statementrrÚ parse_stringr/rºÚ __class__r!Úfuture_featuresrªÚ refactor_tree)r•Údatarrkrîrärrrré[s&   ÿ €ûÿ  zRefactoringTool.refactor_stringcCs’tj ¡}|r)| d¡| |d¡}|js||kr"| |d|¡dS| d¡dS| |d¡}|js7|rB|jrB| t |ƒd|¡dS| d¡dS)NzRefactoring doctests in stdinzzNo doctest changes in stdinzNo changes in stdin) ÚsysÚstdinrârªrçrtrèrérêrë)r•rÊrìrírîrrrÚrefactor_stdinvs    zRefactoringTool.refactor_stdinc CsÌt|j|jƒD]}| ||¡q| |j| ¡¡| |j| ¡¡|j |  ¡¡}t |  ¡ƒrÓ|jj D]˜}||vrÌ||rÌ||j tjjdd|jrV||j tjjdt||ƒD]o}|||vrk|| |¡zt|ƒWn tyzYq\w|jr„||jvr„q\| |¡}|rË| ||¡}|durË| |¡| ¡D]}|js¦g|_|j |¡qž|j |  ¡¡}|D]} | |vrÁg|| <||  || ¡q·q\q4t |  ¡ƒs0t|j|jƒD]}| ||¡qÚ|jS)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)rr‰rŠÚ start_treeÚ traverse_byr“r”rŽÚrunÚleavesÚanyr;rzr®rÚBaseÚdepthÚkeep_line_orderÚ get_linenor6ÚremoverÚ ValueErrorÚfixers_appliedÚmatchÚ transformÚreplacerr=Ú finish_treerê) r•rîrrBÚ match_setÚnodeÚresultsÚnewÚ new_matchesÚfxrrrrró†sP     ý    € Ñ1zRefactoringTool.refactor_treecCsV|sdS|D]"}||jD]}| |¡}|r'| ||¡}|dur'| |¡|}q qdS)aTraverse 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(rrr)r•rzÚ traversalr rBr r rrrrúÕs    €úÿzRefactoringTool.traverse_bycCsˆ|j |¡|dur| |¡d}|durdS||k}| ||||¡|r0| d|¡|js0dS|r<| ||||¡dS| d|¡dS)zR Called when a file has been refactored and there may be changes. NrzNo changes to %szNot writing changes to %s)r‹rrårÂrªrtÚ write_file)r•r¿rÀr¾rÉrÜrÁrrrrèìs  zRefactoringTool.processed_filec CsÈz tj|d|dd}Wnty%}z| d||¡WYd}~dSd}~ww|)z| |¡WntyI}z | d||¡WYd}~nd}~wwWdƒn1sTwY| d|¡d|_dS) 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. ÚwršrÛzCan't create %s: %sNzCan't write %s: %szWrote changes to %sT)r\rÞrßrºrÉrªr†)r•r¿rÀr¾rÜÚfprärrrrs$€þ€ÿ€ý  zRefactoringTool.write_filez>>> z... c Csg}d}d}d}d}|jddD]d}|d7}| ¡ |j¡r?|dur-| | ||||¡¡|}|g}| |j¡} |d| …}q|dur\| ||j¡sV|||j ¡dkr\|  |¡q|durk| | ||||¡¡d}d}|  |¡q|dur„| | ||||¡¡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.) NrT©Úkeependsrrærš) Ú splitlinesÚlstriprÚPS1r=Úrefactor_doctestÚfindÚPS2Úrstriprr¦) r•rìrÀÚresultÚblockÚ block_linenoÚindentÚlinenoÚlineÚirrrrçsB  ÿ ÿ  ÿ  ÿ z"RefactoringTool.refactor_docstringc s(z ˆ ||ˆ¡}Wn4ty=}z(ˆj tj¡r&|D] }ˆ d| d¡¡qˆ d|||j j |¡|WYd}~Sd}~wwˆ  ||¡r’t |ƒj dd}|d|d…||dd…} }| dg|dksjJ| ƒ‚|d d¡sy|dd7<ˆˆj| d ¡g}|r’|‡‡fd 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: %sNTrrr˜rcsg|] }ˆˆj|‘qSr)r)rHr!©rr•rrrL^rÏz4RefactoringTool.refactor_doctest..)Ú parse_blockr/ryÚ isEnabledForrƒÚDEBUGrªrrºrñr!rórërÚendswithrÚpop) r•rr rrÀrîrär!r Úclippedrr#rrDs, ÿ€ú "z RefactoringTool.refactor_doctestcCsÐ|jrd}nd}|js| d|¡n| d|¡|jD]}| |¡q|jr6| d¡|jD]}| |¡q.|jrdt|jƒdkrF| d¡n | dt|jƒ¡|jD]\}}}|j|g|¢Ri|¤ŽqRdSdS) NÚwerez need to bezNo files %s modified.zFiles that %s modified:z$Warnings/messages while refactoring:rzThere was 1 error:zThere were %d errors:)r†r‹r©r…r‚r£)r•r*ÚfileÚmessager·r¸r¹rrrÚ summarizeas(       úzRefactoringTool.summarizecCs"|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Ú parse_tokensÚ wrap_toksr_rò)r•rr rrîrrrr$xszRefactoringTool.parse_blockc csft | ||¡j¡}|D]#\}}\}}\} } } ||d7}| |d7} ||||f| | f| fVq dS)z;Wraps a tokenize stream to systematically modify start/end.rN)rr[Ú gen_linesÚ__next__) r•rr rr<r(rmÚline0Úcol0Úline1Úcol1Ú line_textrrrr/‚s€  øzRefactoringTool.wrap_toksccsx||j}||j}|}|D]'}| |¡r |t|ƒd…Vn|| ¡dkr,dVntd||fƒ‚|}q dVq8)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æzline=%r, prefix=%rTrš)rrrr£rÚAssertionError)r•rrÚprefix1Úprefix2Úprefixr!rrrr0s€   ÿzRefactoringTool.gen_linesrÚ)FF)F)NFNrN)r!r"r#r|r¥r¢r—rˆrºr©rªrÂrÌrÆrårÇrér÷rórúrèrrrrçrr-r$r/r0rrrrrq›sBþ 7(    O ÿ + rqc@r)ÚMultiprocessingUnsupportedNr rrrrr;¤r$r;csFeZdZ‡fdd„Z  d ‡fdd„ Z‡fdd„Z‡fd d „Z‡ZS) ÚMultiprocessRefactoringToolcs&tt|ƒj|i|¤Žd|_d|_dSrN)Úsuperr<r—ÚqueueÚ output_lock©r•r¸Úkwargs©rñrrr—ªs z$MultiprocessRefactoringTool.__init__Frc s2|dkrttˆƒ |||¡Szddl‰Wn tyt‚wˆjdur'tdƒ‚ˆ ¡ˆ_ˆ  ¡ˆ_ ‡‡fdd„t |ƒDƒ}z8|D]}|  ¡q@ttˆƒ |||¡Wˆj  ¡t |ƒD]}ˆj d¡q[|D] }| ¡rp|  ¡qfdˆ_dSˆj  ¡t |ƒD]}ˆj d¡q|D] }| ¡r”|  ¡qŠdˆ_w)Nrrz already doing multiple processescsg|] }ˆjˆjd‘qS))Útarget)ÚProcessÚ_child)rHr"©Úmultiprocessingr•rrrL¼sÿz8MultiprocessRefactoringTool.refactor..)r=r<rÌrGÚ ImportErrorr;r>Ú RuntimeErrorÚ JoinableQueueÚLockr?ÚrangeÚstartr¦ÚputÚis_alive)r•rÈrÉrÊÚ num_processesÚ processesr2r"rBrFrr̯sL ÿ  ÿ    ÿ ÿ  €  ú €z$MultiprocessRefactoringTool.refactorcsf|j ¡}|dur1|\}}ztt|ƒj|i|¤ŽW|j ¡n|j ¡w|j ¡}|dus dSdSrN)r>rr=r<rÇÚ task_done)r•Útaskr¸rArBrrrEÌs  ÿÿ ùz"MultiprocessRefactoringTool._childcs4|jdur|j ||f¡dStt|ƒj|i|¤ŽSrN)r>rNr=r<rÇr@rBrrrÇ×s  ÿÿz)MultiprocessRefactoringTool.refactor_file)FFr)r!r"r#r—rÌrErÇÚ __classcell__rrrBrr<¨s ÿ  r<)T)#rpÚ __author__r\rÃrrõrƒr¬r4Ú itertoolsrÚpgen2rrrÚ fixer_utilrršrr r rŒrr/rr+rErMrPrnroÚobjectrqr;r<rrrrÚs:     (