File: _cmd.py

package info (click to toggle)
python-click-shell 2.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 248 kB
  • sloc: python: 623; makefile: 158; sh: 24
file content (178 lines) | stat: -rw-r--r-- 5,830 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
"""
click_shell._cmd

This module overrides the builtin python cmd module
"""

import inspect
import os
from cmd import Cmd

import click

from click_shell._compat import readline, get_input


class ClickCmd(Cmd, object):
    """
    A simple wrapper around the builtin python cmd module that:
    1) makes completion work on OSX
    2) uses a history file
    3) uses click.echo instead of std*.write()
    4) turns Cmd into a new-style python object :)
    """

    # Allow dashes
    identchars = Cmd.identchars + '-'

    nohelp = "No help on %s"
    nocommand = "Command not found: %s"

    def __init__(self, ctx=None, on_finished=None, hist_file=None, *args, **kwargs):
        # Never allow super() to default to sys.stdout for stdout.
        # Instead pass along a wrapper that delegates to click.echo().
        self._stdout = kwargs.get('stdout')

        super(ClickCmd, self).__init__(*args, **kwargs)

        self.old_completer = None
        self.old_delims = None

        # We need to save the context!!
        self.ctx = ctx
        self.on_finished = on_finished

        # Set the history file
        hist_file = hist_file or os.path.join(os.path.expanduser('~'), '.click-history')
        self.hist_file = os.path.abspath(hist_file)

        # Make the parent directory
        if not os.path.isdir(os.path.dirname(self.hist_file)):
            os.makedirs(os.path.dirname(self.hist_file))

    def preloop(self):
        # read our history
        if readline:
            try:
                readline.read_history_file(self.hist_file)
            except IOError:
                pass

    def postloop(self):
        # Write our history
        if readline:
            readline.set_history_length(1000)
            try:
                readline.write_history_file(self.hist_file)
            except IOError:
                pass

        # Finisher callback on the context
        if self.on_finished:
            self.on_finished(self.ctx)

    # We need to override this to fix readline
    def cmdloop(self, intro=None):  # pylint: disable=too-many-branches
        self.preloop()
        if self.completekey and readline:
            self.old_completer = readline.get_completer()
            self.old_delims = readline.get_completer_delims()
            readline.set_completer(self.complete)
            readline.set_completer_delims(' \n\t')
            to_parse = self.completekey + ': complete'
            if readline.__doc__ and 'libedit' in readline.__doc__:
                # Special case for mac OSX
                to_parse = 'bind ^I rl_complete'
            readline.parse_and_bind(to_parse)
        try:
            if intro is not None:
                self.intro = intro
            if self.intro:
                click.echo(self.intro, file=self._stdout)
            stop = None
            while not stop:
                if self.cmdqueue:
                    line = self.cmdqueue.pop(0)
                else:
                    try:
                        line = get_input(self.get_prompt())
                    except EOFError:
                        # We just want to quit here instead of changing the arg to EOF
                        click.echo(file=self._stdout)
                        break
                    except KeyboardInterrupt:
                        # We don't want to exit the shell on a keyboard interrupt
                        click.echo(file=self._stdout)
                        click.echo('KeyboardInterrupt', file=self._stdout)
                        continue
                line = self.precmd(line)
                stop = self.onecmd(line)
                stop = self.postcmd(stop, line)

        finally:
            self.postloop()
            if self.completekey and readline:
                readline.set_completer(self.old_completer)
                readline.set_completer_delims(self.old_delims)

    def get_prompt(self):
        if callable(self.prompt):
            kwargs = {}
            if hasattr(inspect, 'signature'):
                sig = inspect.signature(self.prompt)
                if 'ctx' in sig.parameters:
                    kwargs['ctx'] = self.ctx
            return self.prompt(**kwargs)
        else:
            return self.prompt

    def emptyline(self):
        # we don't want to repeat the last command if nothing was typed
        return False

    def default(self, line):
        click.echo(self.nocommand % line, file=self._stdout)

    def get_names(self):
        # Do dir(self) instead of dir(self.__class__)
        return dir(self)

    def do_help(self, arg):
        if not arg:
            super(ClickCmd, self).do_help(arg)
            return

        # Override to give better error message
        try:
            func = getattr(self, 'help_' + arg)
        except AttributeError:
            try:
                do_fun = getattr(self, 'do_' + arg, None)

                if do_fun is None:
                    click.echo(self.nocommand % arg, file=self._stdout)
                    return

                doc = do_fun.__doc__
                if doc:
                    click.echo(doc, file=self._stdout)
                    return
            except AttributeError:
                pass
            click.echo(self.nohelp % arg, file=self._stdout)
            return
        func()

    def do_quit(self, arg):  # pylint: disable=unused-argument,no-self-use
        return True

    def do_exit(self, arg):  # pylint: disable=unused-argument,no-self-use
        return True

    def print_topics(self, header, cmds, cmdlen, maxcol):
        if cmds:
            click.echo(header, file=self._stdout)
            if self.ruler:
                click.echo(str(self.ruler * len(header)), file=self._stdout)
            self.columnize(cmds, maxcol - 1)
            click.echo(file=self._stdout)