ifileoperation


Nameifileoperation JSON
Version 1.2.6 PyPI version JSON
download
home_page
SummaryPython wrapper for using Win32's IFileOperation for manipulating the filesystem.
upload_time2023-08-02 04:02:23
maintainer
docs_urlNone
authorlojack5
requires_python>=3.11
licenseBSD 3-Clause
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ![tests](https://github.com/lojack5/structured/actions/workflows/tests.yml/badge.svg)
[![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/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:

```python
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:
```python
__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](
  https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifileoperation-setoperationflags)
  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 `HRESULT`s may be converted into standard library `OSError`s.


## 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](https://semver.org/), 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](https://github.com/lojack5/IFileOperation/issues)
and I'll look into it.


## Known Issues:
1. Incompatible with WINE: see: https://github.com/lojack5/IFileOperation/issues/9

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "ifileoperation",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": "",
    "keywords": "",
    "author": "lojack5",
    "author_email": "",
    "download_url": "https://files.pythonhosted.org/packages/f3/f4/c16f3eafb9bfb73e25e271ed0be539acd3f147e762fc45a7ddaa72b0b8e9/ifileoperation-1.2.6.tar.gz",
    "platform": null,
    "description": "![tests](https://github.com/lojack5/structured/actions/workflows/tests.yml/badge.svg)\r\n[![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)\r\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\r\n[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)\r\n\r\n\r\n# IFileOperation- a simple wrapper to use Windows shell file operations.\r\nThis is a very small wrapper around IFileOperation to expose methods to perform file opereration (\r\nmove, copy, rename, delete) on files using Windows shell operations.  This means these\r\noperations can be undone, files can be recycled, and you can allow users to handle name conflicts:\r\njust like when you do any of these through Windows Explorer.\r\n\r\nAt the moment this is a very young and so only exposes the file operations:\r\n- Move\r\n- Copy\r\n- Rename\r\n- Delete\r\n\r\nMore features may be added in the future, feel free to open a feature request for something\r\nspecific.\r\n\r\n\r\n## Installation:\r\n`pip install ifileoperation`\r\n\r\nAt the moment the library is tested to run on Python 3.11, but other version might work if `pywin32`\r\n and `comtypes` are available.\r\n\r\n\r\n## Usage\r\nAll of the functionality is accessed via the `FileOperator` class.  This is a scheduling context\r\nmanager. If you're familiar with IFileOperation, you'll know that you queue a bunch of operations\r\nyou wish to be performed, then order them to be executed all at once:\r\n\r\n```python\r\nfrom Pathlib import Path\r\nfrom ifileoperation import FileOperator, FileOperationFlags\r\n\r\n\r\nif __name__ == '__main__':\r\n    # Configure for recycling, showing a prompt if the file(s) can't be recyled\r\n    # and must be deleted instead.\r\n    recycle_flags = (\r\n      # ALLOW_UNDO: for versions < Win8, ADDUNDORECORD is preferred for Win8+\r\n      FileOperationFlags.ALLOW_UNDO | FileOperationFlags.ADDUNDORECORD |\r\n      # RECYCLEONDELETE: Perform deletes as recycles\r\n      FileOperationFlags.RECYCLEONDELETE |\r\n      # WANTNUKEWARNING: If a file is too big to be recyled, show a warning that\r\n      #  it will be deleted instead\r\n      FileOperationFlags.WANTNUKEWARNING |\r\n    )\r\n    # Don't show any confirmation dialogs (except for WANTNUKEWARNING), or a UAC\r\n    # prompt if elevation is required (default behavior)\r\n    ui_flags = (\r\n      # NO_CONFIMATION: Don't show confirmation dialogs (ie, treat all dialogs\r\n      # as if 'Yes to All' was selected)\r\n      FileOperationFlags.NO_CONFIRMATION |\r\n    )\r\n    config = recycle_flags | ui_flags\r\n    with FileOperator(flags=config, commit_on_exit=True) as op:\r\n        op.delete_file('Q:/foo.txt')\r\n        op.delete_file(Path.cwd().join('bar.txt'))\r\n```\r\n\r\n\r\n## FileOperator\r\nA thin wrapper around IFileOperation, exposing the move, copy, rename, and\r\ndelete operations.  All paths can be specified as anything implementing the\r\n`os.PathLike[str]` interface: strings, `Path` objects, or your own custom\r\n`PathLike` class.  The `FileOperator` instance is a context manager: you queue\r\noperations in the `with` block, and then `commit` them or have them committed\r\nautomatically.\r\n\r\nNOTE: `FileOperator` is not reentrant.  Once you've entered the `with` block and\r\nexited, you should create a new instance to perform more operations.\r\n\r\n### Configuring:\r\nConfiguration is done via the constructor:\r\n```python\r\n__init__(self, parent=None, flags=FileOperator.DEFAULT_FLAGS, commit_on_exit=False):\r\n```\r\n- `parent`: A optional `HANDLE` to the parent that should own any dialog boxes\r\n  shown. You may also pass a `wx.Window` object, and `FileOperator` will extract\r\n  the handle for you.\r\n- `flags`: A `FileOperationFlags` instance indicating the options you want when\r\n  performing the operations.  See the Microsoft documentation on [these flags](\r\n  https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifileoperation-setoperationflags)\r\n  for more details on what each means.  These are also in the docstrings for\r\n  `FileOperationFlags`, so you can read there them as well.  Some common defaults are\r\n  provided as well:\r\n  - `FileOperator.DEFAULT_FLAGS`: This causes behavior as if the user had initiated the\r\n    file operations from within Windows Exlorer with no modifier keys pressed (Shift,\r\n    Ctrl, etc).\r\n  - `FileOperator.UNDO_FLAGS`: This sets the flags needed to allow your operation to be\r\n    undone with `Ctrl+Z`.\r\n  - `FileOperator.SEMI_SILENT_FLAGS`: This hides the progress dialog, and only shows\r\n    dialogs if user intervention is required. For example, if a name collision occurs, or\r\n    when attempting to move a file that cannot be (temporarily) due to being open in\r\n    another program, or if a file must be deleted instead of recycled.\r\n  - `FileOperator.FULL_SILENT_FLAGS`: This performs the operations fully silently, responding\r\n    to any prompts as if \"Yes to All\" or \"Continue\" was selected.  Note this does *not*\r\n    resolve other errors, and will fail if any error occurs.  The only prompt that may be shown\r\n    is a UAC prompt if elevation is required.  You can further suppress the UAC prompt with\r\n    `FileOperationFlags.REQUIREELEVATION`.\r\n- `commit_on_exit`: If set to `True`, all the queued operations will be\r\n  automatically commited as long as no exceptions occurred withing the `with`\r\n  block before exiting.  Exceptions *might* still occur during the committing\r\n  itself though!\r\n\r\n### Operations supported:\r\nThe basic file operations can be scheduled.  In these method signatures,\r\n`StrPath` is defined as `str | os.PathLike[str]`.\r\n- `move_file(source: StrPath, destination: StrPath, new_name: str | None = None)`:\r\n  Moves the file at `source` to the destination directory `destination`.  If `new_name`\r\n  is supplied, the file will also be renamed to the new name.\r\n- `move_files(sources: Iterable[StrPath], destination: StrPath, new_name: str | None = None)`:\r\n  Moves the specified files `sources` to the destination directory `destination`.  If\r\n  `new_name` is supplied, the files will all be renamed to the new name. In this case,\r\n  you probably also want to supply `FileOperationFlags.RENAMEONCOLLISION`.\r\n- `copy_file(source: StrPath, destination: StrPath, new_name: str | None = None)`:\r\n  Like move, but performs a copy instead.\r\n- `copy_files(sources: Iterable[StrPath], destination: StrPath)`:\r\n  Like move (without the rename), but performs a copy instead.\r\n- `rename_file(source: StrPath, new_name: str, allow_move: bool = True)`:\r\n  Renames a file to a new name.  Normally, `new_name` should just be the name of\r\n  the new file, without its directory.  However, if you're used to other file\r\n  APIs that let you \"rename\" a file into a different directory, `rename_file`\r\n  supports this as well.  When `allow_move` is `True`, if it is detected that\r\n  `new_name` is actually a path resolving to a different directory, this will\r\n  automatically be transformed into a `move_file` operation instead.\r\n- `rename_files(sources: Iterable[StrPath], new_name: str)`:  Renames the given\r\n  files to have the new name.  Unlike `rename_file`, you cannot rename files\r\n  into files in different directories. If multiple source files are in the same\r\n  directory, you probably want to include `FileOperationFlags.RENAMEONCOLLISION` to\r\n  prevent errors while renaming.\r\n- `delete_file(source: StrPath)`: Deletes the target file.\r\n- `delete_files(sources: Iterable[StrPath])`: Deletes the target files.\r\n- `commit()`: Cause all the queue operations to be performed.\r\n\r\n### Post-commit attributes\r\nAfter a `commit`, additional attributes are available on the `FileOperator` instance:\r\n- `return_code`: The `HRESULT` return code from the overall operation. This is usually\r\n  not required to be inspected.\r\n- `aborted`: `True` if any of the operations were aborted / skipped.\r\n- `results`: A mapping of source filenames to destination filenames for successful\r\n  operations.  In the special case of deleted files, the destination will be `'RECYCLED'`\r\n  or `'DELETED'` respectively.\r\n\r\n### Exceptions\r\nThis library may raise any of the following exceptions:\r\n  - `NotADirectoryError`: This can happen when trying to rename a folder if a file\r\n    already exists with the same name, or attempting to move/copy to a directory\r\n    that doesn't exist.\r\n  - `IsADirectoryError`: This can happen when trying to rename a file if a folder\r\n    already exists with the same name.\r\n  - `PermissionError`: Can occur for various reasons, common examples are UAC\r\n    elevation being required, or modifying a read-only file.\r\n  - `FileExistsError`: Can occur when attempting to create a new file, when one\r\n    of the same name already exists.\r\n  - `FileNotFoundError`: Happens if the source file for an operation cannot be\r\n    found.\r\n  - `ifileoperation.IFileOperationError`: For all other errors that occur. These\r\n    are a simple wrapper around `pythoncom.com_error`, and expose an `.hresult`\r\n    attribute with the `HRESULT` that triggered the error.\r\nIn the future, more `HRESULT`s may be converted into standard library `OSError`s.\r\n\r\n\r\n## Contributing\r\nPull requests are welcome!  We use branch protection with some (minimal) tests\r\nat the moment: just formatting really. To install the dependencies needed for\r\ndevelopment, use:\r\n`pip install -r requirements-dev.txt`\r\n\r\nAnd before committing make sure to check against the tests and formatters:\r\n- `pytest`: No tests at the moment\r\n- `black ifileoperation`: Code formatting\r\n- `isort ifileoperation`: Code formatting\r\n- `flake8 ifileoperation`: Linting\r\n\r\nIFileOperation uses [semantic versioning](https://semver.org/), but if you're\r\nunsure if your changes need a version bump feel free to note that in your pull\r\nrequest and you'll get feedback!\r\n\r\nIf you're not into coding, you can open a [Bug Report or Feature Request](https://github.com/lojack5/IFileOperation/issues)\r\nand I'll look into it.\r\n\r\n\r\n## Known Issues:\r\n1. Incompatible with WINE: see: https://github.com/lojack5/IFileOperation/issues/9\r\n",
    "bugtrack_url": null,
    "license": "BSD 3-Clause",
    "summary": "Python wrapper for using Win32's IFileOperation for manipulating the filesystem.",
    "version": "1.2.6",
    "project_urls": {
        "Bug Tracker": "https://github.com/lojack5/IFileOperation/issues",
        "Homepage": "https://github.com/lojack5/IFileOperation"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "c3554d3adca63fdccc9929c4bb9542cff6927857db6b1f947048569054117d18",
                "md5": "d584716322b41e9bcb26a5d8330f2bc1",
                "sha256": "3e76f62541cb9619102162000b92fc188b98e49bd29658aa58c1fdaa988d5916"
            },
            "downloads": -1,
            "filename": "ifileoperation-1.2.6-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "d584716322b41e9bcb26a5d8330f2bc1",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 24441,
            "upload_time": "2023-08-02T04:02:21",
            "upload_time_iso_8601": "2023-08-02T04:02:21.297327Z",
            "url": "https://files.pythonhosted.org/packages/c3/55/4d3adca63fdccc9929c4bb9542cff6927857db6b1f947048569054117d18/ifileoperation-1.2.6-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "f3f4c16f3eafb9bfb73e25e271ed0be539acd3f147e762fc45a7ddaa72b0b8e9",
                "md5": "163367aa7e9843e391f506d7e6026640",
                "sha256": "9a31fb8db574e490030c0c38c40b415920bd7d4ec9e9e0c8e80ee98859517837"
            },
            "downloads": -1,
            "filename": "ifileoperation-1.2.6.tar.gz",
            "has_sig": false,
            "md5_digest": "163367aa7e9843e391f506d7e6026640",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 24589,
            "upload_time": "2023-08-02T04:02:23",
            "upload_time_iso_8601": "2023-08-02T04:02:23.055483Z",
            "url": "https://files.pythonhosted.org/packages/f3/f4/c16f3eafb9bfb73e25e271ed0be539acd3f147e762fc45a7ddaa72b0b8e9/ifileoperation-1.2.6.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-08-02 04:02:23",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "lojack5",
    "github_project": "IFileOperation",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [],
    "lcname": "ifileoperation"
}
        
Elapsed time: 0.09297s