1
0
mirror of https://github.com/arsenetar/send2trash.git synced 2026-01-25 16:11:39 +00:00

11 Commits

Author SHA1 Message Date
484913ba0f Update version for 1.8.0 release 2021-08-08 21:51:06 -05:00
d249f0106b Fix #59, initialize and uninitialize COM for threading 2021-08-07 22:16:33 -05:00
94e1ec007a Add ability to handle pathlib paths
- Handle pathlib paths across all implementations, plat_other already did
- Move preprocessing code to common location
2021-08-07 21:48:10 -05:00
84c220cbd9 Change extra requires to filter on platform
Also created one extra `nativeLib` to replace the orignal two.  Will remove
the others after a couple releases.
2021-08-07 21:04:40 -05:00
6612545110 Add note about pyobjc to README, add extra option 2021-06-22 21:36:14 -05:00
d52b4f206c Fix CHANGES.rst issue 2021-06-21 22:22:48 -05:00
33171dde82 Update version for 1.7.1 release 2021-06-21 22:13:46 -05:00
077598d2ce Merge pull request #57 from BoboTiG/fix-windows-unc-names-legacy
Windows legacy: fix handling of UNC names
2021-06-21 22:06:06 -05:00
Mickaël Schoentgen
436686bf0f Windows legacy: fix handling of UNC names
The legacy implementation was not handling UNC names properly:

  Traceback (most recent call last):
    File "check.py", line 6, in <module>
      send2trash(str(file))
    File "\...\plat_win_legacy.py", line 79, in send2trash
      paths = [get_short_path_name(path) for path in paths]
    File "\...\plat_win_legacy.py", line 79, in <listcomp>
      paths = [get_short_path_name(path) for path in paths]
    File "\...\plat_win_legacy.py", line 62, in get_short_path_name
      raise WindowsError(err_no, FormatError(err_no), long_name[4:])
  OSError: [Errno 123] La syntaxe du nom de fichier, de répertoire ou de volume est incorrecte.: '\\\\SERVER\\folder\\file.txt'
2021-05-26 17:22:26 +02:00
23ce7b8c16 Bump version 2021-05-14 21:44:21 -05:00
9b0d5796c1 Change conditional for macos pyobjc usage
macOS 11.x will occasionally identify as 10.16, since there was no real
reason to prevent on all supported platforms allow.
2021-05-14 21:40:16 -05:00
11 changed files with 119 additions and 31 deletions

View File

@@ -1,8 +1,27 @@
Changes
=======
Version 1.7.0a -- 2020/04/20
----------------------------
Version 1.8.0 -- 2021/08/08
---------------------------
* Add compatibility with pathlib paths (#49)
* Fix thread compatibility of modern windows implementation (#59)
* Fix handling of UNC names in legacy windows implementation (#57)
Version 1.7.1 -- 2021/06/21
---------------------------
* Release stable version with changes from last 3 releases
* Fix handling of UNC names (#57)
Version 1.7.0a1 -- 2021/05/14
-----------------------------
* Changed conditional for when to try to use pyobjc version (#51)
Version 1.7.0a0 -- 2021/04/20
-----------------------------
* Add console_script entry point (#50)
* Increased python CI versions (#52, #54)
* Fix minor issue in setup.py (#53)

View File

@@ -3,11 +3,11 @@ 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`_.
*all platforms*. On OS X, it uses native ``FSMoveObjectToTrashSync`` Cocoa calls or can use pyobjc
with NSFileManager. 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.
@@ -22,10 +22,14 @@ issues and fixes would be most appreciated.
Installation
------------
You can download it with pip::
You can download it with pip:
python -m pip install -U send2trash
To install with pywin32 or pyobjc required specify the extra `nativeLib`:
python -m pip install -U send2trash[nativeLib]
or you can download the source from http://github.com/arsenetar/send2trash and install it with::
>>> python setup.py install

View File

@@ -6,11 +6,11 @@
from gi.repository import GObject, Gio
from .exceptions import TrashPermissionError
from .util import preprocess_paths
def send2trash(paths):
if not isinstance(paths, list):
paths = [paths]
paths = preprocess_paths(paths)
for path in paths:
try:
f = Gio.File.new_for_path(path)

View File

@@ -7,9 +7,9 @@
from platform import mac_ver
from sys import version_info
# If macOS is 11.0 or newer try to use the pyobjc version to get around #51
# NOTE: pyobjc only supports python >= 3.6
if version_info >= (3, 6) and int(mac_ver()[0].split(".", 1)[0]) >= 11:
# NOTE: version of pyobjc only supports python >= 3.6 and 10.9+
macos_ver = tuple(int(part) for part in mac_ver()[0].split("."))
if version_info >= (3, 6) and macos_ver >= (10, 9):
try:
from .plat_osx_pyobjc import send2trash
except ImportError:

View File

@@ -10,6 +10,7 @@ from ctypes import cdll, byref, Structure, c_char, c_char_p
from ctypes.util import find_library
from .compat import binary_type
from .util import preprocess_paths
Foundation = cdll.LoadLibrary(find_library("Foundation"))
CoreServices = cdll.LoadLibrary(find_library("CoreServices"))
@@ -40,8 +41,7 @@ def check_op_result(op_result):
def send2trash(paths):
if not isinstance(paths, list):
paths = [paths]
paths = preprocess_paths(paths)
paths = [
path.encode("utf-8") if not isinstance(path, binary_type) else path
for path in paths

View File

@@ -6,6 +6,7 @@
from Foundation import NSFileManager, NSURL
from .compat import text_type
from .util import preprocess_paths
def check_op_result(op_result):
@@ -16,8 +17,7 @@ def check_op_result(op_result):
def send2trash(paths):
if not isinstance(paths, list):
paths = [paths]
paths = preprocess_paths(paths)
paths = [
path.decode("utf-8") if not isinstance(path, text_type) else path
for path in paths

View File

@@ -30,6 +30,7 @@ except ImportError:
from urllib import quote
from .compat import text_type, environb
from .util import preprocess_paths
from .exceptions import TrashPermissionError
try:
@@ -172,8 +173,7 @@ def get_dev(path):
def send2trash(paths):
if not isinstance(paths, list):
paths = [paths]
paths = preprocess_paths(paths)
for path in paths:
if isinstance(path, text_type):
path_b = fsencode(path)

View File

@@ -7,6 +7,8 @@
from __future__ import unicode_literals
import os.path as op
from .compat import text_type
from .util import preprocess_paths
from ctypes import (
windll,
Structure,
@@ -51,23 +53,57 @@ FOF_ALLOWUNDO = 64
FOF_NOERRORUI = 1024
def prefix_and_path(path):
r"""Guess the long-path prefix based on the kind of *path*.
Local paths (C:\folder\file.ext) and UNC names (\\server\folder\file.ext)
are handled.
Return a tuple of the long-path prefix and the prefixed path.
"""
prefix, long_path = "\\\\?\\", path
if not path.startswith(prefix):
if path.startswith("\\\\"):
# Likely a UNC name
prefix = "\\\\?\\UNC"
long_path = prefix + path[1:]
else:
# Likely a local path
long_path = prefix + path
elif path.startswith(prefix + "UNC\\"):
# UNC name with long-path prefix
prefix = "\\\\?\\UNC"
return prefix, long_path
def get_awaited_path_from_prefix(prefix, path):
"""Guess the correct path to pass to the SHFileOperationW() call.
The long-path prefix must be removed, so we should take care of
different long-path prefixes.
"""
if prefix == "\\\\?\\UNC":
# We need to prepend a backslash for UNC names, as it was removed
# in prefix_and_path().
return "\\" + path[len(prefix) :]
return path[len(prefix) :]
def get_short_path_name(long_name):
if not long_name.startswith("\\\\?\\"):
long_name = "\\\\?\\" + long_name
buf_size = GetShortPathNameW(long_name, None, 0)
prefix, long_path = prefix_and_path(long_name)
buf_size = GetShortPathNameW(long_path, None, 0)
# FIX: https://github.com/hsoft/send2trash/issues/31
# If buffer size is zero, an error has occurred.
if not buf_size:
err_no = GetLastError()
raise WindowsError(err_no, FormatError(err_no), long_name[4:])
raise WindowsError(err_no, FormatError(err_no), long_path)
output = create_unicode_buffer(buf_size)
GetShortPathNameW(long_name, output, buf_size)
return output.value[4:] # Remove '\\?\' for SHFileOperationW
GetShortPathNameW(long_path, output, buf_size)
return get_awaited_path_from_prefix(prefix, output.value)
def send2trash(paths):
if not isinstance(paths, list):
paths = [paths]
paths = preprocess_paths(paths)
# convert data type
paths = [
text_type(path, "mbcs") if not isinstance(path, text_type) else path

View File

@@ -7,6 +7,7 @@
from __future__ import unicode_literals
import os.path as op
from .compat import text_type
from .util import preprocess_paths
from platform import version
import pythoncom
import pywintypes
@@ -15,8 +16,7 @@ from .IFileOperationProgressSink import CreateSink
def send2trash(paths):
if not isinstance(paths, list):
paths = [paths]
paths = preprocess_paths(paths)
# convert data type
paths = [
text_type(path, "mbcs") if not isinstance(path, text_type) else path
@@ -26,6 +26,8 @@ def send2trash(paths):
paths = [op.abspath(path) if not op.isabs(path) else path for path in paths]
# remove the leading \\?\ if present
paths = [path[4:] if path.startswith("\\\\?\\") else path for path in paths]
# Need to initialize the com before using
pythoncom.CoInitialize()
# create instance of file operation object
fileop = pythoncom.CoCreateInstance(
shell.CLSID_FileOperation, None, pythoncom.CLSCTX_ALL, shell.IID_IFileOperation,
@@ -65,3 +67,6 @@ def send2trash(paths):
# convert to standard OS error, allows other code to get a
# normal errno
raise OSError(None, error.strerror, path, error.hresult)
finally:
# Need to make sure we call this once fore every init
pythoncom.CoUninitialize()

16
send2trash/util.py Normal file
View File

@@ -0,0 +1,16 @@
# encoding: utf-8
# Copyright 2017 Virgil Dupras
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license
def preprocess_paths(paths):
if not isinstance(paths, list):
paths = [paths]
# Convert items such as pathlib paths to strings
paths = [
path.__fspath__() if hasattr(path, "__fspath__") else path for path in paths
]
return paths

View File

@@ -24,7 +24,7 @@ with open("README.rst", "rt") as f1, open("CHANGES.rst", "rt") as f2:
setup(
name="Send2Trash",
version="1.7.0a",
version="1.8.0",
author="Andrew Senetar",
author_email="arsenetar@voltaicideas.net",
packages=["send2trash"],
@@ -34,8 +34,16 @@ setup(
license="BSD License",
description="Send file to trash natively under Mac OS X, Windows and Linux.",
long_description=LONG_DESCRIPTION,
long_description_content_type="text/x-rst",
classifiers=CLASSIFIERS,
extras_require={"win32": ["pywin32"]},
extras_require={
"win32": ['pywin32; sys_platform == "win32"'],
"objc": ['pyobjc-framework-Cocoa; sys_platform == "darwin"'],
"nativeLib": [
'pywin32; sys_platform == "win32"',
'pyobjc-framework-Cocoa; sys_platform == "darwin"',
],
},
project_urls={"Bug Reports": "https://github.com/arsenetar/send2trash/issues"},
entry_points={"console_scripts": ["send2trash=send2trash.__main__:main"]},
)