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
SimBiber - A tool for simplifying bibtex with official info

SimBiber: A tool for simplifying bibtex with official info. We often need to sim

336 Jan 02, 2023
Project repository of Apache Airflow, deployed on Docker in Amazon EC2 via GitLab.

Airflow on Docker in EC2 + GitLab's CI/CD Personal project for simple data pipeline using Airflow. Airflow will be installed inside Docker container,

Ammar Chalifah 13 Nov 29, 2022
PaintPrint - This module can colorize any text in your terminal

PaintPrint This module can colorize any text in your terminal Author: tankalxat3

Alexander Podstrechnyy 2 Feb 17, 2022
Ghost source since the developer of the project quit due to reasons

👻 Ghost Selfbot The official code for Ghost which was recently discontinued and released to the public. Feel free to use any of the code found in thi

xannyy 2 Mar 24, 2022
清晰易读的7x7像素点阵中文字体和取模工具

FontChinese7x7 上古神器 III : 7x7像素点阵中文字体 想要在低分辨率屏幕上显示中文, 却发现中文字体实在是太大? 找了全网发现字体库最小也只有12x12? 甚至是好不容易找到了一个8x8字体, 结果发现字体收费且明确说明不得以任何形式嵌入到软件当中? 那就让这个项目来解决你的问

Angelic47 72 Dec 12, 2022
A GUI love Calculator which saves all the User Data in text file(sql based script will be uploaded soon). Interative GUI. Even For Admin Panel

Love-Calculator A GUI love Calculator which saves all the User Data in text file(sql based script will be uploaded soon). Interative GUI, even For Adm

Adithya Krishnan 1 Mar 22, 2022
msImpersonate - User account impersonation written in pure Python3

msImpersonate v1.0 msImpersonate is a Python-native user impersonation tool that is capable of impersonating local or network user accounts with valid

Joe Helle 90 Dec 16, 2022
uMap lets you create maps with OpenStreetMap layers in a minute and embed them in your site.

uMap project About uMap lets you create maps with OpenStreetMap layers in a minute and embed them in your site. Because we think that the more OSM wil

771 Dec 29, 2022
Task dispatcher for Postgres

Features a task being ran as an OS process supports task queue with priority and process limit per node fully database driven (a worker and task can b

2 Dec 06, 2021
Personal Finance Forecaster - An AI tool for forecasting personal expenses

Personal Finance Forecaster - An AI tool for forecasting personal expenses

2 Mar 09, 2022
Snack Rice - A Rice University servery finder, customized for your needs!

Snack Rice - A Rice University servery finder, customized for your needs!

Aidan Gerber 3 Sep 25, 2022
Data on COVID-19 (coronavirus) cases, deaths, hospitalizations, tests • All countries • Updated daily by Our World in Data

COVID-19 Dataset by Our World in Data Find our data on COVID-19 and its documentation in public/data. Documentation Data: complete COVID-19 dataset Da

Our World in Data 5.5k Jan 03, 2023
Chemical equation balancer

Chemical equation balancer Balance your chemical equations with ease! Installation $ git clone

Marijan Smetko 4 Nov 26, 2022
🎴 LearnQuick is a flashcard application that you can study with decks and cards.

🎴 LearnQuick is a flashcard application that you can study with decks and cards. The main function of the application is to show the front sides of the created cards to the user and ask them to guess

Mehmet Güdük 7 Aug 21, 2022
An Notifier Program that Notifies you to relax your eyes Every 15 Minutes👀

Every 15 Minutes ⌛ Every 15 Minutes is an application that is used to Notify you to Relax your eyes Every 15 Minutes, This is fully made with Python a

FSP Gang s' YT 2 Oct 18, 2021
A multi purpose password managing and generating tool called Kyper.

Kyper A multi purpose password managing and generating tool called Kyper. Setup The setup for Kyper is fairly simple only involving the command python

Jan Dorian Poczekaj 1 Feb 05, 2022
Archive, organize, and watch for changes to publicly available information.

0. Overview The Trapper Keeper is a collection of scripts that support archiving information from around the web to make it easier to study and use. I

Bill Fitzgerald 9 Oct 26, 2022
Custom component to calculate estimated power consumption of lights and other appliances

Custom component to calculate estimated power consumption of lights and other appliances. Provides easy configuration to get virtual power consumption sensors in Home Assistant for all your devices w

Bram Gerritsen 552 Dec 28, 2022
Python implementation of an automatic parallel parking system in a virtual environment, including path planning, path tracking, and parallel parking

Automatic Parallel Parking: Path Planning, Path Tracking & Control This repository contains a python implementation of an automatic parallel parking s

134 Jan 09, 2023
This repository contains completed Python projects

My Python projects This repository contains completed Python projects: 1) Build projects Guide for building projects into executable files 2) Calculat

Igor Yunusov 8 Nov 04, 2021