don't try to create pipe on Windows; return 'or', not 'mi' for broken link

This commit is contained in:
Bob Hyman 2020-06-14 00:22:30 -04:00
parent 68f776def0
commit 08a79c97c8
4 changed files with 45 additions and 65 deletions

View file

@ -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:**

View file

@ -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

View file

@ -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)

View file

@ -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