Python library to natively send files to Trash (or Recycle bin) on all platforms.

Overview

Send2Trash -- Send files to trash on all platforms

Send2Trash is a small package that sends files to the Trash (or Recycle Bin) natively and on all platforms. On OS X, it uses native FSMoveObjectToTrashSync Cocoa calls. On Windows, it uses native IFileOperation call if on Vista or newer and pywin32 is installed or falls back to SHFileOperation calls. On other platforms, if PyGObject and GIO are available, it will use this. Otherwise, it will fallback to its own implementation of the trash specifications from freedesktop.org.

ctypes is used to access native libraries, so no compilation is necessary.

Send2Trash supports Python 2.7 and up (Python 3 is supported).

Status: Additional Help Welcome

Additional help is welcome for supporting this package. Specifically help with the OSX and Linux issues and fixes would be most appreciated.

Installation

You can download it with pip:

python -m pip install -U send2trash

or you can download the source from http://github.com/arsenetar/send2trash and install it with:

>>> python setup.py install

Usage

>>> from send2trash import send2trash
>>> send2trash('some_file')
>>> send2trash(['some_file1', 'some_file2'])

On Freedesktop platforms (Linux, BSD, etc.), you may not be able to efficiently trash some files. In these cases, an exception send2trash.TrashPermissionError is raised, so that the application can handle this case. This inherits from PermissionError (OSError on Python 2). Specifically, this affects files on a different device to the user's home directory, where the root of the device does not have a .Trash directory, and we don't have permission to create a .Trash-$UID directory.

For any other problem, OSError is raised.

Comments
  • Windows Performance Improvement & Multi-Item support

    Windows Performance Improvement & Multi-Item support

    • Use pywin32 to implement IFileOperation
    • Add ability to allow passing a list of files to send2trash across all platforms
    • Batch trash operations on windows for both IFileOperation and SHFileOperation

    Notes:

    • My code format plugin formatted the contents of the files I changed, let me know if this is an issue and I can back those out (it fixed some flake8 style issues as well)
    • IFileOperation should open up the ability to address #28 but I'll add in the extras for that in another PR.
    opened by arsenetar 11
  • Fails with python 3.6.3 on windows

    Fails with python 3.6.3 on windows

    I get the following when calling send2trash on python 3.6.3 on windows 7: File "...\AppData\Local\Programs\Python\Python36\lib\site-packages\send2trash\plat_win.py", line 49, in send2trash fileop.pFrom = LPCWSTR(path + '\0') ValueError: embedded null character

    I'm pretty sure it didn't fail until I switched to 3.6.3. My guess is that this fix is responsible.

    opened by rkhwaja 11
  • Error while running send2trash on Python 3.7.0 beta 2

    Error while running send2trash on Python 3.7.0 beta 2

    I have a small script that uses send2trash and works very well in Python 3.6 (Mac OS X 10.11 El Capitan). However, when I try to run it in Python 3.7.0 beta 2, it fails with the traceback bellow.

    best regards, victor

    Traceback (most recent call last):
      File "/Users/fact/Dropbox/Aplicativos/Filemaker_backups/NPK-Backup/remove_old_archives.py", line 26, in <module>
        from send2trash import send2trash
      File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/send2trash/__init__.py", line 12, in <module>
        from .plat_osx import send2trash
      File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/send2trash/plat_osx.py", line 17, in <module>
        GetMacOSStatusCommentString = Foundation.GetMacOSStatusCommentString
      File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ctypes/__init__.py", line 369, in __getattr__
        func = self.__getitem__(name)
      File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ctypes/__init__.py", line 374, in __getitem__
        func = self._FuncPtr((name_or_ordinal, self))
    AttributeError: dlsym(RTLD_DEFAULT, GetMacOSStatusCommentString): symbol not found
    
    opened by victordomingos 9
  • Use bytes paths in plat_other

    Use bytes paths in plat_other

    This fixes issues with non-ascii paths when using the fallback freedesktop trash implementation.

    This is a rather ugly set of changes, but it's based on two things:

    1. urllib.quote() on Python 2 requires bytes. On Python 3, it can handle unicode or bytes.
    2. Paths on Unix filesystems are really bytes. We can usually represent them as unicode, but for 100% fidelity, they should be passed around as bytes.

    So this is a complete switch around from #12: rather than converting all paths to unicode when they're passed in, convert them all to bytes.

    I added tests for passing in a path as unicode or as bytes. Before the changes, both tests fail under Python 2. Afterwards, everything passes, on both versions of Python.

    opened by takluyver 9
  • "OSError: [Errno 120] This function is not supported by the system."

    Hi! Whenever I try running create_project.py i get this error:

    Traceback (most recent call last):
      File "test_project.py", line 14, in <module>
        add_on = create_project.CreateProject( "myaddon","mya")  #This Class Will create the project
      File "C:\Users\Retcy\Downloads\8. Coding Fun\neptune_api\neptune_lib\core\create_project.py", line 108, in __init__
        send2trash.send2trash(self.name)
      File "C:\Users\Retcy\AppData\Local\Programs\Python\Python37-32\lib\site-packages\send2trash\plat_win_legacy.py", line 146, in send2trash
        raise WindowsError(result, FormatError(result), paths)
    OSError: [Errno 120] This function is not supported by the system.: ['C:\\Users\\Retcy\\DOWNLO~1\\8. Coding Fun\\neptune_api\\builds\\myaddon'] 
    

    Code: create_project.txt

    opened by retr0cube 7
  • File not move to Recycle Bin in Windows

    File not move to Recycle Bin in Windows

    I try to use send2trash('file_path') but file still not move to my Recycle Bin with no any error message.

    It's look like the issue came from get_short_path_name() because it return u'' But when I bypass get_short_path_name() method, send2trash() seem working for me.

    def get_short_path_name(long_name):
        return long_name
        # if not long_name.startswith('\\\\?\\'):
        #     long_name = '\\\\?\\' + long_name
        # buf_size = GetShortPathNameW(long_name, None, 0)
        # output = create_unicode_buffer(buf_size)
        # GetShortPathNameW(long_name, output, buf_size)
        # return output.value[4:]  # Remove '\\?\' for SHFileOperationW
    

    I'm using python 2.7.14 on Windows 10.0.16299 .

    opened by Shayen 7
  • 🚧 WIP: macOS trash

    🚧 WIP: macOS trash "put back" feature [ctypes]

    This is a WIP!


    This is my initial attempt at using ctypes, just putting it up online so we have a record of it, and also to help catch if I've done anything wrong.

    I was hopeful about the progress I was making, until I came across some hurdles that have blocked this approach. At the moment running it causes a segmentation fault (I believe because of the incorrectly initialised AEDesc). We need:

    • Need to find a way to initialise:
      • [ ] AEDesc
      • [ ] AEDataStorage
    • Need to confirm how the following values should be defined:
      • [ ] typeKernelProcessID
      • [ ] typeWildCard
      • [ ] keyDirectObject
      • [ ] pid_t

    If this turns out to be too arch-dependent, or too much of a hack, I'll open a different PR using some compiled Objective-C.

    Fixes #9.

    /cc @BoboTiG @hsoft

    opened by acheronfail 7
  • ImportError: no module named mac

    ImportError: no module named mac

    After cloning the directory, entering it, and typing 'pip install .' I got what seemed to be a successful build and install, ending in:

    Successfully built Send2Trash
    Installing collected packages: Send2Trash
    Successfully installed Send2Trash-1.8.1b0
    

    But when I try to run my Python 2.7 script I get an error message:

    Traceback (most recent call last):
      File "/Users/larryy/Dropbox/src/file_rename.py", line 8, in <module>
        from send2trash import send2trash
      File "/usr/local/lib/python2.7/site-packages/send2trash/__init__.py", line 12, in <module>
        from .mac import send2trash
    ImportError: No module named mac
    

    As shown, the line of code in question is a standard import:

    from send2trash import send2trash
    

    This is macOS Monterey 12.4 (latest) and the latest Send2Trash (1.8.1b0). Apologies if I've just done something stupid setting things up.

    opened by larryy 6
  • Add CLI Using __main__.py

    Add CLI Using __main__.py

    Great project. I wish this functionality were included in the standard library. Thank you.

    I'm curious how the methodology here compares with this JavaScript package: https://github.com/sindresorhus/trash

    If you have time, what are your thoughts?

    opened by grantjenks 6
  • Fix Windows compatibility with multithreading

    Fix Windows compatibility with multithreading

    Hi.

    I've faced with some tests of Jupyterhub Notebook repo are failing:

    Traceback (most recent call last):
      File "c:\hostedtoolcache\windows\python\3.6.8\x64\lib\site-packages\tornado\web.py", line 1704, in _execute
        result = await result
      File "c:\hostedtoolcache\windows\python\3.6.8\x64\lib\site-packages\tornado\gen.py", line 234, in wrapper
        yielded = ctx_run(next, result)
      File "c:\hostedtoolcache\windows\python\3.6.8\x64\lib\site-packages\tornado\gen.py", line 162, in _fake_ctx_run
        return f(*args, **kw)
      File "D:\a\notebook\notebook\notebook\services\contents\handlers.py", line 237, in delete
        yield maybe_future(cm.delete(path))
      File "D:\a\notebook\notebook\notebook\services\contents\manager.py", line 279, in delete
        self.delete_file(path)
      File "D:\a\notebook\notebook\notebook\services\contents\filemanager.py", line 533, in delete_file
        send2trash(os_path)
      File "c:\hostedtoolcache\windows\python\3.6.8\x64\lib\site-packages\send2trash\plat_win_modern.py", line 31, in send2trash
        shell.CLSID_FileOperation, None, pythoncom.CLSCTX_ALL, shell.IID_IFileOperation,
    pywintypes.com_error: (-2147221008, 'CoInitialize has not been called.', None, None)
    

    According to https://stackoverflow.com/questions/37258257/why-does-this-script-not-work-with-threading-python, it is important to call .CoInitialize() method to allow win32 API calls in multithread mode.

    opened by dolfinus 5
  • fix trash_move

    fix trash_move

    the same as https://github.com/arsenetar/send2trash/pull/41

    this bug prevents using jupyter* in research environments where file systems aren't always local

    e.g. https://github.com/jupyterlab/jupyterlab/issues/5781 and our own use at https://cri.uchicago.edu/

    cc @annawoodard

    opened by makslevental 4
  • Can't delete files or folders on OneDrive

    Can't delete files or folders on OneDrive

    I'm using JupyterLab and OneDrive on an M1 Macbook Air with Macos 12.6.1. I get an OSError when trying to delete a file or folder on Onedrive with JupyterLab. Maybe OneDrive's Files On-Demand is causing the problem.

          File "/opt/homebrew/Caskroom/mambaforge/base/lib/python3.10/site-packages/tornado/web.py", line 1713, in _execute
            result = await result
          File "/opt/homebrew/Caskroom/mambaforge/base/lib/python3.10/site-packages/jupyter_server/services/contents/handlers.py", line 275, in delete
            await ensure_async(cm.delete(path))
          File "/opt/homebrew/Caskroom/mambaforge/base/lib/python3.10/site-packages/jupyter_server/services/contents/manager.py", line 417, in delete
            self.delete_file(path)
          File "/opt/homebrew/Caskroom/mambaforge/base/lib/python3.10/site-packages/jupyter_server/services/contents/filemanager.py", line 511, in delete_file
            send2trash(os_path)
          File "/opt/homebrew/Caskroom/mambaforge/base/lib/python3.10/site-packages/send2trash/plat_osx_pyobjc.py", line 29, in send2trash
            check_op_result(op_result)
          File "/opt/homebrew/Caskroom/mambaforge/base/lib/python3.10/site-packages/send2trash/plat_osx_pyobjc.py", line 16, in check_op_result
            raise OSError(op_result[2].localizedFailureReason())
        OSError: None```
    opened by peccator085 0
  • Failed to move files to trash in overlayfs

    Failed to move files to trash in overlayfs

    In overlayfs, directories always have st_dev from lower filesystem whereas files can have st_dev either from lower filesystem or upper filesystem. Due to this behavior, https://github.com/arsenetar/send2trash/blob/1.8.1b0/send2trash/plat_other.py#L178 always fails when the file's st_dev is from the upper filesystem. gio already tackled this issue, by avoiding comparison b/w file_dev and trash_dev, instead, it compares parent_dir(file).dev to trash_dev (ref: https://gitlab.gnome.org/GNOME/glib/-/blob/main/gio/glocalfile.c#L2019). I think send2trash also can adapt the same approach.

    opened by pjknkda 1
  • Module not callable

    Module not callable

    Working through the Automate the Boring stuff book and came across Send2Trash. Tried following the tutorial but found that the module was not callable.

    my code is

    send to trash

    I am doing this in VSCode and pylint keeps giving the error that the module is not callable:

    I have tried reinstalling send2trash and restarting the computer.

    opened by PeterCLarsen 1
  • send2trash function failed to delete a directory with unknown error

    send2trash function failed to delete a directory with unknown error

    This function is referred in Jupyter server folder deletion logic. We observed the error in jupyterlab app and here in this description we try to simplify the scenario and reproduce it in a windows command prompt.

    In python interpreter, we import the send2trash: from send2trash import send2trash

    And run this code line, deleting the directory: send2trash('C:\\Users\\traveler\\AppData\\Local\\projects\\Untitled Folder') In this case the "Untitled Folder" is newly created under a jlabapp project and there is a newly created ipynb file in it, it failed to delete "Untitled Folder" and throws message:

    >>> send2trash('C:\Users\traveler\AppData\Local\projects\Untitled Folder') Traceback (most recent call last): File "C:\Users\traveler\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\send2trash\plat_win_modern.py", line 61, in send2trash result = fileop.PerformOperations() pywintypes.com_error: (-2144927705, 'OLE error 0x80270027', None, None)

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last): File "", line 1, in File "C:\Users\traveler\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\send2trash\plat_win_modern.py", line 69, in send2trash raise OSError(None, error.strerror, path, error.hresult) OSError: [WinError -2144927705] OLE error 0x80270027: 'C:\Users\traveler\AppData\Local\projects\Untitled Folder'

    However, when we close the project tab and reopen it. The above code line (exactly the same func and path) works and was able to delete "Untitled Folder".

    Watching "Untitled Folder" in File Explorer and it seems there is no visible difference between the above two cases.

    opened by WentingTan 2
  • Five tests error/fail in a testing chroot on Linux with bizarre error messages

    Five tests error/fail in a testing chroot on Linux with bizarre error messages

    Hi! As part of the Debian build, the package tests are run in a minimal chroot environment. I've set the HOME environment variable first:

    (sid-sbuild)[email protected]:/build/send2trash-IKQvlF/send2trash-1.8.1~b0$ export HOME=$(mktemp -p)
    (sid-sbuild)[email protected]:/build/send2trash-IKQvlF/send2trash-1.8.1~b0$ echo $HOME
    /tmp/tmp.CSc9dofzh7/
    (sid-sbuild)[email protected]:/build/send2trash-IKQvlF/send2trash-1.8.1~b0$ mkdir -p $HOME/.local/share/Trash
    (sid-sbuild)[email protected]:/build/send2trash-IKQvlF/send2trash-1.8.1~b0$ dpkg-buildpackage -b -us -uc
    dpkg-buildpackage: info: source package send2trash
    dpkg-buildpackage: info: source version 1.8.1~b0-1
    dpkg-buildpackage: info: source distribution unstable
    dpkg-buildpackage: info: source changed by Julian Gilbey <[email protected]>
    dpkg-buildpackage: info: host architecture amd64
    [... build the python package, snipped ...]
    Successfully built Send2Trash-1.8.1b0-py3-none-any.whl
    I: pybuild plugin_pyproject:123: Unpacking wheel built for python3.10 with "installer" module
    [... do the same for Python 3.9, snipped ...]
    Successfully built Send2Trash-1.8.1b0-py3-none-any.whl
    I: pybuild plugin_pyproject:123: Unpacking wheel built for python3.9 with "installer" module
       dh_auto_test -O--buildsystem=pybuild
    I: pybuild base:237: cd '/build/send2trash-IKQvlF/send2trash-1.8.1~b0/.pybuild/cpython3_3.10/build'; python3.10 -m pytest tests
    ============================= test session starts ==============================
    platform linux -- Python 3.10.2, pytest-6.2.5, py-1.10.0, pluggy-0.13.0
    rootdir: /build/send2trash-IKQvlF/send2trash-1.8.1~b0/.pybuild/cpython3_3.10/build
    collected 10 items / 1 skipped / 9 selected                                    
    
    tests/test_plat_other.py ....FFFEF                                       [ 80%]
    tests/test_script_main.py ..                                             [100%]
    
    ==================================== ERRORS ====================================
    ________________ ERROR at teardown of test_trash_topdir_failure ________________
    
        @pytest.fixture
        def testExtVol():
            trashTopdir = mkdtemp(prefix="s2t")
            volume = ExtVol(trashTopdir)
            fileName = "test.txt"
            filePath = op.join(volume.trashTopdir, fileName)
            touch(filePath)
            assert op.exists(filePath) is True
            yield volume, fileName, filePath
    >       volume.cleanup()
    
    tests/test_plat_other.py:156: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    tests/test_plat_other.py:144: in cleanup
        shutil.rmtree(self.trashTopdir)
    /usr/lib/python3.10/shutil.py:717: in rmtree
        _rmtree_safe_fd(fd, path, onerror)
    /usr/lib/python3.10/shutil.py:674: in _rmtree_safe_fd
        onerror(os.unlink, fullname, sys.exc_info())
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    topfd = 11, path = '/tmp/s2tw916afk4'
    onerror = <function rmtree.<locals>.onerror at 0x7fa2915feef0>
    
        def _rmtree_safe_fd(topfd, path, onerror):
            try:
                with os.scandir(topfd) as scandir_it:
                    entries = list(scandir_it)
            except OSError as err:
                err.filename = path
                onerror(os.scandir, path, sys.exc_info())
                return
            for entry in entries:
                fullname = os.path.join(path, entry.name)
                try:
                    is_dir = entry.is_dir(follow_symlinks=False)
                except OSError:
                    is_dir = False
                else:
                    if is_dir:
                        try:
                            orig_st = entry.stat(follow_symlinks=False)
                            is_dir = stat.S_ISDIR(orig_st.st_mode)
                        except OSError:
                            onerror(os.lstat, fullname, sys.exc_info())
                            continue
                if is_dir:
                    try:
                        dirfd = os.open(entry.name, os.O_RDONLY, dir_fd=topfd)
                    except OSError:
                        onerror(os.open, fullname, sys.exc_info())
                    else:
                        try:
                            if os.path.samestat(orig_st, os.fstat(dirfd)):
                                _rmtree_safe_fd(dirfd, fullname, onerror)
                                try:
                                    os.rmdir(entry.name, dir_fd=topfd)
                                except OSError:
                                    onerror(os.rmdir, fullname, sys.exc_info())
                            else:
                                try:
                                    # This can only happen if someone replaces
                                    # a directory with a symlink after the call to
                                    # os.scandir or stat.S_ISDIR above.
                                    raise OSError("Cannot call rmtree on a symbolic "
                                                  "link")
                                except OSError:
                                    onerror(os.path.islink, fullname, sys.exc_info())
                        finally:
                            os.close(dirfd)
                else:
                    try:
    >                   os.unlink(entry.name, dir_fd=topfd)
    E                   PermissionError: [Errno 13] Permission denied: 'test.txt'
    
    /usr/lib/python3.10/shutil.py:672: PermissionError
    =================================== FAILURES ===================================
    ______________________________ test_trash_topdir _______________________________
    
    testExtVol = (<tests.test_plat_other.ExtVol object at 0x7fa2915df430>, 'test.txt', '/tmp/s2t1_l_qvbt/test.txt')
    
        def test_trash_topdir(testExtVol):
            trashDir = op.join(testExtVol[0].trashTopdir, ".Trash")
            os.mkdir(trashDir, 0o777 | stat.S_ISVTX)
        
    >       s2t(testExtVol[2])
    
    tests/test_plat_other.py:163: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    send2trash/plat_other.py:213: in send2trash
        dest_trash = find_ext_volume_trash(topdir)
    send2trash/plat_other.py:169: in find_ext_volume_trash
        trash_dir = find_ext_volume_fallback_trash(volume_root)
    send2trash/plat_other.py:158: in find_ext_volume_fallback_trash
        check_create(trash_dir)
    send2trash/plat_other.py:96: in check_create
        os.makedirs(dir, 0o700)
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    name = b'/tmp/s2t1_l_qvbt/test.txt/.Trash-1000', mode = 448, exist_ok = False
    
        def makedirs(name, mode=0o777, exist_ok=False):
            """makedirs(name [, mode=0o777][, exist_ok=False])
        
            Super-mkdir; create a leaf directory and all intermediate ones.  Works like
            mkdir, except that any intermediate path segment (not just the rightmost)
            will be created if it does not exist. If the target directory already
            exists, raise an OSError if exist_ok is False. Otherwise no exception is
            raised.  This is recursive.
        
            """
            head, tail = path.split(name)
            if not tail:
                head, tail = path.split(head)
            if head and tail and not path.exists(head):
                try:
                    makedirs(head, exist_ok=exist_ok)
                except FileExistsError:
                    # Defeats race condition when another thread created the path
                    pass
                cdir = curdir
                if isinstance(tail, bytes):
                    cdir = bytes(curdir, 'ASCII')
                if tail == cdir:           # xxx/newdir/. exists if xxx/newdir exists
                    return
            try:
    >           mkdir(name, mode)
    E           NotADirectoryError: [Errno 20] Not a directory: b'/tmp/s2t1_l_qvbt/test.txt/.Trash-1000'
    
    /usr/lib/python3.10/os.py:225: NotADirectoryError
    __________________________ test_trash_topdir_fallback __________________________
    
    testExtVol = (<tests.test_plat_other.ExtVol object at 0x7fa291497a60>, 'test.txt', '/tmp/s2tmx3ups_b/test.txt')
    
        def test_trash_topdir_fallback(testExtVol):
    >       s2t(testExtVol[2])
    
    tests/test_plat_other.py:175: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    send2trash/plat_other.py:213: in send2trash
        dest_trash = find_ext_volume_trash(topdir)
    send2trash/plat_other.py:169: in find_ext_volume_trash
        trash_dir = find_ext_volume_fallback_trash(volume_root)
    send2trash/plat_other.py:158: in find_ext_volume_fallback_trash
        check_create(trash_dir)
    send2trash/plat_other.py:96: in check_create
        os.makedirs(dir, 0o700)
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    name = b'/tmp/s2tmx3ups_b/test.txt/.Trash-1000', mode = 448, exist_ok = False
    
        def makedirs(name, mode=0o777, exist_ok=False):
            """makedirs(name [, mode=0o777][, exist_ok=False])
        
            Super-mkdir; create a leaf directory and all intermediate ones.  Works like
            mkdir, except that any intermediate path segment (not just the rightmost)
            will be created if it does not exist. If the target directory already
            exists, raise an OSError if exist_ok is False. Otherwise no exception is
            raised.  This is recursive.
        
            """
            head, tail = path.split(name)
            if not tail:
                head, tail = path.split(head)
            if head and tail and not path.exists(head):
                try:
                    makedirs(head, exist_ok=exist_ok)
                except FileExistsError:
                    # Defeats race condition when another thread created the path
                    pass
                cdir = curdir
                if isinstance(tail, bytes):
                    cdir = bytes(curdir, 'ASCII')
                if tail == cdir:           # xxx/newdir/. exists if xxx/newdir exists
                    return
            try:
    >           mkdir(name, mode)
    E           NotADirectoryError: [Errno 20] Not a directory: b'/tmp/s2tmx3ups_b/test.txt/.Trash-1000'
    
    /usr/lib/python3.10/os.py:225: NotADirectoryError
    __________________________ test_trash_topdir_failure ___________________________
    
    testExtVol = (<tests.test_plat_other.ExtVol object at 0x7fa2914756c0>, 'test.txt', '/tmp/s2tw916afk4/test.txt')
    
        def test_trash_topdir_failure(testExtVol):
            os.chmod(testExtVol[0].trashTopdir, 0o500)  # not writable to induce the exception
    >       pytest.raises(TrashPermissionError, s2t, [testExtVol[2]])
    
    tests/test_plat_other.py:182: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    send2trash/plat_other.py:213: in send2trash
        dest_trash = find_ext_volume_trash(topdir)
    send2trash/plat_other.py:169: in find_ext_volume_trash
        trash_dir = find_ext_volume_fallback_trash(volume_root)
    send2trash/plat_other.py:158: in find_ext_volume_fallback_trash
        check_create(trash_dir)
    send2trash/plat_other.py:96: in check_create
        os.makedirs(dir, 0o700)
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    name = b'/tmp/s2tw916afk4/test.txt/.Trash-1000', mode = 448, exist_ok = False
    
        def makedirs(name, mode=0o777, exist_ok=False):
            """makedirs(name [, mode=0o777][, exist_ok=False])
        
            Super-mkdir; create a leaf directory and all intermediate ones.  Works like
            mkdir, except that any intermediate path segment (not just the rightmost)
            will be created if it does not exist. If the target directory already
            exists, raise an OSError if exist_ok is False. Otherwise no exception is
            raised.  This is recursive.
        
            """
            head, tail = path.split(name)
            if not tail:
                head, tail = path.split(head)
            if head and tail and not path.exists(head):
                try:
                    makedirs(head, exist_ok=exist_ok)
                except FileExistsError:
                    # Defeats race condition when another thread created the path
                    pass
                cdir = curdir
                if isinstance(tail, bytes):
                    cdir = bytes(curdir, 'ASCII')
                if tail == cdir:           # xxx/newdir/. exists if xxx/newdir exists
                    return
            try:
    >           mkdir(name, mode)
    E           NotADirectoryError: [Errno 20] Not a directory: b'/tmp/s2tw916afk4/test.txt/.Trash-1000'
    
    /usr/lib/python3.10/os.py:225: NotADirectoryError
    ______________________________ test_trash_symlink ______________________________
    
    testExtVol = (<tests.test_plat_other.ExtVol object at 0x7fa2915da8c0>, 'test.txt', '/tmp/s2t9qns8us6/test.txt')
    
        def test_trash_symlink(testExtVol):
            # Use mktemp (race conditioney but no symlink equivalent)
            # Since is_parent uses realpath(), and our getdev uses is_parent,
            # this should work
            slDir = mktemp(prefix="s2t", dir=op.expanduser("~"))
            os.mkdir(op.join(testExtVol[0].trashTopdir, "subdir"), 0o700)
            filePath = op.join(testExtVol[0].trashTopdir, "subdir", testExtVol[1])
            touch(filePath)
            os.symlink(op.join(testExtVol[0].trashTopdir, "subdir"), slDir)
    >       s2t(op.join(slDir, testExtVol[1]))
    
    tests/test_plat_other.py:195: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    send2trash/plat_other.py:213: in send2trash
        dest_trash = find_ext_volume_trash(topdir)
    send2trash/plat_other.py:169: in find_ext_volume_trash
        trash_dir = find_ext_volume_fallback_trash(volume_root)
    send2trash/plat_other.py:158: in find_ext_volume_fallback_trash
        check_create(trash_dir)
    send2trash/plat_other.py:96: in check_create
        os.makedirs(dir, 0o700)
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    name = b'/tmp/s2t9qns8us6/subdir/test.txt/.Trash-1000', mode = 448
    exist_ok = False
    
        def makedirs(name, mode=0o777, exist_ok=False):
            """makedirs(name [, mode=0o777][, exist_ok=False])
        
            Super-mkdir; create a leaf directory and all intermediate ones.  Works like
            mkdir, except that any intermediate path segment (not just the rightmost)
            will be created if it does not exist. If the target directory already
            exists, raise an OSError if exist_ok is False. Otherwise no exception is
            raised.  This is recursive.
        
            """
            head, tail = path.split(name)
            if not tail:
                head, tail = path.split(head)
            if head and tail and not path.exists(head):
                try:
                    makedirs(head, exist_ok=exist_ok)
                except FileExistsError:
                    # Defeats race condition when another thread created the path
                    pass
                cdir = curdir
                if isinstance(tail, bytes):
                    cdir = bytes(curdir, 'ASCII')
                if tail == cdir:           # xxx/newdir/. exists if xxx/newdir exists
                    return
            try:
    >           mkdir(name, mode)
    E           NotADirectoryError: [Errno 20] Not a directory: b'/tmp/s2t9qns8us6/subdir/test.txt/.Trash-1000'
    
    /usr/lib/python3.10/os.py:225: NotADirectoryError
    =========================== short test summary info ============================
    FAILED tests/test_plat_other.py::test_trash_topdir - NotADirectoryError: [Err...
    FAILED tests/test_plat_other.py::test_trash_topdir_fallback - NotADirectoryEr...
    FAILED tests/test_plat_other.py::test_trash_topdir_failure - NotADirectoryErr...
    FAILED tests/test_plat_other.py::test_trash_symlink - NotADirectoryError: [Er...
    ERROR tests/test_plat_other.py::test_trash_topdir_failure - PermissionError: ...
    =============== 4 failed, 6 passed, 1 skipped, 1 error in 0.20s ================
    E: pybuild pybuild:367: test: plugin pyproject failed with: exit code=1: cd '/build/send2trash-IKQvlF/send2trash-1.8.1~b0/.pybuild/cpython3_3.10/build'; python3.10 -m pytest tests
    [... similar errors with Python 3.9 snipped ...]
    

    The thing that seems so weird to me is the strange filenames/directories it's trying to work with, such as b'/tmp/s2t9qns8us6/subdir/test.txt/.Trash-1000'. It strikes me that something has gone wrong in the calculation of the trash directory name. (And there's no sign of the HOME directory I so carefully set up!)

    opened by juliangilbey 7
Releases(1.8.1b0)
  • 1.8.1b0(Aug 21, 2021)

  • 1.8.0(Aug 9, 2021)

    • Add compatibility with pathlib paths (#49)
    • Fix thread compatibility of modern windows implementation (#59)
    • Fix handling of UNC names in legacy windows implementation (#57)
    Source code(tar.gz)
    Source code(zip)
  • 1.7.1(Jun 23, 2021)

    • Release stable version with changes from last 3 releases including:
      • Changed conditional for when to try to use pyobjc version (#51)
      • Add console_script entry point (#50)
      • Increased python CI versions (#52, #54)
      • Fix minor issue in setup.py (#53)
      • Fix issue with windows tests importing modules on non-windows (#55)
      • Unit test cleanups, rewrites, and flake8 cleanups
      • Windows: Fix legacy windows platform for multi-byte unicode and add tests
      • macOS: Add alternative pyobjc version to potentially improve compatibility (#51)
      • Add main method which allows calling via python -m send2trash somefile
      • Windows: Add support for using IFileOperation when pywin32 is present on Vista and newer
      • Add support for passing multiple files at once in a list
      • Windows: Batch multi-file calls to improve performance (#42)
      • Windows: Fix issue with SHFileOperation failing silently when path is not found (#33)
    • Fix handling of UNC names (#57)
    Source code(tar.gz)
    Source code(zip)
  • 1.7.0a1(Jun 23, 2021)

  • 1.7.0a0(Jun 23, 2021)

    • Add console_script entry point (#50)
    • Increased python CI versions (#52, #54)
    • Fix minor issue in setup.py (#53)
    • Fix issue with windows tests importing modules on non-windows (#55)
    • Unit test cleanups, rewrites, and flake8 cleanups
    • Windows: Fix legacy windows platform for multi-byte unicode and add tests
    • macOS: Add alternative pyobjc version to potentially improve compatibility (#51)
    Source code(tar.gz)
    Source code(zip)
  • 1.6.0b1(Jun 19, 2020)

    This is a beta version including the following changes:

    • Add main method which allows calling via python -m send2trash somefile
    • Windows: Add support for using IFileOperation when pywin32 is present on Vista and newer
    • Add support for passing multiple files at once in a list
    • Windows: Batch multi-file calls to improve performance (#42)
    • Windows: Fix issue with SHFileOperation failing silently when path is not found (#33)
    Source code(tar.gz)
    Source code(zip)
Owner
Andrew Senetar
Andrew Senetar
A (hopefully) considerably copious collection of classical cipher crackers

ClassicalCipherCracker A (hopefully) considerably copious collection of classical cipher crackers Written in Python3 (and run with PyPy) TODOs Write a

Stanley Zhong 2 Feb 22, 2022
A python program to detect rickrolls with just the youtube link.

rickroll_detector A python program to detect rickrolls with just the youtube link. Usage: clone this repo or download zip run the main.py file with py

Tricky 4 Nov 06, 2022
ThnoolBox - A thneed is a multi-use versatile object

ThnoolBox Have you ever wanted a collection of bodged desktop apps that are Lorax themed ? No ? Sucks to suck I guess Apps & their downsides CalculaTh

pocoyo 1 Jan 21, 2022
Convert Roman numerals to modern numerals and vice-versa

Roman Numeral Conversion Utilities This is a utility module for converting from and to Roman numerals. It supports numbers upto 3,999,999, using the v

Fictive Kin 1 Dec 17, 2021
PyWorkflow(PyWF) - A Python Binding of C++ Workflow

PyWorkflow(PyWF) - A Python Binding of C++ Workflow 概览 C++ Workflow是一个高性能的异步引擎,本项目着力于实现一个Python版的Workflow,让Python用户也能享受Workflow带来的绝佳体验。

Sogou-inc 108 Dec 01, 2022
NeoInterface - Neo4j made easy for Python programmers!

Neointerface - Neo4j made easy for Python programmers! A Python interface to use the Neo4j graph database, and simplify its use. class NeoInterface: C

15 Dec 15, 2022
A small project of two newbies, who wanted to learn something about Python language programming, via fun way.

HaveFun A small project of two newbies, who wanted to learn something about Python language programming, via fun way. What's this project about? Well.

Patryk Sobczak 2 Nov 24, 2021
Recreate the joys of Office Assistant from the comfort of the Python interpreter

Recreate the joys of Office Assistant from the comfort of the Python interpreter.

Louis Sven Goulet 3 May 21, 2022
Animation picker for Audodesk Maya 2017 (or higher)

Dreamwall Picker Animation picker for Audodesk Maya 2017 (or higher) Authors: Lionel Brouyère, Olivier Evers This tool is a fork of Hotbox Designer (L

DreamWall 93 Dec 21, 2022
A simple service that allows you to run commands on the server using text

Server Text A simple flask service that allows you to run commands on the server/computer over sms. Think of it as a shell where you run commands over

MT Devs 49 Nov 09, 2021
Tool to automate the enumeration of a website (CTF)

had4ctf Tool to automate the enumeration of a website (CTF) DISCLAIMER: THE TOOL HAS BEEN DEVELOPED SOLELY FOR EDUCATIONAL PURPOSE ,I WILL NOT BE LIAB

Had 2 Oct 24, 2021
A Tandy Color Computer 1, 2, and 3 assembler written in Python

CoCo Assembler and File Utility Table of Contents What is it? Requirements License Installing Assembler Assembler Usage Input File Format Print Symbol

Craig Thomas 16 Nov 03, 2022
flake8 plugin which checks that there is no use of sleep in the code.

flake8-sleep flake8 plugin which checks for use of sleep function. installation Using Pypi: pip install flake8-sleep flake8 codes Code Description SLP

1 Nov 26, 2021
Script de monitoramento das teclas do teclado, salvando todos os dados digitados em um arquivo de log juntamente com os dados de rede.

listenerPython Script de monitoramento das teclas do teclado, salvando todos os dados digitados em um arquivo de log juntamente com os dados de rede.

Vinícius Azevedo 4 Nov 27, 2022
An Android app that runs Elm in a webview. And a Python script to build the app or install it on the device.

Requirements You need to have installed: the Android SDK Elm Python git Starting a project Clone this repo and cd into it: $ git clone https://github.

Benjamin Le Forestier 11 Mar 17, 2022
github action test, because I dont know it.

mad-y testing testing pip install -r requirements.txt add the DISCORD_TOKEN value to your env vars. and run mad-y how to Deploy ` docker build -t mad-

Mit 1 Oct 29, 2021
Box CRUD API With Python

Box CRUD API: Consider a store which has an inventory of boxes which are all cuboid(which have length breadth and height). Each Cuboid has been added

Akhil Bhalerao 3 Feb 17, 2022
My tools box script for sigma

sigma_python_toolbox My tools box script for sigma purpose My goal is not to replace sigma but to put at disposal the scripts that I think to help me

4 Jun 20, 2022
Coded in Python 3 - I make for education, easily clone simple website.

Simple Website Cloner - Single Page Coded in Python 3 - I make for education, easily clone simple website. How to use ? Install Python 3 first. Instal

Phạm Đức Thanh 2 Jan 13, 2022
Mannaggia is a python application to praise or more likely to curse the saints

Mannaggia-py 👼 Remember Mannaggia? This is a Python remake of it, with new features. mannaggia is a python application to praise or more likely to cu

Christian Visintin 9 Aug 12, 2022