xonsh/xontrib/free_cwd.py

107 lines
3.3 KiB
Python
Raw Normal View History

2022-05-05 00:32:20 +05:30
"""Windows only xontrib, to release the lock on the current directory whenever the prompt is shown.
Enabling this will allow other programs or
Windows Explorer to delete or rename the current or parent
directories. Internally, it is accomplished by temporarily resetting
CWD to the root drive folder while waiting at the prompt. This only
works with the prompt_toolkit backend and can cause issues
2022-05-05 00:32:20 +05:30
if any extensions are enabled that hook the prompt and relies on
``os.getcwd()``.
2017-03-09 22:23:44 +01:00
"""
import functools
2022-01-31 21:26:34 +05:30
import os
2017-12-03 21:35:32 +01:00
from pathlib import Path
from xonsh.built_ins import XSH, XonshSession
2022-01-31 21:26:34 +05:30
from xonsh.platform import ON_CYGWIN, ON_MSYS, ON_WINDOWS
from xonsh.tools import print_exception
2017-03-09 22:23:44 +01:00
2017-03-10 08:20:16 +01:00
2017-03-09 22:23:44 +01:00
def _chdir_up(path):
"""Change directory to path or if path does not exist
the first valid parent.
2017-03-09 22:23:44 +01:00
"""
2017-12-03 21:35:32 +01:00
path = Path(path)
2017-03-09 22:23:44 +01:00
try:
os.chdir(path)
return str(path.absolute())
2017-03-09 23:02:26 +01:00
except (FileNotFoundError, NotADirectoryError):
2017-12-03 21:35:32 +01:00
path.resolve()
return _chdir_up(path.parent)
2017-03-09 22:23:44 +01:00
2017-03-10 08:20:16 +01:00
2017-03-09 22:23:44 +01:00
def _cwd_release_wrapper(func):
"""Decorator for Windows to wrap the prompt function and release
the process lock on the current directory while the prompt is
displayed. This works by temporarily setting
the workdir to the users home directory.
2017-03-09 22:23:44 +01:00
"""
env = XSH.env
2019-04-26 11:11:11 -04:00
if env.get("UPDATE_PROMPT_ON_KEYPRESS"):
return func if not hasattr(func, "_orgfunc") else func._orgfunc
2017-03-09 22:23:44 +01:00
2019-04-26 11:11:11 -04:00
if hasattr(func, "_orgfunc"):
2017-03-09 22:23:44 +01:00
# Already wrapped
return func
else:
2019-04-26 11:11:11 -04:00
2017-03-09 22:23:44 +01:00
@functools.wraps(func)
def wrapper(*args, **kwargs):
2017-12-03 21:35:32 +01:00
anchor = Path(os.getcwd()).anchor
os.chdir(anchor)
2017-03-09 22:23:44 +01:00
try:
out = func(*args, **kwargs)
finally:
try:
2019-04-26 11:11:11 -04:00
pwd = env.get("PWD", anchor)
2017-03-09 22:23:44 +01:00
os.chdir(pwd)
2017-03-09 23:02:26 +01:00
except (FileNotFoundError, NotADirectoryError):
2017-03-09 22:23:44 +01:00
print_exception()
2017-03-09 23:02:26 +01:00
newpath = _chdir_up(pwd)
XSH.env["PWD"] = newpath
2017-03-09 23:02:26 +01:00
raise KeyboardInterrupt
2017-03-09 22:23:44 +01:00
return out
2019-04-26 11:11:11 -04:00
2017-03-09 22:23:44 +01:00
wrapper._orgfunc = func
return wrapper
def _cwd_restore_wrapper(func):
"""Decorator for Windows which will temporary restore the true working
directory. Designed to wrap completer callbacks from the
prompt_toolkit or readline.
2017-03-09 22:23:44 +01:00
"""
env = XSH.env
2019-04-26 11:11:11 -04:00
if env.get("UPDATE_PROMPT_ON_KEYPRESS"):
return func if not hasattr(func, "_orgfunc") else func._orgfunc
2017-03-09 22:23:44 +01:00
2019-04-26 11:11:11 -04:00
if hasattr(func, "_orgfunc"):
2017-03-09 22:23:44 +01:00
# Already wrapped
return func
else:
2019-04-26 11:11:11 -04:00
2017-03-09 22:23:44 +01:00
@functools.wraps(func)
def wrapper(*args, **kwargs):
workdir = os.getcwd()
2019-04-26 11:11:11 -04:00
_chdir_up(env.get("PWD", workdir))
2017-03-09 22:23:44 +01:00
out = func(*args, **kwargs)
_chdir_up(workdir)
return out
2019-04-26 11:11:11 -04:00
2017-03-09 22:23:44 +01:00
wrapper._orgfunc = func
return wrapper
def setup_release_cwd_hook(prompter, history, completer, bindings, **kw):
2017-12-31 16:32:19 +01:00
if ON_WINDOWS and not ON_CYGWIN and not ON_MSYS:
prompter.prompt = _cwd_release_wrapper(prompter.prompt)
if completer.completer:
# Temporarily restore cwd for callbacks to the completer
2019-04-26 11:11:11 -04:00
completer.completer.complete = _cwd_restore_wrapper(
completer.completer.complete
)
def _load_xontrib_(xsh: XonshSession, **_):
xsh.builtins.events.on_ptk_create(setup_release_cwd_hook)