Re# @sdZddlZddlZddlZddlZddlZddlZddlZddlZddl m Z yddl m Z Wn"e k rddlm Z YnXddlmZddlmZmZmZmZddlZddlmZmZmZmZmZmZmZmZm Z m!Z!m"Z"m#Z#ddlm$Z$dd l%m&Z&dd l'm(Z(dd l)m*Z*dd l+m,Z,dd l-m.Z.ej/dZ0ej/dej1Z2ej/dZ3ej/dej1j4Z5dj6Z7ddddgZ8dZ9dZ:e:j;dej<dddeZ=ddZ>ddZ?dd Z@dd!dZAdd"d#ZBdd$d%ZCdedd&dZDdd'd(ZEd)d*ZFej/d+ej1ZGeFd,d-ZHGd.d/d/eIZJGd0d1d1eJZKGd2ddeZLej/d3jMZNd4d5ZOd6d7ZPd8d9ZQdd:d;ZRd<d=ZSGd>d?d?eIZTGd@dAdAejUZVejWjXdBdCZYeRe9eYZYdDdEZZdFdGZ[dS)Hz#PyPI and direct package downloadingN)wraps) splituser)six)urllib http_client configparsermap) CHECKOUT_DIST Distribution BINARY_DISTnormalize_path SOURCE_DIST Environmentfind_distributions safe_name safe_version to_filename Requirement DEVELOP_DIST) ssl_support)log)DistutilsError) translate)strip_fragment)get_all_headersz^egg=([-A-Za-z0-9_.+!]+)$zhref\s*=\s*['"]?([^'"> ]+)z([^<]+) \s+\(md5\)z([-+.a-z0-9]{2,}):z.tar.gz .tar.bz2 .tar .zip .tgz PackageIndexdistros_for_urlparse_bdist_wininstinterpret_distro_namezsz(interpret_distro_name..r:Nr8 py_versionrJrW)r@anyrangerTr join)rUrVrOr`rJrWrBr^r%r%r&rs* 5 ccst}|j}|dkrSxjtjj|j|D]}|||Vq7Wn8x5|D]-}||}||krZ|||VqZWdS)zHList unique elements, preserving order. Remember all elements ever seen.N)setaddrmoves filterfalse __contains__)iterablekeyseenZseen_addelementkr%r%r&unique_everseens         rncs"tfdd}|S)zs Wrap a function returning an iterable such that the resulting iterable only ever yields unique items. cst||S)N)rn)argskwargs)funcr%r&wrapperszunique_values..wrapper)r)rqrrr%)rqr& unique_valuessrsz(<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>ccsxtj|D]}|j\}}tttj|jjd}d|ksgd|krx:t j|D])}t j j |t |jdVqwWqWxddD]\}|j|}|d krt j||}|rt j j |t |jdVqWdS) zEFind rel="homepage" and rel="download" links in `page`, yielding URLs,Zhomepager9r8 Home PageDownload URLN)rurvr<)RELfinditergroupsrdrstrstripr0r@HREFrr"urljoin htmldecoderNfindsearch)rApagerMtagrelZrelsposr%r%r&find_external_linkss'+  rc@s:eZdZdZddZddZddZdS) ContentCheckerzP A null content checker that defines the interface for checking content cCsdS)z3 Feed a block of data to the hash. Nr%)selfblockr%r%r&feedszContentChecker.feedcCsdS)zC Check the hash. Return False if validation fails. Tr%)rr%r%r&is_validszContentChecker.is_validcCsdS)zu Call reporter with information about the checker (hash name) substituted into the template. Nr%)rreportertemplater%r%r&reportszContentChecker.reportN)__name__ __module__ __qualname____doc__rrrr%r%r%r&rs   rc@saeZdZejdZddZeddZddZ dd Z d d Z d S) HashCheckerzK(?Psha1|sha224|sha384|sha256|sha512|md5)=(?P[a-f0-9]+)cCs(||_tj||_||_dS)N) hash_namehashlibnewhashexpected)rrrr%r%r&__init__s zHashChecker.__init__cCsRtjj|d}|s#tS|jj|}|sBtS||jS)z5Construct a (possibly null) ContentChecker from a URLr8r<)rr"r>rpatternr groupdict)clsrArHrMr%r%r&from_url szHashChecker.from_urlcCs|jj|dS)N)rupdate)rrr%r%r&rszHashChecker.feedcCs|jj|jkS)N)r hexdigestr)rr%r%r&rszHashChecker.is_validcCs||j}||S)N)r)rrrmsgr%r%r&rs zHashChecker.reportN) rrrr\compilerr classmethodrrrrr%r%r%r&rs     rcseZdZdZddJddddZdd d Zdd d Zdd dZddZddZ ddZ ddZ dddZ ddZ dfddZddZdd Zd!d"Zd#d$Zd%d&Zddddd'd(Zddd)d*Zd+d,Zd-Zd.d/Zd0d1Zdd2d3Zd4d5Zd6d7Zd8d9Zd:d;Zd<d=Ze dd>d?Z!d@dAZ"dBdCZ#dDdEZ$dFdGZ%dHdIZ&S)Krz;A distribution index that scans web pages for download URLszhttps://pypi.python.org/simple*NTcOstj||||dd|jd |_i|_i|_i|_tjdj t t |j |_ g|_|otjo|ptj}|rtj||_ntjj|_dS)Nr7|)rrr1 index_url scanned_urls fetched_urls package_pagesr\rrcrrrMallowsto_scanrZ is_availableZfind_ca_bundleZ opener_foropenerrrequesturlopen)rrhostsZ ca_bundleZ verify_sslrokwZuse_sslr%r%r&r$s!   '  zPackageIndex.__init__Fc Cs^||jkr| rdSd|j|rr)rrAfatalsis_filerr%r%r&r{s !%zPackageIndex.url_okcCsEttjj|}dd|D}ttj|j|dS)Ncss@|]6}tj|D] }|jdr||fVqqdS)z .egg-linkN)rYrr1)r]rEentryr%r%r&r_sz.PackageIndex.scan_egg_links..)filterrYrErr itertoolsstarmap scan_egg_link)r search_pathdirsZ egg_linksr%r%r&scan_egg_linkss  zPackageIndex.scan_egg_linksc Csttjj||(}ttdttj|}WdQRXt |dkr\dS|\}}xQt tjj||D]4}tjj|||_ t |_ |j|qWdS)Nr:)openrYrErcrrrrzr{rTrrUr rJre)rrErZ raw_lineslinesZegg_pathZ setup_pathrPr%r%r&rs( " zPackageIndex.scan_egg_linkc sfdd}xXtj|D]G}y,|tjj|t|jdWq"tk rhYq"Xq"W||\}}|rxvt||D]e}t |\}} |j dr| r|r|d||f7}n j |j |qWt jdd|SdSd S) z#Process the contents of a PyPI pagecs|jjrtttjj|tjdjd}t|dkrd|dkrt |d}t |d}dj j |j i|.scanr8z.pyz #egg=%s-%scSsd|jdddS)Nz%sr8r r:)rN)mr%r%r&sz,PackageIndex.process_index..rN)r|rxrr"r}r~rNr#rrIr1need_version_infoscan_urlPYPI_MD5sub) rrArrrMrrnew_urlr4fragr%)rr&rs$,   zPackageIndex.process_indexcCs|jd|dS)NzPPage at %s links to .py file(s) without version info; an index scan is required.)scan_all)rrAr%r%r&rszPackageIndex.need_version_infocGsI|j|jkr5|r(|j|||jd|j|jdS)Nz6Scanning index of all packages (this may take a while))rrrrr)rrror%r%r&rs zPackageIndex.scan_allcCs|j|j|jd|jj|jsK|j|j|jd|jj|jsm|j|x3t|jj|jfD]}|j|qWdS)Nr7) rr unsafe_namerrrj project_namenot_found_in_indexr)r requirementrAr%r%r& find_packagess %zPackageIndex.find_packagescsk|j|j|x8||jD])}||kr;|S|jd||q%Wtt|j||S)Nz%s does not match %s)prescanrrjrsuperrobtain)rr installerrP) __class__r%r&rs   zPackageIndex.obtaincCsf|j|jd||jsb|jtj|td|jjtj j |fdS)z- checker is a ContentChecker zValidating %%s checksum for %sz7%s validation failed for %s; possible download problem?N) rrrrrYunlinkrrr3rErV)rcheckerrZtfpr%r%r& check_hashs     zPackageIndex.check_hashcCsrxk|D]c}|jdksJt| sJ|jdsJtt|rZ|j|q|jj|qWdS)z;Add `urls` to the list that will be prescanned for searchesNzfile:)rrr2rrrappend)rurlsrAr%r%r&add_find_linkss  zPackageIndex.add_find_linkscCs/|jr"tt|j|jd|_dS)z7Scan urls scheduled for prescanning (e.g. --find-links)N)rrrr)rr%r%r&rs zPackageIndex.prescancCsN||jr |jd}}n|jd}}|||j|jdS)Nz#Couldn't retrieve index page for %rz3Couldn't find index page for %r (maybe misspelled?))rjrrrr)rrmethrr%r%r&rs   zPackageIndex.not_found_in_indexcCst|tst|}|ry|j|jd||}t|\}}|jdru|j|||}|Stj j |r|St |}t |j ||ddS)aLocate and/or download `spec` to `tmpdir`, returning a local path `spec` may be a ``Requirement`` object, or a string containing a URL, an existing local filename, or a project/version requirement spec (i.e. the string form of a ``Requirement`` object). If it is the URL of a .py file with an unambiguous ``#egg=name-version`` tag (i.e., one that escapes ``-`` as ``_`` throughout), a trivial ``setup.py`` is automatically created alongside the downloaded file. If `spec` is a ``Requirement`` object or a string containing a project/version requirement spec, this method returns the location of a matching distribution (possibly after downloading it to `tmpdir`). If `spec` is a locally existing file or directory name, it is simply returned unchanged. If `spec` is a URL, it is downloaded to a subpath of `tmpdir`, and the local filename is returned. Various errors may be raised if a problem occurs during downloading. r8z.pyrUN)rrr _download_urlrNrIr1 gen_setuprYrErr'rfetch_distribution)rr$tmpdirrCfoundr4rHr%r%r&r9 s  zPackageIndex.downloadc s:jd|id}dfdd}|rfjj|||}| r|dk r|||}|dkrjdk rj||}|dkr| rj|||}|dkrjdrdp d|n#jd||jd |jSdS) a|Obtain a distribution suitable for fulfilling `requirement` `requirement` must be a ``pkg_resources.Requirement`` instance. If necessary, or if the `force_scan` flag is set, the requirement is searched for in the (online) package index as well as the locally installed packages. If a distribution matching `requirement` is found, the returned distribution's ``location`` is the value you would have gotten from calling the ``download()`` method with the matching distribution's URL or filename. If no matching distribution is found, ``None`` is returned. If the `source` flag is set, only source distributions and source checkout links will be considered. Unless the `develop_ok` flag is set, development and system eggs (i.e., those using the ``.egg-info`` format) will be ignored. zSearching for %sNcs|dkr}x||jD]}|jtkre re|kr jd|d|.findz:No local packages or working download links found for %s%sza source distribution of rzBest match: %srU)rrrrrcloner) rrr force_scanr r Z local_indexrPrr%)r rr r rr&rBs0!          zPackageIndex.fetch_distributioncCs/|j||||}|dk r+|jSdS)a3Obtain a file suitable for fulfilling `requirement` DEPRECATED; use the ``fetch_distribution()`` method now instead. For backward compatibility, this routine is identical but returns the ``location`` of the downloaded distribution instead of a distribution object. N)rrU)rrrr r rPr%r%r&fetchs zPackageIndex.fetchc Cs\tj|}|r=ddt||jddDp@g}t|dkr-tjj|}tjj||krtjj ||}ddl m }|||st j |||}ttjj |dd?} | jd|dj|djtjj|dfWdQRX|S|rLtd ||fn td dS) NcSsg|]}|jr|qSr%)version)r]dr%r%r& s z*PackageIndex.gen_setup..r8r)samefilezsetup.pywzIfrom setuptools import setup setup(name=%r, version=%r, py_modules=[%r]) zCan't unambiguously interpret project/version identifier %r; any dashes in the name or version should be escaped using underscores. %rzpCan't process plain .py files without an '#egg=name-version' suffix to enable automatic setup script generation.)rLrMrrNrTrYrErVdirnamercZsetuptools.command.easy_installrshutilcopy2rwriterrsplitextr) rrZrHrrMrrVdstrrr%r%r&rs2 !"zPackageIndex.gen_setupi c Cs|jd|d\}}zStj|}|jt|}t|tjjrwt d||j |j f|j}d}|j }d } d|krt |d} ttt| } |j||||| t|dw} xZ|j|} | rK|j| | j| |d7}|j||||| qPqW|j||| WdQRX|SWd|r|jXdS) NzDownloading %szCan't download %s: %s %srr8zcontent-lengthzContent-Lengthwb)NNr<)rrrrrrrrrrrr dl_blocksizermaxrint reporthookrrrrrr) rrArZfprrrblocknumbssizesizesrrr%r%r& _download_tos:       zPackageIndex._download_tocCsdS)Nr%)rrArZr Zblksizer"r%r%r&rszPackageIndex.reporthookcCs|jdrt|Syt||jSWnttjfk r}zSdjdd|jD}|r|j ||nt d||fWYdd}~XnIt j j k r}z |SWYdd}~Xnt j jk r8}z:|r |j ||jnt d||jfWYdd}~Xntjk r}z:|ri|j ||jnt d||jfWYdd}~Xn`tjtj fk r}z4|r|j ||nt d||fWYdd}~XnXdS)Nzfile: cSsg|]}t|qSr%)rz)r]argr%r%r&rs z)PackageIndex.open_url..z%s %szDownload error for %s: %sz;%s returned a bad status line. The server might be down, %s)r2 local_openopen_with_authrr#r InvalidURLrcrorrrrrURLErrorreason BadStatusLineline HTTPExceptionsocket)rrAwarningvrr%r%r&rs6 (%%zPackageIndex.open_urlcCsKt|\}}|rLx7d|krH|jddjdd}qWnd}|jdrq|dd}tjj||}|dks|jd r|j||S|d ks|jd r|j||S|jd r|j ||S|d kr't j j t j j|dS|j|d|j||SdS)Nz...\_Z__downloaded__z.egg.zipr,svnzsvn+gitzgit+zhg+rr:Tr/)rIreplacer1rYrErcr2 _download_svn _download_git _download_hgrr url2pathnamer"r>r_attempt_download)rrCrArr3rHrZr%r%r&r s$%  zPackageIndex._download_urlcCs|j|ddS)NT)r)rrAr%r%r&r'szPackageIndex.scan_urlcCsK|j||}d|jddjkrC|j|||S|SdS)Nrz content-typer)r$rr0_download_html)rrArZrr%r%r&r<*szPackageIndex._attempt_downloadcCst|}xT|D]L}|jrtjd|r^|jtj||j||SPqW|jtj|td|dS)Nz ([^- ]+ - )?Revision \d+:zUnexpected HTML page found at ) r���r{���r\���r���r���rY���r���r8��r���)r���rA���r���rZ���r���r-��r%���r%���r&���r=��1��s����       zPackageIndex._download_htmlc�������������C���si��|�j��d�d��d�}�d�}�|�j���j�d��r8d�|�k�r8t�j�j�|��\�}�}�}�}�}�} �|� r8|�j�d��r8d�|�d �d���k�r8|�d �d���j��d�d��\�}�}�t�|��\�} �} �| �r8d �| �k�r�| �j��d �d��\�} �} �d �| �| �f�}�n �d �| �}�| �}�|�|�|�|�|�| �f�}�t�j�j�|��}�|��j�d �|�|��t �j �d�|�|�|�f��|�S)Nr;���r8���r���r���zsvn:@z//r7���r:���:z --username=%s --password=%sz --username=z'Doing subversion checkout from %s to %szsvn checkout%s -q %s %s) r@���r0���r2���r���r"���r>���r��� urlunparser���rY���system)r���rA���rZ���credsrC���netlocrE���r^���qr���authhostuserpwrB���r%���r%���r&���r8��@��s$����!$,"  zPackageIndex._download_svnc�������������C���s���t��j�j�|���\�}�}�}�}�}�|�j�d�d��d�}�|�j�d�d��d�}�d��}�d�|�k�rw�|�j�d�d��\�}�}�t��j�j�|�|�|�|�d�f��}��|��|�f�S)N+r8���r;���r���r>��r���r<���)r���r"���urlsplitr@���rsplit urlunsplit)rA��� pop_prefixrC���rC��rE���rG���r���revr%���r%���r&���_vcs_split_rev_from_urlU��s����! !z$PackageIndex._vcs_split_rev_from_urlc�������������C���s���|�j��d�d��d�}�|��j�|�d�d�\�}�}�|��j�d�|�|��t�j�d�|�|�f��|�d��k �r�|��j�d�|��t�j�d �|�|�f��|�S) Nr;���r8���r���rM��TzDoing git clone from %s to %szgit clone --quiet %s %szChecking out %sz"(cd %s && git checkout --quiet %s))r@���rO��r���rY���rA��)r���rA���rZ���rN��r%���r%���r&���r9��g��s����   zPackageIndex._download_gitc�������������C���s���|�j��d�d��d�}�|��j�|�d�d�\�}�}�|��j�d�|�|��t�j�d�|�|�f��|�d��k �r�|��j�d�|��t�j�d �|�|�f��|�S) Nr;���r8���r���rM��TzDoing hg clone from %s to %szhg clone --quiet %s %szUpdating to %sz(cd %s && hg up -C -r %s >&-))r@���rO��r���rY���rA��)r���rA���rZ���rN��r%���r%���r&���r:��w��s����   zPackageIndex._download_hgc�������������G���s���t��j�|�|��d��S)N)r���r���)r���r���ro���r%���r%���r&���r�����s����zPackageIndex.debugc�������������G���s���t��j�|�|��d��S)N)r���r���)r���r���ro���r%���r%���r&���r�����s����zPackageIndex.infoc�������������G���s���t��j�|�|��d��S)N)r���r���)r���r���ro���r%���r%���r&���r�����s����zPackageIndex.warn)r���)'r���r���r���r���r���r���r���r���r���r���r���r���r���r���r���r���r���r���r���r9���r��r��r��r��r$��r��r���r��r���r<��r=��r8�� staticmethodrO��r9��r:��r���r���r���r%���r%���)r���r&���r���!��sL��� 2  +      #D ) $ #         z!&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?c�������������C���s6���t��|��t��s�|��S|��d�k�r,�t�j�|���St�|���S)N���)r���r��r���unichrchr)cr%���r%���r&���uchr��s ����  rU��c�������������C���s���|��j��d��}�|�j�d��r:�t�|�d�d���d��}�nL�|�j�d��rb�t�|�d�d����}�n$�t�j�j�j�j�|�|��j��d���}�t�|��S)Nr8���z#xr:���r*���r;���r���) rN���r2���r��r���rf��� html_entitiesname2codepointr���rU��)rM���whatr%���r%���r&��� decode_entity��s����$rY��c�������������C���s ���t��t�|���S)z'Decode HTML entities in the given text.) entity_subrY��)textr%���r%���r&���r~�����s����r~���c����������������s�����f�d�d���}�|�S)Nc����������������s������f�d�d���}�|�S)Nc����������� ������s?���t��j���}�t��j���z���|��|���SWd��t��j�|��Xd��S)N)r/��getdefaulttimeoutsetdefaulttimeout)ro���rp���Z old_timeout)rq���timeoutr%���r&���_socket_timeout��s ����  z@socket_timeout.<locals>._socket_timeout.<locals>._socket_timeoutr%���)rq���r_��)r^��)rq���r&���r_����s����z'socket_timeout.<locals>._socket_timeoutr%���)r^��r_��r%���)r^��r&���socket_timeout��s���� r`��c�������������C���sI���t��j�j�|���}�|�j���}�t�j�|��}�|�j���}�|�j�d�d��S)aq�� A function compatible with Python 2.3-3.3 that will encode auth from a URL suitable for an HTTP header. >>> str(_encode_auth('username%3Apassword')) 'dXNlcm5hbWU6cGFzc3dvcmQ=' Long auth strings should not cause a newline to be inserted. >>> long_auth = 'username:' + 'password'*10 >>> chr(10) in str(_encode_auth(long_auth)) False  r���)r���r"���r?���encodebase64 encodestringr���r7��)rE��Zauth_sZ auth_bytesZ encoded_bytesencodedr%���r%���r&��� _encode_auth��s ����   rf��c���������������@���s:���e��Z�d��Z�d�Z�d�d���Z�d�d���Z�d�d���Z�d�S) Credentialz: A username/password pair. Use like a namedtuple. c�������������C���s���|�|��_��|�|��_�d��S)N)usernamepassword)r���rh��ri��r%���r%���r&���r�����s���� zCredential.__init__c�������������c���s���|��j��V|��j�Vd��S)N)rh��ri��)r���r%���r%���r&���__iter__��s����zCredential.__iter__c�������������C���s���d�t��|���S)Nz%(username)s:%(password)s)vars)r���r%���r%���r&���__str__��s����zCredential.__str__N)r���r���r���r���r���rj��rl��r%���r%���r%���r&���rg����s���   rg��c���������������@���sF���e��Z�d��Z�d�d���Z�e�d�d����Z�d�d���Z�d�d���Z�d �S) PyPIConfigc�������������C���sr���t��j�d�d�d�g�d��}�t�j�j�|��|��t�j�j�t�j�j�d��d��}�t�j�j �|��rn�|��j �|��d�S)z% Load from ~/.pypirc rh��ri�� repositoryr���~z.pypircN) dictfromkeysr���RawConfigParserr���rY���rE���rc��� expanduserr���r���)r���defaultsrcr%���r%���r&���r�����s ����!zPyPIConfig.__init__c����������������s5�����f�d�d�����j����D�}�t�t���j�|���S)Nc����������������s.���g��|��]$�}���j��|�d���j���r�|��q�S)rn��)r���r{���)r]���section)r���r%���r&���r����s��� �z2PyPIConfig.creds_by_repository.<locals>.<listcomp>)sectionsrp��r���_get_repo_cred)r���Zsections_with_repositoriesr%���)r���r&���creds_by_repository��s����zPyPIConfig.creds_by_repositoryc�������������C���sO���|��j��|�d��j���}�|�t�|��j��|�d��j���|��j��|�d��j����f�S)Nrn��rh��ri��)r���r{���rg��)r���rv��repor%���r%���r&���rx����s����zPyPIConfig._get_repo_credc�������������C���s7���x0�|��j��j���D]�\�}�}�|�j�|��r�|�Sq�Wd�S)z If the URL indicated appears to be a repository defined in this config, return the credential for that repository. N)ry��itemsr2���)r���rA���rn��credr%���r%���r&���find_credential��s����zPyPIConfig.find_credentialN)r���r���r���r���propertyry��rx��r}��r%���r%���r%���r&���rm����s���   rm��c�������������C���s��t��j�j�|���\�}�}�}�}�}�}�|�j�d��rB�t�j�d���|�d �k�rc�t�|��\�}�} �n�d�}�|�s�t���j�|���} �| �r�t �| ��}�| �j �|��f�} �t �j �d�| ��|�rd�t �|��}�|�| �|�|�|�|�f�} �t��j�j�| ��} �t��j�j�| ��}�|�j�d�|��n�t��j�j�|���}�|�j�d �t��|�|��}�|�rt��j�j�|�j��\�}�}�}�}�}�}�|�|�k�r|�| �k�r|�|�|�|�|�|�f�} �t��j�j�| ��|�_�|�S) z4Open a urllib2 request, handling HTTP authenticationr?��znonnumeric port: ''httphttpsNz*Authenticating as %s for %s (from .pypirc)zBasic Authorizationz User-Agent)r��r��)r���r"���r>���r1���r���r)��r���rm��r}��rz���rh��r���r���rf��r@��r���Request add_header user_agentrA���)rA���r���rC���rC��rE���paramsrG���r���rE��rF��r|��r���rB���r���r���r��s2h2path2Zparam2Zquery2Zfrag2r%���r%���r&���r(�� ��s6����$   'r(��c�������������C���s���|��S)Nr%���)rA���r%���r%���r&��� fix_sf_url<��s����r��c���������� ���C���s��t��j�j�|���\�}�}�}�}�}�}�t��j�j�|��}�t�j�j�|��rX�t��j�j�|���S|�j �d��rNt�j�j �|��rNg��}�x�t�j �|��D]�} �t�j�j �|�| ��} �| �d�k�r�t �| �d���} �| �j���} �Wd�QRXPn�t�j�j �| ��r�| �d�7} �|�j�d�j�d�| ���q�Wd�} �| �j�d�|��d �d �j �|���} �d�\�}�}�n�d�\�}�}�} �d�d�i�}�t�j�| ��}�t��j�j�|��|�|�|�|��S)z7Read a local path, with special support for directoriesr7���z index.htmlrNz<a href="{name}">{name}</a>r3���zB<html><head><title>{url}{files}rAfilesraOKPath not found Not foundz content-typez text/html)rr)rrr)rr"r>rr;rYrEisfilerr1rrrcrrrformatrStringIOrr)rArCrDrEparamrGrrZrrfilepathrbodyrstatusmessagerZ body_streamr%r%r&r'@s,$!  ! r')\rsysrYr\rr/rcrr functoolsr urllib.parser ImportErrorurllib2Zsetuptools.externrZsetuptools.extern.six.movesrrrrr! pkg_resourcesr r r r r rrrrrrrr distutilsrdistutils.errorsrfnmatchrZsetuptools.py26compatrZsetuptools.py27compatrrrLIr|rrMrr@rS__all__Z_SOCKET_TIMEOUTZ_tmplrrrr'rrIrrKr[rrnrsrwrobjectrrrrrZrUrYr~r`rfrgrrrmrrr(rr'r%r%r%r&s~         " R    %  " !t   &.