B š•Reš\ã@s|ddlmZddlZddlZddlZddlZddlZddlZddlm Z ddl m Z ddl m Z mZddlmZmZmZddlmZddlmZmZmZmZmZmZmZmZmZdd lm Z m!Z!dd l"m#Z#e#r dd l$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-dd l.m/Z/e 0e1¡Z2d d„Z3dd„Z4e4dd„ƒZ5dd„Z6dd„Z7dd„Z8Gdd„de9ƒZ:Gdd„de9ƒZ;Gdd„de9ƒZ.unique)Ú functoolsÚwraps)r4r5r+)r4r,Ú_unique=sr8ccs–t t| d¡ƒ¡}x||D]t}tj |j|d¡}|V| d¡rtj  |¡\}}|dd…}tj ||d¡}|Vtj ||d¡}|VqWdS)a Yield all the uninstallation paths for dist based on RECORD-without-.py[co] Yield paths to all the files in RECORD. For each .py file in RECORD, add the .pyc and .pyo in the same directory. UninstallPathSet.add() takes care of the __pycache__ .py[co]. ÚRECORDrz.pyNéýÿÿÿz.pycz.pyo) ÚcsvÚreaderr Úget_metadata_linesr!r"r#ÚlocationÚendswithÚsplit)r%ÚrÚrowr"Údnr4Úbaser+r+r,Úuninstallation_pathsJs    rEcsNtjj‰tƒ}x:t|tdD]*‰t‡‡fdd„|Dƒƒ}|s| ˆ¡qW|S)zÈCompact a path set to contain the minimal number of paths necessary to contain all paths in the set. If /a/path/ and /a/path/to/a/file.txt are both in the set, leave only the shorter path.)Úkeyc3s:|]2}ˆ | d¡¡o0ˆt| d¡ ˆ¡ƒˆkVqdS)Ú*N)Ú startswithÚrstripÚlen)Ú.0Ú shortpath)r"Úsepr+r,ú mszcompact..)r!r"rMr.ÚsortedrJÚanyr/)ÚpathsÚ short_pathsÚ should_skipr+)r"rMr,Úcompactbs  rTc stdd„|Dƒƒ}t|ƒ}ttdd„| ¡Dƒƒtd}tƒ}dd„‰x¤|D]œ‰t‡fdd„|DƒƒrhqLtƒ}tƒ}xPt ˆ¡D]B\‰}}| ‡‡‡fdd„|Dƒ¡| ‡‡‡fd d„|Dƒ¡q€W||sL|  |¡|  ˆtj ¡qLWtt |j |ƒƒ|BS) z«Returns a set containing the paths that need to be renamed. This set may include directories when the original sequence of paths included every file on disk. css|]}tj |¡|fVqdS)N)r!r"Únormcase)rKÚpr+r+r,rN}sz&compress_for_rename..css|]}tj |¡dVqdS)rN)r!r"r@)rKrVr+r+r,rNs)rFcWstj tjj|Ž¡S)N)r!r"rUr#)Úar+r+r,Ú norm_joinƒsz&compress_for_rename..norm_joinc3s |]}tj ˆ¡ |¡VqdS)N)r!r"rUrH)rKÚw)Úrootr+r,rNˆsc3s|]}ˆˆˆ|ƒVqdS)Nr+)rKÚd)ÚdirnamerXrZr+r,rNsc3s|]}ˆˆˆ|ƒVqdS)Nr+)rKÚf)r\rXrZr+r,rN’s)Údictr.rOÚvaluesrJrPr!ÚwalkÚupdateÚdifference_updater/rMÚmapÚ __getitem__) rQÚcase_mapÚ remainingÚ uncheckedÚ wildcardsÚ all_filesÚ all_subdirsÚsubdirsÚfilesr+)r\rXrZr,Úcompress_for_renamevs*      rmc Cs t|ƒ}tƒ}tƒ}tƒ}xF|D]>}| d¡r0q | d¡sBd|krT| tj |¡¡| |¡q Wtttjj|ƒƒ}t|ƒ}xt|D]l}xft  |¡D]X\}} } xL| D]D} |  d¡r²q¢tj  || ¡} tj  | ¡r¢tj | ¡|kr¢| | ¡q¢Wq’Wq‚W|dd„|DƒB}||fS)asReturns a tuple of 2 sets of which paths to display to user The first set contains paths that would be deleted. Files of a package are not added and the top-level directory of the package has a '*' added at the end - to signify that all it's contents are removed. The second set contains files that would have been skipped in the above folders. z.pycz __init__.pyz .dist-infocSsh|]}tj |d¡’qS)rG)r!r"r#)rKÚfolderr+r+r,ú Êsz.compress_for_output_listing..) r.r?r/r!r"r\rcrUrTr`r#Úisfile) rQÚ will_removeÚ will_skipÚfoldersrlr"Ú_normcased_filesrnÚdirpathÚ_ÚdirfilesÚfnameÚfile_r+r+r,Úcompress_for_output_listingžs0        rzc@sLeZdZdZdd„Zdd„Zdd„Zdd „Zd d „Zd d „Z e dd„ƒZ dS)ÚStashedUninstallPathSetzWA set of file rename operations to stash files while tentatively uninstalling them.cCsi|_g|_dS)N)Ú _save_dirsÚ_moves)Úselfr+r+r,Ú__init__Ôsz StashedUninstallPathSet.__init__cCsDy t|ƒ}Wntk r*tdd}YnX||jtj |¡<|jS)zStashes a directory. Directories are stashed adjacent to their original location if possible, or else moved/copied into the user's temp dir.Ú uninstall)Úkind)rÚOSErrorrr|r!r"rU)r~r"Úsave_dirr+r+r,Ú_get_directory_stashÝs  z,StashedUninstallPathSet._get_directory_stashcCs¼tj |¡}tj |¡d}}d}xd||krfy|j|}PWntk rPYnXtj |¡|}}q$Wtj |¡}tdd}||j|<tj ||¡}|r¶|tjjkr¶tj  |j|¡S|jS)z‚Stashes a file. If no root has been provided, one will be created for the directory in the user's temp directory.Nr€)r) r!r"rUr\r|ÚKeyErrorrÚrelpathÚcurdirr#)r~r"ÚheadÚold_headrƒr†r+r+r,Ú_get_file_stashìs"      z'StashedUninstallPathSet._get_file_stashcCsltj |¡otj |¡ }|r*| |¡}n | |¡}|j ||f¡|r^tj |¡r^t |¡t ||ƒ|S)zStashes the directory or file and returns its new location. Handle symlinks as files to avoid modifying the symlink targets. ) r!r"ÚisdirÚislinkr„rŠr}r$Úrmdirr)r~r"Ú path_is_dirÚnew_pathr+r+r,Ústashs    zStashedUninstallPathSet.stashcCs0x|j ¡D]\}}| ¡q Wg|_i|_dS)z0Commits the uninstall by removing stashed files.N)r|ÚitemsÚcleanupr})r~rvrƒr+r+r,Úcommits zStashedUninstallPathSet.commitc CsÎx|jD]}tjd|žŽqWx¢|jD]˜\}}yTt d||¡tj |¡sVtj |¡rbt |¡ntj  |¡rvt |ƒt ||ƒWq&t k r¼}zt  d|¡t d|¡Wdd}~XYq&Xq&W| ¡dS)z2Undoes the uninstall by moving stashed files back.úMoving to %s from %szReplacing %s from %szFailed to restore %sz Exception: %sN)r”)r}ÚloggerÚinfoÚdebugr!r"rprŒÚunlinkr‹rrr‚Úerrorr“)r~rVrr"Úexr+r+r,Úrollback&s    "z StashedUninstallPathSet.rollbackcCs t|jƒS)N)Úboolr})r~r+r+r,Ú can_rollback:sz$StashedUninstallPathSet.can_rollbackN) Ú__name__Ú __module__Ú __qualname__Ú__doc__rr„rŠrr“r›Úpropertyrr+r+r+r,r{Ñs r{c@s^eZdZdZdd„Zdd„Zdd„Zdd „Zdd d „Zd d„Z dd„Z dd„Z e dd„ƒZ dS)ÚUninstallPathSetzMA set of file paths to be removed in the uninstallation of a requirement.cCs(tƒ|_tƒ|_i|_||_tƒ|_dS)N)r.rQÚ_refuseÚpthr%r{Ú _moved_paths)r~r%r+r+r,rCs zUninstallPathSet.__init__cCst|ƒS)zs Return True if the given path is one we are permitted to remove/modify, False otherwise. )r)r~r"r+r+r,Ú _permittedKszUninstallPathSet._permittedcCsˆtj |¡\}}tj t|ƒtj |¡¡}tj |¡s:dS| |¡rR|j  |¡n |j   |¡tj  |¡ddkr„t r„|  t |ƒ¡dS)Néz.py)r!r"r@r#rrUÚexistsr§rQr/r¤Úsplitextr r)r~r"rˆÚtailr+r+r,r/Ts   zUninstallPathSet.addcCsLt|ƒ}| |¡r<||jkr*t|ƒ|j|<|j| |¡n |j |¡dS)N)rr§r¥ÚUninstallPthEntriesr/r¤)r~Úpth_fileÚentryr+r+r,Úadd_pthhs   zUninstallPathSet.add_pthFc CsÂ|jst d|jj¡dS|jjd|jj}t d|¡tƒx|sP| |¡r´|j}t |jƒ}x*t t |ƒƒD]}|  |¡t  d|¡qnWx|j ¡D] }| ¡q˜Wt d|¡WdQRXdS)z[Remove paths in ``self.paths`` with confirmation (unless ``auto_confirm`` is True).z7Can't uninstall '%s'. No files were found to uninstall.Nú-zUninstalling %s:zRemoving file or directory %szSuccessfully uninstalled %s)rQr•r–r%Ú project_nameÚversionr Ú_allowed_to_proceedr¦rmrOrTrr—r¥r_Úremove)r~Ú auto_confirmÚverboseÚdist_name_versionÚmovedÚ for_renamer"r¥r+r+r,r´rs"     zUninstallPathSet.removecCsndd„}|st|jƒ\}}nt|jƒ}tƒ}|d|ƒ|d|ƒ|d|jƒ|r`|dt|jƒƒtddƒd kS) zIDisplay which files would be deleted and prompt for confirmation c SsH|sdSt |¡tƒ&xtt|ƒƒD]}t |¡q(WWdQRXdS)N)r•r–r rOrT)ÚmsgrQr"r+r+r,Ú_display—s  z6UninstallPathSet._allowed_to_proceed.._displayz Would remove:z+Would not remove (might be manually added):z%Would not remove (outside of prefix):zWill actually move:zProceed (y/n)? )ÚyÚnr¼)rzrQr.r¤rmr )r~r¶r»rqrrr+r+r,r³’s     z$UninstallPathSet._allowed_to_proceedcCsV|jjst d|jj¡dSt d|jj¡|j ¡x|j  ¡D] }| ¡qBWdS)z1Rollback the changes previously made by remove().z'Can't roll back %s; was not uninstalledNzRolling back uninstall of %s) r¦rr•r™r%r±r–r›r¥r_)r~r¥r+r+r,r›±s  zUninstallPathSet.rollbackcCs|j ¡dS)z?Remove temporary save dir: rollback will no longer be possible.N)r¦r“)r~r+r+r,r“¿szUninstallPathSet.commitc sêt|jƒ}t|ƒs.t d|j|tj¡||ƒS|dd„t  d¡t  d¡hDƒkrht d|j|¡||ƒS||ƒ}t |ƒ}d  t   |j¡¡}|joœtj |j¡}t|jddƒ}|rº|j d ¡rº|j |¡sº| |j¡| d ¡r"xÎ| d ¡ ¡D]&}tj tj |j|¡¡} | | ¡qöWn”| d ¡rð| d ¡rF| d ¡‰ng‰xj‡fd d„| d ¡ ¡DƒD]J} tj |j| ¡} | | ¡| | d¡| | d¡| | d¡qhWn6|rÔtd  |j¡ƒ‚n|j d¡r*| |j¡tj |j¡d} tj tj |j¡d¡} | | d| ¡nÆ|r^|j d¡r^x°t |ƒD]} | | ¡qHWn’|ràt!|dƒ} tj "|  #¡ $¡¡}WdQRX||jks°t%d  ||j|j¡ƒ‚| |¡tj tj |¡d¡} | | |j¡nt &d||j¡| d¡rd| 'd¡rdxZ| (d¡D]L}t)|ƒr(t*}nt+}| tj ||¡¡t,r| tj ||¡d¡qWg}|j-dd}x$| .¡D]}| /t0||dƒ¡q~W|j-d d}x$| .¡D]}| /t0||d!ƒ¡q°Wx|D]}| |¡qÒW|S)"Nz1Not uninstalling %s at %s, outside environment %scSsh|] }|r|’qSr+r+)rKrVr+r+r,roÑsz-UninstallPathSet.from_dist..ÚstdlibÚ platstdlibzúsz.UninstallPathSet.from_dist..z.pyz.pycz.pyoz©Cannot uninstall {!r}. It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall.z.eggr¨zeasy-install.pthz./z .dist-inforAz;Egg-link {} does not match installed location of {} (at {})z)Not sure how to uninstall: %s - Check: %sÚscriptsz.batÚconsole_scripts)ÚgroupFÚ gui_scriptsT)1rr>rr•r–rFÚsysÚprefixÚ sysconfigÚget_pathrÚformatrÚ to_filenamer±Úegg_infor!r"r©ÚgetattrÚ _providerr?r/Ú has_metadataÚ get_metadataÚ splitlinesÚnormpathr#rr@r\r¯rEÚopenrUÚreadlineÚstripÚAssertionErrorr—Úmetadata_isdirÚmetadata_listdirr rrrÚ get_entry_mapÚkeysÚextendr-)Úclsr%Ú dist_pathr*Údevelop_egg_linkÚdevelop_egg_link_egg_infoÚegg_info_existsÚdistutils_egg_infoÚinstalled_filer"Ú top_level_pkgÚeasy_install_eggÚeasy_install_pthÚfhÚ link_pointerÚscriptr(Ú_scripts_to_removerÃÚnamerÅÚsr+)rÀr,Ú from_distÄs¬                zUninstallPathSet.from_distN)FF)ržrŸr r¡rr§r/r¯r´r³r›r“Ú classmethodrìr+r+r+r,r£@s  r£c@s,eZdZdd„Zdd„Zdd„Zdd„Zd S) r¬cCs||_tƒ|_d|_dS)N)Úfiler.ÚentriesÚ _saved_lines)r~r­r+r+r,rJszUninstallPthEntries.__init__cCs<tj |¡}tr,tj |¡ds,| dd¡}|j |¡dS)Nrú\ú/)r!r"rUrÚ splitdriveÚreplacerïr/)r~r®r+r+r,r/Ps  zUninstallPthEntries.addc Cst d|j¡tj |j¡s.t d|j¡dSt|jdƒ}| ¡}||_ WdQRXt dd„|Dƒƒrld}nd}|rž|d  |  d ¡¡sž|d|  d ¡|d<xH|j D]>}y$t d |¡| ||  d ¡¡Wq¦tk râYq¦Xq¦Wt|jd ƒ}| |¡WdQRXdS) NzRemoving pth entries from %s:z.Cannot remove entries from nonexistent file %sÚrbcss|]}d|kVqdS)s Nr+)rKÚliner+r+r,rNnsz-UninstallPthEntries.remove..z Ú éÿÿÿÿzutf-8zRemoving entry: %sÚwb)r•r—rîr!r"rpÚwarningrÓÚ readlinesrðrPr?Úencoderïr´Ú ValueErrorÚ writelines)r~ræÚlinesÚendliner®r+r+r,r´`s*    zUninstallPthEntries.removec CsR|jdkrt d|j¡dSt d|j¡t|jdƒ}| |j¡WdQRXdS)Nz.Cannot roll back changes to %s, none were madeFz!Rolling %s back to previous staterùT)rðr•r™rîr—rÓrþ)r~rær+r+r,r›~s  zUninstallPthEntries.rollbackN)ržrŸr rr/r´r›r+r+r+r,r¬Isr¬)=Ú __future__rr;r6Úloggingr!rÆrÈÚ pip._vendorrÚpip._internal.exceptionsrÚpip._internal.locationsrrÚpip._internal.utils.compatrrr Úpip._internal.utils.loggingr Úpip._internal.utils.miscr r r rrrrrrÚpip._internal.utils.temp_dirrrÚpip._internal.utils.typingrÚtypingrrrrrrrrrZpip._vendor.pkg_resourcesr Ú getLoggerržr•r-r8rErTrmrzÚobjectr{r£r¬r+r+r+r,Ús:    ,  ,   (3o