from __future__ import absolute_import from __future__ import division from __future__ import print_function import subprocess import sys import pytest # test for _argcomplete but not specific for any application def equal_with_bash(prefix, ffc, fc, out=None): res = ffc(prefix) res_bash = set(fc(prefix)) retval = set(res) == res_bash if out: out.write("equal_with_bash {} {}\n".format(retval, res)) if not retval: out.write(" python - bash: %s\n" % (set(res) - res_bash)) out.write(" bash - python: %s\n" % (res_bash - set(res))) return retval # copied from argcomplete.completers as import from there # also pulls in argcomplete.__init__ which opens filedescriptor 9 # this gives an IOError at the end of testrun def _wrapcall(*args, **kargs): try: if sys.version_info > (2, 7): return subprocess.check_output(*args, **kargs).decode().splitlines() if "stdout" in kargs: raise ValueError("stdout argument not allowed, it will be overridden.") process = subprocess.Popen(stdout=subprocess.PIPE, *args, **kargs) output, unused_err = process.communicate() retcode = process.poll() if retcode: cmd = kargs.get("args") if cmd is None: cmd = args[0] raise subprocess.CalledProcessError(retcode, cmd) return output.decode().splitlines() except subprocess.CalledProcessError: return [] class FilesCompleter(object): "File completer class, optionally takes a list of allowed extensions" def __init__(self, allowednames=(), directories=True): # Fix if someone passes in a string instead of a list if type(allowednames) is str: allowednames = [allowednames] self.allowednames = [x.lstrip("*").lstrip(".") for x in allowednames] self.directories = directories def __call__(self, prefix, **kwargs): completion = [] if self.allowednames: if self.directories: files = _wrapcall( ["bash", "-c", "compgen -A directory -- '{p}'".format(p=prefix)] ) completion += [f + "/" for f in files] for x in self.allowednames: completion += _wrapcall( [ "bash", "-c", "compgen -A file -X '!*.{0}' -- '{p}'".format(x, p=prefix), ] ) else: completion += _wrapcall( ["bash", "-c", "compgen -A file -- '{p}'".format(p=prefix)] ) anticomp = _wrapcall( ["bash", "-c", "compgen -A directory -- '{p}'".format(p=prefix)] ) completion = list(set(completion) - set(anticomp)) if self.directories: completion += [f + "/" for f in anticomp] return completion class TestArgComplete(object): @pytest.mark.skipif("sys.platform in ('win32', 'darwin')") def test_compare_with_compgen(self): from _pytest._argcomplete import FastFilesCompleter ffc = FastFilesCompleter() fc = FilesCompleter() for x in ["/", "/d", "/data", "qqq", ""]: assert equal_with_bash(x, ffc, fc, out=sys.stdout) @pytest.mark.skipif("sys.platform in ('win32', 'darwin')") def test_remove_dir_prefix(self): """this is not compatible with compgen but it is with bash itself: ls /usr/ """ from _pytest._argcomplete import FastFilesCompleter ffc = FastFilesCompleter() fc = FilesCompleter() for x in "/usr/".split(): assert not equal_with_bash(x, ffc, fc, out=sys.stdout)