149

Is there any way to remove a directory and its contents in the PathLib module? With path.unlink() it only removes a file, with path.rmdir() the directory has to be empty. Is there no way to do it in one function call?

danronmoon
  • 3,814
  • 5
  • 34
  • 56
Jasonca1
  • 4,848
  • 6
  • 25
  • 42

7 Answers7

182

As you already know, the only two Path methods for removing files/directories are .unlink() and .rmdir() and neither does what you want.

Pathlib is a module that provides object oriented paths across different OS's, it isn't meant to have lots of diverse methods.

The aim of this library is to provide a simple hierarchy of classes to handle filesystem paths and the common operations users do over them.

The "uncommon" file system alterations, such as recursively removing a directory, is stored in different modules. If you want to recursively remove a directory, you should use the shutil module. (It works with Path instances too!)

import shutil
import pathlib
import os  # for checking results

print(os.listdir())
# ["a_directory", "foo.py", ...]

path = pathlib.Path("a_directory")

shutil.rmtree(path)
print(os.listdir())
# ["foo.py", ...]
phoenix
  • 7,988
  • 6
  • 39
  • 45
Taku
  • 31,927
  • 11
  • 74
  • 85
  • 4
    just for mention, can be done this way -https://stackoverflow.com/a/49782093/4249707 – El Ruso Nov 07 '18 at 22:44
  • 149
    I still don’t get, why a recursive version is not part of pathlib.Path, when everything, what is needed is already there. I was really hoping, this confusing usage of os.path, os.mkdir, shutil, etc. would end with pathlib. – Sebastian Werk Apr 09 '19 at 09:29
  • 4
    @SebastianWerk PR! PR! PR! -- although being in the stdlibrary it won't be out for a while sadly and would take a lot of effort to get in. I share your sentiments – SwimBikeRun Jul 12 '19 at 00:33
  • 1
    what is wrong with doing `pth.rmdir()` if `pth` is a `Path` obj compared to your answer? – Charlie Parker Jun 16 '22 at 19:10
  • 6
    `path.rmdir()` will remove directory only if its empty – Rahul A Ranger Jul 17 '22 at 12:50
  • link to `shutil.rmtree` [documentation](https://docs.python.org/3/library/shutil.html#shutil.rmtree) – Leland Hepworth Jul 21 '22 at 15:16
  • **I can tell you guys, why recursive directory removal is not part of Pathlib**: if you have a bug and instead of `/home/usr/my_proj/tmp_data` you remove `/home/usr/my_proj` you get the answer the hard way ;). I got pretty paranoid about that topic after a pretty intense experience I would not like to repeat. If you use formulations like this in your code on a regular basis, sooner or later you will get into the same situation. – Markus Dutschke Feb 28 '23 at 15:14
29

Here's a pure pathlib implementation:

from pathlib import Path


def rm_tree(pth):
    pth = Path(pth)
    for child in pth.glob('*'):
        if child.is_file():
            child.unlink()
        else:
            rm_tree(child)
    pth.rmdir()
vvvvv
  • 25,404
  • 19
  • 49
  • 81
MatteoLacki
  • 423
  • 4
  • 4
  • 5
    This would delete the contents of symlinked directories, I'd have thought? – user2846495 Jul 22 '21 at 13:30
  • 2
    The unsafe version of `shutil.rmtree` does something similar ([source](https://github.com/python/cpython/blob/0ac5372bf6b937ed44a8f9c4e402d024fcd80870/Lib/shutil.py#L595)), but it raises an `OSError("Cannot call rmtree on a symbolic link")` in case of symlinked dir. – djvg Feb 11 '22 at 13:24
  • what is wrong with doing `shutil.rmtree(path)` compared to your answer? – Charlie Parker Jun 16 '22 at 19:11
16

Otherwise, you can try this one if you want only pathlib:

from pathlib import Path


def rm_tree(pth: Path):
    for child in pth.iterdir():
        if child.is_file():
            child.unlink()
        else:
            rm_tree(child)
    pth.rmdir()

rm_tree(your_path)
Rami
  • 540
  • 4
  • 8
  • Is it safe to mutate the dir you're iterating over? Might have to extract the full results of the `iterdir()` to a list before starting to iterate. – Roger Dahl Jun 17 '21 at 06:26
  • The recursive function will continue removing files (child.unlink()) until the directory is empty. Once empty, the directory is removed (pth.rmdir()). – Rami Jun 21 '21 at 19:32
8

Simple and effective:

def rmtree(f: Path):
    if f.is_file():
        f.unlink()
    else:
        for child in f.iterdir():
            rmtree(child)
        f.rmdir()
maf88
  • 137
  • 1
  • 12
7

You might use pathlib3x - it offers a backport of the latest (at the date of writing this answer Python 3.10.a0) Python pathlib for Python 3.6 or newer, and a few additional functions like rmtree

>>> python -m pip install pathlib3x

>>> import pathlib3x as pathlib

>>> my_path = pathlib.Path('c:/tmp/some_directory')
>>> my_path.rmtree(ignore_errors=True)


you can find it on github or PyPi


Disclaimer: I'm the author of the pathlib3x library.

bitranox
  • 1,664
  • 13
  • 21
5

If you don't mind using a third-party library give path a try. Its API is similar to pathlib.Path, but provides some additional methods, including Path.rmtree() to recursively delete a directory tree.

AXO
  • 8,198
  • 6
  • 62
  • 63
5
def rm_rf(basedir):
    if isinstance(basedir,str): basedir = pathlib.Path(basedir)
    if not basedir.is_dir(): return
    for p in reversed(list(basedir.rglob("*"))):
        if p.is_file(): p.unlink()
        elif p.is_dir(): p.rmdir()
    basedir.rmdir()
evandrix
  • 6,041
  • 4
  • 27
  • 38
zalavari
  • 819
  • 9
  • 21
  • this does not look like it will remove non-empty subdirectories of basedir? The recursive answer from @maf88 looks better. – Thomas Browne Aug 15 '22 at 13:59