
    >                        S r SSKJr  SSKJr  SSKJr  SSKrSSKrSSKJr  SSK	J
r
  SSK	Jr  SS	K	Jr  SS
KJr  SSKJr  SSKrSSKJr  SrSS/rSrSrSr " S S\5      r " S S\5      r " S S\
R6                  5      r " S S\
R6                  5      r " S S\R<                  5      r " S S\ 5      r! " S S \ 5      r"S! r#S" r$ S&S$ jr%\S#\$S#S4S% jr&g)'a  Library for ignoring files for upload.

This library very closely mimics the semantics of Git's gitignore file:
https://git-scm.com/docs/gitignore

See `gcloud topic gcloudignore` for details.

A typical use would be:

  file_chooser = gcloudignore.GetFileChooserForDir(upload_directory)
  for f in file_chooser.GetIncludedFiles('some/path'):
    print 'uploading {}'.format(f)
    # actually do the upload, too
    )absolute_import)division)unicode_literalsN)glob)
exceptions)log)
propertiesencoding)files)mapz.gcloudignorez.git
.gitignorea  # This file specifies files that are *not* uploaded to Google Cloud
# using gcloud. It follows the same syntax as .gitignore, with the addition of
# "#!include" directives (which insert the entries of the given .gitignore-style
# file at that point).
#
# For more information, run:
#   $ gcloud topic gcloudignore
#
.gcloudignore
# If you would like to upload your .git directory, .gitignore file or files
# from your .gitignore file, remove the corresponding line
# below:
.git
.gitignore
/z(?<!\\)\\(\\\\)*$c                       \ rS rSrSrSrg)InternalParserErrorF   z)An internal error in ignore file parsing. N__name__
__module____qualname____firstlineno____doc____static_attributes__r       3lib/googlecloudsdk/command_lib/util/gcloudignore.pyr   r   F   s    1r   r   c                       \ rS rSrSrSrg)BadFileErrorJ   2Error indicating that a provided file was invalid.r   Nr   r   r   r   r   r   J       :r   r   c                       \ rS rSrSrSrg)BadIncludedFileErrorN   r    r   Nr   r   r   r   r#   r#   N   r!   r   r#   c                       \ rS rSrSrSrg)SymlinkLoopErrorR   z.Error indicating that there is a symlink loop.r   Nr   r   r   r   r&   r&   R   s    6r   r&   c                   $    \ rS rSrSrSrSrSrSrg)MatchV   zIndicates whether an ignore pattern matches or explicitly includes a path.

INCLUDE: path matches, and is included
IGNORE: path matches, and is ignored
NO_MATCH: file is not matched
         r   N)	r   r   r   r   r   INCLUDEIGNORENO_MATCHr   r   r   r   r)   r)   V   s     '&(r   r)   c                   <    \ rS rSrSrSS jrS	S jr\S 5       rSr	g)
Patternc   zAn ignore-file pattern.

Corresponds to one non-blank, non-comment line in the ignore-file.

See https://git-scm.com/docs/gitignore for full syntax specification.

If it matches a string, will return Match.IGNORE (or Match.INCLUDE if
negated).
c                 (    Xl         X l        X0l        g N)patternnegatedmust_be_dir)selfr6   r7   r8   s       r   __init__Pattern.__init__n   s    LL"r   c                     U R                   R                  XS9(       a1  U R                  (       a  [        R                  $ [        R
                  $ [        R                  $ )z4Returns a Match for this pattern and the given path.is_dir)r6   Matchesr7   r)   r.   r/   r0   )r9   pathr>   s      r   r?   Pattern.Matchess   s:    ||D0"llU]]<<^^r   c                     UR                  S5      (       a%  [        R                  " SR                  U5      5      eUR                  S5      (       a  USS nSnOSnU " [        R                  R                  U5      US9$ )	a  Creates a pattern for an individual line of an ignore file.

Windows-style newlines must be removed.

Args:
  line: str, The line to parse.

Returns:
  Pattern.

Raises:
  InvalidLineError: if the line was invalid (comment, blank, contains
    invalid consecutive stars).
#zLine [{}] begins with `#`.!r+   NTF)r7   )
startswithr   InvalidLineErrorformatGlob
FromString)clsliner7   s      r   rI   Pattern.FromStringz   sp      s!!">"E"Ed"KLLs!"Xdggtyy##D)7;;r   )r8   r7   r6   N)FFF)
r   r   r   r   r   r:   r?   classmethodrI   r   r   r   r   r2   r2   c   s%    #
 < <r   r2   c                   t    \ rS rSrSrSrS rSS jrS rSS jr	\
SS	 j5       r\
S
 5       r\
SS j5       rSrg)FileChooser   a_  A FileChooser determines which files in a directory to upload.

It's a fancy way of constructing a predicate (IsIncluded) along with a
convenience method for walking a directory (GetIncludedFiles) and listing
files to be uploaded based on that predicate.

How the predicate operates is based on a gcloudignore file (see module
docstring for details).
z	!include:c                     Xl         g r5   patterns)r9   rT   s     r   r:   FileChooser.__init__   s    Mr   c                 h   [         R                  " U5      SS nU H  n[        R                  nU R                   H6  nXA:g  =(       d    UnUR                  XGS9nU[        R                  Ld  M4  UnM8     U[        R                  L d  Mn  [        R                  " SR                  U5      5          g   g)ag  Returns whether the given file/directory should be included.

This is determined according to the rules at
https://git-scm.com/docs/gitignore except that symlinks are followed.

In particular:
- the method goes through pattern-by-pattern in-order
- any matches of a parent directory on a particular pattern propagate to its
  children
- if a parent directory is ignored, its children cannot be re-included

Args:
  path: str, the path (relative to the root upload directory) to test.
  is_dir: bool, whether the path is a directory (or symlink to a directory).

Returns:
  bool, whether the file should be uploaded
r+   Nr=   zSkipping file [{}]FT)
r   GetPathPrefixesr)   r0   rT   r?   r/   r   debugrG   )	r9   r@   r>   path_prefixespath_prefixprefix_matchr6   is_prefix_dirmatchs	            r   
IsIncludedFileChooser.IsIncluded   s    & ((.qr2M$^^l]]'#+5vB&,	 #
 
	%		&--d34 % r   c                    [         R                  R                  [        R                  " USS95      (       d  g[         R
                  " U5      n[        5       n[         R                  R                  U5      (       al  X#;   a  [        SR                  U5      5      eUR                  U5        [         R
                  " U5      n[         R                  R                  U5      (       a  Ml  [         R                  R                  U5      nU(       a  [         R                  R                  U5      (       a  [         R                  R                  X!5      (       a  [        SR                  U5      5      e[         R                  R                  U5      nU(       a'  [         R                  R                  U5      (       a  M  gggg)z;Raise SymlinkLoopError if the given path is a symlink loop.zutf-8r
   Nz"The symlink [{}] refers to itself.z8The symlink [{}] refers to its own containing directory.)osr@   islinkr   Encodereadlinksetr&   rG   adddirnamebasenamesamefile)r9   	full_pathptargetss       r   _RaiseOnSymlinkLoopFileChooser._RaiseOnSymlinkLoop   s.   77>>(//)gFGG 	IAeG
''..

	
077	BD 	Dkk!n
++a.a ''..

 		"A
  ##			!	'	'FMM 	 ''//!
a   ##!#!r   c              #     #    [         R                  " [        R                  " U5      SS9 GH  u  p4n[        R
                  " U5      nU Vs/ s H  n[        R
                  " U5      PM     nnU Vs/ s H  n[        R
                  " U5      PM     nnX1:X  a  Sn	O[         R                  R                  X15      n	U Hl  n[         R                  R                  X5      n
U R                  [         R                  R                  X85      5        U R                  U
5      (       d  Mh  U
v   Mn     U H  n[         R                  R                  X5      n
[         R                  R                  X65      nU R                  U
SS9(       a   U R                  U5        U(       a  U
v   Mt  Mv  UR                  U5        M     GM     gs  snf s  snf 7f)ah  Yields the files in the given directory that this FileChooser includes.

Args:
  upload_directory: str, the path of the directory to upload.
  include_dirs: bool, whether to include directories

Yields:
  str, the files and directories that should be uploaded.
Raises:
  SymlinkLoopError: if there is a symlink referring to its own containing
  dir or itself.
T)followlinks r=   N)ra   walksix
ensure_strr   Decoder@   relpathjoinrm   r^   remove)r9   upload_directoryinclude_dirsdirpathorig_dirnames	filenamesrg   dirnamesfilenamerv   file_relpathrj   s               r   GetIncludedFilesFileChooser.GetIncludedFiles   sT     .0WW'(d.<)	(g:GH-w(//'*-hH=FGY8??8,YiG		$''//'<(ww||G6  g!@A??<((
	  
 'ww||G5GGLL2	??<?5

"
"9
-  

w
' .< IGs&   AG G-G3 GBG&B%GNc                    / nUR                  5        H  nUR                  S5      (       aT  USS R                  5       R                  U R                  5      (       a!  UR	                  U R                  XSU5      5        Mm   UR                  [        R                  U5      5        M     U " U5      $ ! [        R                   a     M  f = f)aB  Constructs a FileChooser from the given string.

See `gcloud topic gcloudignore` for details.

Args:
  text: str, the string (many lines, in the format specified in the
    documentation).
  recurse: int, how many layers of "#!include" directives to respect. 0
    means don't respect the directives, 1 means to respect the directives,
    but *not* in any "#!include"d files, etc.
  dirname: str, the base directory from which to "#!include"

Raises:
  BadIncludedFileError: if a file being included does not exist or is not
    in the same directory.

Returns:
  FileChooser.
rC   r+   N)
splitlinesrE   lstrip_INCLUDE_DIRECTIVEextend_GetIncludedPatternsappendr2   rI   r   rF   )rJ   textrecurserg   rT   rK   s         r   rI   FileChooser.FromString  s    * H!			8??''(>(>??
//#224'J
K**401 " x= "" s   $B11C	C	c                    U(       d  [        S5      eUR                  U R                  5      nX[        U R                  5      -   S n[        U;   a  [        S5      eU(       d  [        R                  " SU5        / $ [        R                  R                  X%5      n U R                  XcS-
  5      R                  $ ! [         a$  n[        [        R                  " U5      5      eSnAff = f)ao  Gets the patterns from an '#!include' line.

Args:
  line: str, the line containing the '#!include' directive
  dirname: str, the name of the base directory from which to include files
  recurse: int, how many layers of "#!include" directives to respect. 0
    means don't respect the directives, 1 means to respect the directives,
    but *not* in any "#!include"d files, etc.

Returns:
  list of Pattern, the patterns recursively included from the specified
    file.

Raises:
  ValueError: if dirname is not provided
  BadIncludedFileError: if the file being included does not exist or is not
    in the same directory.
z4dirname must be provided in order to include a file.Nz-May only include files in the same directory.z+Not respecting `#!include` directive: [%s].r+   )
ValueErrorfindr   len_GCLOUDIGNORE_PATH_SEPr#   r   infora   r@   rw   FromFilerT   r   rs   	text_type)rJ   rK   rg   r   	start_idxincluded_fileincluded_patherrs           r   r    FileChooser._GetIncludedPatterns"  s    ( MNN		#001IS)?)?%@@ABM. 
9; ;	hh<dCiGGLL8M5\\-15>>> 5 s!3445s   B; ;
C)C$$C)c                      [         R                  " U5      nU R                  U[        R                  R                  U5      US9$ ! [         R                   a  n[        SR	                  X5      5      eSnAff = f)a  Constructs a FileChooser from the given file path.

See `gcloud topic gcloudignore` for details.

Args:
  ignore_file_path: str, the path to the file in .gcloudignore format.
  recurse: int, how many layers of "#!include" directives to respect. 0
    means don't respect the directives, 1 means to respect the directives,
    but *not* in any "#!include"d files, etc.

Raises:
  BadIncludedFileError: if the file being included does not exist or is not
    in the same directory.

Returns:
  FileChooser.
z#Could not read ignore file [{}]: {}N)rg   r   )	r   ReadFileContentsErrorr   rG   rI   ra   r@   rg   )rJ   ignore_file_pathr   r   r   s        r   r   FileChooser.FromFileG  s|    &O##$45d >>$8H(I")  + + ;; O
/
6
67G
MO OOs   A A9A44A9rS   rM   T)r   N)r+   )r   r   r   r   r   r   r:   r^   rm   r   rN   rI   r   r   r   r   r   r   rP   rP      sc     #@.$(L  @ "5 "5H + +r   rP   c                     U Vs/ s H"  n[         R                  R                  X5      PM$     nn[        [	        [         R                  R
                  U5      5      $ s  snf r5   )ra   r@   rw   anyr   exists)	directorynamesnamefiles_to_checks       r   AnyFileOrDirExistsr   c  sC    >CDedBGGLL1e.D	S0	11 Es   )Ac                 "    [        U [        5      $ r5   )r   	GIT_FILES)r   s    r   _GitFilesExistr   h  s    	Iy	11r   Tc                     U nU(       aG  [         R                  R                  [         R                  R                  US5      5      (       a  US-  nU$ )Nr   z#!include:.gitignore
)ra   r@   r   rw   )default_ignore_filer   include_gitignoreignore_file_contentss       r   _GetIgnoreFileContentsr   l  sE     -277>>ggll9l+- -44	r   c                    U(       a   [         R                  R                  X5      nO|[        R                  R
                  R                  R                  5       (       d!  [        R                  " S5        [        / 5      $ [         R                  R                  U [        5      n [        R                  U5      n[        R                  " SR                  U5      5        U$ ! [         a     Of = fU" U 5      (       d!  [        R                  " S5        [        / 5      $ [        XU5      n[        R                  " SR                  SU5      5        U(       a|   [         R"                  " XhSS9  [        R$                  R'                  S5        OF! [         R(                   a/  n	[        R                  " S	R                  U	5      5         S
n	A	OS
n	A	ff = f[        R+                  USU S9$ )a  Gets the FileChooser object for the given directory.

In order of preference:
- If ignore_file is not none, use it to skip files.
  If the specified file does not exist, raise error.
- Use .gcloudignore file in the top-level directory.
- Evaluates creation predicate to determine whether to generate .gcloudignore.
  include_gitignore determines whether the generated .gcloudignore will
  include the user's .gitignore if one exists. If the directory is not
  writable, the file chooser corresponding to the ignore file that would have
  been generated is used.
- If the creation predicate evaluates to false, returned FileChooser
  will choose all files.

Args:
  directory: str, the path of the top-level directory to upload
  default_ignore_file: str, the ignore file to use if one is not found (and
    the directory has Git files).
  write_on_disk: bool, whether to save the generated gcloudignore to disk.
  gcloud_ignore_creation_predicate: one argument function, indicating if a
    .gcloudignore file should be created. The argument is the path of the
    directory that would contain the .gcloudignore file. By default
    .gcloudignore file will be created if and only if the directory contains
    .gitignore file or .git directory.
  include_gitignore: bool, whether the generated gcloudignore should include
    the user's .gitignore if present.
  ignore_file: custom ignore_file name.
            Override .gcloudignore file to customize files to be skipped.

Raises:
  BadIncludedFileError: if a file being included does not exist or is not in
    the same directory.

Returns:
  FileChooser: the FileChooser for the directory. If there is no .gcloudignore
  file and it can't be created the returned FileChooser will choose all files.
zGNot using a .gcloudignore file since gcloudignore is globally disabled.zUsing ignore file at [{}].zNot using ignore file.z,Using default gcloudignore file:
{0}
{1}
{0}z2--------------------------------------------------F)	overwritezHCreated .gcloudignore file. See `gcloud topic gcloudignore` for details.z&Could not write .gcloudignore file: {}Nr+   )r   rg   )ra   r@   rw   r	   VALUESgcloudignoreenabledGetBoolr   r   rP   IGNORE_FILE_NAMEr   rG   r   r   r   WriteFileContentsstatusPrintr   rI   )
r   r   write_on_disk gcloud_ignore_creation_predicater   ignore_filegcloudignore_pathchooserignore_contentsr   s
             r   GetFileChooserForDirr   v  s   T Y<))1199;;	hh  _Y0@A""#45G HH)001BCDN	 
 		
 
*)	4	4HH%&r?*+>+<>/((<CC:OM N5/(-/
 
jj 4 5 ;; E	hh7>>sCDDE
 
		I		NNs*   %C! !
C.-C.F G"%GGr   )'r   
__future__r   r   r   ra   enumgooglecloudsdk.command_lib.utilr   googlecloudsdk.corer   r   r	   googlecloudsdk.core.utilr   r   rs   	six.movesr   r   r   DEFAULT_IGNORE_FILEr   _ENDS_IN_ODD_NUMBER_SLASHES_RE	Exceptionr   r   r   r#   r&   Enumr)   objectr2   rP   r   r   r   r   r   r   r   <module>r      s    '  ' 	  0 * # * - * 
 " \"	    !5 2) 2;& ;;:++ ;7z'' 7
DII 
.<f .<bL+& L+^2
2 .2 $7d%3tJOr   