]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/distutils/filelist.py
   3 Provides the FileList class, used for poking about the filesystem 
   4 and building lists of files. 
   7 # This module should be kept compatible with Python 1.5.2. 
  15 from distutils
.util 
import convert_path
 
  16 from distutils
.errors 
import DistutilsTemplateError
, DistutilsInternalError
 
  17 from distutils 
import log
 
  21     """A list of files built by on exploring the filesystem and filtered by 
  22     applying various patterns to what we find there. 
  26         directory from which files will be taken -- only used if 
  27         'allfiles' not supplied to constructor 
  29         list of filenames currently being built/filtered/manipulated 
  31         complete list of files under consideration (ie. without any 
  38         # ignore argument to FileList, but keep them for backwards 
  44     def set_allfiles (self
, allfiles
): 
  45         self
.allfiles 
= allfiles
 
  47     def findall (self
, dir=os
.curdir
): 
  48         self
.allfiles 
= findall(dir) 
  50     def debug_print (self
, msg
): 
  51         """Print 'msg' to stdout if the global DEBUG (taken from the 
  52         DISTUTILS_DEBUG environment variable) flag is true. 
  54         from distutils
.debug 
import DEBUG
 
  58     # -- List-like methods --------------------------------------------- 
  60     def append (self
, item
): 
  61         self
.files
.append(item
) 
  63     def extend (self
, items
): 
  64         self
.files
.extend(items
) 
  67         # Not a strict lexical sort! 
  68         sortable_files 
= map(os
.path
.split
, self
.files
) 
  71         for sort_tuple 
in sortable_files
: 
  72             self
.files
.append(apply(os
.path
.join
, sort_tuple
)) 
  75     # -- Other miscellaneous utility methods --------------------------- 
  77     def remove_duplicates (self
): 
  78         # Assumes list has been sorted! 
  79         for i 
in range(len(self
.files
) - 1, 0, -1): 
  80             if self
.files
[i
] == self
.files
[i 
- 1]: 
  84     # -- "File template" methods --------------------------------------- 
  86     def _parse_template_line (self
, line
): 
  87         words 
= string
.split(line
) 
  90         patterns 
= dir = dir_pattern 
= None 
  92         if action 
in ('include', 'exclude', 
  93                       'global-include', 'global-exclude'): 
  95                 raise DistutilsTemplateError
, \
 
  96                       "'%s' expects <pattern1> <pattern2> ..." % action
 
  98             patterns 
= map(convert_path
, words
[1:]) 
 100         elif action 
in ('recursive-include', 'recursive-exclude'): 
 102                 raise DistutilsTemplateError
, \
 
 103                       "'%s' expects <dir> <pattern1> <pattern2> ..." % action
 
 105             dir = convert_path(words
[1]) 
 106             patterns 
= map(convert_path
, words
[2:]) 
 108         elif action 
in ('graft', 'prune'): 
 110                 raise DistutilsTemplateError
, \
 
 111                      "'%s' expects a single <dir_pattern>" % action
 
 113             dir_pattern 
= convert_path(words
[1]) 
 116             raise DistutilsTemplateError
, "unknown action '%s'" % action
 
 118         return (action
, patterns
, dir, dir_pattern
) 
 120     # _parse_template_line () 
 123     def process_template_line (self
, line
): 
 125         # Parse the line: split it up, make sure the right number of words 
 126         # is there, and return the relevant words.  'action' is always 
 127         # defined: it's the first word of the line.  Which of the other 
 128         # three are defined depends on the action; it'll be either 
 129         # patterns, (dir and patterns), or (dir_pattern). 
 130         (action
, patterns
, dir, dir_pattern
) = self
._parse
_template
_line
(line
) 
 132         # OK, now we know that the action is valid and we have the 
 133         # right number of words on the line for that action -- so we 
 134         # can proceed with minimal error-checking. 
 135         if action 
== 'include': 
 136             self
.debug_print("include " + string
.join(patterns
)) 
 137             for pattern 
in patterns
: 
 138                 if not self
.include_pattern(pattern
, anchor
=1): 
 139                     log
.warn("warning: no files found matching '%s'", 
 142         elif action 
== 'exclude': 
 143             self
.debug_print("exclude " + string
.join(patterns
)) 
 144             for pattern 
in patterns
: 
 145                 if not self
.exclude_pattern(pattern
, anchor
=1): 
 146                     log
.warn(("warning: no previously-included files " 
 147                               "found matching '%s'"), pattern
) 
 149         elif action 
== 'global-include': 
 150             self
.debug_print("global-include " + string
.join(patterns
)) 
 151             for pattern 
in patterns
: 
 152                 if not self
.include_pattern(pattern
, anchor
=0): 
 153                     log
.warn(("warning: no files found matching '%s' " + 
 154                               "anywhere in distribution"), pattern
) 
 156         elif action 
== 'global-exclude': 
 157             self
.debug_print("global-exclude " + string
.join(patterns
)) 
 158             for pattern 
in patterns
: 
 159                 if not self
.exclude_pattern(pattern
, anchor
=0): 
 160                     log
.warn(("warning: no previously-included files matching " 
 161                               "'%s' found anywhere in distribution"), 
 164         elif action 
== 'recursive-include': 
 165             self
.debug_print("recursive-include %s %s" % 
 166                              (dir, string
.join(patterns
))) 
 167             for pattern 
in patterns
: 
 168                 if not self
.include_pattern(pattern
, prefix
=dir): 
 169                     log
.warn(("warngin: no files found matching '%s' " + 
 170                                 "under directory '%s'"),  
 173         elif action 
== 'recursive-exclude': 
 174             self
.debug_print("recursive-exclude %s %s" % 
 175                              (dir, string
.join(patterns
))) 
 176             for pattern 
in patterns
: 
 177                 if not self
.exclude_pattern(pattern
, prefix
=dir): 
 178                     log
.warn(("warning: no previously-included files matching " 
 179                               "'%s' found under directory '%s'"), 
 182         elif action 
== 'graft': 
 183             self
.debug_print("graft " + dir_pattern
) 
 184             if not self
.include_pattern(None, prefix
=dir_pattern
): 
 185                 log
.warn("warning: no directories found matching '%s'", 
 188         elif action 
== 'prune': 
 189             self
.debug_print("prune " + dir_pattern
) 
 190             if not self
.exclude_pattern(None, prefix
=dir_pattern
): 
 191                 log
.warn(("no previously-included directories found " + 
 192                           "matching '%s'"), dir_pattern
) 
 194             raise DistutilsInternalError
, \
 
 195                   "this cannot happen: invalid action '%s'" % action
 
 197     # process_template_line () 
 200     # -- Filtering/selection methods ----------------------------------- 
 202     def include_pattern (self
, pattern
, 
 203                          anchor
=1, prefix
=None, is_regex
=0): 
 204         """Select strings (presumably filenames) from 'self.files' that 
 205         match 'pattern', a Unix-style wildcard (glob) pattern.  Patterns 
 206         are not quite the same as implemented by the 'fnmatch' module: '*' 
 207         and '?'  match non-special characters, where "special" is platform- 
 208         dependent: slash on Unix; colon, slash, and backslash on 
 209         DOS/Windows; and colon on Mac OS. 
 211         If 'anchor' is true (the default), then the pattern match is more 
 212         stringent: "*.py" will match "foo.py" but not "foo/bar.py".  If 
 213         'anchor' is false, both of these will match. 
 215         If 'prefix' is supplied, then only filenames starting with 'prefix' 
 216         (itself a pattern) and ending with 'pattern', with anything in between 
 217         them, will match.  'anchor' is ignored in this case. 
 219         If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and 
 220         'pattern' is assumed to be either a string containing a regex or a 
 221         regex object -- no translation is done, the regex is just compiled 
 224         Selected strings will be added to self.files. 
 226         Return 1 if files are found. 
 229         pattern_re 
= translate_pattern(pattern
, anchor
, prefix
, is_regex
) 
 230         self
.debug_print("include_pattern: applying regex r'%s'" % 
 233         # delayed loading of allfiles list 
 234         if self
.allfiles 
is None: 
 237         for name 
in self
.allfiles
: 
 238             if pattern_re
.search(name
): 
 239                 self
.debug_print(" adding " + name
) 
 240                 self
.files
.append(name
) 
 248     def exclude_pattern (self
, pattern
, 
 249                          anchor
=1, prefix
=None, is_regex
=0): 
 250         """Remove strings (presumably filenames) from 'files' that match 
 251         'pattern'.  Other parameters are the same as for 
 252         'include_pattern()', above. 
 253         The list 'self.files' is modified in place. 
 254         Return 1 if files are found. 
 257         pattern_re 
= translate_pattern(pattern
, anchor
, prefix
, is_regex
) 
 258         self
.debug_print("exclude_pattern: applying regex r'%s'" % 
 260         for i 
in range(len(self
.files
)-1, -1, -1): 
 261             if pattern_re
.search(self
.files
[i
]): 
 262                 self
.debug_print(" removing " + self
.files
[i
]) 
 273 # ---------------------------------------------------------------------- 
 276 def findall (dir = os
.curdir
): 
 277     """Find all files under 'dir' and return the list of full filenames 
 280     from stat 
import ST_MODE
, S_ISREG
, S_ISDIR
, S_ISLNK
 
 289         names 
= os
.listdir(dir) 
 292             if dir != os
.curdir
:        # avoid the dreaded "./" syndrome 
 293                 fullname 
= os
.path
.join(dir, name
) 
 297             # Avoid excess stat calls -- just one will do, thank you! 
 298             stat 
= os
.stat(fullname
) 
 301                 list.append(fullname
) 
 302             elif S_ISDIR(mode
) and not S_ISLNK(mode
): 
 308 def glob_to_re (pattern
): 
 309     """Translate a shell-like glob pattern to a regular expression; return 
 310     a string containing the regex.  Differs from 'fnmatch.translate()' in 
 311     that '*' does not match "special characters" (which are 
 314     pattern_re 
= fnmatch
.translate(pattern
) 
 316     # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which 
 317     # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, 
 318     # and by extension they shouldn't match such "special characters" under 
 319     # any OS.  So change all non-escaped dots in the RE to match any 
 320     # character except the special characters. 
 321     # XXX currently the "special characters" are just slash -- i.e. this is 
 323     pattern_re 
= re
.sub(r
'(^|[^\\])\.', r
'\1[^/]', pattern_re
) 
 329 def translate_pattern (pattern
, anchor
=1, prefix
=None, is_regex
=0): 
 330     """Translate a shell-like wildcard pattern to a compiled regular 
 331     expression.  Return the compiled regex.  If 'is_regex' true, 
 332     then 'pattern' is directly compiled to a regex (if it's a string) 
 333     or just returned as-is (assumes it's a regex object). 
 336         if type(pattern
) is StringType
: 
 337             return re
.compile(pattern
) 
 342         pattern_re 
= glob_to_re(pattern
) 
 346     if prefix 
is not None: 
 347         prefix_re 
= (glob_to_re(prefix
))[0:-1] # ditch trailing $ 
 348         pattern_re 
= "^" + os
.path
.join(prefix_re
, ".*" + pattern_re
) 
 349     else:                               # no prefix -- respect anchor flag 
 351             pattern_re 
= "^" + pattern_re
 
 353     return re
.compile(pattern_re
) 
 355 # translate_pattern ()