
    ci              
          d Z dZddlZddlZddl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m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 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)Z) e+       Z,ejZ                  j]                  d      rdZ/dZ0ndZ/dZ0	  ed      Z1 e       Z2e3ji                  ejj                  d      Z6 G d de7      Z8 G d de+      Z9	 dDde:dee   de;de!fdZ<	 dDdee:   dee   de;dee!   fdZ=dEd Z>de:d!ee:   de?fd"Z@de:de:fd#ZA e(d$      dFd%e:d&eee:e:f      dee:   fd'       ZB	 dGd(ee:   dee   d)ee:   dee:   fd*ZCdFde:d)ee:   de:fd+ZDe%fd,e:d-e:d.ee:   dee:   fd/ZEe%fd-e:d.ee:   dee:   fd0ZFdHd%e:d1e?de:fd2ZGd3 ZHd4 ZId5 ZJe1ffd6e:d7eee:   ee:   f   de?fd8ZKd6e:d9e:de?fd:ZL	 dGd(ee:   dee   d;ee:   dee:   fd<ZMd(ee:   d=ee:ee#   f   dee+e:e:f   fd>ZN	 ddlOZOd6e:de?fd?ZP	 dFd(ee:   deee:      deee;e+f   ee:   f   fd@ZQd%e:de?fdAZRdBe:dee:   fdCZSy# e*$ r dZ)Y w xY w# e$ r dZ1Y w xY w# e*$ r dZOY cw xY w)IaJ  Python modules manipulation utility functions.

:type PY_SOURCE_EXTS: tuple(str)
:var PY_SOURCE_EXTS: list of possible python source file extension

:type STD_LIB_DIR: str
:var STD_LIB_DIR: directory where standard modules are located

:type BUILTIN_MODULES: dict
:var BUILTIN_MODULES: dictionary with builtin module names as key
zrestructuredtext en    N)	splitextjoinabspathisdirdirnameexists
expandusernormcaserealpath)find_moduleload_module	C_BUILTINPY_COMPILEDPKG_DIRECTORY)get_python_lib)DistutilsPlatformError)DictListOptionalAnyTupleUnionSequence)
ModuleType)
FileFinder)STD_BLACKLIST_handle_blacklist)callable_deprecatedwin)pypyw)dllpyd)r    )soT)standard_libz//c                       e Zd ZdZy)NoSourceFilezaexception raised when we are not able to get a python
    source file for a precompiled file
    N)__name__
__module____qualname____doc__     9/usr/lib/python3/dist-packages/logilab/common/modutils.pyr'   r'   Y   s    r-   r'   c                   4     e Zd ZdZd Zd Z fdZd Z xZS )
LazyObjectak  
    This class allows to lazyly declare a object (most likely only a callable
    according to the code) from a module without importing it.

    The import will be triggered when the user tries to access attributes of
    the object/callable or call it.

    Trying to set or delete attributes of the wrapped object/callable will not
    works as expected.
    c                 .    || _         || _        d | _        y N)moduleobj	_imported)selfr3   r4   s      r.   __init__zLazyObject.__init__k   s    r-   c                     | j                   .t        t        | j                        | j                        | _         | j                   S r2   )r5   getattrload_module_from_namer3   r4   )r6   s    r.   _getobjzLazyObject._getobjp   s2    >>!$%:4;;%GRDN~~r-   c                 ~    	 t         t        |   |      S # t        $ r t	        | j                         |      cY S w xY wr2   )superr0   __getattribute__AttributeErrorr9   r;   )r6   attr	__class__s     r.   r>   zLazyObject.__getattribute__u   s<    	1T;DAA 	14<<>400	1s    #<<c                 .     | j                         |i |S r2   )r;   )r6   argskwargss      r.   __call__zLazyObject.__call__{   s    t||~t.v..r-   )	r(   r)   r*   r+   r7   r;   r>   rE   __classcell__)rA   s   @r.   r0   r0   _   s    	

1/r-   r0   dotted_namepathuse_sysreturnc                 ^    t        | j                  d      ||      }|t        d| z        |S )a"  Load a Python module from its name.

    :type dotted_name: str
    :param dotted_name: python name of a module or package

    :type path: list or None
    :param path:
      optional list of path where the module or package should be
      searched (use sys.path if nothing or None is given)

    :type use_sys: bool
    :param use_sys:
      boolean indicating whether the sys.modules dictionary should be
      used or not


    :raise ImportError: if the module or package is not found

    :rtype: module
    :return: the loaded module
    .zmodule %s doesn't exist)load_module_from_modpathsplitImportError)rG   rH   rI   r3   s       r.   r:   r:      s8    0 &k&7&7&<dGLF~3kABBMr-   partsc           
         |r#	 t         j                  dj                  |          S g }d}| D ]-  }|j	                  |       dj                  |      }d}t        |      t        |       k7  r t         j                  j                  |      }n!|rt         j                  j                  |      }|2t        ||      \  }}	}
	 t        |||	|
      }||j                          	 |rt        |||       t        |dd      }|}|st        |      r|s@t        |      t        |       k7  r)t        ddj                  | t        |      d       z        t        |      g}0 S # t        $ r Y Ew xY w# ||j                          w w xY w)aB  Load a python module from its splitted name.

    :type parts: list(str) or tuple(str)
    :param parts:
      python name of a module or package splitted on '.'

    :type path: list or None
    :param path:
      optional list of path where the module or package should be
      searched (use sys.path if nothing or None is given)

    :type use_sys: bool
    :param use_sys:
      boolean indicating whether the sys.modules dictionary should be used or not

    :raise ImportError: if the module or package is not found

    :rtype: module
    :return: the loaded module
    rL   N__file__ zno module in %s)sysmodulesr   KeyErrorappendlengetr   r   closesetattrr9   _is_namespacerO   r   )rP   rH   rI   modpath
prevmodulepartcurnamer3   mp_filemp_filenamemp_desc_files               r.   rM   rM      sw   . 	;;sxx// GJ  t((7#w<3u:%[[__W-F[[__W-F>,7d,C)G['$ %Wg{GL&MMOJf-
B/
w/WU3/#((5W;P2QQRR9 : MC  		, &MMO 's   !E ?E/	E,+E,/Fc                 4    t        | |      }t        |||      S )a  Load a Python module from it's path.

    :type filepath: str
    :param filepath: path to the python module or package

    :type path: list or None
    :param path:
      optional list of path where the module or package should be
      searched (use sys.path if nothing or None is given)

    :type use_sys: bool
    :param use_sys:
      boolean indicating whether the sys.modules dictionary should be
      used or not


    :raise ImportError: if the module or package is not found

    :rtype: module
    :return: the loaded module
    )modpath_from_filerM   )filepathrH   rI   	extrapathr]   s        r.   load_module_from_fileri      s    ,  )4G#GT7;;r-   mod_pathc                     g }|D ]G  }|j                  |       t        | |      } t        dj                  |            r;t        |       rG y y)z2check there are some __init__.py all along the wayrL   FT)rW   r   r\   	_has_init)rH   rj   r]   r_   s       r.   _check_initrm      sM    G tD$SXXg./	$	
 r-   c                 *    t        t        |             S r2   )r   r	   )rH   s    r.   _canonicalize_pathro      s    Jt$%%r-   z*you should avoid using modpath_from_file()filenamerh   c                 B   t        |       } t        j                  j                  |       d   }|t	        t         |      D ]  }t        |      }|st        |dt        |             t        |      k(  s5|t        |      d j                  t        j                        D cg c]  }|s|	 }}t        ||dd       s||   j                  d      |z   c S  t	        t         t        j                        D ]n  }|st        |      j                  |      s!|t        |      d j                  t        j                        D cg c]  }|s|	 }}t        ||dd       sl|c S  t        d| ddj                  t        j                              c c}w c c}w )a  DEPRECATED: doens't play well with symlinks and sys.meta_path

    Given a file path return the corresponding splitted module's name
    (i.e name of a module or package splitted on '.')

    :type filename: str
    :param filename: file's path for which we want the module's name

    :type extrapath: dict
    :param extrapath:
      optional extra search path, with path as key and package name for the path
      as value. This is usually useful to handle package splitted in multiple
      directories using __path__ trick.


    :raise ImportError:
      if the corresponding module's name has not been found

    :rtype: list(str)
    :return: the corresponding splitted module's name
    r   NrL   zUnable to find module for  in z, 
)ro   osrH   r   mapr   r
   rX   rN   seprm   rT   
startswithrO   r   )rp   rh   basepath_rH   pkg
submodpathr]   s           r.   rf   rf     sk   . "(+H77H%a(D+Y7 	DE5>DkD	!23x~E-1#d)+->-D-DRVV-LTcPScT
TtZ_5$U+11#6CC	D &1 HTN--d3&*3t9;&7&=&=bff&EMssMGM4".	 hTWT\T\H]^
__ U Ns   "F*FFFr]   context_filec                     |t        |      }n|}| d   dk(  r	 t        dg| dd z   ||      S | ddgk(  rt        j                  j
                  S t        | ||      S # t        $ r t        | ||      cY S w xY w)a  given a mod path (i.e. splitted module / package name), return the
    corresponding file, giving priority to source file over precompiled
    file if it exists

    :type modpath: list or tuple
    :param modpath:
      splitted module's name (i.e name of a module or package splitted
      on '.')
      (this means explicit relative imports that start with dots have
      empty strings in this list!)

    :type path: list or None
    :param path:
      optional list of path where the module or package should be
      searched (use sys.path if nothing or None is given)

    :type context_file: str or None
    :param context_file:
      context file to consider, necessary if the identifier has been
      introduced using a relative import unresolvable in the actual
      context (i.e. modutils)

    :raise ImportError: if there is no such module in the directory

    :rtype: str or None
    :return:
      the path to the module's file or None if it's an integrated
      builtin module such as 'sys'
    Nr   xml_xmlplus   rt   rH   )r   _file_from_modpathrO   rt   rH   rR   )r]   rH   r|   contexts       r.   file_from_modpathr   /  s    B ,'qzU	>%zlWQR[&@$PP 
T6N	"wwgtW55  	>%gtW==	>s   A A54A5c           	         | j                  d      ry| j                  d      }|)|d   t        v rt        |      dkD  rt	        |       |d   S d}d}|d   dk(  r|J d       g }d}||   dk(  r|dz  }|J t        |      }||   dk(  rt        |t        |            D ]  }	 t        |||dz    ||	        | S # t        $ r5 |t        dt        |      dz
        k\  s dj                  |d|       cY c S w xY w)
a  given a dotted name return the module part of the name :

    >>> get_module_part('logilab.common.modutils.get_module_part')
    'logilab.common.modutils'

    :type dotted_name: str
    :param dotted_name: full name of the identifier we are interested in

    :type context_file: str or None
    :param context_file:
      context file to consider, necessary if the identifier has been
      introduced using a relative import unresolvable in the actual
      context (i.e. modutils)


    :raise ImportError: if there is no such module in the directory

    :rtype: str or None
    :return:
      the module part of the name or None if we have not been able at
      all to import the given name

    XXX: deprecated, since it doesn't handle package precedence over module
    (see #10066)
    zos.pathrL   Nr      rS   z.explicit relative import, but no context_file?r   )rH   r|   )
rw   rN   BUILTIN_MODULESrX   rO   r   ranger   maxr   )rG   r|   rP   rH   startiis         r.   get_module_partr   `  sB   6 i(c"E 8&5zA~!+..8ODFQx2~'Y)YY'
-2
!'''|, -2
 63u:& '	'eFQU3$\Z' 	  	'As5zA~..88E"1I&&	's   &B>>9C<;C<packagesrc_directory	blacklistc                    g }t        j                  |      D ]  \  }}}t        |||       d|vrd|dd ||k7  r@|t        |      d j	                  t         j
                  d      }|j                  | |z          |D ]`  }t        |      s|dk7  st        ||      }	| |	t        |      d z   }
|j                  |
j	                  t         j
                  d             b  |S )aS  given a package directory return a list of all available python
    modules in the package and its subpackages

    :type package: str
    :param package: the python name for the package

    :type src_directory: str
    :param src_directory:
      path of the directory corresponding to the package

    :type blacklist: list or tuple
    :param blacklist:
      optional list of files or directory to ignore, default to
      the value of `logilab.common.STD_BLACKLIST`

    :rtype: list
    :return:
      the list of all available python modules in the package and its
      subpackages
    __init__.pyr,   NrL   )	rt   walkr   rX   replacerv   rW   _is_python_filer   )r   r   r   rU   	directorydirnames	filenamesdir_packagerp   srcr3   s              r.   get_modulesr     s    . G*,''-*@ <&	8Y)Xy9	)HQK%#C$6$89AA"&&#NKNN7[01! 	<Hx(X-F9h/ 3s='9B#??v~~bffc:;		<< Nr-   c                     g }t        j                  |       D ]M  \  }}}t        |||       d|vrd|dd |D ]+  }t        |      st	        ||      }|j                  |       - O |S )a  given a package directory return a list of all available python
    module's files in the package and its subpackages

    :type src_directory: str
    :param src_directory:
      path of the directory corresponding to the package

    :type blacklist: list or tuple
    :param blacklist:
      optional list of files or directory to ignore, default to the value of
      `logilab.common.STD_BLACKLIST`

    :rtype: list
    :return:
      the list of all available python module's files in the package and
      its subpackages
    r   r,   N)rt   r   r   r   r   rW   )r   r   filesr   r   r   rp   r   s           r.   get_module_filesr     s~    $ E*,''-*@ 	"&	8Y)Xy9	)HQK! 	"Hx(9h/S!	"	" Lr-   include_no_extc                     t        t        |             \  }}t        D ]  }|d|}t        |      s|c S  |r|st        |      r|S t	        |       )a  given a python module's file name return the matching source file
    name (the filename will be returned identically if it's a already an
    absolute path to a python source file...)

    :type filename: str
    :param filename: python module's file name


    :raise NoSourceFile: if no source file exists on the file system

    :rtype: str
    :return: the absolute path of the source file if it exists
    rL   )r   r   PY_SOURCE_EXTSr   r'   )rp   r   rx   orig_extextsource_paths         r.   get_source_filer     s[     gh/0ND( !%s++ h6$<
x
  r-   c                     g }t        t        j                  j                               D ]Q  \  }}t	        |dd      }|s| D ]7  }|j                  |      s|j                  |       t        j                  |=  Q S |S )z5remove submodules of `directories` from `sys.modules`rR   N)listrT   rU   itemsr9   rw   rW   )directoriescleanedmodnamer3   modfiler   s         r.   cleanup_sys_modulesr     s|    G 1 1 34 &*d3( 	%%i0NN7+G,	 Nr-   c                     t               }t        t        j                        D ]>  }| D ]7  }|j	                  |      st        j                  |= |j                  |        > @ |S )zDremove submodules starting with name from `names` from `sys.modules`)setr   rT   rU   rw   add)namesr   r   names       r.   clean_sys_modulesr   	  s`    eG$  	D!!$'KK(G$		 Nr-   c                 0    t        |       d   dd t        v S )zN
    rtype: bool
    return: True if the filename is a python source file
    r   N)r   r   )rp   s    r.   is_python_sourcer     s     
 Ha $66r-   r   std_pathc                    | j                  d      d   } 	 t        | g      }|t        |        S t	        |      }|j                  t              ry|D ]  }|j                  t	        |            s y y# t        $ r Y yw xY w)aO  try to guess if a module is a standard python module (by default,
    see `std_path` parameter's description)

    :type modname: str
    :param modname: name of the module we are interested in

    :type std_path: list(str) or tuple(str)
    :param std_path: list of path considered as standard


    :rtype: bool
    :return:
      true if the module:
      - is located on the path listed in one of the directory in `std_path`
      - is a built-in module

    Note: this function is known to return wrong values when inside virtualenv.
    See https://www.logilab.org/ticket/294756.
    rL   r   FT)rN   r   rO   r\   r   rw   EXT_LIB_DIR)r   r   rp   rH   s       r.   is_standard_moduler     s    , mmC #G$gY/  )))x H;' wt}-    s   A5 5	B B	from_filec                     t        |      st        |      }|t        j                  v ry	 t	        | j                  d      d   |g       y# t        $ r Y yw xY w)av  return true if the given module name is relative to the given
    file name

    :type modname: str
    :param modname: name of the module we are interested in

    :type from_file: str
    :param from_file:
      path of the module from which modname has been imported

    :rtype: bool
    :return:
      true if the module has been imported relatively to `from_file`
    FrL   r   T)r   r   rT   rH   r   rN   rO   )r   r   s     r.   is_relativer   H  sY     I&	CHHGMM#&q)I;7 s   A 	AAr   c                 @   t        |       dkD  sJ |	 t        | |g      \  }}nt        | |      \  }}|t        k(  r	 |J t	        |      S |t        k(  ry|t        k(  r|J t        |      }|S # t        $ r t        | |      \  }}Y Ww xY w# t
        $ r |cY S w xY w)zgiven a mod path (i.e. splitted module / package name), return the
    corresponding file

    this function is used internally, see `file_from_modpath`'s
    documentation for more information
    r   N)	rX   _module_filerO   r   r   r'   r   r   rl   )r]   rH   r   mtyperb   s        r.   r   r   e  s     w<!	=!-gy!AE; *'48{	***";// 
)		-	&&&,!  	=!-gt!<E;	=  		s"   A1 B 1BBBBpicc           	      x   |j                         D ]  \  }}|	|j                  | d         s|j                  dj                  |             s&t        ddj                  | dd        d|d|       t        t        |      dz   dj                  |       z   |fc S  t        ddj                  |       z        )Nr   /zNo module named rL   r   rs   zNo module named %s)r   r   r   rO   ZIPFILEr   )r]   r   rg   importers       r.   _search_zipr     s     "iik V(##GAJ/++CHHW,=>%9<'!"+9NPXZab   1C 7#((7:K KXUUV *SXXg->>
??r-   c                 :    t         d uxr | t         j                  v S r2   )pkg_resources_namespace_packages)r   s    r.   r\   r\     s!     	T!Rg1R1R&Rr-   c           	      \   	 t         j                  }||nt         j                  }|D ]   }||vs	 t        j                  |      ||<   " d}t        | d         rV| d   t         j                  v rAt         j                  | j                  d         }t        |j                        }| st        dfS g }| r_| d   }	 t        ||      \  }	}
}|rj|
rhD cg c]  }t        |       }}	 |j                  t!        t        |
                  }t#        |       \  }}}||j                  |      kD  r||fS 	 d}	 |j)                  | j                  d             |d   }| r|t*        k7  r/t'        ddj-                  |       ddj-                  |            	 t/        t-        |
d	            5 }|j1                  d
      }ddd       dv rBd|v r>t         j                  D cg c]$  }t3        t-        |g|       st-        |g| & }}n|
g}	 | r_
fS # t        j
                  $ r	 d||<   Y w xY w# t        $ r d}Y w xY wc c}w # t$        $ r Y &t&        $ r Y 1w xY w# t&        $ r |rt#        |       dd cY S  w xY w# 1 sw Y   xY wc c}w # t4        $ r |
g}Y w xY w)a  get a module type / file path

    :type modpath: list or tuple
    :param modpath:
      splitted module's name (i.e name of a module or package splitted
      on '.'), with leading empty strings for explicit relative import

    :type path: list or None
    :param path:
      optional list of path where the module or package should be
      searched (use sys.path if nothing or None is given)


    :rtype: tuple(int, str)
    :return: the module type flag and the file path for a module
    NTFr   r   z
No module rL   rs   r   i   pkgutilextend_path)rT   path_importer_cacherH   	zipimportzipimporterZipImportErrorr?   r\   rU   popr   __path__r   r   r   indexr   r   
ValueErrorrO   rW   r   r   openreadr   IOError)r]   rH   r   _path__path	checkeggsr3   importedr   _rb   rc   xfullabspath	pathindexemtypeemp_filenamezippathr   streamdataps                         r.   r   r     s   (%%(chh 	'FS '"+"7"7"?CK	' 	 WQZ WQZ3;;%> W[[^,
 FOO$d?"H
!*	"&1'4&@#A{G [389awqz99
 + 1 1''+:N2O PI4?4M1FL' 5;;w#77%|33 8 "	A'
%!#((7:KSXXV^M_"`aa)${M:; -v!;;t,D-
 $$)> 9<^1E$qJ\S[J\D]D.X.^D^'=De f +O !// '"&CK'  	H : " " !  	"7C0!44	6- - _  %#}%s   -H8 H	H8 <I* I
)A
I J -J?J !J<JH51H8 4H55H8 8II	I'I'&I'*JJJJ J+*J+c                 8    dD ]  }| j                  |      s y y)zkreturn true if the given filename should be considered as a python file

    .pyc and .pyo are ignored
    )z.pyz.soz.pydz.pywTF)endswith)rp   r   s     r.   r   r     s)    
 . S! r-   r   c                 n    t        | d      }t        dz   D ]  }t        |dz   |z         s|dz   |z   c S  y)z\if the given directory has a valid __init__ file, return its path,
    else return None
    r7   )pycpyorL   N)r   r   r   )r   mod_or_packr   s      r.   rl   rl     sK     y*-K. ++#c)*$s**+ r-   )NT)NTNr2   )NN)F)Tr+   __docformat__rT   rt   os.pathr   r   r   r   r   r   r	   r
   r   impr   r   r   r   r   distutils.sysconfigr   distutils.errorsr   typingr   r   r   r   r   r   r   typesr   _frozen_importlib_externalr   logilab.commonr   r   logilab.common.deprecationr   r   rO   objectr   platformrw   r   PY_COMPILED_EXTSSTD_LIB_DIRr   dictfromkeysbuiltin_module_namesr   	Exceptionr'   r0   strintr:   rM   ri   boolrm   ro   rf   r   r   r   r   r   r   r   r   r   r   r   r   r   r\   r   r   rl   r,   r-   r.   <module>r      sC  $
 & 
 	
 
 
 P O . 3 D D D  1 ; : ( <<5!"N%N d3K -- 8 8$?9 / /B BF$SM;>> BF;9;$SM;;>;j;|<4c T#Y 4 &S &S & AB'` '`c3h0H '`TXY\T] '` C'`V SW.6#Y.6&sm.6BJ3-.6c].6b7 7HSM 7S 7v BO&&!$&19#&	#Y&R ER C HSM VZ[^V_ @!c !4 !C !0	7 =H>((!$s)U3Z"78(	(V   < NR#Y&sm=Ec]c]@@#Y@!#x
';";<@
63@3 4  59b#Yb&tCy1b
5fx},-bJc d 
 
# 
u   I0  KH   Ms6   *H: 	I 8I :IIIII I 