Commit 8c0127c1 authored by Greg Ward's avatar Greg Ward

Fixed 'findall()' so it only returns regular files -- no directories.

Changed 'prune_file_list()' so it also prunes out RCS and CVS directories.
Added 'is_regex' parameter to 'select_pattern()', 'exclude_pattern()',
  and 'translate_pattern()', so that you don't have to be constrained
  by the simple shell-glob-like pattern language, and can escape into
  full-blown regexes when needed.  Currently this is only available
  in code -- it's not exposed in the manifest template mini-language.
Added 'prune' option (controlled by --prune and --no-prune) to determine
  whether we call 'prune_file_list()' or not -- it's true by default.
Fixed 'negative_opt' -- it was misnamed and not being seen by dist.py.
Added --no-defaults to the option table, so it's seen by FancyGetopt.
parent ab9a2ff4
...@@ -46,6 +46,14 @@ class sdist (Command): ...@@ -46,6 +46,14 @@ class sdist (Command):
('use-defaults', None, ('use-defaults', None,
"include the default file set in the manifest " "include the default file set in the manifest "
"[default; disable with --no-defaults]"), "[default; disable with --no-defaults]"),
('no-defaults', None,
"don't include the default file set"),
('prune', None,
"specifically exclude files/directories that should not be "
"distributed (build tree, RCS/CVS dirs, etc.) "
"[default; disable with --no-prune]"),
('no-prune', None,
"don't automatically exclude anything"),
('manifest-only', 'o', ('manifest-only', 'o',
"just regenerate the manifest and then stop " "just regenerate the manifest and then stop "
"(implies --force-manifest)"), "(implies --force-manifest)"),
...@@ -64,7 +72,8 @@ class sdist (Command): ...@@ -64,7 +72,8 @@ class sdist (Command):
"list available distribution formats", show_formats), "list available distribution formats", show_formats),
] ]
negative_opts = {'use-defaults': 'no-defaults'} negative_opt = {'no-defaults': 'use-defaults',
'no-prune': 'prune' }
default_format = { 'posix': 'gztar', default_format = { 'posix': 'gztar',
'nt': 'zip' } 'nt': 'zip' }
...@@ -78,6 +87,7 @@ class sdist (Command): ...@@ -78,6 +87,7 @@ class sdist (Command):
# 'use_defaults': if true, we will include the default file set # 'use_defaults': if true, we will include the default file set
# in the manifest # in the manifest
self.use_defaults = 1 self.use_defaults = 1
self.prune = 1
self.manifest_only = 0 self.manifest_only = 0
self.force_manifest = 0 self.force_manifest = 0
...@@ -217,7 +227,9 @@ class sdist (Command): ...@@ -217,7 +227,9 @@ class sdist (Command):
if template_exists: if template_exists:
self.read_template () self.read_template ()
# Prune away the build and source distribution directories # Prune away any directories that don't belong in the source
# distribution
if self.prune:
self.prune_file_list() self.prune_file_list()
# File list now complete -- sort it so that higher-level files # File list now complete -- sort it so that higher-level files
...@@ -509,18 +521,21 @@ class sdist (Command): ...@@ -509,18 +521,21 @@ class sdist (Command):
def prune_file_list (self): def prune_file_list (self):
"""Prune off branches that might slip into the file list as created """Prune off branches that might slip into the file list as created
by 'read_template()', but really don't belong there: specifically, by 'read_template()', but really don't belong there:
the build tree (typically "build") and the release tree itself * the build tree (typically "build")
(only an issue if we ran "sdist" previously with --keep-tree, or it * the release tree itself (only an issue if we ran "sdist"
aborted). previously with --keep-tree, or it aborted)
* any RCS or CVS directories
""" """
build = self.get_finalized_command('build') build = self.get_finalized_command('build')
base_dir = self.distribution.get_fullname() base_dir = self.distribution.get_fullname()
self.exclude_pattern (self.files, None, prefix=build.build_base) self.exclude_pattern (self.files, None, prefix=build.build_base)
self.exclude_pattern (self.files, None, prefix=base_dir) self.exclude_pattern (self.files, None, prefix=base_dir)
self.exclude_pattern (self.files, r'/(RCS|CVS)/.*', is_regex=1)
def select_pattern (self, files, pattern, anchor=1, prefix=None): def select_pattern (self, files, pattern,
anchor=1, prefix=None, is_regex=0):
"""Select strings (presumably filenames) from 'files' that match """Select strings (presumably filenames) from 'files' that match
'pattern', a Unix-style wildcard (glob) pattern. Patterns are not 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not
quite the same as implemented by the 'fnmatch' module: '*' and '?' quite the same as implemented by the 'fnmatch' module: '*' and '?'
...@@ -536,10 +551,15 @@ class sdist (Command): ...@@ -536,10 +551,15 @@ class sdist (Command):
(itself a pattern) and ending with 'pattern', with anything in between (itself a pattern) and ending with 'pattern', with anything in between
them, will match. 'anchor' is ignored in this case. them, will match. 'anchor' is ignored in this case.
If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and
'pattern' is assumed to be either a string containing a regex or a
regex object -- no translation is done, the regex is just compiled
and used as-is.
Return the list of matching strings, possibly empty. Return the list of matching strings, possibly empty.
""" """
matches = [] matches = []
pattern_re = translate_pattern (pattern, anchor, prefix) pattern_re = translate_pattern (pattern, anchor, prefix, is_regex)
self.debug_print("select_pattern: applying regex r'%s'" % self.debug_print("select_pattern: applying regex r'%s'" %
pattern_re.pattern) pattern_re.pattern)
for name in files: for name in files:
...@@ -552,13 +572,14 @@ class sdist (Command): ...@@ -552,13 +572,14 @@ class sdist (Command):
# select_pattern () # select_pattern ()
def exclude_pattern (self, files, pattern, anchor=1, prefix=None): def exclude_pattern (self, files, pattern,
anchor=1, prefix=None, is_regex=0):
"""Remove strings (presumably filenames) from 'files' that match """Remove strings (presumably filenames) from 'files' that match
'pattern'. 'pattern', 'anchor', 'and 'prefix' are the same 'pattern'. Other parameters are the same as for
as for 'select_pattern()', above. The list 'files' is modified 'select_pattern()', above. The list 'files' is modified in place.
in place.
""" """
pattern_re = translate_pattern (pattern, anchor, prefix)
pattern_re = translate_pattern (pattern, anchor, prefix, is_regex)
self.debug_print("exclude_pattern: applying regex r'%s'" % self.debug_print("exclude_pattern: applying regex r'%s'" %
pattern_re.pattern) pattern_re.pattern)
for i in range (len(files)-1, -1, -1): for i in range (len(files)-1, -1, -1):
...@@ -674,6 +695,8 @@ def findall (dir = os.curdir): ...@@ -674,6 +695,8 @@ def findall (dir = os.curdir):
"""Find all files under 'dir' and return the list of full filenames """Find all files under 'dir' and return the list of full filenames
(relative to 'dir'). (relative to 'dir').
""" """
from stat import ST_MODE, S_ISREG, S_ISDIR, S_ISLNK
list = [] list = []
stack = [dir] stack = [dir]
pop = stack.pop pop = stack.pop
...@@ -688,8 +711,13 @@ def findall (dir = os.curdir): ...@@ -688,8 +711,13 @@ def findall (dir = os.curdir):
fullname = os.path.join (dir, name) fullname = os.path.join (dir, name)
else: else:
fullname = name fullname = name
# Avoid excess stat calls -- just one will do, thank you!
stat = os.stat(fullname)
mode = stat[ST_MODE]
if S_ISREG(mode):
list.append (fullname) list.append (fullname)
if os.path.isdir (fullname) and not os.path.islink(fullname): elif S_ISDIR(mode) and not S_ISLNK(mode):
push (fullname) push (fullname)
return list return list
...@@ -716,10 +744,18 @@ def glob_to_re (pattern): ...@@ -716,10 +744,18 @@ def glob_to_re (pattern):
# glob_to_re () # glob_to_re ()
def translate_pattern (pattern, anchor=1, prefix=None): def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0):
"""Translate a shell-like wildcard pattern to a compiled regular """Translate a shell-like wildcard pattern to a compiled regular
expression. Return the compiled regex. expression. Return the compiled regex. If 'is_regex' true,
then 'pattern' is directly compiled to a regex (if it's a string)
or just returned as-is (assumes it's a regex object).
""" """
if is_regex:
if type(pattern) is StringType:
return re.compile(pattern)
else:
return pattern
if pattern: if pattern:
pattern_re = glob_to_re (pattern) pattern_re = glob_to_re (pattern)
else: else:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment