
    VhRD                       U d 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	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mZmZmZmZmZmZmZmZmZmZmZm Z m!Z!m"Z"m#Z#m$Z$m%Z% ddl&m'Z'm(Z(m)Z)m*Z* dd	l+m,Z, dd
l-m.Z. ddl/m0Z0m1Z1 i Z2de3d<    G d d      Z4 G d d      Z5 G d d      Z6e6jo                           G d d      Z8d(dZ9d)dZ:d*dZ;d+d,dZ<d+d,dZ=ej|                  d-d       Z?	 	 d.	 	 	 	 	 	 	 	 	 	 	 d/dZ@d0dZAed1d       ZBd2dZCd3d ZDd4d5d!ZEd6d"ZFd7d#ZG	 	 	 d8	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d9d$ZH	 	 	 	 	 	 	 	 	 	 	 d:	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d;d%ZId<d=d&ZJd>d?d'ZKy)@z1Common utility code that depends on CommonConfig.    )annotationsN   )ANSIBLE_BIN_SYMLINK_MAP)to_bytes)cachedisplayget_ansible_versionremove_treeMODE_DIRECTORYMODE_FILE_EXECUTE	MODE_FILEOutputStreamPYTHON_PATHSraw_commandANSIBLE_TEST_DATA_ROOTANSIBLE_TEST_TARGET_ROOTANSIBLE_TEST_TARGET_TOOLS_ROOTApplicationErrorSubprocessErrorgenerate_nameverified_chmod)	make_dirsread_text_filewrite_text_filewrite_json_file)data_context)LayoutMessages)PythonConfigVirtualPythonConfigzdict[str, t.Any]CHECK_YAML_VERSIONSc                  b    e Zd ZU dZg Zded<   edd       Zeej                  dd              Z
y)	ExitHandlerz#Simple exit handler implementation.z<list[tuple[t.Callable, tuple[t.Any, ...], dict[str, t.Any]]]
_callbacksc                H    t         j                  j                  | ||f       y)zYRegister the given function and args as a callback to execute during program termination.N)r"   r#   append)funcargskwargss      R/home/dcms/DCMS/lib/python3.12/site-packages/ansible_test/_internal/util_common.pyregisterzExitHandler.registerF   s     	%%tT6&:;    c               #    K   d} 	 d t        t        j                        }|r |j                         \  }}}	  ||i | |r | r| y# t        $ r$}|} t        j                  d|        Y d}~0d}~ww xY w# t        t        j                        }|rQ|j                         \  }}}	  ||i | n0# t        $ r$}|} t        j                  d|        Y d}~nd}~ww xY w|rQ| r| w xY ww)z7Run all registered handlers when the context is exited.NzExit handler failed: )listr"   r#   popBaseExceptionr   fatal)last_exceptionqueuer&   r'   r(   exs         r)   contextzExitHandler.contextK   s     04	%//0E%*YY["dF@$)&)	  $$ 	 % @%'NMM$9"">??@ //0E%*YY["dF@$)&)$ @%'NMM$9"">??@  $$ sz   C/A: /C/A
 C/C/
	A7A2-C/2A77C/:0C,+B43C,4	C!=CC,C!!C,'C,,C/N)r&   z
t.CallablereturnNone)r5   zt.Generator[None, None, None])__name__
__module____qualname____doc__r#   __annotations__staticmethodr*   
contextlibcontextmanagerr4    r+   r)   r"   r"   B   sD    -OQJLQ< < %  %r+   r"   c                  2    e Zd ZdZddZddZedd       Zy)	ShellScriptTemplatez1A simple substitution template for shell scripts.c                    || _         y N)template)selfrD   s     r)   __init__zShellScriptTemplate.__init__f   s	     r+   c                     t         fd|j                         D              t        j                  d      }|j	                  fd j
                        }|S )z3Return a string templated with the given arguments.c              3  J   K   | ]  \  }}|j                  |      f  y wrC   )quote).0kvrE   s      r)   	<genexpr>z1ShellScriptTemplate.substitute.<locals>.<genexpr>k   s"     A$!QAtzz!}%As    #z#{(?P<name>[^}]+)}c                ,    | j                  d         S )Nname)group)matchkvps    r)   <lambda>z0ShellScriptTemplate.substitute.<locals>.<lambda>m   s    #ekk&.A*B r+   )dictitemsrecompilesubrD   )rE   r(   patternvaluerR   s   `   @r)   
substitutezShellScriptTemplate.substitutei   sB    A&,,.AA**23BDMMRr+   c                    t        | t              r$t        j                  dj	                  |             S t        j                  |       S )z1Return a shell quoted version of the given value. )
isinstancer-   shlexrI   join)rZ   s    r)   rI   zShellScriptTemplate.quotep   s4     eT";;sxx//{{5!!r+   N)rD   strr5   r6   )r(   t.Union[str, list[str]]r5   ra   )rZ   rb   r5   ra   )r7   r8   r9   r:   rF   r[   r<   rI   r?   r+   r)   rA   rA   c   s#    ;! " "r+   rA   c                      e Zd ZU dZdZd ed<   dZd ed<   dZd ed<   dZd ed<   dZ	d ed<   dZ
d ed<   dZd ed	<   edd
       ZddZedd       Zedd       ZddZy)
ResultTypezTest result type.NBOTCOVERAGEDATAJUNITLOGSREPORTSTMPc                    t        d      t         _        t        d      t         _        t        d      t         _        t        d      t         _        t        d      t         _        t        d      t         _        t        d      t         _        y )Nbotcoveragedatajunitlogsreportsz.tmp)rd   re   rf   rg   rh   ri   rj   rk   r?   r+   r)   	_populatezResultType._populate   sY    #E*
(4
$V,
%g.
$V,
'	2
#F+
r+   c                    || _         y rC   rO   )rE   rO   s     r)   rF   zResultType.__init__   s	    	r+   c                    t         j                  j                  t               j                  j
                  | j                        S )z)The content relative path to the results.)ospathr`   r   contentresults_pathrO   rE   s    r)   relative_pathzResultType.relative_path   s+     ww||LN22??KKr+   c                    t         j                  j                  t               j                  j
                  | j                        S )z!The absolute path to the results.)rw   rx   r`   r   ry   rootr|   r{   s    r)   rx   zResultType.path   s-     ww||LN22779K9KLLr+   c                    | j                   S rC   ru   r{   s    r)   __str__zResultType.__str__   s    yyr+   r5   r6   )rO   ra   r5   r6   r5   ra   )r7   r8   r9   r:   re   r;   rf   rg   rh   ri   rj   rk   r<   rs   rF   propertyr|   rx   r   r?   r+   r)   rd   rd   y   s    CHjD*E:D*GZC, , L L M Mr+   rd   c                       e Zd ZdZddZddZy)CommonConfigz%Configuration common to all commands.c                B   || _         d| _        d| _        d | _        |j                  | _        |j
                  | _        |j                  | _        |j                  | _        |j                  | _        |j                  | _	        d| _
        t               | _        i | _        y )NFT)commandinteractivecheck_layoutsuccesscolorexplain	verbositydebugtruncateredactdisplay_stderrr   session_namer   )rE   r'   r   s      r)   rF   zCommonConfig.__init__   s{      )-::
!\\"nn::
!]] KK$))O')
r+   c                J    t         j                  j                  t        d      S )z;Return the path to the Ansible config for the given config.zansible.cfg)rw   rx   r`   r   r{   s    r)   get_ansible_configzCommonConfig.get_ansible_config   s    ww||2MBBr+   N)r'   zt.Anyr   ra   r5   r6   r   )r7   r8   r9   r:   rF   r   r?   r+   r)   r   r      s    /*&Cr+   r   c                   d}| j                  |      st        d|  d|       t               }t        j                  d|      r;dj                  |j                  d      dd       }d| d	}| j                  ||      } | S )
a<  
    Return the given docs.ansible.com URL updated to match the running ansible-test version, if it is not a pre-release version.
    The URL should be in the form: https://docs.ansible.com/ansible/devel/path/to/doc.html
    Where 'devel' will be replaced with the current version, unless it is a pre-release version.
    When run under a pre-release version, the URL will remain unchanged.
    This serves to provide a fallback URL for pre-release versions.
    It also makes searching the source for docs links easier, since a full URL is provided to this function.
    z,https://docs.ansible.com/ansible-core/devel/zURL "z" does not start with: z	^[0-9.]+$.N   z&https://docs.ansible.com/ansible-core//)
startswith
ValueErrorr	   rV   searchr`   splitreplace)url
url_prefixansible_versionurl_version
new_prefixs        r)   get_docs_urlr      s     @J>>*%5%<ZLIJJ)+O	yy/hh44S9"1=>=k]!L
kk*j1Jr+   c                    | j                   ryt        t        j                  j                         t        t        j
                  j                         y)zCreate result directories.N)r   r   rd   rf   rx   rg   )r'   s    r)   create_result_directoriesr      s2    ||j!!&&'joo""#r+   c                   | sy| j                   D ]  }t        j                   |d        | j                  D ]  }t        j                  |        | j                  r$t	        dj                  | j                              y)z"Display the given layout messages.Nr   r   
)infor   warningerrorr   r`   )messagesmessages     r)   handle_layout_messagesr      sr    == +W*+ ## ! ! ~~tyy899 r+   c                f   | j                   r\t        j                  j                  t	        j
                         |xs t	        j                          t                |xs d       S t	        j                  ||      \  }t        j                  |       t        j                  fd       S )z^Return the path to a temporary file that will be automatically removed when the process exits. prefixsuffixc                 .    t        j                         S rC   )rw   removerx   s   r)   rS   z/process_scoped_temporary_file.<locals>.<lambda>   s    RYYt_ r+   )r   rw   rx   r`   tempfile
gettempdirgettempprefixr   mkstempcloser"   r*   )r'   r   r   temp_fdrx   s       @r)   process_scoped_temporary_filer      s    ||ww||H//1f6X@V@V@X5YZgZiYjkqkwuwjx3yz K	 !((vF
45Kr+   c                6   | j                   r\t        j                  j                  t	        j
                         |xs t	        j                          t                |xs d       S t	        j                  ||      t        j                  fd       S )zcReturn the path to a temporary directory that will be automatically removed when the process exits.r   r   c                     t               S rC   r
   r   s   r)   rS   z4process_scoped_temporary_directory.<locals>.<lambda>  s    [%6 r+   )r   rw   rx   r`   r   r   r   r   mkdtempr"   r*   )r'   r   r   rx   s      @r)   "process_scoped_temporary_directoryr      s    ||ww||H//1f6X@V@V@X5YZgZiYjkqkwuwjx3yz
 K vf=67Kr+   c              #  T  K   | j                   r,t        j                  j                  |xs d|d|       yt	        j
                  |||      5 }|j                  t        |             |j                          	 |j                   	 ddd       y# w xY w# 1 sw Y   yxY ww)z+Context manager for a named temporary file./tmptempr   r   dirN)
r   rw   rx   r`   r   NamedTemporaryFilewriter   flushrO   )r'   r   r   	directoryry   tempfile_fds         r)   named_temporary_filer     s      ||ggll9.ff0MNN((v9U 	Ydhw/0!&&&	 	 	 	s0   AB(+B?B	B(BBB%!B(c                x    t         j                  j                  | j                  |      }t        ||d||       y)z`Write the given json content to the specified test results path, creating directories as needed.T)create_directories	formattedencoderN)rw   rx   r`   r   )categoryrO   ry   r   r   rx   s         r)   write_json_test_resultsr     s-     77<<t,DD'diY`ar+   c                t    t         j                  j                  | j                  |      }t        ||d       y)z`Write the given text content to the specified test results path, creating directories as needed.T)r   N)rw   rx   r`   r   )r   rO   ry   rx   s       r)   write_text_test_resultsr   "  s&    77<<t,DD'd;r+   c                   
 t        j                  ddd      
t        j                  d
 dd       t	        t        t              g d	z         } d
dt        fddt        ff}t        j                  j                  t        d      }| D ]6  }t        j                  d
t        j                  j                  
|             8 |D ]u  \  }}}t        j                  j                  ||      }t        j                  j                  
|      }t        |      }t        ||      }t!        ||       t#        ||       w t#        
t$               d
fd}	t&        j)                  |	       
S )zgReturn the path to a directory which contains a `python.py` executable and associated injector scripts.ansible-test-z	-injectorr   r   zInitializing "z&" as the temporary injector directory.r   r   )zimporter.pypytestzansible_connection_cli_stub.pyz	python.pyz/usr/bin/env pythonzvirtualenv.shz/usr/bin/env bashinjectorc                     t                y)z(Remove the temporary injector directory.Nr   )injector_paths   r)   cleanup_injectorz+get_injector_path.<locals>.cleanup_injectorK  s    M"r+   r   )r   r   r   r   sortedr-   r   r   r   rw   rx   r`   r   symlinkr   set_shebangr   r   r   r"   r*   )injector_namesscriptssource_pathrO   shebangmodesrcdstscriptr   r   s             @r)   get_injector_pathr   (  sF    $$OKU[\MLL>-0VWcdeD!89 =  N 
+->?	-y9G
 '',,7DK C


;]D ABC  ' "gtggll;-ggll=$/$VW-V$sD!" =.1# )*r+   c                    d}||z   }|ddf}| j                         t        fd|D              r|d<   nj                  d|       dj                        } | S )zKReturn the given script with the specified executable used for the shebang.z#!z# auto-shebangz# shellcheck shell=c              3  F   K   | ]  }d    j                  |        yw)r   N)r   )rJ   rZ   liness     r)   rM   zset_shebang.<locals>.<genexpr>a  s      
=%58u%
=s   !r   r   )
splitlinesanyinsertr`   )r   
executabler   r   	overwriter   s        @r)   r   r   T  sl    Fz!G 	I E

=9
==aQ YYuFMr+   c                x   t        j                  |       }|r|S d}d}d}t        j                  |||      }t        j
                  j                  |d      }t        j                  d|d| dd	
       t        | |       t        |t               t         st        j                  t               |t         | <   |S )zhReturn the path to a directory which contains a `python` executable that runs the specified interpreter.zpython-z-ansibler   r   pythonzInjecting "z" as a execv wrapper for the "z" interpreter.r   r   )r   getr   r   rw   rx   r`   r   r   create_interpreter_wrapperr   r   r"   r*   cleanup_python_paths)interpreterpython_pathr   r   root_temp_dirinjected_interpreters         r)   get_python_pathr   k  s    "";/KFFM""&]SK77<<X> LLRfhst  AB  C{,@A;/12 +Lr+   c                z    t        j                  | xs d|xs d|      }t        j                  t        |       |S )zKCreate a temporary directory that persists until the current process exits.tmpr   r   )r   r   r"   r*   r
   )r   r   base_dir	temp_paths       r)   create_temp_dirr     s5      %"RZ[Ii0r+   c                    t         j                  }t        j                  d|d| d      j	                         }t        ||       t        |t               y)zHCreate a wrapper for the given Python interpreter at the specified path.z
    #!zk

    from __future__ import annotations

    from os import execv
    from sys import argv

    python = 'z.'

    execv(python, [python] + argv[1:])
    N)sysr   textwrapdedentlstripr   r   r   )r   r   shebang_interpretercodes       r)   r   r     sM     ..?? - . /5fh 	 ($/'):;r+   c                     t        t        j                               D ]'  } t        j                  d| z  d       t        |        ) y)z*Clean up all temporary python directories.z*Cleaning up temporary python directory: %sr   r   N)r   r   valuesr   r   r
   r   s    r)   r   r     s<    |**,- ADHTUVDr+   c           	        |j                         }t        |      }t               }t        |t              r*t
        j                  j                  |j                        }	nt        |j                        }	t
        j                  j                  j                  ||	|d   g      |d<   |j                  |d<   |j                  |d<   t        | ||||||      S )a9  
    Run a command while intercepting invocations of Python to control the version used.
    If the specified Python is an ansible-test managed virtual environment, it will be added to PATH to activate it.
    Otherwise a temporary directory will be created to ensure the correct Python can be found in PATH.
    PATHANSIBLE_TEST_PYTHON_VERSIONANSIBLE_TEST_PYTHON_INTERPRETER)captureenvro   cwdalways)copyr-   r   r^   r   rw   rx   dirnamer   pathsepr`   versionrun_command)
r'   r   cmdr  r  ro   r  r  inject_pathr   s
             r)   intercept_pythonr    s     ((*C
s)C#%K &-.ggoofkk2%fkk2''//&&[#f+'NOCK)/C%&-3[[C)*tS's3W]^^r+   c                T    | j                   xr | }t        |||||||||	|
|||      S )zBRun the specified command and return stdout and stderr as a tuple.)r  r  ro   r  r   stdinstdoutr   output_streamcmd_verbosity
str_errorserror_callback)r   r   )r'   r  r  r  ro   r  r  r  r  r   r  r  r  r  r   s                  r)   r  r    sH    " ll)6zG##% r+   c                    t        | j                  t        j                  j                  t        d      gd|      d   }|ryt        j                  |      }|d   sy|d   S )z]Return True if PyYAML has libyaml support, False if it does not and None if it was not found.zyamlcheck.pyT)r  r   r   Nyamlcloader)r   rx   rw   r`   r   jsonloads)r   r   r  results       r)   	yamlcheckr&    si    &++rww||4RTb'cdnr  }D  E  FG  HFZZF&>)r+   c                ,   	 t         | j                     S # t        $ r Y nw xY wt        |       }||r|t         | j                  <   |sL|&|r"t	        j
                  d| j                  z         |S |s"t	        j
                  d| j                  z         |S )z
    Return True if PyYAML has libyaml support, False if it does not and None if it was not found.
    The result is cached if True or required.
    z+PyYAML is not installed for interpreter: %szSPyYAML will be slow due to installation without libyaml support for interpreter: %s)r    rx   KeyErrorr&  r   r   )r   requiredquietstates       r)   check_pyyamlr,    s    
"6;;//  fEH ,1FKK(= MPVP[P[ [\ L OOqtztt  ALs    	!!)r   ra   r5   ra   )r'   r   r5   r6   )r   zt.Optional[LayoutMessages]r5   r6   )r   N)r'   r   r   t.Optional[str]r   r-  r5   ra   )r'   r   r   ra   r   ra   r   r-  ry   ra   r5   zc.Iterator[str])TN)r   rd   rO   ra   ry   z&t.Union[list[t.Any], dict[str, t.Any]]r   boolr   z$t.Optional[t.Type[json.JSONEncoder]]r5   r6   )r   rd   rO   ra   ry   ra   r5   r6   r   )r   ra   r   ra   r5   ra   )r   ra   r5   ra   )NNN)r   r-  r   r-  r   r-  r5   ra   )r   ra   r   ra   r5   r6   r   )NNF)r'   r   r   r   r  z	list[str]r  zdict[str, str]r  r.  ro   r-  r  r-  r  r.  r5   'tuple[t.Optional[str], t.Optional[str]])NNNFNNFNr   strictN)r'   r   r  zc.Iterable[str]r  r.  r  zt.Optional[dict[str, str]]ro   r-  r  r-  r  r.  r  t.Optional[t.IO[bytes]]r  r1  r   r.  r  zt.Optional[OutputStream]r  intr  ra   r  z/t.Optional[c.Callable[[SubprocessError], None]]r5   r/  )F)r   r   r   r.  r5   t.Optional[bool])TF)r   r   r)  r.  r*  r.  r5   r3  )Lr:   
__future__r   collections.abcabccr=   r#  rw   rV   r_   r  r   r  typingt	constantsr   encodingr   utilr   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   ior   r   r   r   ro   r   provider.layoutr   host_configsr   r   r    r;   r"   rA   rd   rs   r   r   r   r   r   r   r>   r   r   r   r   r   r   r   r   r   r  r  r&  r,  r?   r+   r)   <module>r@     s   7 "    	 	  
       ( 
 )+ % *% %B" ",# #N    C C62$:	  & 48	b	b
	b 4	b 		b
 2	b 
	b< ( (V."J<0 !_
__ 
_ 
	_
 _ _ 
_ _ -_H '+ %)&*.2FJ 
 	    
$	 
   
    #  $    ,      D  - Fr+   