o
    iC                     @   s2  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	m
Z
mZ ddlmZ ddlmZ ddlmZ ddlmZmZ ddlmZ dd	lmZmZmZmZmZmZ dd
lmZ ddl Z ddl!m"Z" ddl#m$Z$m%Z%m&Z&m'Z'm(Z( ddl)m*Z*m+Z+m,Z,m-Z- de.de.de.de/fddZ0G dd de+Z1dS )OExtension manager using pip as package manager and PyPi.org as packages source.    N)datetime	timedeltatimezone)partial)groupby)Path)CalledProcessErrorrun)TarFile)AnyCallableDictListOptionalTuple)ZipFile)
alru_cache)CFloatCIntUnicodeconfigobserve)ActionResultExtensionManagerExtensionManagerMetadataExtensionPackagenamelatest_versionbase_urlreturnc                    sZ   t j }|j|d|  d| d ddidI d H }t|jd  fddd	D S )
N//jsonContent-Typeapplication/jsonheadersinfoc                    s   i | ]}|  |qS  )get).0kdatar(   U/var/www/edux/Edux_v2/venv/lib/python3.10/site-packages/jupyterlab/extensions/pypi.py
<dictcomp>+   s    
z+_fetch_package_metadata.<locals>.<dictcomp>)	authorbugtrack_urldocs_url	home_pagelicensepackage_urlproject_urlproject_urlssummary)tornado
httpclientAsyncHTTPClientfetchjsonloadsbodyr)   )r   r   r   http_clientresponser(   r,   r.   _fetch_package_metadata"   s   

rB   c                       sh  e Zd ZdZeddddZeddddZeddd	dZ	ed
dddZ
			d1dee dee deej ddf fddZedefddZdedee fddZdedefddZdededefddZedd d! Zd"ed#ed$edeeeef ee f fd%d&Z de!eeef  fd'd(Z"d2d)ed*ee de#fd+d,Z$dede#fd-d.Z%d)edefd/d0Z&  Z'S )3PyPIExtensionManagerr   zhttps://pypi.org/pypiTzThe base URL of PyPI index.)r   helpg     r@z.PyPI extensions list cache timeout in seconds.i  z$The cache size for package metadata.g      ?zGThrottling time in seconds between PyPI requests using the XML-RPC API.Napp_optionsext_optionsparentr    c                    s   t  ||| t| _| d| ji tj | _t	j
| j| _tjtjdt| jd d | _d | _| jd| j d d S )Nnewtz)\(?secondsz%Extensions list will be fetched from .)super__init__rB   $_observe_package_metadata_cache_sizepackage_metadata_cache_sizer9   r:   r;   _http_clientxmlrpcclientServerProxyr   _rpc_clientr   nowr   utcr   cache_timeout5_PyPIExtensionManager__last_all_packages_request_time)_PyPIExtensionManager__all_packages_cachelogdebug)selfrE   rF   rG   	__class__r(   r.   rP   N   s   
zPyPIExtensionManager.__init__c                 C   s   t ddtjS )zExtension manager metadata.PyPIT)r   sysprefixr_   r(   r(   r.   metadatab   s   zPyPIExtensionManager.metadatapkgc                    sn   z#t j }|j| jd| d ddidI dH }t|jd}W n
 t	y.   Y dS w t
|dS )	zReturn the latest available version for a given extension.

        Args:
            pkg: The extension to search for
        Returns:
            The latest available version
        r!   r"   r#   r$   r%   Nr'   version)r9   r:   r;   r<   r   r=   r>   r?   r)   	Exceptionr   get_semver_version)r_   rg   r@   rA   r-   r(   r(   r.   get_latest_versiong   s   
z'PyPIExtensionManager.get_latest_version	extensionc                 C   s6   |j dur|j }|d dkr| |d S | |jS )a  Normalize extension name.

        Extension have multiple parts, npm package, Python package,...
        Sub-classes may override this method to ensure the name of
        an extension from the service provider and the local installed
        listing is matching.

        Args:
            extension: The extension metadata
        Returns:
            The normalized name
        NpackageManagerpythonpackageName)install_normalize_namer   )r_   rl   install_metadatar(   r(   r.   get_normalized_name{   s
   
z(PyPIExtensionManager.get_normalized_name	recursivefnc           	   
      s.  t jj }z|jd|g|R  I dH }W |S  tjjy } zq|jdkr}|j	
drd}td|j	}|durBt|dp@d}| jd| d	 t|| j d
 I dH  |ri| jd|g|R  I dH }n"|jd|g|R  I dH }W Y d}~|S W Y d}~|S W Y d}~|S W Y d}~|S d}~ww )a/  Throttle XMLRPC API request

        Args:
            recursive: Whether to call the throttling recursively once or not.
            fn: API method to call
            *args: API method arguments
        Returns:
            Result of the method
        Raises:
            xmlrpc.client.Fault
        NizHTTPTooManyRequests:rK   z!Limit may reset in (\d+) seconds.   1z>HTTPTooManyRequests - Perform next call to PyPI XMLRPC API in zs.g{Gz?F)r9   ioloopIOLoopcurrentrun_in_executorrT   rU   Fault	faultCodefaultString
startswithresearchintgroupr]   r'   asynciosleeprpc_request_throttling&_PyPIExtensionManager__throttleRequest)	r_   rt   ru   argscurrent_loopr-   errdelaymatchr(   r(   r.   __throttleRequest   s:   
$


z&PyPIExtensionManager.__throttleRequestrR   c                 C   s   t |d dt| _d S )NrH   )maxsize)r   rB   )r_   changer(   r(   r.   rQ      s   z9PyPIExtensionManager._observe_package_metadata_cache_sizequerypageper_pagec                    sj  |   I dH }i }d}|d | }|| }tt fdd|dd D ]\}	}
|d7 }||k s4||kr5q$t|
d \}}| |	|| jI dH }| |	}|dpSi }|d}|d	pb|d
}|dpl|d}|dpv|d}|p|dp|dp|p|p|}t||d||d|dt	
|d|||d|d||< q$|t|d | fS )aw  List the available extensions.

        Note:
            This will list the packages based on the classifier
                Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt

            Then it filters it with the query

            We do not try to check if they are compatible (version wise)

        Args:
            query: The search extension query
            page: The result page
            per_page: The number of results per page
        Returns:
            The available extensions in a mapping {name: metadata}
            The results last page; None if the manager does not support pagination
        Nrv   c                    s    | d v S Nr   r(   )mr   r(   r.   <lambda>   s    z4PyPIExtensionManager.list_packages.<locals>.<lambda>c                 S   s   | d S r   r(   )er(   r(   r.   r          r7   zSource Coder3   Homepager2   Documentationr1   zBug Trackerr6   r5   r8   r0   r4   prebuilt)r   descriptionhomepage_urlr0   r4   r   pkg_typebug_tracker_urldocumentation_urlpackage_manager_urlrepository_url))_PyPIExtensionManager__get_all_extensionsr   filterlistrB   r   rq   r)   r   r   rj   mathceil)r_   r   r   r   matches
extensionscounter	min_index	max_indexr   r   _r   r-   normalized_namepackage_urls
source_urlr   r   r   best_guess_home_urlr(   r   r.   list_packages   sV   $

	z"PyPIExtensionManager.list_packagesc                    sj   | j d u stjtjd| jt| jd kr2| j	d | 
d| jjdgI d H | _ tjtjd| _| j S )NrI   rL   z?Requesting PyPI.org RPC API for prebuilt JupyterLab extensions.Tz<Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt)r\   r   rX   r   rY   r[   r   rZ   r]   r^   r   rW   browsere   r(   r(   r.   __get_all_extensions   s   z)PyPIExtensionManager.__get_all_extensionsr   rh   c              
      s  t jj }tjdddddddg}|dur!|  d	|  n|  i }zA| }|d
d |d
d |d
d |	dt
t|dddI dH }t|jd}tt fdd|dg d }W n7 ty }	 z| jjd|	j |	d W Y d}	~	n+d}	~	w ty }
 z| jjd|
d W Y d}
~
nd}
~
ww | jdt| d | jdd| d |	dt
t|ddI dH }| jd|j  | jd|jd  |jd}|jdkr| jd|  d}z|d i d!}|dur| j|I dH }|d"rPtt |j!)}td#d |" D ] t|# }|d$}|dur= nq'W d   n	1 sJw   Y  nC|d%rt$t |j!,}td&d |% D ] t&|'|( }|d$}|dur nqhW d   n	1 sw   Y  W n ty }	 z| jjd'|	d W Y d}	~	nd}	~	ww d(g}|dur|d)i }d*|v r|d* d+|v r|d+ t)d,|d-S | j*d.  d/|j d0|  t)d1|d2S )3a  Install the required extension.

        Note:
            If the user must be notified with a message (like asking to restart the
            server), the result should be
            {"status": "warning", "message": "<explanation for the user>"}

        Args:
            name: The extension name
            version: The version to install; default None (i.e. the latest possible)
        Returns:
            The action result
        -mpiprp   
--no-inputz--quietz--progress-baroffNz==r   z	--dry-runz--report-T)capture_outputcheckutf-8c                    s   |  di  d ddkS )Nrf   r   r   r   )r)   replace)pr   r(   r.   r   4  s    z.PyPIExtensionManager.install.<locals>.<lambda>r   z!Fail to get installation report: exc_infoz Fail to get installation report.zActions to be executed by pip rN   Executing ' 'r   return code: stdout: stderr: download_infourlz.whlc                 S      t | jdkS Nzpackage.jsonr   r   fr(   r(   r.   r   S      
jupyterlabztar.gzc                 S   r   r   r   r   r(   r(   r.   r   ]  r   zFail to get package.json.frontend	discoverykernelserverokstatusneeds_restartFailed to installed : code 
errorr   message)+r9   rx   ry   rz   rc   
executableappendcopyinsertr{   r   r
   r=   r>   stdoutdecoder   r   r)   r	   r]   r^   stderrri   dumpsjoin
returncoderS   r<   endswithr   ioBytesIOr?   namelistreadr   getnamesloadextractfile	getmemberr   r   )r_   r   rh   r   cmdline
pkg_actiontmp_cmdresultaction_infor   r   r   jlab_metadatadownload_urlrA   wheelr-   sdist
follow_upsr   r(   r   r.   rp     s   




$



	

	




 zPyPIExtensionManager.installc              
      s  t jj }tjddddd|g}d}zJ| }|d |dt	t
|ddI dH }td	d
 tdd
 |jd }tdd
 tt|D ]}t| }	|	d}|dur[ nqGW n tyw }
 z| jjd|
d W Y d}
~
nd}
~
ww | jdd| d |dt	t
|ddI dH }| jd|j  | jd|jd  |jd}|jdkr| jd|  dg}|dur|di }d|v r|d d|v r|d td|dS | jd| d|j d |  td!|d"S )#aj  Uninstall the required extension.

        Note:
            If the user must be notified with a message (like asking to restart the
            server), the result should be
            {"status": "warning", "message": "<explanation for the user>"}

        Args:
            extension: The extension name
        Returns:
            The action result
        r   r   	uninstallz--yesr   NTr   c                 S   s
   |  dS r   )r   liner(   r(   r.   r        
 z0PyPIExtensionManager.uninstall.<locals>.<lambda>c                 S   s   |   S N)stripr   r(   r(   r.   r     r   r   c                 S   s
   | j dkS r   r   r   r(   r(   r.   r     r  r   z%Fail to list files to be uninstalled.r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )r9   rx   ry   rz   rc   r   r   remover{   r   r
   r   mapr   r   
splitlinesr   r=   r>   
read_bytesr)   ri   r]   r^   r   r   r   r   r   r   )r_   rl   r   r   r   r   r   linesfilepathr-   r   r   r   r   r(   r(   r.   r   v  sn   






 zPyPIExtensionManager.uninstallc                 C   s   | dd dd ddS )zNormalize extension name.

        Remove `@` from npm scope and replace `/` and `_` by `-`.

        Args:
            name: Extension name
        Returns:
            Normalized name
        @ r!   r   r   )r   )r_   r   r(   r(   r.   rq     s   
z$PyPIExtensionManager._normalize_name)NNNr  )(__name__
__module____qualname____doc__r   r   r   rZ   r   rR   r   r   dictr   ConfigurablerP   propertyr   rf   strrk   r   rs   boolr   r   r   r   rQ   r   r   r   r   r   r   r   rp   r   rq   __classcell__r(   r(   r`   r.   rC   ;   s\    "

FnIrC   )2r  r   r   r=   r   r   rc   xmlrpc.clientrT   r   r   r   	functoolsr   	itertoolsr   pathlibr   
subprocessr	   r
   tarfiler   typingr   r   r   r   r   r   zipfiler   r9   	async_lrur   	traitletsr   r   r   r   r   jupyterlab.extensions.managerr   r   r   r   r  r  rB   rC   r(   r(   r(   r.   <module>   s,    