diff --git a/boltons/fileutils.py b/boltons/fileutils.py index 10c7014..d797754 100644 --- a/boltons/fileutils.py +++ b/boltons/fileutils.py @@ -1,9 +1,12 @@ # -*- coding: utf-8 -*- - import os +import re import stat import errno +import fnmatch import tempfile +from shutil import copy2, copystat, Error + VALID_PERM_CHARS = 'rwx' @@ -157,13 +160,122 @@ class AtomicSaver(object): return -#with atomic_save('/home/mahmoud/tmp/final.txt') as f: -# f.write('rofl') -# raise ValueError('nope') -# f.write('\n') +_CUR_DIR = os.path.dirname(os.path.abspath(__file__)) + + +def iter_find_files(directory, patterns, ignored=None): + """\ + Finds files under a `directory`, matching `patterns` using "glob" + syntax (e.g., "*.txt"). It's also possible to ignore patterns with + the `ignored` argument, which uses the same format as `patterns. + + >>> filenames = sorted(iter_find_files(_CUR_DIR, '*.py')) + >>> filenames[-1].split('/')[-1] + 'tzutils.py' + >>> filenames = iter_find_files(_CUR_DIR, '*.py', ignored='.#*') + + That last example ignores emacs lockfiles. + """ + if isinstance(patterns, basestring): + patterns = [patterns] + pats_re = re.compile('|'.join([fnmatch.translate(p) for p in patterns])) + + if not ignored: + ignored = [] + elif isinstance(ignored, basestring): + ignored = [ignored] + ign_re = re.compile('|'.join([fnmatch.translate(p) for p in ignored])) + for root, dirs, files in os.walk(directory): + for basename in files: + if pats_re.match(basename): + if ignored and ign_re.match(basename): + continue + filename = os.path.join(root, basename) + yield filename + return + + +def mkdir_p(path): + try: + os.makedirs(path) + except OSError as exc: + if exc.errno == errno.EEXIST and os.path.isdir(path): + return + raise + return + + +def copytree(src, dst, symlinks=False, ignore=None): + """Recursively copy a directory tree using copy2(). + + The destination directory is allowed to already exist. + If exception(s) occur, an Error is raised with a list of reasons. + + If the optional symlinks flag is true, symbolic links in the + source tree result in symbolic links in the destination tree; if + it is false, the contents of the files pointed to by symbolic + links are copied. + + The optional ignore argument is a callable. If given, it + is called with the `src` parameter, which is the directory + being visited by copytree(), and `names` which is the list of + `src` contents, as returned by os.listdir(): + + callable(src, names) -> ignored_names + + Since copytree() is called recursively, the callable will be + called once for each directory that is copied. It returns a + list of names relative to the `src` directory that should + not be copied. + + XXX Consider this example code rather than the ultimate tool. + + """ + names = os.listdir(src) + if ignore is not None: + ignored_names = ignore(src, names) + else: + ignored_names = set() + + mkdir_p(dst) + errors = [] + for name in names: + if name in ignored_names: + continue + srcname = os.path.join(src, name) + dstname = os.path.join(dst, name) + try: + if symlinks and os.path.islink(srcname): + linkto = os.readlink(srcname) + os.symlink(linkto, dstname) + elif os.path.isdir(srcname): + copytree(srcname, dstname, symlinks, ignore) + else: + # Will raise a SpecialFileError for unsupported file types + copy2(srcname, dstname) + # catch the Error from the recursive copytree so that we can + # continue with other files + except Error as e: + errors.extend(e.args[0]) + except EnvironmentError, why: + errors.append((srcname, dstname, str(why))) + try: + copystat(src, dst) + except OSError, why: + if WindowsError is not None and isinstance(why, WindowsError): + # Copying file access times may fail on Windows + pass + else: + errors.append((src, dst, str(why))) + if errors: + raise Error(errors) if __name__ == '__main__': + #with atomic_save('/tmp/final.txt') as f: + # f.write('rofl') + # raise ValueError('nope') + # f.write('\n') def _main(): up = FilePerms() diff --git a/boltons/osutils.py b/boltons/osutils.py index a24bed4..556306d 100644 --- a/boltons/osutils.py +++ b/boltons/osutils.py @@ -1,117 +1,4 @@ # -*- coding: utf-8 -*- -import os -import re -import errno -import fnmatch -from shutil import copy2, copystat, Error - -_CUR_DIR = os.path.dirname(os.path.abspath(__file__)) - - -def iter_find_files(directory, patterns, ignored=None): - """\ - Finds files under a `directory`, matching `patterns` using "glob" - syntax (e.g., "*.txt"). It's also possible to ignore patterns with - the `ignored` argument, which uses the same format as `patterns. - - >>> filenames = sorted(iter_find_files(_CUR_DIR, '*.py')) - >>> filenames[-1].split('/')[-1] - 'tzutils.py' - >>> filenames = iter_find_files(_CUR_DIR, '*.py', ignored='.#*') - - That last example ignores emacs lockfiles. - """ - if isinstance(patterns, basestring): - patterns = [patterns] - pats_re = re.compile('|'.join([fnmatch.translate(p) for p in patterns])) - - if not ignored: - ignored = [] - elif isinstance(ignored, basestring): - ignored = [ignored] - ign_re = re.compile('|'.join([fnmatch.translate(p) for p in ignored])) - for root, dirs, files in os.walk(directory): - for basename in files: - if pats_re.match(basename): - if ignored and ign_re.match(basename): - continue - filename = os.path.join(root, basename) - yield filename - return - - -def mkdir_p(path): - try: - os.makedirs(path) - except OSError as exc: - if exc.errno == errno.EEXIST and os.path.isdir(path): - return - raise - return - - -def copytree(src, dst, symlinks=False, ignore=None): - """Recursively copy a directory tree using copy2(). - - The destination directory is allowed to already exist. - If exception(s) occur, an Error is raised with a list of reasons. - - If the optional symlinks flag is true, symbolic links in the - source tree result in symbolic links in the destination tree; if - it is false, the contents of the files pointed to by symbolic - links are copied. - - The optional ignore argument is a callable. If given, it - is called with the `src` parameter, which is the directory - being visited by copytree(), and `names` which is the list of - `src` contents, as returned by os.listdir(): - - callable(src, names) -> ignored_names - - Since copytree() is called recursively, the callable will be - called once for each directory that is copied. It returns a - list of names relative to the `src` directory that should - not be copied. - - XXX Consider this example code rather than the ultimate tool. - - """ - names = os.listdir(src) - if ignore is not None: - ignored_names = ignore(src, names) - else: - ignored_names = set() - - mkdir_p(dst) - errors = [] - for name in names: - if name in ignored_names: - continue - srcname = os.path.join(src, name) - dstname = os.path.join(dst, name) - try: - if symlinks and os.path.islink(srcname): - linkto = os.readlink(srcname) - os.symlink(linkto, dstname) - elif os.path.isdir(srcname): - copytree(srcname, dstname, symlinks, ignore) - else: - # Will raise a SpecialFileError for unsupported file types - copy2(srcname, dstname) - # catch the Error from the recursive copytree so that we can - # continue with other files - except Error as e: - errors.extend(e.args[0]) - except EnvironmentError, why: - errors.append((srcname, dstname, str(why))) - try: - copystat(src, dst) - except OSError, why: - if WindowsError is not None and isinstance(why, WindowsError): - # Copying file access times may fail on Windows - pass - else: - errors.append((src, dst, str(why))) - if errors: - raise Error(errors) +print 'Deprecation warning: the contents of osutils have moved to fileutils' +from fileutils import *