Skip to content

Commit 9a11bc0

Browse files
committed
Fix removal of locked libraries on Windows
This commit moves locked files (e.g. DLLs, pyd) to Data/Trash directory, to defer removal until next restart of ST, while enabling placing new file at the same location upon library upgrade. Technically, charset_normalizer failed to upgrade with WinError 5 due to a .pyd file in Lib/ directory being locked by Windows OS. The strategy is already used by `delete_directory()` function.
1 parent c62ba95 commit 9a11bc0

1 file changed

Lines changed: 25 additions & 2 deletions

File tree

package_control/library.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import re
33
import shutil
44

5+
from datetime import datetime
6+
from hashlib import sha1
7+
58
import sublime
69

710
from . import sys_path
@@ -401,6 +404,26 @@ def remove(installed_library):
401404
cache_dir = sys_path.python_libs_cache_path(python_version)
402405
cache_ext = ".cpython-{}.opt-1.pyc".format(python_version.replace(".", ""))
403406

407+
# use timestamp as session id, in case library is installed/removed
408+
# multiple times to avoid naming conflicts, when moving to trash.
409+
session_id = str(datetime.now())
410+
411+
# Especially on Windows, files may be locked and therefore can't be removed,
412+
# while loaded. They can however be renamed, thus moving them to trash directory is
413+
# possible in order to simulate deletion for the sense of managing packages/libraries.
414+
trash_dir = sys_path.trash_path()
415+
os.makedirs(trash_dir, exist_ok=True)
416+
417+
def remove_file(path):
418+
try:
419+
os.remove(path)
420+
except OSError:
421+
trash_path = os.path.join(
422+
trash_dir,
423+
sha1((session_id + path).encode('utf-8')).hexdigest().lower()
424+
)
425+
os.rename(path, trash_path)
426+
404427
for rel_path in dist_info.top_level_paths():
405428
# Remove the .dist-info dir last so we have info for clean-up in case
406429
# we hit an error along the way
@@ -417,12 +440,12 @@ def remove(installed_library):
417440
delete_directory(os.path.join(cache_dir, rel_path))
418441

419442
elif os.path.isfile(abs_path):
420-
os.remove(abs_path)
443+
remove_file(abs_path)
421444

422445
# remove bytecode cache
423446
if cache_dir and abs_path.endswith(".py"):
424447
try:
425-
os.remove(os.path.join(cache_dir, rel_path[:-3] + cache_ext))
448+
remove_file(os.path.join(cache_dir, rel_path[:-3] + cache_ext))
426449
except OSError:
427450
pass
428451

0 commit comments

Comments
 (0)