mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
don't try to create pipe on Windows; return 'or', not 'mi' for broken link
This commit is contained in:
parent
68f776def0
commit
08a79c97c8
4 changed files with 45 additions and 65 deletions
|
@ -21,7 +21,7 @@
|
|||
See (#3608)[https://github.com/xonsh/xonsh/issues/3608].
|
||||
* pyghooks.color_files now follows implememntation of ls --color closely. Thanks @qwenger!
|
||||
However, a few documented differences remain due to use in Xonsh.
|
||||
Precedence of color for file with multiple matches improved; now follows only next symlink in a chain.
|
||||
|
||||
* $LS_COLORS['ln'] = 'target' now works. Also fixes #3578.
|
||||
|
||||
**Security:**
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
import pytest
|
||||
import os
|
||||
import stat
|
||||
import pathlib
|
||||
|
||||
from tempfile import TemporaryDirectory
|
||||
from xonsh.platform import ON_WINDOWS
|
||||
|
||||
from xonsh.pyghooks import (
|
||||
XonshStyle,
|
||||
|
@ -16,8 +18,6 @@ from xonsh.pyghooks import (
|
|||
|
||||
from xonsh.environ import LsColors
|
||||
|
||||
# from tools import skip_if_on_windows
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xonsh_builtins_LS_COLORS(xonsh_builtins):
|
||||
|
@ -166,13 +166,13 @@ _cf = {
|
|||
"fi": "regular",
|
||||
"di": "simple_dir",
|
||||
"ln": "sym_link",
|
||||
"pi": "pipe",
|
||||
"pi": None if ON_WINDOWS else "pipe",
|
||||
"so": None,
|
||||
"do": None,
|
||||
# bug ci failures: 'bd': '/dev/sda',
|
||||
# bug ci failures:'cd': '/dev/tty',
|
||||
"or": None,
|
||||
"mi": "missing", # can't test till ln:target "missing_link",
|
||||
"or": "orphan",
|
||||
"mi": None, # never used
|
||||
"su": "set_uid",
|
||||
"sg": "set_gid",
|
||||
"ca": None, # Separate special case test,
|
||||
|
@ -186,8 +186,6 @@ _cf = {
|
|||
"mh": "hard_link",
|
||||
}
|
||||
|
||||
# TODO: create equivalent zoo of files for Windows.
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def colorizable_files():
|
||||
|
@ -214,14 +212,15 @@ def colorizable_files():
|
|||
pass
|
||||
elif k == "ex":
|
||||
os.chmod(file_path, stat.S_IXUSR)
|
||||
elif k == "ln":
|
||||
elif k == "ln": # cook ln test case.
|
||||
os.chmod(file_path, stat.S_IXUSR) # link to *executable* file
|
||||
os.rename(file_path, file_path + "_target")
|
||||
os.symlink(file_path + "_target", file_path)
|
||||
elif k == "mi":
|
||||
elif k == "or":
|
||||
os.rename(file_path, file_path + "_target")
|
||||
os.symlink(file_path + "_target", file_path)
|
||||
os.remove(file_path + "_target")
|
||||
elif k == "pi":
|
||||
elif k == "pi": # not on Windows
|
||||
os.remove(file_path)
|
||||
os.mkfifo(file_path)
|
||||
elif k == "su":
|
||||
|
@ -253,13 +252,10 @@ def colorizable_files():
|
|||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"key,file_path",
|
||||
[(key, file_path) for key, file_path in _cf.items() if file_path and key != "mi"],
|
||||
"key,file_path", [(key, file_path) for key, file_path in _cf.items() if file_path],
|
||||
)
|
||||
def test_colorize_file(key, file_path, colorizable_files, xonsh_builtins_LS_COLORS):
|
||||
# xonsh_builtins_LS_COLORS.__xonsh__.shell.shell.styler = (
|
||||
# XonshStyle()
|
||||
# ) # default style
|
||||
"""test proper file codes with symlinks colored normally"""
|
||||
ffp = colorizable_files + "/" + file_path
|
||||
stat_result = os.lstat(ffp)
|
||||
color_token, color_key = color_file(ffp, stat_result)
|
||||
|
@ -268,42 +264,31 @@ def test_colorize_file(key, file_path, colorizable_files, xonsh_builtins_LS_COLO
|
|||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"key,file_path",
|
||||
[(key, file_path) for key, file_path in _cf.items() if file_path],
|
||||
"key,file_path", [(key, file_path) for key, file_path in _cf.items() if file_path],
|
||||
)
|
||||
def test_colorize_file_symlink(
|
||||
key, file_path, colorizable_files, xonsh_builtins_LS_COLORS
|
||||
):
|
||||
"""test coloring of symlink to each kind of file"""
|
||||
"""test proper file codes with symlinks colored target."""
|
||||
xonsh_builtins_LS_COLORS.__xonsh__.env["LS_COLORS"]["ln"] = "target"
|
||||
ffp = colorizable_files + "/" + file_path
|
||||
if key != "mi": # 'mi' is special case -- must use link that is actually broken.
|
||||
ffp += "_symlink"
|
||||
ffp = colorizable_files + "/" + file_path + "_symlink"
|
||||
stat_result = os.lstat(ffp)
|
||||
assert stat.S_ISLNK(stat_result.st_mode)
|
||||
|
||||
color_token, color_key = color_file(ffp, stat_result)
|
||||
assert color_key == key, "File classified as expected kind"
|
||||
assert color_token == file_color_tokens[key], "Color token is as expected"
|
||||
_, color_key = color_file(ffp, stat_result)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"file_path,key",
|
||||
[
|
||||
("regular_symlink", "ln"),
|
||||
("regular", "fi"),
|
||||
("sym_link_symlink", "ln"),
|
||||
("sym_link", "ln"),
|
||||
],
|
||||
try:
|
||||
tar_stat_result = os.stat(ffp) # stat the target of the link
|
||||
tar_ffp = str(pathlib.Path(ffp).resolve())
|
||||
_, tar_color_key = color_file(tar_ffp, tar_stat_result)
|
||||
if tar_color_key.startswith("*"):
|
||||
tar_color_key = (
|
||||
"fi" # all the *.* zoo, link is colored 'fi', not target type.
|
||||
)
|
||||
def test_colorize_file_multi_symlink(
|
||||
file_path, key, colorizable_files, xonsh_builtins_LS_COLORS
|
||||
):
|
||||
"""verify returns color for immediate target of a symlink, not the ultimate target."""
|
||||
ffp = colorizable_files + "/" + file_path
|
||||
stat_result = os.lstat(ffp)
|
||||
color_token, color_key = color_file(ffp, stat_result)
|
||||
assert color_key == key, "File classified as expected kind"
|
||||
except FileNotFoundError: # orphan symlinks always colored 'or'
|
||||
tar_color_key = "or" # Fake if for missing file
|
||||
|
||||
assert color_key == tar_color_key, "File classified as expected kind, via symlink"
|
||||
|
||||
|
||||
import xonsh.lazyimps
|
||||
|
|
|
@ -93,11 +93,6 @@ def html():
|
|||
|
||||
@lazyobject
|
||||
def os_listxattr():
|
||||
if getattr(os, "listxattr", None):
|
||||
return os.listxattr
|
||||
else:
|
||||
|
||||
def xx(*args, **kwargs):
|
||||
def dummy_listxattr(*args, **kwargs):
|
||||
return []
|
||||
|
||||
return xx
|
||||
return getattr(os, 'listxattr', dummy_listxattr)
|
||||
|
|
|
@ -1358,7 +1358,8 @@ events.on_lscolors_change(on_lscolors_change)
|
|||
|
||||
|
||||
def color_file(file_path: str, path_stat: os.stat_result) -> (Color, str):
|
||||
"""Determine color to use for file *approximately* as ls --color would, given lstat() results and its name.
|
||||
"""Determine color to use for file *approximately* as ls --color would,
|
||||
given lstat() results and its path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
@ -1377,28 +1378,24 @@ def color_file(file_path: str, path_stat: os.stat_result) -> (Color, str):
|
|||
* implementation follows one authority:
|
||||
https://github.com/coreutils/coreutils/blob/master/src/ls.c#L4879
|
||||
* except:
|
||||
1. does not return 'mi' symlink to missing file unless ln=target,
|
||||
because caller provides an existing file.
|
||||
1. does not return 'mi'. That's the color ls uses to show the (missing) *target* of a symlink
|
||||
(in ls -l, not ls).
|
||||
2. in dircolors, setting type code to '0 or '00' bypasses that test and proceeds to others.
|
||||
In our implementation, setting code to '00' paints the file with no color.
|
||||
This is arguably a bug.
|
||||
3. dircolors with ln=target only colors links to fundamental types, not to *.zoo
|
||||
(file extension patterns). This function will paint the link with the ultimate file type color.
|
||||
This is arguably better for the user typing a filename on the command line.
|
||||
"""
|
||||
|
||||
lsc = builtins.__xonsh__.env["LS_COLORS"]
|
||||
color_key = "fi"
|
||||
|
||||
# if ln=target, get name of target (next link only) and its lstat_result
|
||||
if lsc.is_target("ln") and stat.S_ISLNK(path_stat.st_mode):
|
||||
# if symlink, get info on (final) target
|
||||
if stat.S_ISLNK(path_stat.st_mode):
|
||||
try:
|
||||
file_path = os.readlink(
|
||||
file_path
|
||||
) # next hop in symlink chain, just like ls
|
||||
path_stat = os.lstat(file_path) # and work with its properties
|
||||
except FileNotFoundError:
|
||||
color_key = "mi" # early exit
|
||||
tar_path_stat = os.stat(file_path) # and work with its properties
|
||||
if lsc.is_target("ln"): # if ln=target
|
||||
path_stat = tar_path_stat
|
||||
except FileNotFoundError: # bug always color broken link 'or'
|
||||
color_key = "or" # early exit
|
||||
ret_color_token = file_color_tokens.get(color_key, Text)
|
||||
return ret_color_token, color_key
|
||||
|
||||
|
@ -1442,7 +1439,10 @@ def color_file(file_path: str, path_stat: os.stat_result) -> (Color, str):
|
|||
else:
|
||||
color_key = "or" # any other type --> orphan
|
||||
|
||||
if color_key == "fi": # if still normal file -- try color by file extension.
|
||||
# if still normal file -- try color by file extension.
|
||||
# note: symlink to *.<ext> will be colored 'fi' unless the symlink itself
|
||||
# ends with .<ext>. `ls` does the same. Bug-for-bug compatibility!
|
||||
if color_key == "fi":
|
||||
match = color_file_extension_RE.match(file_path)
|
||||
if match:
|
||||
ext = "*" + match.group(1) # look for *.<fileExtension> coloring
|
||||
|
|
Loading…
Add table
Reference in a new issue