Python : How to delete a directory recursively using shutil.rmtree()

In this article we will discuss how to delete an empty directory and also all contents of directory recursively i.e including contents of its sub directories.

Delete an empty directory using os.rmdir()

Python’s os module provide a function to delete an empty directory i.e.

os.rmdir(pathOfDir)

Path of directory can be relative or absolute. It will delete the empty folder at given path.
It can also raise errors in following scenarios,

  • If directory is not empty then it will cause OSError i.e.
    • OSError: [WinError 145] The directory is not empty:
  • If given directory path is not pointing to a directory, then this error will be raised,
    • NotADirectoryError: [WinError 267] The directory name is invalid:
  • If there is no directory at given path then this error will be raised,
    • FileNotFoundError: [WinError 2] The system cannot find the file specified:

Let’s use this to delete an empty directory,

import os

#  Delete an empty directory using os.rmdir() and handle exceptions
try:
   os.rmdir('/somedir/log9')
except:
   print('Error while deleting directory')

Delete all files in a directory & sub-directories recursively using shutil.rmtree()

Python’s shutil module provides a function to delete all the contents of a directory i.e.

shutil.rmtree(path, ignore_errors=False, onerror=None)

It accepts 3 arguments ignore_errors, onerror and path.

path argument should be a path of the directory to be deleted. We we will discuss other arguments very soon.

Module required,

import shutil

Let’s use this to delete all the contents of a directory i.e.

import shutil

dirPath = '/somedir/logs/';

# Delete all contents of a directory using shutil.rmtree() and  handle exceptions
try:
   shutil.rmtree(dirPath)
except:
   print('Error while deleting directory')

It will delete all the contents of directory’/somedir/logs/’

But if any of the file in directory has read only attributes i.e. user can not delete that file, then it will raise an exception i.e.
PermissionError: [WinError 5] Access is denied:

Also it will not delete the remaining files. To handle this kind of scenario let’s use other argument ignore_errors.

shutil.rmtree() & ignore_errors

by passing ignore_errors=True in shultil.rmtree() we can ignore the errors encountered. It will go forward with deleting all the files and skip the files which raise exceptions while deleting.

Suppose we have a file in log directory that can not be deleted due to permission issues. So,

shutil.rmtree(dirPath, ignore_errors=True)

will remove all the other files from ‘/somedir/logs’ directory except the file with permission issues. Also, it will not raise any error.

But this might not be the case always, we might want to handle errors instead of ignoring them. For that we have other argument of shutil.rmtree() i.e. onerror.

Passing callbacks in shutil.rmtree() with onerror

shutil.rmtree(path, ignore_errors=False, onerror=None)

In onerror parameter we can pass a callback function to handle errors i.e.

shutil.rmtree(dirPath, onerror=handleError )

callback function passed in onerror must be a callable like this,

def handleError(func, path, exc_info):
    pass

It should accepts three parameters:

  • function
    • function which raised the exception
  • path
    • path name passed which raised the exception while removal
  • excinfo
    • exception information returned by sys.exc_info()

If any exception occurs while deleting a file in rmtree() and onerror callback is provided. Then callback will be called to handle the error. Afterwards shutil.rmtree() will continue deleting other files.

Now suppose we want to delete all the contents of directory ‘/somedir/logs’ . But we have a file in logs directory that can not be deleted due to permission issues. Let’s pass a callback to handle the error

import os
import shutil

'''
Error handler function
It will try to change file permission and call the calling function again,
'''
def handleError(func, path, exc_info):
    print('Handling Error for file ' , path)
    print(exc_info)
    # Check if file access issue
    if not os.access(path, os.W_OK):
       print('Hello')
       # Try to change the permision of file
       os.chmod(path, stat.S_IWUSR)
       # call the calling function again
       func(path)
# Delete all contents of a directory and handle errors
shutil.rmtree(dirPath, onerror=handleError )

Now while deleting all the files in given directory, as soon as rmtree() encounters a file that can not be deleted, it calls the callback passed in onerror parameter for that file.
In this callback we will check if it’s am access issue then we will change the file permission and then call called function func i.e. rmtree() with the path of file. t will eventually delete the file. Then rmtree() will continue deleting other files in the directory.

Complete example is as follows,

import os
import shutil
import stat

'''
Error handler function
It will try to change file permission and call the calling function again,
'''
def handleError(func, path, exc_info):
    print('Handling Error for file ' , path)
    print(exc_info)
    # Check if file access issue
    if not os.access(path, os.W_OK):
       print('Hello')
       # Try to change the permision of file
       os.chmod(path, stat.S_IWUSR)
       # call the calling function again
       func(path)

def main():

    print("******** Delete an empty directory *********")

    #  Delete an empty directory using os.rmdir() and handle exceptions
    try:
       os.rmdir('/somedir/log9')
    except:
       print('Error while deleting directory')

    print("******** Delete all contents of a directory *********")

    dirPath = '/somedir/logs/';

    # Delete all contents of a directory using shutil.rmtree() and  handle exceptions
    try:
       shutil.rmtree(dirPath)
    except:
       print('Error while deleting directory')

    # Delete all contents of a directory and ignore errors
    shutil.rmtree(dirPath, ignore_errors=True)

    # Delete all contents of a directory and handle errors
    shutil.rmtree(dirPath, onerror=handleError )

if __name__ == '__main__':
   main()

 

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top