Path: csiph.com!fu-berlin.de!uni-berlin.de!not-for-mail From: eryk sun Newsgroups: comp.lang.python Subject: Re: Remove directory tree without following symlinks Date: Sat, 23 Apr 2016 15:22:35 -0500 Lines: 53 Message-ID: References: <571a3ba2$0$1597$c3e8da3$5496439d@news.astraweb.com> <1461337766.365000.586700849.0DDBDB0B@webmail.messagingengine.com> <571a5be6$0$1590$c3e8da3$5496439d@news.astraweb.com> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 X-Trace: news.uni-berlin.de 8KCvq9X0XUDe3ZTQthLEygr8DpLJ6gInmFGskLaErL7g== Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'received:209.85.223': 0.03; 'handler': 0.04; 'paths': 0.05; '-0500': 0.07; 'only,': 0.07; 'raises': 0.07; 'read-only': 0.07; 'seemed': 0.07; '"%s"': 0.09; '22,': 0.09; 'cmd': 0.09; 'raised,': 0.09; 'url:github': 0.09; 'windowserror': 0.09; 'example:': 0.10; 'python': 0.10; 'assume': 0.11; '2016': 0.16; '23,': 0.16; 'decode': 0.16; 'filenames.': 0.16; 'fyi,': 0.16; 'received:io': 0.16; 'received:psf.io': 0.16; 'rmdir': 0.16; 'subject:Remove': 0.16; 'subprocess': 0.16; 'url:pip': 0.16; 'wrote:': 0.16; 'nested': 0.18; 'skip:a 60': 0.18; 'shell': 0.18; '>>>': 0.20; 'windows': 0.20; 'am,': 0.23; '(or': 0.23; 'help.': 0.23; 'sat,': 0.23; 'thanks,': 0.24; 'implemented': 0.24; 'header:In-Reply-To:1': 0.24; 'command': 0.26; 'external': 0.27; 'error': 0.27; 'fri,': 0.27; 'message-id:@mail.gmail.com': 0.27; 'dos': 0.27; "skip:' 10": 0.28; 'looks': 0.29; 'long.': 0.29; 'sure,': 0.29; 'allows': 0.30; 'date:': 0.31; 'skip:s 30': 0.31; 'option': 0.31; 'aside': 0.32; 'returned': 0.32; 'useful': 0.33; 'call,': 0.33; 'that,': 0.34; 'received:google.com': 0.35; 'could': 0.35; 'dir': 0.35; 'false': 0.35; 'path': 0.35; 'question,': 0.35; 'unicode': 0.35; 'but': 0.36; 'too': 0.36; 'there': 0.36; 'received:209.85': 0.36; '(i.e.': 0.36; 'volume': 0.36; 'to:addr:python-list': 0.36; 'pm,': 0.36; 'subject:: ': 0.37; 'thought': 0.37; 'skip:z 10': 0.38; "won't": 0.38; 'received:209': 0.38; 'names': 0.38; 'data': 0.39; 'from:': 0.39; 'to:addr:python.org': 0.40; 'still': 0.40; 'email addr:gmail.com': 0.62; 'complete': 0.63; 'deeply': 0.64; 'limit': 0.65; 'today': 0.65; 'concerns': 0.66; 'improvement.': 0.66; 'exceed': 0.72; '8.3': 0.84; 'subject:tree': 0.84; 'tree,': 0.84; 'wins': 0.84; 'hand,': 0.97 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to; bh=ic2m0a7QIqASqVPQ03JBBRGaQj2GW8Xqr0JBO0tg5m8=; b=hzDaF0y64YW6L1EdsrPd6pzIlyU9kqJwBofMYfw8kvLxTgZKyuqwd31nxH9LauvftB 924XwgbySsYb1BRbtDvZNKdpBTqE35v2U1wxiXm3uuAz3cyA4xUAJO+KrvqndmmngXYT l1GUYyUqFAp92As37knvh0PMtzSm2BvNPlzD4b3cjvBVYtzHqzBAojS/j4p1g4lZU5Iv fIBkf9poZBZIiz9ekG5Km5TZX67HphTEPqbhYQU0oVuecqqyz7gklZsuckD40NNlgZBm bXrRB5UfhHOd7eIZ7d0UOrDawKTY2mAxGWya49bcVGXraXGnr3Nqp23PGAa/kTHtcqJW Z/4Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to; bh=ic2m0a7QIqASqVPQ03JBBRGaQj2GW8Xqr0JBO0tg5m8=; b=S/bjaK8PVGCdwASrpZHGtUamAzo//RhSsIB8qSmcqU+F9zwSavYO6ch+4fSIKkg4lN m+USy/c2wSjxRuKO+7RPAcQymttpmw174aCQslfcwwgVASSOWQN/tcTrsL1hIttLXN6+ ZgjQPd4IW+IguHF9wbmwj7ns4qS+N7lr5O0G51KSJQRtE9vpXMWLOtJGv3x7hteubifN dHCIvXB0IDLAmlYLQDD4LuqVeKr3cafNoT62mrHfQjuk5/DZSeXkm+40kNZsQcAUpgYi 0Q5TKcGWxcukes80MOzP0niC/Q3h+v8R1d4+ETM2qqOckvVxMnInIQFpYNGIHXYy8z7D 1fvw== X-Gm-Message-State: AOPr4FWGAjXUOG+cAUmETrxs4z55ykJdRPOWa84esKsaVv8tMY8TdssaN3SnvDv5RXiVQADrvCEyB28pvGMM3Q== X-Received: by 10.107.39.138 with SMTP id n132mr34262045ion.103.1461442995503; Sat, 23 Apr 2016 13:23:15 -0700 (PDT) In-Reply-To: X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Mailman-Original-Message-ID: X-Mailman-Original-References: <571a3ba2$0$1597$c3e8da3$5496439d@news.astraweb.com> <1461337766.365000.586700849.0DDBDB0B@webmail.messagingengine.com> <571a5be6$0$1590$c3e8da3$5496439d@news.astraweb.com> Xref: csiph.com comp.lang.python:107518 On Sat, Apr 23, 2016 at 4:34 AM, Albert-Jan Roskam wrote: > >> From: eryksun@gmail.com >> Date: Fri, 22 Apr 2016 13:28:01 -0500 >> On Fri, Apr 22, 2016 at 12:39 PM, Albert-Jan Roskam >> wrote: >> > FYI, Just today I found out that shutil.rmtree raises a WindowsError if >> > the dir is read-only (or its contents). Using 'ignore_errors', won't help. >> > Sure, no error is raised, but the dir is not deleted either! A 'force' option >> > would be a nice improvement. >> >> Use the onerror handler to call os.chmod(path, stat.S_IWRITE). For >> example, see pip's rmtree_errorhandler: >> >> https://github.com/pypa/pip/blob/8.1.1/pip/utils/__init__.py#L105 > > Thanks, that looks useful indeed. I thought about os.chmod, but with > os.walk. That seemed expensive. So I used subprocess.call('rmdir "%s" /s /q' > % dirname). That's Windows only, of course, but aside of that, is using > subprocess less preferable? I assume you used shell=True in the above call, and not an external rmdir.exe. There are security concerns with using the shell if you're not in complete control of the command line. As to performance, cmd's rmdir wins without question, not only because it's implemented in C, but also because it uses the stat data from the WIN32_FIND_DATA returned by FindFirstFile/FindNextFile to check for FILE_ATTRIBUTE_DIRECTORY and FILE_ATTRIBUTE_READONLY. On the other hand, Python wins when it comes to working with deeply nested directories. Paths in cmd are limited to MAX_PATH characters. rmdir uses DOS 8.3 short names (i.e. cAlternateFileName in WIN32_FIND_DATA), but that could still exceed MAX_PATH for a deeply nested tree, or the volume may not even have 8.3 DOS filenames. shutil.rmtree allows you to work around the DOS limit by prefixing the path with "\\?\". For example: >>> subprocess.call(r'rmdir /q/s Z:\Temp\long', shell=True) The path Z:\Temp\long\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaa is too long. 0 >>> shutil.rmtree(r'\\?\Z:\Temp\long') >>> os.path.exists(r'Z:\Temp\long') False Using "\\?\" requires a path that's fully qualified, normalized (backslash only), and unicode (i.e. decode a Python 2 str).