xonsh/xontrib/free_cwd.py
Noorhteen Raja NJ 38295a1dd9
Remove globals (#4280)
* refactor: remove usage of global variables in abbrevs.py

* chore: add flake8-mutable to prevent mutable defaults

* fix: abbrevs expand test

* refactor: add xonsh session singleton

* refactor: fix circular errors when using xonshSession as singleton

* refactor: remove black magicked builtin attributes

* style: black format tests as well

* refactor: update tests to use xonsh-session singleton

* refactor: update abbrevs to not use builtins

* test: remove DummyCommandsCache and patch orig class

* fix: failing test_command_completers

* test: use monkeypatch to update xession fixture

* fix: failing test_pipelines

* fix: failing test_main

* chore: run test suit as single invocation

* test: fix tests/test_xonsh.xsh

* refactor: remove builtins from docs/conf.py

* fix: mypy error in jobs

* fix: test error from test_main

* test: close xession error in test_command_completers

* chore: use pytest-cov for reporting coverage

this will include subprocess calls, and will increase coverage

* style:
2021-05-20 13:14:26 +03:00

102 lines
3.3 KiB
Python

""" This will 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 cause issues
if any extensions are enabled that hook the prompt and relies on
``os.getcwd()``.
"""
import os
import functools
from pathlib import Path
from xonsh.tools import print_exception
from xonsh.built_ins import XSH
from xonsh.platform import ON_WINDOWS, ON_CYGWIN, ON_MSYS
def _chdir_up(path):
"""Change directory to path or if path does not exist
the first valid parent.
"""
path = Path(path)
try:
os.chdir(path)
return str(path.absolute())
except (FileNotFoundError, NotADirectoryError):
path.resolve()
return _chdir_up(path.parent)
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.
"""
env = XSH.env
if env.get("UPDATE_PROMPT_ON_KEYPRESS"):
return func if not hasattr(func, "_orgfunc") else func._orgfunc
if hasattr(func, "_orgfunc"):
# Already wrapped
return func
else:
@functools.wraps(func)
def wrapper(*args, **kwargs):
anchor = Path(os.getcwd()).anchor
os.chdir(anchor)
try:
out = func(*args, **kwargs)
finally:
try:
pwd = env.get("PWD", anchor)
os.chdir(pwd)
except (FileNotFoundError, NotADirectoryError):
print_exception()
newpath = _chdir_up(pwd)
XSH.env["PWD"] = newpath
raise KeyboardInterrupt
return out
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.
"""
env = XSH.env
if env.get("UPDATE_PROMPT_ON_KEYPRESS"):
return func if not hasattr(func, "_orgfunc") else func._orgfunc
if hasattr(func, "_orgfunc"):
# Already wrapped
return func
else:
@functools.wraps(func)
def wrapper(*args, **kwargs):
workdir = os.getcwd()
_chdir_up(env.get("PWD", workdir))
out = func(*args, **kwargs)
_chdir_up(workdir)
return out
wrapper._orgfunc = func
return wrapper
@XSH.builtins.events.on_ptk_create
def setup_release_cwd_hook(prompter, history, completer, bindings, **kw):
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
completer.completer.complete = _cwd_restore_wrapper(
completer.completer.complete
)