
    >                        d Z ddlmZ ddlmZ ddlmZ ddlZddlZddlmZ ddl	m
Z
 ddl	mZ dd	l	mZ dd
lmZ ddlmZ ddlZddlmZ dZddgZdZdZdZ G d de      Z G d de      Z G d de
j6                        Z G d de
j6                        Z G d dej<                        Z G d de       Z! G d d e       Z"d! Z#d" Z$	 d&d$Z%ed#e$d#dfd%Z&y)'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                       e Zd ZdZy)InternalParserErrorz)An internal error in ignore file parsing.N__name__
__module____qualname____doc__     3lib/googlecloudsdk/command_lib/util/gcloudignore.pyr   r   F   s    1r   r   c                       e Zd ZdZy)BadFileError2Error indicating that a provided file was invalid.Nr   r   r   r   r   r   J       :r   r   c                       e Zd ZdZy)BadIncludedFileErrorr   Nr   r   r   r   r   r   N   r   r   r   c                       e Zd ZdZy)SymlinkLoopErrorz.Error indicating that there is a symlink loop.Nr   r   r   r   r!   r!   R   s    6r   r!   c                       e Zd ZdZdZdZdZy)MatchzIndicates 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
           N)r   r   r   r   INCLUDEIGNORENO_MATCHr   r   r   r#   r#   V   s     '&(r   r#   c                   0    e Zd ZdZddZddZed        Zy)PatternzAn 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                 .    || _         || _        || _        y N)patternnegatedmust_be_dir)selfr.   r/   r0   s       r   __init__zPattern.__init__n   s    DLDL"Dr   c                     | j                   j                  ||      r,| j                  rt        j                  S t        j
                  S t        j                  S )z4Returns a Match for this pattern and the given path.is_dir)r.   Matchesr/   r#   r'   r(   r)   )r1   pathr5   s      r   r6   zPattern.Matchess   s<    ||D0"llU]]<<^^r   c                     |j                  d      r$t        j                  dj                  |            |j                  d      r|dd }d}nd} | t        j                  j                  |      |      S )	a4  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)r/   )
startswithr   InvalidLineErrorformatGlob
FromString)clsliner/   s      r   r?   zPattern.FromStringz   sj      s!!">"E"Ed"KLLs!"Xdggtyy##D)7;;r   N)FFF)r   r   r   r   r2   r6   classmethodr?   r   r   r   r+   r+   c   s%    #
 < <r   r+   c                   d    e Zd ZdZdZd ZddZd ZddZe	dd       Z
e	d	        Ze	dd
       Zy)FileChooserak  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                     || _         y r-   )patterns)r1   rG   s     r   r2   zFileChooser.__init__   s	    DMr   c                 V   t        j                  |      dd }|D ]  }t        j                  }| j                  D ]3  }||k7  xs |}|j                  ||      }|t        j                  us2|}5 |t        j                  u sht        j                  dj                  |              y y)a  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$   Nr4   zSkipping file [{}]FT)
r   GetPathPrefixesr#   r)   rG   r6   r(   r   debugr=   )	r1   r7   r5   path_prefixespath_prefixprefix_matchr.   is_prefix_dirmatchs	            r   
IsIncludedzFileChooser.IsIncluded   s    & ((.qr2M$^^l]]'#t+5vMB&,	 #
 
	%		&--d34 % r   c                 .   t         j                  j                  t        j                  |d            syt        j
                  |      }t               }t         j                  j                  |      rd||v rt        dj                  |            |j                  |       t        j
                  |      }t         j                  j                  |      rdt         j                  j                  |      }|rt         j                  j                  |      r}t         j                  j                  ||      rt        dj                  |            t         j                  j                  |      }|r!t         j                  j                  |      r{yyyy)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.)osr7   islinkr   Encodereadlinksetr!   r=   adddirnamebasenamesamefile)r1   	full_pathptargetss       r   _RaiseOnSymlinkLoopzFileChooser._RaiseOnSymlinkLoop   s    77>>(//)gFG 	IAeG
''..
	
g077	BD 	Dkk!n
++a.a ''..
 		"A
  #			!Y	'FMM 	 ''//!
a   #!#!r   c              #   l  K   t        j                  t        j                  |      d      D ]x  \  }}}t	        j
                  |      }|D cg c]  }t	        j
                  |       }}|D cg c]  }t	        j
                  |       }}||k(  rd}	n t         j                  j                  ||      }	|D ]g  }t         j                  j                  |	|      }
| j                  t         j                  j                  ||             | j                  |
      sd|
 i |D ]  }t         j                  j                  |	|      }
t         j                  j                  ||      }| j                  |
d      r| j                  |       |sj|
 o|j                  |        { yc c}w c c}w w)a  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 r4   N)rR   walksix
ensure_strr   Decoder7   relpathjoinr^   rP   remove)r1   upload_directoryinclude_dirsdirpathorig_dirnames	filenamesrX   dirnamesfilenamerf   file_relpathr[   s               r   GetIncludedFileszFileChooser.GetIncludedFiles   sb     .0WW'(d.< .<)	(g:GH-w(//'*-hH=FGY8??8,YiG	$	$''//'+;<(ww||GX6  gx!@A??<(
	  
 'ww||GW5GGLL'2	??<?5

"
"9
- 

w
' .< IGs,   AF4F**F40F/BF4A1F4&F4Nc                 |   g }|j                         D ]  }|j                  d      rO|dd j                         j                  | j                        r"|j	                  | j                  |||             c	 |j                  t        j                  |               | |      S # t        j                  $ r Y w xY w)az  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.
    r9   r$   N)
splitlinesr;   lstrip_INCLUDE_DIRECTIVEextend_GetIncludedPatternsappendr+   r?   r   r<   )r@   textrecurserX   rG   rA   s         r   r?   zFileChooser.FromString  s    * H!		8??''(>(>?
//#224'J
K**401 " x= "" s   7$B%%B;:B;c                    |st        d      |j                  | j                        }||t        | j                        z   d }t        |v rt        d      |st        j                  d|       g S t        j                  j                  ||      }	 | j                  ||dz
        j                  S # t        $ r#}t        t        j                  |            d}~ww xY w)a  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findru   len_GCLOUDIGNORE_PATH_SEPr   r   inforR   r7   rg   FromFilerG   r   rc   	text_type)r@   rA   rX   rz   	start_idxincluded_fileincluded_patherrs           r   rw   z 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   B1 1	C:CCc                     	 t        j                  |      }| j                  |t        j                  j                  |      |      S # t         j                  $ r }t        dj	                  ||            d}~ww xY w)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)rX   rz   )	r   ReadFileContentsErrorr   r=   r?   rR   r7   rX   )r@   ignore_file_pathrz   ry   r   s        r   r   zFileChooser.FromFileG  s~    &O##$45d >>$8H(I")  + + ;; O
/
6
67G
MO OOs   A A;A66A;rB   T)r   N)r$   )r   r   r   r   ru   r2   rP   r^   rq   rC   r?   rw   r   r   r   r   rE   rE      sc     #@.$(L  @ "5 "5H + +r   rE   c                     |D cg c]"  }t         j                  j                  | |      $ }}t        t	        t         j                  j
                  |            S c c}w r-   )rR   r7   rg   anyr   exists)	directorynamesnamefiles_to_checks       r   AnyFileOrDirExistsr   c  sE    >CDedBGGLLD1e.D	S0	11 Es   'Ac                 "    t        | t              S r-   )r   	GIT_FILES)r   s    r   _GitFilesExistr   h  s    	Iy	11r   Tc                     | }|rBt         j                  j                  t         j                  j                  |d            r|dz  }|S )Nr   z#!include:.gitignore
)rR   r7   r   rg   )default_ignore_filer   include_gitignoreignore_file_contentss       r   _GetIgnoreFileContentsr   l  s@     -277>>ggll9l+-44	r   c                    |r!t         j                  j                  | |      }nvt        j                  j
                  j                  j                         s t        j                  d       t        g       S t         j                  j                  | t              }	 t        j                  |      }t        j                  dj                  |             |S # t        $ r Y nw xY w ||       s t        j                  d       t        g       S t        || |      }t        j                  dj                  d|             |r}	 t!        j"                  ||d       t        j$                  j'                  d       nD# t         j(                  $ r.}	t        j                  d	j                  |	             Y d
}	~	nd
}	~	ww xY wt        j+                  |d|       S )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$   )rz   rX   )rR   r7   rg   r	   VALUESgcloudignoreenabledGetBoolr   r   rE   IGNORE_FILE_NAMEr   r=   r   r   r   WriteFileContentsstatusPrintr   r?   )
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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"E: :F;$F66F;r   )'r   
__future__r   r   r   rR   enumgooglecloudsdk.command_lib.utilr   googlecloudsdk.corer   r   r	   googlecloudsdk.core.utilr   r   rc   	six.movesr   r   r   DEFAULT_IGNORE_FILEr   _ENDS_IN_ODD_NUMBER_SLASHES_RE	Exceptionr   r   r   r   r!   Enumr#   objectr+   rE   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   