U
    eel                     @   s   d Z ddlZddlm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 ddlZddlZddlZddlmZmZ dd Zdd
dZdd Zdd Zdd Zdd Zdd Zd ddZe Zd!ddZ d"ddZ!d#ddZ"dS )$a   This module offers highlevel functions to get package metadata
    like the METADATA file, the name, or a list of dependencies.

    Usage examples:

       # Getting package name from pip reference:
       from pythonforandroid.pythonpackage import get_package_name
       print(get_package_name("pillow"))
       # Outputs: "Pillow" (note the spelling!)

       # Getting package dependencies:
       from pythonforandroid.pythonpackage import get_package_dependencies
       print(get_package_dependencies("pep517"))
       # Outputs: "['pytoml']"

       # Get package name from arbitrary package source:
       from pythonforandroid.pythonpackage import get_package_name
       print(get_package_name("/some/local/project/folder/"))
       # Outputs package name

    NOTE:

    Yes, this module doesn't fit well into python-for-android, but this
    functionality isn't available ANYWHERE ELSE, and upstream (pip, ...)
    currently has no interest in taking this over, so it has no other place
    to go.
    (Unless someone reading this puts it into yet another packaging lib)

    Reference discussion/upstream inclusion attempt:

    https://github.com/pypa/packaging-problems/issues/247

    N)open)unquote)urlparse)rmdir
ensure_dirc                 C   sz   |  ddkrv|  d|  dk s*d| krv| dr@| d d } | dd  dd d | dd   }|S | S )	N@r   ://#   z#eggz#egg=)findendswith	partitionstrip)
dependencyurl r   B/tmp/pip-unpacked-wheel-h4dze4ss/pythonforandroid/pythonpackage.pytransform_dep_for_pip7   s    
r   Fc                 C   s   | dkrt dtj|r(tj|r0t d|rDtdt|   tjdd}zHt
| rtjt| tj|dtdd	 tj|d} t| | W 5 t	| X dS )
a_   Extracts metdata files from the given package to the given folder,
        which may be referenced in any way that is permitted in
        a requirements.txt file or install_requires=[] listing.

        Current supported metadata files that will be extracted:

        - pytoml.yml  (only if package wasn't obtained as wheel)
        - METADATA
    Nzpackage cannot be Nonez)output folder needs to be existing folderz=extract_metainfo_files_from_package: extracting for package: zpythonpackage-package-copy-prefixpackage.tox)ignore)
ValueErrorospathexistsisfileprintstrtempfilemkdtempr   is_filesystem_pathshutilcopytreeparse_as_folder_referencejoinignore_patterns+_extract_metainfo_files_from_package_unsafe)r   output_folderdebugZtemp_folderr   r   r   #extract_metainfo_files_from_packageM   s&    r,   c                  C   s  t tds6t tdr0tjtjtjtjkr6tjS t tdrHtj} ntj} dd }|| }|dk	rj|S g }g }d}d}tj	
ddd	D ]}tj|tj| sqd
}|sHtjj}d| krd|kr|drddht||sd| krD|tjjd s8|tjjd tjj rD|| qd
}|| q|stj| d
dD ],\}	}
}|
D ]}|tj|	| qvqhdd }t|t|d}t|t|d}|| D ] }||}|dk	r|  S qtdtj||dS )zm Returns the path the system-wide python binary.
        (In case we're running in a virtualenv or venv)
    real_prefixbase_prefixc                    sV    fdd}dt j }||s<|ddkr<|dd }q||rRtj |S d S )Nc              	      s   t jt j | r,t jt j | r0dS zt}W n tk
rP   t}Y nX z$tj	t j | dgtj
d W dS  tj|fk
r   Y dS X dS )zx Helper function to see if a given binary name refers
                to a usable python interpreter binary
            Nz	--version)stderrTF)r   r   r   r'   isdirFileNotFoundError	NameErrorOSError
subprocesscheck_outputSTDOUTCalledProcessError)Z
python_binfilenotfounderrorr   r   r   binary_is_usable   s(    
 zZ_get_system_python_executable.<locals>.python_binary_from_folder.<locals>.binary_is_usablepython.r   )sysversionr   
rpartitionr   r   r'   )r   r:   python_namer   r9   r   python_binary_from_folder   s    
z@_get_system_python_executable.<locals>.python_binary_from_folderNFPATH :TZsystem32Zusrz/opt/pythonhomer   Zusersbin)topdownc                 S   s   t | t | S N)len)abr   r   r   candidate_cmp   s    z4_get_system_python_executable.<locals>.candidate_cmp)keyzGfailed to locate system python in: {} - checked candidates were: {}, {})hasattrr=   r   r   normpathr.   r   
executabler-   environgetsplit
startswithseplowerintersectionsetr   appendwalkr'   sorted	functools
cmp_to_keyRuntimeErrorformat)Zsearch_prefixrA   resultZbad_candidatesZgood_candidatesZever_had_nonvenv_pathZ"ever_had_path_starting_with_prefixprU   rootdirsfilesnamerL   r   r   r   _get_system_python_executabley   s    


$



  
  rf   c                 C   s  t jdd}zzjttjdd dk rVtjtj	dddt
  tj|d	g|d
 n"tjt
 dd	tj|d	g|d
 W nB tjk
r } z"|jdd}tdt| W 5 d}~X Y nX tj|d	}zt}W n tk
r   t}Y nX z$ttj|ddddddg W n2 |k
rD   tdt
 tjddY nX ttj|d ttj|dddd }dd }||t|  W 5 Q R X zDtjtj|ddddddd tj|dgtjtj|dd! W n8 tjk
r } ztd"t|j W 5 d}~X Y nX tt tj|ddkr<W zd#S tj|dt tj|dd }d$}	tj!|s*|"d%r|"d&rd}	t#$|,}|%tj|dd' tj|dd'}W 5 Q R X n^|&d(dkrt'|,}|%tj|dd' tj|dd'}W 5 Q R X ntd)t| tj!|rtt |d*krtj!tj|t |d rtj|t |d }q*t  }
t|
 t()||
 |	|
fW S t| X dS )+a<   This function downloads the given package / dependency and extracts
        the raw contents into a folder.

        Afterwards, it returns a tuple with the type of distribution obtained,
        and the temporary folder it extracted to. It is the caller's
        responsibility to delete the returned temp folder after use.

        Examples of returned values:

        ("source", "/tmp/pythonpackage-venv-e84toiwjw")
        ("wheel", "/tmp/pythonpackage-venv-85u78uj")

        What the distribution type will be depends on what pip decides to
        download.
    zpythonpackage-venv-r   r<   r      z-m
virtualenvz	--python=venv)cwdutf-8replacez1venv creation unexpectedly failed. error output: NrF   pipinstallz-Uwheelzvenv appears to be missing pip. did we fail to use a proper system python??
system python path detected: {}
os.environ['PATH']: {}rB   rC   downloadzrequirements.txtwencodingc                 S   s*   z|  dW S  tk
r$   |  Y S X d S )Nrk   )decodeAttributeError)sr   r   r   
to_unicodeE  s    z)get_package_as_folder.<locals>.to_unicodez	--no-depsz-rz../requirements.txtz-d)r/   rj   zpackage download failed: )NNsource)z.zip.whlry   Z	extractedz.tar.z"unknown archive or download type:    )*r!   r"   r   intr=   r>   r   r4   r5   rP   rf   r   r   r'   r7   outputrt   r   r    r1   r2   r3   r^   r_   rQ   rR   r   r   writer   r6   rI   listdirr0   r   zipfileZipFile
extractallr   tarfiler$   r%   )r   Zvenv_parenter|   Z	venv_pathr8   frw   Zresult_folder_or_fileZdl_typeresult_pathr   r   r   get_package_as_folder   s     	  

   

     
	$
       r   c           
   
   C   sT  d}d}t | }|d kr8t| \}}|d kr4tdd}zd }|dkrtj|}tj	|d}t
|d6}| D ]&}||D ]}	|d||	 qqvW 5 Q R X n$tj	|d	d
 t|D d d}t
tj	|dd>}z|| W n( tk
r   ||dd Y nX W 5 Q R X t|tj	|d W 5 |rNt| X d S )NFrx   zXcannot get info for this package, pip says it has no downloads (conditional dependency?)Tro   Zbuilt_metadatarq   z{}: {}
c                 S   s   g | ]}| d r|qS )z
.dist-info)r   ).0r   r   r   r   
<listcomp>  s     
 z?_extract_metainfo_files_from_package_unsafe.<locals>.<listcomp>r   METADATAmetadata_sourcerk   rl   )r&   r   r   r   buildutilZproject_wheel_metadatar   r   r'   r   keysget_allr}   r_   r~   	TypeErrorrt   r$   copyfile)
r   Zoutput_pathZclean_up_path	path_typer   Zmetadata_pathmetadatar   rM   valuer   r   r   r)     s@    
"	"r)   c                 C   s   t | dk	S )z Convenience function around parse_as_folder_reference() to
        check if a dependency refers to a folder path or something remote.

        Returns True if local, False if remote.
    N)r&   depr   r   r   r#     s    r#   c                 C   s   |  ddkr\|  d|  dk s*d| kr\|  d|  dk sFd| kr\t| dd  S | ds|  ddkr|  ddk s| dkr| d	rtt| j} | S d
S )z See if a dependency reference refers to a folder path.
        If it does, return the folder path (which parses and
        resolves file:// urls in the process).
        If it doesn't, return None.
    r   r   /rD   r   )r   file://r   )rC   r<   r   N)r   r&   r   lstriprT   
urlunquoter   r   r   r   r   r   r&     s*    

r&   c              	   C   s  |rt d|| tjdd}zt| ||d ttj	|dd}|
  }W 5 Q R X ttj	|dddd	}|
 d
d  }W 5 Q R X |dkrd}|D ],}	|	 dr|	dd    W S q|dkrtd|W S |dkr|r|dkr|rt d tdg }
tjtj	|dr|rttj	|d*}t|d }d|krx|
|d 7 }
W 5 Q R X n|r|
d |
dd |D 7 }
tt|
W S W 5 t| X dS )z Internal function to extract metainfo from a package.
        Currently supported info types:

        - name
        - dependencies  (a list of dependencies)
    zT_extract_info_from_package called with extract_type={} include_build_requirements={}zpythonpackage-metafolder-r   )r+   r   rr   rk   rr   z

r   re   Nzname:rD   r   zfailed to obtain package namedependenciesro   zz_extract_info_from_package: was called with include_build_requirements=True on package obtained as wheel, raising error...z9fetching build requirements for wheels is not implementedzpyproject.tomlzbuild-systemrequires
setuptoolsc                 S   s(   g | ] }| d r|dd  qS )zRequires-DistzRequires-Dist:r   )rT   r?   r   )r   entryr   r   r   r   =  s   
z._extract_info_from_package.<locals>.<listcomp>)r   r_   r!   r"   r   r,   r   r   r   r'   readr   r   
splitlinesrV   rT   r   NotImplementedErrorr   tomlloadrY   listrX   )r   extract_typer+   include_build_requirementsr*   r   Zmetadata_source_typeZmetadata_entriesre   Z
meta_entryrequirementsZ	build_sysr   r   r   _extract_info_from_package  sv       
  


r   Tc                 C   sh   dd }z,t |  }|d d | kr2|r2|d W S W n tk
rH   Y nX t| dd}| |ft | < |S )Nc                   S   s,   z
t  W S  tk
r&   t    Y S X d S rH   )time	monotonicru   r   r   r   r   	timestampM  s    
z#get_package_name.<locals>.timestampr   g     @rz   re   )r   )package_name_cacheKeyErrorr   )r   Z	use_cacher   r   r`   r   r   r   get_package_nameK  s    r   c                 C   s  t  }| g}t  }t  }t|dkr|}g }|D ]h}	t  }
|rPtd|	  t|	} |  |krfq2|rtd|  td| ||   |
t|	d||d}
|rtd|	t	|
 |
D ]}zt|}W nn t
k
rD } zN|ddkrW Y 8q|r$td	t	| d
 t	|  tdt	| W 5 d}~X Y nX | |krVq| |krn|| || ||  q|s2g |dd<  qq2q|rtd| |S )z Obtain the dependencies from a package. Please note this
        function is possibly SLOW, especially if you enable
        the recursive mode.
    r   z@get_package_dependencies: resolving dependency to package name: z0get_package_dependencies: processing package: {}z2get_package_dependencies: Packages seen so far: {}r   )r   r+   r   z4get_package_dependencies: collected deps of '{}': {};z=get_package_dependencies: unexpected failure to get name of 'z': z"failed to get name of dependency: Nz.get_package_dependencies: returning result: {})rX   rI   r   r   rV   r_   addunionr   r    r   r   r^   rY   )r   	recursiveverboser   Zpackages_processedZpackage_queuereqsZreqs_as_namesZcurrent_queueZpackage_depZnew_reqsZnew_reqreq_namer   r   r   r   get_package_dependencies]  s    
  

r   c           
      C   s   t | |||d}|r$tdt|  t }|D ]x}d}|rhd|krh|drhd|dd dd	  }n(|rd|kr|dsd|dd  }t|| }	||	 q.|S )
z Gets the dependencies from the package in the given folder,
        then attempts to deduce the actual package name resulting
        from each dependency line, stripping away everything else.
    )r   r   r   zFget_dep_names_of_package_folder: processing dependency list to names: rC   z(==)z==r   Nr
   )r   r   r    rX   r   r?   r   r   )
r   Zkeep_version_pinsr   r   r   r   Zdependency_namesr   Zpin_to_appenddep_namer   r   r   get_dep_names_of_package  s(      r   )F)NFF)T)FFF)FFFF)#__doc__r\   ior   r   r$   r4   r=   r   r!   r   urllib.parser   r   r   r   r   Z
build.utilr   Zpythonforandroid.utilr   r   r   r,   rf   r   r)   r#   r&   r   dictr   r   r   r   r   r   r   r   <module>   sR   # 
,  <	   
T 
   
P    