mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
which refactor p1
This commit is contained in:
parent
c8f9415bb9
commit
2dd7c5541d
4 changed files with 238 additions and 192 deletions
|
@ -3,15 +3,12 @@
|
|||
from __future__ import unicode_literals, print_function
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
|
||||
import xonsh.built_ins as built_ins
|
||||
from xonsh.aliases import Aliases
|
||||
from xonsh.aliases import _which
|
||||
from xonsh.environ import Env
|
||||
from xonsh.tools import ON_WINDOWS
|
||||
|
||||
from tools import skip_if_on_windows
|
||||
|
||||
|
@ -54,84 +51,3 @@ def test_eval_recursive(xonsh_builtins):
|
|||
def test_eval_recursive_callable_partial(xonsh_builtins):
|
||||
xonsh_builtins.__xonsh_env__ = Env(HOME=os.path.expanduser('~'))
|
||||
assert ALIASES.get('indirect_cd')(['arg2', 'arg3']) == ['..', 'arg2', 'arg3']
|
||||
|
||||
class TestWhich:
|
||||
# Tests for the _whichgen function which is the only thing we
|
||||
# use from the _which.py module.
|
||||
def setup(self):
|
||||
# Setup two folders with some test files.
|
||||
self.testdirs = [tempfile.TemporaryDirectory(),
|
||||
tempfile.TemporaryDirectory()]
|
||||
if ON_WINDOWS:
|
||||
self.testapps = ['whichtestapp1.exe',
|
||||
'whichtestapp2.wta']
|
||||
self.exts = ['.EXE']
|
||||
else:
|
||||
self.testapps = ['whichtestapp1']
|
||||
self.exts = None
|
||||
for app in self.testapps:
|
||||
for d in self.testdirs:
|
||||
path = os.path.join(d.name, app)
|
||||
open(path, 'wb').write(b'')
|
||||
os.chmod(path, 0o755)
|
||||
|
||||
def teardown_module(self):
|
||||
for d in self.testdirs:
|
||||
d.cleanup()
|
||||
|
||||
def test_whichgen(self):
|
||||
testdir = self.testdirs[0].name
|
||||
arg = 'whichtestapp1'
|
||||
matches = list(_which.whichgen(arg, path=[testdir], exts=self.exts))
|
||||
assert len(matches) == 1
|
||||
assert self._file_match(matches[0][0], os.path.join(testdir, arg))
|
||||
|
||||
def test_whichgen_failure(self):
|
||||
testdir = self.testdirs[0].name
|
||||
arg = 'not_a_file'
|
||||
matches = list(_which.whichgen(arg, path=[testdir], exts=self.exts))
|
||||
assert len(matches) == 0
|
||||
|
||||
def test_whichgen_verbose(self):
|
||||
testdir = self.testdirs[0].name
|
||||
arg = 'whichtestapp1'
|
||||
matches = list(_which.whichgen(arg, path=[testdir], exts=self.exts,
|
||||
verbose=True))
|
||||
assert len(matches) == 1
|
||||
match, from_where = matches[0]
|
||||
assert self._file_match(match, os.path.join(testdir, arg))
|
||||
assert from_where == 'from given path element 0'
|
||||
|
||||
def test_whichgen_multiple(self):
|
||||
testdir0 = self.testdirs[0].name
|
||||
testdir1 = self.testdirs[1].name
|
||||
arg = 'whichtestapp1'
|
||||
matches = list(_which.whichgen(arg, path=[testdir0, testdir1],
|
||||
exts=self.exts))
|
||||
assert len(matches) == 2
|
||||
assert self._file_match(matches[0][0], os.path.join(testdir0, arg))
|
||||
assert self._file_match(matches[1][0], os.path.join(testdir1, arg))
|
||||
|
||||
if ON_WINDOWS:
|
||||
def test_whichgen_ext_failure(self):
|
||||
testdir = self.testdirs[0].name
|
||||
arg = 'whichtestapp2'
|
||||
matches = list(_which.whichgen(arg, path=[testdir], exts=self.exts))
|
||||
assert len(matches) == 0
|
||||
|
||||
def test_whichgen_ext_success(self):
|
||||
testdir = self.testdirs[0].name
|
||||
arg = 'whichtestapp2'
|
||||
matches = list(_which.whichgen(arg, path=[testdir], exts=['.wta']))
|
||||
assert len(matches) == 1
|
||||
assert self._file_match(matches[0][0], os.path.join(testdir, arg))
|
||||
|
||||
def _file_match(self, path1, path2):
|
||||
if ON_WINDOWS:
|
||||
path1 = os.path.normpath(os.path.normcase(path1))
|
||||
path2 = os.path.normpath(os.path.normcase(path2))
|
||||
path1 = os.path.splitext(path1)[0]
|
||||
path2 = os.path.splitext(path2)[0]
|
||||
return path1 == path2
|
||||
else:
|
||||
return os.path.samefile(path1, path2)
|
||||
|
|
87
tests/test_xoreutils.py
Normal file
87
tests/test_xoreutils.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
import os
|
||||
import tempfile
|
||||
|
||||
from xonsh.xoreutils import _which
|
||||
from xonsh.tools import ON_WINDOWS
|
||||
|
||||
|
||||
class TestWhich:
|
||||
# Tests for the _whichgen function which is the only thing we
|
||||
# use from the _which.py module.
|
||||
def setup(self):
|
||||
# Setup two folders with some test files.
|
||||
self.testdirs = [tempfile.TemporaryDirectory(),
|
||||
tempfile.TemporaryDirectory()]
|
||||
if ON_WINDOWS:
|
||||
self.testapps = ['whichtestapp1.exe',
|
||||
'whichtestapp2.wta']
|
||||
self.exts = ['.EXE']
|
||||
else:
|
||||
self.testapps = ['whichtestapp1']
|
||||
self.exts = None
|
||||
for app in self.testapps:
|
||||
for d in self.testdirs:
|
||||
path = os.path.join(d.name, app)
|
||||
open(path, 'wb').write(b'')
|
||||
os.chmod(path, 0o755)
|
||||
|
||||
def teardown_module(self):
|
||||
for d in self.testdirs:
|
||||
d.cleanup()
|
||||
|
||||
def test_whichgen(self):
|
||||
testdir = self.testdirs[0].name
|
||||
arg = 'whichtestapp1'
|
||||
matches = list(_which.whichgen(arg, path=[testdir], exts=self.exts))
|
||||
assert len(matches) == 1
|
||||
assert self._file_match(matches[0][0], os.path.join(testdir, arg))
|
||||
|
||||
def test_whichgen_failure(self):
|
||||
testdir = self.testdirs[0].name
|
||||
arg = 'not_a_file'
|
||||
matches = list(_which.whichgen(arg, path=[testdir], exts=self.exts))
|
||||
assert len(matches) == 0
|
||||
|
||||
def test_whichgen_verbose(self):
|
||||
testdir = self.testdirs[0].name
|
||||
arg = 'whichtestapp1'
|
||||
matches = list(_which.whichgen(arg, path=[testdir], exts=self.exts,
|
||||
verbose=True))
|
||||
assert len(matches) == 1
|
||||
match, from_where = matches[0]
|
||||
assert self._file_match(match, os.path.join(testdir, arg))
|
||||
assert from_where == 'from given path element 0'
|
||||
|
||||
def test_whichgen_multiple(self):
|
||||
testdir0 = self.testdirs[0].name
|
||||
testdir1 = self.testdirs[1].name
|
||||
arg = 'whichtestapp1'
|
||||
matches = list(_which.whichgen(arg, path=[testdir0, testdir1],
|
||||
exts=self.exts))
|
||||
assert len(matches) == 2
|
||||
assert self._file_match(matches[0][0], os.path.join(testdir0, arg))
|
||||
assert self._file_match(matches[1][0], os.path.join(testdir1, arg))
|
||||
|
||||
if ON_WINDOWS:
|
||||
def test_whichgen_ext_failure(self):
|
||||
testdir = self.testdirs[0].name
|
||||
arg = 'whichtestapp2'
|
||||
matches = list(_which.whichgen(arg, path=[testdir], exts=self.exts))
|
||||
assert len(matches) == 0
|
||||
|
||||
def test_whichgen_ext_success(self):
|
||||
testdir = self.testdirs[0].name
|
||||
arg = 'whichtestapp2'
|
||||
matches = list(_which.whichgen(arg, path=[testdir], exts=['.wta']))
|
||||
assert len(matches) == 1
|
||||
assert self._file_match(matches[0][0], os.path.join(testdir, arg))
|
||||
|
||||
def _file_match(self, path1, path2):
|
||||
if ON_WINDOWS:
|
||||
path1 = os.path.normpath(os.path.normcase(path1))
|
||||
path2 = os.path.normpath(os.path.normcase(path2))
|
||||
path1 = os.path.splitext(path1)[0]
|
||||
path2 = os.path.splitext(path2)[0]
|
||||
return path1 == path2
|
||||
else:
|
||||
return os.path.samefile(path1, path2)
|
110
xonsh/aliases.py
110
xonsh/aliases.py
|
@ -22,7 +22,7 @@ from xonsh.timings import timeit_alias
|
|||
from xonsh.tools import (XonshError, argvquote, escape_windows_cmd_string,
|
||||
to_bool)
|
||||
from xonsh.xontribs import xontribs_main
|
||||
from xonsh.xoreutils import _which
|
||||
from xonsh.xoreutils.which import which_main
|
||||
|
||||
import xonsh.completers._aliases as xca
|
||||
|
||||
|
@ -362,112 +362,6 @@ class AWitchAWitch(argparse.Action):
|
|||
parser.exit()
|
||||
|
||||
|
||||
def which(args, stdin=None, stdout=None, stderr=None):
|
||||
"""
|
||||
Checks if each arguments is a xonsh aliases, then if it's an executable,
|
||||
then finally return an error code equal to the number of misses.
|
||||
If '-a' flag is passed, run both to return both `xonsh` match and
|
||||
`which` match.
|
||||
"""
|
||||
desc = "Parses arguments to which wrapper"
|
||||
parser = argparse.ArgumentParser('which', description=desc)
|
||||
parser.add_argument('args', type=str, nargs='+',
|
||||
help='The executables or aliases to search for')
|
||||
parser.add_argument('-a', '--all', action='store_true', dest='all',
|
||||
help='Show all matches in $PATH and xonsh.aliases')
|
||||
parser.add_argument('-s', '--skip-alias', action='store_true',
|
||||
help='Do not search in xonsh.aliases', dest='skip')
|
||||
parser.add_argument('-V', '--version', action='version',
|
||||
version='{}'.format(_which.__version__),
|
||||
help='Display the version of the python which module '
|
||||
'used by xonsh')
|
||||
parser.add_argument('-v', '--verbose', action='store_true', dest='verbose',
|
||||
help='Print out how matches were located and show '
|
||||
'near misses on stderr')
|
||||
parser.add_argument('-p', '--plain', action='store_true', dest='plain',
|
||||
help='Do not display alias expansions or location of '
|
||||
'where binaries are found. This is the '
|
||||
'default behavior, but the option can be used to '
|
||||
'override the --verbose option')
|
||||
parser.add_argument('--very-small-rocks', action=AWitchAWitch)
|
||||
if ON_WINDOWS:
|
||||
parser.add_argument('-e', '--exts', nargs='*', type=str,
|
||||
help='Specify a list of extensions to use instead '
|
||||
'of the standard list for this system. This can '
|
||||
'effectively be used as an optimization to, for '
|
||||
'example, avoid stat\'s of "foo.vbs" when '
|
||||
'searching for "foo" and you know it is not a '
|
||||
'VisualBasic script but ".vbs" is on PATHEXT. '
|
||||
'This option is only supported on Windows',
|
||||
dest='exts')
|
||||
if len(args) == 0:
|
||||
parser.print_usage(file=stderr)
|
||||
return -1
|
||||
pargs = parser.parse_args(args)
|
||||
|
||||
if pargs.all:
|
||||
pargs.verbose = True
|
||||
|
||||
if ON_WINDOWS:
|
||||
if pargs.exts:
|
||||
exts = pargs.exts
|
||||
else:
|
||||
exts = builtins.__xonsh_env__['PATHEXT']
|
||||
else:
|
||||
exts = None
|
||||
|
||||
failures = []
|
||||
for arg in pargs.args:
|
||||
nmatches = 0
|
||||
# skip alias check if user asks to skip
|
||||
if (arg in builtins.aliases and not pargs.skip):
|
||||
if pargs.plain or not pargs.verbose:
|
||||
if not callable(builtins.aliases[arg]):
|
||||
print(' '.join(builtins.aliases[arg]), file=stdout)
|
||||
else:
|
||||
print(arg, file=stdout)
|
||||
else:
|
||||
print("aliases['{}'] = {}".format(arg, builtins.aliases[arg]), file=stdout)
|
||||
if callable(builtins.aliases[arg]):
|
||||
builtins.__xonsh_superhelp__(builtins.aliases[arg])
|
||||
nmatches += 1
|
||||
if not pargs.all:
|
||||
continue
|
||||
# which.whichgen gives the nicest 'verbose' output if PATH is taken
|
||||
# from os.environ so we temporarily override it with
|
||||
# __xosnh_env__['PATH']
|
||||
original_os_path = os.environ['PATH']
|
||||
os.environ['PATH'] = builtins.__xonsh_env__.detype()['PATH']
|
||||
matches = _which.whichgen(arg, exts=exts, verbose=pargs.verbose)
|
||||
for abs_name, from_where in matches:
|
||||
if ON_WINDOWS:
|
||||
# Use list dir to get correct case for the filename
|
||||
# i.e. windows is case insensitive but case preserving
|
||||
p, f = os.path.split(abs_name)
|
||||
f = next(s.name for s in scandir(p) if s.name.lower() == f.lower())
|
||||
abs_name = os.path.join(p, f)
|
||||
if builtins.__xonsh_env__.get('FORCE_POSIX_PATHS', False):
|
||||
abs_name.replace(os.sep, os.altsep)
|
||||
if pargs.plain or not pargs.verbose:
|
||||
print(abs_name, file=stdout)
|
||||
else:
|
||||
print('{} ({})'.format(abs_name, from_where), file=stdout)
|
||||
nmatches += 1
|
||||
if not pargs.all:
|
||||
break
|
||||
os.environ['PATH'] = original_os_path
|
||||
if not nmatches:
|
||||
failures.append(arg)
|
||||
if len(failures) == 0:
|
||||
return 0
|
||||
else:
|
||||
print('{} not in $PATH'.format(', '.join(failures)), file=stderr, end='')
|
||||
if not pargs.skip:
|
||||
print(' or xonsh.builtins.aliases', file=stderr, end='')
|
||||
print('', end='\n')
|
||||
return len(failures)
|
||||
|
||||
|
||||
def xonfig(args, stdin=None):
|
||||
"""Runs the xonsh configuration utility."""
|
||||
from xonsh.xonfig import xonfig_main # lazy import
|
||||
|
@ -534,7 +428,7 @@ def make_default_aliases():
|
|||
'scp-resume': ['rsync', '--partial', '-h', '--progress', '--rsh=ssh'],
|
||||
'showcmd': showcmd,
|
||||
'ipynb': ['jupyter', 'notebook', '--no-browser'],
|
||||
'which': which,
|
||||
'which': which_main,
|
||||
'xontrib': xontribs_main,
|
||||
'completer': xca.completer_alias
|
||||
}
|
||||
|
|
149
xonsh/xoreutils/which.py
Normal file
149
xonsh/xoreutils/which.py
Normal file
|
@ -0,0 +1,149 @@
|
|||
import argparse
|
||||
import builtins
|
||||
import functools
|
||||
import os
|
||||
|
||||
from xonsh.xoreutils import _which
|
||||
from xonsh.platform import ON_WINDOWS, scandir
|
||||
|
||||
|
||||
@functools.lru_cache()
|
||||
def _which_create_parser():
|
||||
desc = "Parses arguments to which wrapper"
|
||||
parser = argparse.ArgumentParser('which', description=desc)
|
||||
parser.add_argument('args', type=str, nargs='+',
|
||||
help='The executables or aliases to search for')
|
||||
parser.add_argument('-a', '--all', action='store_true', dest='all',
|
||||
help='Show all matches in $PATH and xonsh.aliases')
|
||||
parser.add_argument('-s', '--skip-alias', action='store_true',
|
||||
help='Do not search in xonsh.aliases', dest='skip')
|
||||
parser.add_argument('-V', '--version', action='version',
|
||||
version='{}'.format(_which.__version__),
|
||||
help='Display the version of the python which module '
|
||||
'used by xonsh')
|
||||
parser.add_argument('-v', '--verbose', action='store_true', dest='verbose',
|
||||
help='Print out how matches were located and show '
|
||||
'near misses on stderr')
|
||||
parser.add_argument('-p', '--plain', action='store_true', dest='plain',
|
||||
help='Do not display alias expansions or location of '
|
||||
'where binaries are found. This is the '
|
||||
'default behavior, but the option can be used to '
|
||||
'override the --verbose option')
|
||||
parser.add_argument('--very-small-rocks', action=AWitchAWitch)
|
||||
if ON_WINDOWS:
|
||||
parser.add_argument('-e', '--exts', nargs='*', type=str,
|
||||
help='Specify a list of extensions to use instead '
|
||||
'of the standard list for this system. This can '
|
||||
'effectively be used as an optimization to, for '
|
||||
'example, avoid stat\'s of "foo.vbs" when '
|
||||
'searching for "foo" and you know it is not a '
|
||||
'VisualBasic script but ".vbs" is on PATHEXT. '
|
||||
'This option is only supported on Windows',
|
||||
dest='exts')
|
||||
return parser
|
||||
|
||||
|
||||
def print_modules(arg, verbose=False):
|
||||
"""Print the module."""
|
||||
|
||||
|
||||
def print_path(abs_name, from_where, verbose=False):
|
||||
if ON_WINDOWS:
|
||||
# Use list dir to get correct case for the filename
|
||||
# i.e. windows is case insensitive but case preserving
|
||||
p, f = os.path.split(abs_name)
|
||||
f = next(s.name for s in scandir(p) if s.name.lower() == f.lower())
|
||||
abs_name = os.path.join(p, f)
|
||||
if builtins.__xonsh_env__.get('FORCE_POSIX_PATHS', False):
|
||||
abs_name.replace(os.sep, os.altsep)
|
||||
if not pargs.verbose:
|
||||
print(abs_name, file=stdout)
|
||||
else:
|
||||
print('{} ({})'.format(abs_name, from_where), file=stdout)
|
||||
|
||||
|
||||
def print_alias(arg, verbose=False):
|
||||
"""Print the alias"""
|
||||
if not verbose:
|
||||
if not callable(builtins.aliases[arg]):
|
||||
print(' '.join(builtins.aliases[arg]), file=stdout)
|
||||
else:
|
||||
print(arg, file=stdout)
|
||||
else:
|
||||
print("aliases['{}'] = {}".format(arg, builtins.aliases[arg]), file=stdout)
|
||||
if callable(builtins.aliases[arg]):
|
||||
builtins.__xonsh_superhelp__(builtins.aliases[arg])
|
||||
|
||||
|
||||
def which(args, stdin=None, stdout=None, stderr=None):
|
||||
"""
|
||||
Checks if each arguments is a xonsh aliases, then if it's an executable,
|
||||
then finally return an error code equal to the number of misses.
|
||||
If '-a' flag is passed, run both to return both `xonsh` match and
|
||||
`which` match.
|
||||
"""
|
||||
parser = _which_create_parser()
|
||||
if len(args) == 0:
|
||||
parser.print_usage(file=stderr)
|
||||
return -1
|
||||
|
||||
pargs = parser.parse_args(args)
|
||||
verbose = pargs.verbose or pargs.all
|
||||
if pargs.plain:
|
||||
verbose = False
|
||||
if ON_WINDOWS:
|
||||
if pargs.exts:
|
||||
exts = pargs.exts
|
||||
else:
|
||||
exts = builtins.__xonsh_env__['PATHEXT']
|
||||
else:
|
||||
exts = None
|
||||
failures = []
|
||||
for arg in pargs.args:
|
||||
# skip alias check if user asks to skip
|
||||
if (arg in builtins.aliases and not pargs.skip):
|
||||
print_alias(arg)
|
||||
nmatches += 1
|
||||
if not pargs.all:
|
||||
continue
|
||||
# which.whichgen gives the nicest 'verbose' output if PATH is taken
|
||||
# from os.environ so we temporarily override it with
|
||||
# __xosnh_env__['PATH']
|
||||
original_os_path = os.environ['PATH']
|
||||
os.environ['PATH'] = builtins.__xonsh_env__.detype()['PATH']
|
||||
matches = _which.whichgen(arg, exts=exts, verbose=pargs.verbose)
|
||||
for abs_name, from_where in matches:
|
||||
print_path(abs_name, from_where)
|
||||
nmatches += 1
|
||||
if not pargs.all:
|
||||
break
|
||||
os.environ['PATH'] = original_os_path
|
||||
if not nmatches:
|
||||
failures.append(arg)
|
||||
if len(failures) == 0:
|
||||
return 0
|
||||
else:
|
||||
print('{} not in $PATH'.format(', '.join(failures)), file=stderr, end='')
|
||||
if not pargs.skip:
|
||||
print(' or xonsh.builtins.aliases', file=stderr, end='')
|
||||
print('', end='\n')
|
||||
return len(failures)
|
||||
|
||||
|
||||
class AWitchAWitch(argparse.Action):
|
||||
SUPPRESS = '==SUPPRESS=='
|
||||
|
||||
def __init__(self, option_strings, version=None, dest=SUPPRESS,
|
||||
default=SUPPRESS, **kwargs):
|
||||
super().__init__(option_strings=option_strings, dest=dest,
|
||||
default=default, nargs=0, **kwargs)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
import webbrowser
|
||||
webbrowser.open('https://github.com/xonsh/xonsh/commit/f49b400')
|
||||
parser.exit()
|
||||
|
||||
|
||||
def which_main(args=None, stdin=None):
|
||||
"""This is the which command entry point."""
|
||||
which(args, stdin=stdin)
|
Loading…
Add table
Reference in a new issue