B
    `6                 @   s   d Z ddlmZmZmZ e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mZmZmZ ddlmZ ed	gZd
d Zdd Zdd Zdd ZdddZdd Zdd ZG dd dejZdS )z!Analyze python import statements.    )absolute_importdivisionprint_functionN   )types)read_binary_file)displayApplicationError	is_subdir)data_contextzansible.module_utils.sixc                s  t  }tdd |D |8 }i  x| D ]}t|j| |j< q*W d fdd	x||D ]t}|}|| x\ D ]T}| | krxxBt|D ]6}| | krtjd|||f dd	  | | qW qxW q\W t	d
d |B D }x. D ]&}x  | D ]}|| |  qW qW xHD ]@}d
|ddd }	||	 ||< tjd||	f dd	 q W xVt|D ]J}|| snt|dd}
tj|
rtj|
sqntd|  qnW |S )zReturn a dictionary of module_utils names mapped to sets of python file paths.
    :type compile_targets: list[TestTarget]
    :rtype: dict[str, set[str]]
    c             3   s(   | ]  t  fd dtD r V  qdS )c             3   s   | ]}  d | V  qdS )z%s.N)
startswith).0v)m U/home/dcms/DCMS/lib/python3.7/site-packages/ansible_test/_internal/import_analysis.py	<genexpr>&   s    z<get_python_module_utils_imports.<locals>.<genexpr>.<genexpr>N)anyVIRTUAL_PACKAGES)r   r   )r   r   r   &   s    z2get_python_module_utils_imports.<locals>.<genexpr>r   Nc       	         s8  t jdd| | f dd |dkr,t| g}t| g}| tkrx^tD ]R}|d|  rH||krdqH|| t||d |}x|D ]}|| qW qHW t| }| krt| dd	}| krtd
|  xdt | ddD ]P}|krq||krq|| t||d |}x|D ]}|| qW qW |S )z@Recursively expand module_utils imports from module_utils files.zmodule_utils import: %s%sz     )	verbosityNz%s.r   T)packagez1Cannot determine path for module_utils import: %s)reverse)	r   infosetr   sortedr   addget_import_pathr	   )	import_namedepthseenresultsZ
sub_importmatchesresultZimport_pathname)imports_by_target_pathrecurse_importvirtual_utilsr   r   r&   .   s8    





z7get_python_module_utils_imports.<locals>.recurse_importz%s inherits import %s via %s   )r   c             S   s   g | ]}|t  fqS r   )r   )r   module_utilr   r   r   
<listcomp>k   s    z3get_python_module_utils_imports.<locals>.<listcomp>.z)%s reports imports from parent package %sT)r   z0No imports found which use the "%s" module_util.)r   N)enumerate_module_utilsr   #extract_python_module_utils_importspathremover   r   r   r   dictjoinsplitr   osexistsgetsizewarning)Zcompile_targetsmodule_utilstargetr)   Zmodule_util_importstarget_pathZmodule_util_importimportsZvirtual_utilparent_packagepackage_pathr   )r%   r&   r'   r   get_python_module_utils_imports   s>    
0





r>   c             C   s   t  jj}t  jjr*dt  jjj d }nd}| drDtj| } | |krR|}n.|d tj	tj
| |d tjjd }|S )z=Return a namespace and name from the given module_utils path.zansible_collections.zplugins.module_utilszansible.module_utilsz/__init__.pyr+   r   )r   contentmodule_utils_path
collectionprefixendswithr4   r/   dirnamesplitextrelpathreplacesep)r/   	base_pathrB   r$   r   r   r   get_python_module_utils_name   s    


.rJ   c              C   sR   g } xDt  jt  jjD ],}tj|d }|dkr8q| t| qW t	| S )zJReturn a list of available module_utils imports.
    :rtype: set[str]
    r   z.py)
r   r?   Z
walk_filesr@   r4   r/   rE   appendrJ   r   )r8   r/   extr   r   r   r-      s    r-   c          
   C   sn   t | }yt|}W n< tk
rR } ztd| |j|jf  t S d}~X Y nX t	| |}|
| |jS )zReturn a list of module_utils imports found in the specified source file.
    :type path: str
    :type module_utils: set[str]
    :rtype: set[str]
    z6%s:%s Syntax error extracting module_utils imports: %sN)r   astparseSyntaxErrorr   r7   linenomsgr   ModuleUtilFindervisitr;   )r/   r8   codetreeexfinderr   r   r   r.      s    	

r.   Fc             C   s   |rt j| ddd}nd| dd }| ds>| dkrNt jd|}nZt jjr| dt jjj s| d	t jjj krd|	dd
d }nt
d|  |S )z"Return a path from an import name.r+   /z__init__.pyz%s.pyzansible.module_utils.zansible.module_utilslibz,ansible_collections.%s.plugins.module_utils.z+ansible_collections.%s.plugins.module_utils   NzUnexpected import name: %s)r4   r/   r2   rG   r   r   r?   rA   	full_namer3   	Exception)r$   r   filenamer/   r   r   r   r      s    
r   c             C   s6   t j| d t jjd}|dr2|dd }|S )z(Convert the given path to a module name.r   r+   z	.__init__Ni)r4   r/   rE   rG   rH   rC   )r/   moduler   r   r   path_to_module   s    
r_   c             C   s   |dkr| }nv|s2t dd| | ||f  d}nR|d}|t|krjt dd| | |||f  d}nd|d|  | g }|S )z0Convert a relative import to an absolute import.r   z@Cannot resolve relative import "%s%s" in unknown module at %s:%dr+   zrelative.nomodulez@Cannot resolve relative import "%s%s" above module "%s" at %s:%dzrelative.abovelevelN)r   r7   r3   lenr2   )r$   levelr^   r/   rP   Zabsolute_namepartsr   r   r   relative_to_absolute   s    
rc   c               @   sD   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Ze	dd Z
dS )rR   z/AST visitor to find valid module_utils imports.c             C   s   || _ || _t | _|dr.tj |d }|drttj |d dddd }|dkrt|t	krt| 
|d d| _t jjrd	}xZ|D ]2\}}t|| j rt||| j }t|| _P qW nttj t jjj| j | _dS )
zReturn a list of module_utils imports found in the specified source file.
        :type path: str
        :type module_utils: set[str]
        z/__init__.pyr   zlib/ansible/module_utils/rX   r+   r   Nzansible.module_utils))z%^hacking/build_library/build_ansible/zbuild_ansible/)z^lib/ansible/zansible/)z5^test/lib/ansible_test/_data/sanity/validate-modules/zvalidate_modules/)z^test/units/ztest/units/)z!^test/lib/ansible_test/_internal/zansible_test/_internal/)zN^test/integration/targets/.*/ansible_collections/(?P<ns>[^/]*)/(?P<col>[^/]*)/z#ansible_collections/\g<ns>/\g<col>/)z%^test/integration/targets/.*/library/zansible/modules/)r/   r8   r   r;   rC   r4   r3   r   rG   r   
add_importr^   r   r?   
is_ansibleresearchsubr_   r2   rA   	directory)selfr/   r8   r   Zpath_mappatternreplacementZrevised_pathr   r   r   __init__   s$    

 
	
zModuleUtilFinder.__init__c             C   s(   |  | | dd |jD |j dS )z(
        :type node: ast.Import
        c             S   s   g | ]
}|j qS r   )r$   )r   aliasr   r   r   r*   +  s    z1ModuleUtilFinder.visit_Import.<locals>.<listcomp>N)generic_visitadd_importsnamesrP   )rj   noder   r   r   visit_Import#  s    
zModuleUtilFinder.visit_Importc                s^   |  | |jsdS t|j|j| j| j|j  ds<dS |  fdd|jD |j dS )z,
        :type node: ast.ImportFrom
        NZansiblec                s   g | ]}d  |j f qS )z%s.%s)r$   )r   rn   )r^   r   r   r*   A  s    z5ModuleUtilFinder.visit_ImportFrom.<locals>.<listcomp>)	ro   r^   rc   ra   r/   rP   r   rp   rq   )rj   rr   r   )r^   r   visit_ImportFrom/  s    

z!ModuleUtilFinder.visit_ImportFromc             C   s   |}xd|  |rh|| jkrN|| jkrJtjd| j||f dd | j| dS d|ddd }qW t	| jt
 jjrdS td| j||f  dS )z@
        :type name: str
        :type line_number: int
        z%s:%d imports module_utils: %s   )r   Nr+   r,   z%%s:%d Invalid module_utils import: %s)is_module_util_namer8   r;   r   r   r/   r   r2   r3   r
   r   r?   Z	test_pathr7   )rj   r$   line_numberr   r   r   r   rd   C  s    

zModuleUtilFinder.add_importc             C   s(   x"|D ]}|  |r| || qW dS )z<Add the given import names if they are module_utils imports.N)rv   rd   )rj   rq   line_nor$   r   r   r   rp   [  s    

zModuleUtilFinder.add_importsc             C   s@   t  jjr| drdS t  jjr<| dt  jjj r<dS dS )zrReturn True if the given name is a module_util name for the content under test. External module_utils are ignored.zansible.module_utils.Tz,ansible_collections.%s.plugins.module_utils.F)r   r?   re   r   rA   r[   )r$   r   r   r   rv   a  s
     z$ModuleUtilFinder.is_module_util_nameN)__name__
__module____qualname____doc__rm   rs   rt   rd   rp   staticmethodrv   r   r   r   r   rR      s   1rR   )F) r|   
__future__r   r   r   type__metaclass__rM   r4   rf    r   tior   utilr   r	   r
   datar   r   r   r>   rJ   r-   r.   r   r_   rc   ZNodeVisitorrR   r   r   r   r   <module>   s&   e

