55 votes

Complément de tabulation dans la fonction raw_input() de Python

Je sais que je peux le faire pour obtenir l'effet de la complétion de tabulation en python bien sûr.

import readline
COMMANDS = ['extra', 'extension', 'stuff', 'errors',
            'email', 'foobar', 'foo']

def complete(text, state):
    for cmd in COMMANDS:
        if cmd.startswith(text):
            if not state:
                return cmd
            else:
                state -= 1

readline.parse_and_bind("tab: complete")
readline.set_completer(complete)
raw_input('Enter section name: ')

Je suis maintenant intéressé par la complétion de tabulation avec les répertoires. (/home/user/doc >tab)

Comment dois-je m'y prendre pour accomplir une telle tâche ?

68voto

samplebias Points 19805

Voici un exemple rapide de la manière de compléter de manière incrémentielle les chemins du système de fichiers. J'ai modifié votre exemple, en l'organisant dans une classe où les méthodes nommées complete_[name] indiquent les commandes de premier niveau.

J'ai changé la fonction d'achèvement pour utiliser le tampon interne de la ligne de lecture pour déterminer l'état de l'achèvement global, ce qui rend la logique d'état un peu plus simple. La complétion du chemin est dans le fichier _complete_path(path) et j'ai branché la méthode de la extra pour effectuer des complétions de chemin sur ses arguments.

Je suis sûr que le code pourrait être encore simplifié mais il devrait vous fournir un point de départ décent :

import os
import re
import readline

COMMANDS = ['extra', 'extension', 'stuff', 'errors',
            'email', 'foobar', 'foo']
RE_SPACE = re.compile('.*\s+$', re.M)

class Completer(object):

    def _listdir(self, root):
        "List directory 'root' appending the path separator to subdirs."
        res = []
        for name in os.listdir(root):
            path = os.path.join(root, name)
            if os.path.isdir(path):
                name += os.sep
            res.append(name)
        return res

    def _complete_path(self, path=None):
        "Perform completion of filesystem path."
        if not path:
            return self._listdir('.')
        dirname, rest = os.path.split(path)
        tmp = dirname if dirname else '.'
        res = [os.path.join(dirname, p)
                for p in self._listdir(tmp) if p.startswith(rest)]
        # more than one match, or single match which does not exist (typo)
        if len(res) > 1 or not os.path.exists(path):
            return res
        # resolved to a single directory, so return list of files below it
        if os.path.isdir(path):
            return [os.path.join(path, p) for p in self._listdir(path)]
        # exact file match terminates this completion
        return [path + ' ']

    def complete_extra(self, args):
        "Completions for the 'extra' command."
        if not args:
            return self._complete_path('.')
        # treat the last arg as a path and complete it
        return self._complete_path(args[-1])

    def complete(self, text, state):
        "Generic readline completion entry point."
        buffer = readline.get_line_buffer()
        line = readline.get_line_buffer().split()
        # show all commands
        if not line:
            return [c + ' ' for c in COMMANDS][state]
        # account for last argument ending in a space
        if RE_SPACE.match(buffer):
            line.append('')
        # resolve command to the implementation function
        cmd = line[0].strip()
        if cmd in COMMANDS:
            impl = getattr(self, 'complete_%s' % cmd)
            args = line[1:]
            if args:
                return (impl(args) + [None])[state]
            return [cmd + ' '][state]
        results = [c + ' ' for c in COMMANDS if c.startswith(cmd)] + [None]
        return results[state]

comp = Completer()
# we want to treat '/' as part of a word, so override the delimiters
readline.set_completer_delims(' \t\n;')
readline.parse_and_bind("tab: complete")
readline.set_completer(comp.complete)
raw_input('Enter section name: ')

Utilisation :

% python complete.py 
Enter section name: ext<tab>
extension extra
Enter section name: extra foo<tab>
foo.py foo.txt foo/
Enter section name: extra foo/<tab>
foo/bar.txt foo/baz.txt
Enter section name: extra foo/bar.txt

Mise à jour Il complétera les chemins à partir de la racine si l'utilisateur tape / :

% python complete.py
Enter section name: extra /Use<tab>
/Users/.localized  /Users/Shared/  /Users/user1 /Users/user2
Enter section name: extra /Users/use<tab>
/Users/user1  /Users/user2

1 votes

Cela me permet de comprendre comment faire un peu mieux, mais je cherche un moyen d'utiliser les répertoires entiers de mon système d'exploitation, par exemple. #python complete.py Enter directory name: /home/<tab> user1 user2 user3

0 votes

Si l'utilisateur tape /<tab> il sera complété par la racine, donc /home/<tab> fonctionnera. Je vais mettre à jour l'exemple. Voulez-vous que la liste des fichiers n'affiche que les noms de fichiers ?

1 votes

Quelle(s) caractéristique(s) manque(nt) dans l'exemple ci-dessus ?

9voto

AsksAnyway Points 290

C'est suffisant pour activer la complétion de l'onglet du répertoire intégré avec raw_input() :

import readline
readline.parse_and_bind("tab: complete")

6 votes

Il est probablement utile de préciser que cela ne fonctionne pas sous Windows.

1 votes

Cette solution ne fonctionne que par rapport au cwd. Par exemple, vous ne pouvez pas utiliser la complétion automatique pour / si vous n'êtes pas actuellement dans /

0 votes

Je n'arrive pas à le faire fonctionner sur Windows 7 en utilisant python 2.7. J'ai également essayé pyreadline.

2voto

Peter Mitrano Points 696

Cette version est pour python3, utilise pathlib, et une version minimaliste qui complète les fichiers/dossiers. Elle est basée sur certaines des réponses ci-dessus, mais ne fonctionne que pour les fichiers/dossiers.

#!/usr/bin/python

import pathlib
import readline

def complete_path(text, state):
    incomplete_path = pathlib.Path(text)
    if incomplete_path.is_dir():
        completions = [p.as_posix() for p in incomplete_path.iterdir()]
    elif incomplete_path.exists():
        completions = [incomplete_path]
    else:
        exists_parts = pathlib.Path('.')
        for part in incomplete_path.parts:
            test_next_part = exists_parts / part
            if test_next_part.exists():
                exists_parts = test_next_part

        completions = []
        for p in exists_parts.iterdir():
            p_str = p.as_posix()
            if p_str.startswith(text):
                completions.append(p_str)
    return completions[state]

# we want to treat '/' as part of a word, so override the delimiters
readline.set_completer_delims(' \t\n;')
readline.parse_and_bind("tab: complete")
readline.set_completer(complete_path)
print(input('tab complete a filename: '))

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X