GithubHelp home page GithubHelp logo

lojack5 / ifileoperation Goto Github PK

View Code? Open in Web Editor NEW
4.0 4.0 0.0 38 KB

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

License: BSD 3-Clause "New" or "Revised" License

Python 100.00%

ifileoperation's People

Contributors

lojack5 avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar

ifileoperation's Issues

Implement IFileOperationSink

To allow for per-file progress tracking. At the most basic level, this will allow us to return a mapping of source_files -> final_files.

Heisenbug - CoInitialize sometimes needed when not in the main thread

Ran into this one when using FileOperator in a different thread.

Sometimes, when the code is running in a different thread than the original, we get an error that CoInitialize hasn't been called yet (the underlying API is through COM objects).

This one's a bit weird, The specifics of this case: I was using wxtrio to offload some long work into a thread using cooperative multitasking (async). I'll have to dig into the specifics of trio (wxtrio just runs trio in guest mode - and I was launching long sync functions using trio.to_thread.run_sync). I know trio uses a thread pool to run this jobs, but the question is: can a different thread end up running that code a different points?

The reason this needs a bit more digging is that CoInitialize and CoUninitialize must be called in balanced pairs in each thread msdn. If trio ends up having the code hop threads, this messes up the whole thing.

Normally, pythoncom handles this for us, but something is throwing it off. For the moment, a workaround is the call CoInitialize / CoUninitialize myself when in code that can run in a different thread (it's safe to call the pair more than once in a single thread), but I want to make sure there's no thread hopping going on. This brute force approach might end up with unbalanced calls.

Investigate dropping `comtypes`

I found this pywin32 example of wrapping an implementation for an IUnknown based interface. Investigate if we can use this method to wrap our implementations, letting us drop comtypes as a dependency.

I've tinkered with this a little and got IFileOperationProgressSink to work fine (basically just copied what the example did), but ran into issues with IBindCtx. Note pywin32 does have IFileOperationProgressSink defined in it's code, but not IBindCtx, so there's probably some more background work required there.

Incorrect calling of `IFileOperation` `.CopyItems`, `.DeleteItems`, `.RenameItems`, and `.MoveItems`

Each of these interface methods expect an IShellItemArray to hold the items to operate on, but we're passing in a bare list.

So for now these are all broken. Options:

  • Make these just iterate and call the non-plural version instead. This is the fix that would make it work, but need to investigate if the plural version have other side-effects (possibly only in the GUI?).
  • Track down the correct way to create these arrays. See SHCreateShellItemArray - although there's some weirdness about specifying a parent folder, so this might only work for files in the same directory?

WINE issues

It seems (some?, all?) WINE implementations only have stubs for IFileOperation.

WINE Info:

Note that everything past the IUnknown interface methods are not implemented! So it seems IFileOperation is pretty much a no-go on WINE until they implement this.

In the meantime, we could probably catch this specific case and convert it to a more useful error (NotImplementedError is the natural one, but I'm thinking not to use that because it's not based on OSError, and could mask a intended NotImplementedError elsewhere in the code. Then it's up to users of the library to decide how to proceed from there (use SHFileOperation instead, use native python methods, etc).


Initial information:

Partial traceback:

 File "C:\Program Files\Mopy\bash\env\windows.py", line 109, in file_operation
    with FileOperator(parent, op_flgs) as fo:
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\site-packages\ifileoperation\fileoperator.py", line 137, in __init__
    self.sink_cookie = self.ifo.Advise(self.sink.com_ptr)

pywintypes.com_error: (-2147467263, 'Not implemented.', None, None)

And this part happens in a section not wrapped by @convert_exceptions since this was unexpected, so the correct looking HRESULT is 0x80004001 - E_NOTIMPL.

@llde on the Wrye Bash discord dug a little further and got:

0148:fixme:shell:file_operation_Advise (0000000000439D90, 000000000A7AD098, 0000000000209220): stub.
0148:fixme:shell:file_operation_QueryInterface not implemented for {df0b3d60-548f-101b-8e65-08002b2bd119}.
0148:fixme:shell:file_operation_Advise (00000000003AB430, 000000000A7AEC98, 0000000000209220): stub.
0148:fixme:shell:file_operation_QueryInterface not implemented for {df0b3d60-548f-101b-8e65-08002b2bd119}.

Worst then ever all IFileOperation is a stub in wine

So needs some more information:

  • Host operating system information
  • WINE information (version, etc)
  • Any links to trackers for this issue for said WINE distributions (correct terminology?)

More standard exceptions

This is a catch-all issue for raising more standard exception types. Any HRESULTs found that should be converted to a built-in Python exception rather than IFileOperationError should reference this issue.

Finish setting up the repo

Just various bookkeeping/automation things to set up:

  • Automated tests for formatting (black, flake8, isort)
  • Automated tests for the code (not sure on this one since it has to interact with the filesystem)
  • Write a readme
  • Setup issue templates
  • Setup branch protection (do last).

Probably do a squash -> force push once everything is setup, just before branch protection, to cleanup the codebase.

Exception on commit when no operation is queued

Describe the bug
If no operation is queued using IFileOperation and IFileOperation.Commit is called, an exception with HRESULT 0x8000FFFF is raised (E_UNEXPECTED).

To Reproduce

from ifileoperation import *


def main():
    with FileOperator() as op:
        op.commit()


if __name__ == '__main__':
    main()

Expected behavior
This really should just be treated as a noop instead.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.