1from __future__ import absolute_import, division, print_function 2import subprocess 3import sys 4import pytest 5 6# test for _argcomplete but not specific for any application 7 8 9def equal_with_bash(prefix, ffc, fc, out=None): 10 res = ffc(prefix) 11 res_bash = set(fc(prefix)) 12 retval = set(res) == res_bash 13 if out: 14 out.write("equal_with_bash %s %s\n" % (retval, res)) 15 if not retval: 16 out.write(" python - bash: %s\n" % (set(res) - res_bash)) 17 out.write(" bash - python: %s\n" % (res_bash - set(res))) 18 return retval 19 20 21# copied from argcomplete.completers as import from there 22# also pulls in argcomplete.__init__ which opens filedescriptor 9 23# this gives an IOError at the end of testrun 24 25 26def _wrapcall(*args, **kargs): 27 try: 28 if sys.version_info > (2, 7): 29 return subprocess.check_output(*args, **kargs).decode().splitlines() 30 if "stdout" in kargs: 31 raise ValueError("stdout argument not allowed, it will be overridden.") 32 process = subprocess.Popen(stdout=subprocess.PIPE, *args, **kargs) 33 output, unused_err = process.communicate() 34 retcode = process.poll() 35 if retcode: 36 cmd = kargs.get("args") 37 if cmd is None: 38 cmd = args[0] 39 raise subprocess.CalledProcessError(retcode, cmd) 40 return output.decode().splitlines() 41 except subprocess.CalledProcessError: 42 return [] 43 44 45class FilesCompleter(object): 46 "File completer class, optionally takes a list of allowed extensions" 47 48 def __init__(self, allowednames=(), directories=True): 49 # Fix if someone passes in a string instead of a list 50 if type(allowednames) is str: 51 allowednames = [allowednames] 52 53 self.allowednames = [x.lstrip("*").lstrip(".") for x in allowednames] 54 self.directories = directories 55 56 def __call__(self, prefix, **kwargs): 57 completion = [] 58 if self.allowednames: 59 if self.directories: 60 files = _wrapcall( 61 ["bash", "-c", "compgen -A directory -- '{p}'".format(p=prefix)] 62 ) 63 completion += [f + "/" for f in files] 64 for x in self.allowednames: 65 completion += _wrapcall( 66 [ 67 "bash", 68 "-c", 69 "compgen -A file -X '!*.{0}' -- '{p}'".format(x, p=prefix), 70 ] 71 ) 72 else: 73 completion += _wrapcall( 74 ["bash", "-c", "compgen -A file -- '{p}'".format(p=prefix)] 75 ) 76 77 anticomp = _wrapcall( 78 ["bash", "-c", "compgen -A directory -- '{p}'".format(p=prefix)] 79 ) 80 81 completion = list(set(completion) - set(anticomp)) 82 83 if self.directories: 84 completion += [f + "/" for f in anticomp] 85 return completion 86 87 88class TestArgComplete(object): 89 90 @pytest.mark.skipif("sys.platform in ('win32', 'darwin')") 91 def test_compare_with_compgen(self): 92 from _pytest._argcomplete import FastFilesCompleter 93 94 ffc = FastFilesCompleter() 95 fc = FilesCompleter() 96 for x in ["/", "/d", "/data", "qqq", ""]: 97 assert equal_with_bash(x, ffc, fc, out=sys.stdout) 98 99 @pytest.mark.skipif("sys.platform in ('win32', 'darwin')") 100 def test_remove_dir_prefix(self): 101 """this is not compatible with compgen but it is with bash itself: 102 ls /usr/<TAB> 103 """ 104 from _pytest._argcomplete import FastFilesCompleter 105 106 ffc = FastFilesCompleter() 107 fc = FilesCompleter() 108 for x in "/usr/".split(): 109 assert not equal_with_bash(x, ffc, fc, out=sys.stdout) 110