Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I unlock locked files and folders (mac) with Python

As a "clean up" after my script's main purpose is complete, a function is called to recursively look through each folder and remove all files that end in a pre-determined set of extensions.

I during my testing, I discovered that some files with a file extension in the list of ones to delete actually throw an error: [Errno 1] Operation not permitted: '/location/of/locked/file.png. Looking at the file itself, it appears to be Locked (on mac).

  1. How would I go about removing the locked attribute (should it exist) from every file/folder using Python, then delete the file if it ends in the extension?
    Preferably this can all be done in the same function below, as it takes a long time to traverse the input directory - handling each only once is the way to go.
  2. How does this affect the script's integrity on Windows?
    I have taken care of programming it in a way that makes it compatible between the OSs but (to my knowledge) the locked attribute does not exist on Windows the way it does on mac and could cause unknown side-effects.

REMOVE_FILETYPES = ('.png', '.jpg', '.jpeg', '.pdf')

def cleaner(currentPath):
  if not os.path.isdir(currentPath):
    if currentPath.endswith(REMOVE_FILETYPES) or os.path.basename(currentPath).startswith('.'):
      try:
        os.remove(currentPath)
        print('REMOVED: \"{removed}\"'.format(removed = currentPath))
      except BaseException as e:
        print('ERROR: Could not remove: \"{failed}\"'.format(failed = str(e)))
      finally:
        return True
    return False
    
  if all([cleaner(os.path.join(currentPath, file)) for file in os.listdir(currentPath)]):
    try:
      os.rmdir(currentPath)
      print('REMOVED: \"{removed}\"'.format(removed = currentPath))
    except:
      print('ERROR: Could not remove: \"{failed}\"'.format(failed = currentPath))
    finally:
      return True
  return False

cleaner(r'/path/to/parent/dir')

I would really appreciate if somebody could show me how to integrate such functionality into the sub-routine. Cheers.


EDIT: Removed error handling as per request

def cleaner(currentPath):
    if sys.platform == 'darwin':
        os.system('chflags nouchg {}'.format(currentPath))
    if not os.path.isdir(currentPath):
        if currentPath.endswith(REMOVE_FILETYPES) or os.path.basename(currentPath).startswith('.'):
            try:
                os.remove(currentPath)
                print('REMOVED: \"{removed}\"'.format(removed=currentPath))
            except PermissionError:
                if sys.platform == 'darwin':
                    os.system('chflags nouchg {}'.format(currentPath))
                    os.remove(currentPath)
    if all([cleaner(os.path.join(currentPath, file)) for file in os.listdir(currentPath)]) and not currentPath == SOURCE_DIR:
        os.rmdir(currentPath)
        print('REMOVED: \"{removed}\"'.format(removed=currentPath))
like image 798
ProGrammer Avatar asked Feb 07 '18 23:02

ProGrammer


1 Answers

You can unlock the file with the chflags command:

os.system('chflags nouchg {}'.format(filename))

(There is a function os.chflags, but the flag associated with the locked status is not a regular one, but what the os module documentation calls a "user-defined" flag, as you can see by looking at os.stat(locked_filename).st_flags.)

To solve your problem I'd add the chflags command above to a specific except: for the error you get trying to remove a locked file, along with a platform check:

try:
    os.remove(currentPath)
    print('REMOVED: \"{removed}\"'.format(removed = currentPath))
except PermissionError:
    if sys.platform == 'darwin':
        os.system('chflags nouchg {}'.format(currentPath))
        os.remove(currentPath)
    else:
        raise
except BaseException as e:
    ...
like image 120
Nathan Vērzemnieks Avatar answered Oct 20 '22 17:10

Nathan Vērzemnieks