Skip to content

Python wrapper around Win32's IFileOperation for performing filesystem operations.

License

Notifications You must be signed in to change notification settings

lojack5/IFileOperation

Repository files navigation

tests License Code style: black Imports: isort

IFileOperation- a simple wrapper to use Windows shell file operations.

This is a very small wrapper around IFileOperation to expose methods to perform file opereration ( move, copy, rename, delete) on files using Windows shell operations. This means these operations can be undone, files can be recycled, and you can allow users to handle name conflicts: just like when you do any of these through Windows Explorer.

At the moment this is a very young and so only exposes the file operations:

  • Move
  • Copy
  • Rename
  • Delete

More features may be added in the future, feel free to open a feature request for something specific.

Installation:

pip install ifileoperation

At the moment the library is tested to run on Python 3.11, but other version might work if pywin32 and comtypes are available.

Usage

All of the functionality is accessed via the FileOperator class. This is a scheduling context manager. If you're familiar with IFileOperation, you'll know that you queue a bunch of operations you wish to be performed, then order them to be executed all at once:

from Pathlib import Path
from ifileoperation import FileOperator, FileOperationFlags


if __name__ == '__main__':
    # Configure for recycling, showing a prompt if the file(s) can't be recyled
    # and must be deleted instead.
    recycle_flags = (
      # ALLOW_UNDO: for versions < Win8, ADDUNDORECORD is preferred for Win8+
      FileOperationFlags.ALLOW_UNDO | FileOperationFlags.ADDUNDORECORD |
      # RECYCLEONDELETE: Perform deletes as recycles
      FileOperationFlags.RECYCLEONDELETE |
      # WANTNUKEWARNING: If a file is too big to be recyled, show a warning that
      #  it will be deleted instead
      FileOperationFlags.WANTNUKEWARNING |
    )
    # Don't show any confirmation dialogs (except for WANTNUKEWARNING), or a UAC
    # prompt if elevation is required (default behavior)
    ui_flags = (
      # NO_CONFIMATION: Don't show confirmation dialogs (ie, treat all dialogs
      # as if 'Yes to All' was selected)
      FileOperationFlags.NO_CONFIRMATION |
    )
    config = recycle_flags | ui_flags
    with FileOperator(flags=config, commit_on_exit=True) as op:
        op.delete_file('Q:/foo.txt')
        op.delete_file(Path.cwd().join('bar.txt'))

FileOperator

A thin wrapper around IFileOperation, exposing the move, copy, rename, and delete operations. All paths can be specified as anything implementing the os.PathLike[str] interface: strings, Path objects, or your own custom PathLike class. The FileOperator instance is a context manager: you queue operations in the with block, and then commit them or have them committed automatically.

NOTE: FileOperator is not reentrant. Once you've entered the with block and exited, you should create a new instance to perform more operations.

Configuring:

Configuration is done via the constructor:

__init__(self, parent=None, flags=FileOperator.DEFAULT_FLAGS, commit_on_exit=False):
  • parent: A optional HANDLE to the parent that should own any dialog boxes shown. You may also pass a wx.Window object, and FileOperator will extract the handle for you.
  • flags: A FileOperationFlags instance indicating the options you want when performing the operations. See the Microsoft documentation on these flags for more details on what each means. These are also in the docstrings for FileOperationFlags, so you can read there them as well. Some common defaults are provided as well:
    • FileOperator.DEFAULT_FLAGS: This causes behavior as if the user had initiated the file operations from within Windows Exlorer with no modifier keys pressed (Shift, Ctrl, etc).
    • FileOperator.UNDO_FLAGS: This sets the flags needed to allow your operation to be undone with Ctrl+Z.
    • FileOperator.SEMI_SILENT_FLAGS: This hides the progress dialog, and only shows dialogs if user intervention is required. For example, if a name collision occurs, or when attempting to move a file that cannot be (temporarily) due to being open in another program, or if a file must be deleted instead of recycled.
    • FileOperator.FULL_SILENT_FLAGS: This performs the operations fully silently, responding to any prompts as if "Yes to All" or "Continue" was selected. Note this does not resolve other errors, and will fail if any error occurs. The only prompt that may be shown is a UAC prompt if elevation is required. You can further suppress the UAC prompt with FileOperationFlags.REQUIREELEVATION.
  • commit_on_exit: If set to True, all the queued operations will be automatically commited as long as no exceptions occurred withing the with block before exiting. Exceptions might still occur during the committing itself though!

Operations supported:

The basic file operations can be scheduled. In these method signatures, StrPath is defined as str | os.PathLike[str].

  • move_file(source: StrPath, destination: StrPath, new_name: str | None = None): Moves the file at source to the destination directory destination. If new_name is supplied, the file will also be renamed to the new name.
  • move_files(sources: Iterable[StrPath], destination: StrPath, new_name: str | None = None): Moves the specified files sources to the destination directory destination. If new_name is supplied, the files will all be renamed to the new name. In this case, you probably also want to supply FileOperationFlags.RENAMEONCOLLISION.
  • copy_file(source: StrPath, destination: StrPath, new_name: str | None = None): Like move, but performs a copy instead.
  • copy_files(sources: Iterable[StrPath], destination: StrPath): Like move (without the rename), but performs a copy instead.
  • rename_file(source: StrPath, new_name: str, allow_move: bool = True): Renames a file to a new name. Normally, new_name should just be the name of the new file, without its directory. However, if you're used to other file APIs that let you "rename" a file into a different directory, rename_file supports this as well. When allow_move is True, if it is detected that new_name is actually a path resolving to a different directory, this will automatically be transformed into a move_file operation instead.
  • rename_files(sources: Iterable[StrPath], new_name: str): Renames the given files to have the new name. Unlike rename_file, you cannot rename files into files in different directories. If multiple source files are in the same directory, you probably want to include FileOperationFlags.RENAMEONCOLLISION to prevent errors while renaming.
  • delete_file(source: StrPath): Deletes the target file.
  • delete_files(sources: Iterable[StrPath]): Deletes the target files.
  • commit(): Cause all the queue operations to be performed.

Post-commit attributes

After a commit, additional attributes are available on the FileOperator instance:

  • return_code: The HRESULT return code from the overall operation. This is usually not required to be inspected.
  • aborted: True if any of the operations were aborted / skipped.
  • results: A mapping of source filenames to destination filenames for successful operations. In the special case of deleted files, the destination will be 'RECYCLED' or 'DELETED' respectively.

Exceptions

This library may raise any of the following exceptions:

  • NotADirectoryError: This can happen when trying to rename a folder if a file already exists with the same name, or attempting to move/copy to a directory that doesn't exist.
  • IsADirectoryError: This can happen when trying to rename a file if a folder already exists with the same name.
  • PermissionError: Can occur for various reasons, common examples are UAC elevation being required, or modifying a read-only file.
  • FileExistsError: Can occur when attempting to create a new file, when one of the same name already exists.
  • FileNotFoundError: Happens if the source file for an operation cannot be found.
  • ifileoperation.IFileOperationError: For all other errors that occur. These are a simple wrapper around pythoncom.com_error, and expose an .hresult attribute with the HRESULT that triggered the error. In the future, more HRESULTs may be converted into standard library OSErrors.

Contributing

Pull requests are welcome! We use branch protection with some (minimal) tests at the moment: just formatting really. To install the dependencies needed for development, use: pip install -r requirements-dev.txt

And before committing make sure to check against the tests and formatters:

  • pytest: No tests at the moment
  • black ifileoperation: Code formatting
  • isort ifileoperation: Code formatting
  • flake8 ifileoperation: Linting

IFileOperation uses semantic versioning, but if you're unsure if your changes need a version bump feel free to note that in your pull request and you'll get feedback!

If you're not into coding, you can open a Bug Report or Feature Request and I'll look into it.

Known Issues:

  1. Incompatible with WINE: see: #9

About

Python wrapper around Win32's IFileOperation for performing filesystem operations.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages