# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors.# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info.importargparseimportosimportsubprocessdef_call(*args,**kwargs):# TODO: replace "universal_newlines" with "text" once 3.6 support is droppedkwargs["universal_newlines"]=Truetry:returnsubprocess.check_output(*args,**kwargs).splitlines()exceptsubprocess.CalledProcessError:return[]classBaseCompleter:""" This is the base class that all argcomplete completers should subclass. """def__call__(self,*,prefix:str,action:argparse.Action,parser:argparse.ArgumentParser,parsed_args:argparse.Namespace)->None:raiseNotImplementedError("This method should be implemented by a subclass.")
[docs]classFilesCompleter(BaseCompleter):""" File completer class, optionally takes a list of allowed extensions """
[docs]def__init__(self,allowednames=(),directories=True):# Fix if someone passes in a string instead of a listifisinstance(allowednames,(str,bytes)):allowednames=[allowednames]self.allowednames=[x.lstrip("*").lstrip(".")forxinallowednames]self.directories=directories
[docs]def__call__(self,prefix,**kwargs):completion=[]ifself.allowednames:ifself.directories:files=_call(["bash","-c","compgen -A directory -- '{p}'".format(p=prefix)])completion+=[f+"/"forfinfiles]forxinself.allowednames:completion+=_call(["bash","-c","compgen -A file -X '!*.{0}' -- '{p}'".format(x,p=prefix)])else:completion+=_call(["bash","-c","compgen -A file -- '{p}'".format(p=prefix)])anticomp=_call(["bash","-c","compgen -A directory -- '{p}'".format(p=prefix)])completion=list(set(completion)-set(anticomp))ifself.directories:completion+=[f+"/"forfinanticomp]returncompletion
class_FilteredFilesCompleter(BaseCompleter):def__init__(self,predicate):""" Create the completer A predicate accepts as its only argument a candidate path and either accepts it or rejects it. """assertpredicate,"Expected a callable predicate"self.predicate=predicatedef__call__(self,prefix,**kwargs):""" Provide completions on prefix """target_dir=os.path.dirname(prefix)try:names=os.listdir(target_diror".")exceptException:return# empty iteratorincomplete_part=os.path.basename(prefix)# Iterate on target_dir entries and filter on given predicatefornameinnames:ifnotname.startswith(incomplete_part):continuecandidate=os.path.join(target_dir,name)ifnotself.predicate(candidate):continueyieldcandidate+"/"ifos.path.isdir(candidate)elsecandidate