Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / command_shell.py @ 0d4a6d0a

History | View | Annotate | Download (11.6 kB)

1 b9331a9f Stavros Sachtouris
# Copyright 2012 GRNET S.A. All rights reserved.
2 b9331a9f Stavros Sachtouris
#
3 b9331a9f Stavros Sachtouris
# Redistribution and use in source and binary forms, with or
4 b9331a9f Stavros Sachtouris
# without modification, are permitted provided that the following
5 b9331a9f Stavros Sachtouris
# conditions are met:
6 b9331a9f Stavros Sachtouris
#
7 b9331a9f Stavros Sachtouris
#   1. Redistributions of source code must retain the above
8 fd5db045 Stavros Sachtouris
#      copyright notice, this list of conditions and the following
9 fd5db045 Stavros Sachtouris
#      disclaimer.
10 b9331a9f Stavros Sachtouris
#
11 b9331a9f Stavros Sachtouris
#   2. Redistributions in binary form must reproduce the above
12 fd5db045 Stavros Sachtouris
#      copyright notice, this list of conditions and the following
13 fd5db045 Stavros Sachtouris
#      disclaimer in the documentation and/or other materials
14 fd5db045 Stavros Sachtouris
#      provided with the distribution.
15 b9331a9f Stavros Sachtouris
#
16 b9331a9f Stavros Sachtouris
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 b9331a9f Stavros Sachtouris
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 b9331a9f Stavros Sachtouris
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 b9331a9f Stavros Sachtouris
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 b9331a9f Stavros Sachtouris
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 b9331a9f Stavros Sachtouris
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 b9331a9f Stavros Sachtouris
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 b9331a9f Stavros Sachtouris
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 b9331a9f Stavros Sachtouris
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 b9331a9f Stavros Sachtouris
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 b9331a9f Stavros Sachtouris
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 b9331a9f Stavros Sachtouris
# POSSIBILITY OF SUCH DAMAGE.
28 b9331a9f Stavros Sachtouris
#
29 b9331a9f Stavros Sachtouris
# The views and conclusions contained in the software and
30 b9331a9f Stavros Sachtouris
# documentation are those of the authors and should not be
31 b9331a9f Stavros Sachtouris
# interpreted as representing official policies, either expressed
32 b9331a9f Stavros Sachtouris
# or implied, of GRNET S.A.
33 b9331a9f Stavros Sachtouris
34 b9331a9f Stavros Sachtouris
from cmd import Cmd
35 b9331a9f Stavros Sachtouris
from os import popen
36 ce48608f Stavros Sachtouris
from sys import stdout
37 d486baec Stavros Sachtouris
38 b6a99832 Stavros Sachtouris
from kamaki.cli import exec_cmd, print_error_message, print_subcommands_help
39 074f5027 Stavros Sachtouris
from kamaki.cli.argument import ArgumentParseManager
40 b6a99832 Stavros Sachtouris
from kamaki.cli.utils import print_dict, split_input
41 d486baec Stavros Sachtouris
from kamaki.cli.history import History
42 db950b10 Stavros Sachtouris
from kamaki.cli.errors import CLIError
43 fce93ff6 Stavros Sachtouris
from kamaki.clients import ClientError
44 b9331a9f Stavros Sachtouris
45 fd5db045 Stavros Sachtouris
46 074f5027 Stavros Sachtouris
def _init_shell(exe_string, parser):
47 074f5027 Stavros Sachtouris
    parser.arguments.pop('version', None)
48 75c3fc42 Stavros Sachtouris
    shell = Shell()
49 75c3fc42 Stavros Sachtouris
    shell.set_prompt(exe_string)
50 75c3fc42 Stavros Sachtouris
    from kamaki import __version__ as version
51 75c3fc42 Stavros Sachtouris
    shell.greet(version)
52 75c3fc42 Stavros Sachtouris
    shell.do_EOF = shell.do_exit
53 75c3fc42 Stavros Sachtouris
    from kamaki.cli.command_tree import CommandTree
54 75c3fc42 Stavros Sachtouris
    shell.cmd_tree = CommandTree(
55 75c3fc42 Stavros Sachtouris
        'kamaki', 'A command line tool for poking clouds')
56 75c3fc42 Stavros Sachtouris
    return shell
57 fd5db045 Stavros Sachtouris
58 b9331a9f Stavros Sachtouris
59 b9331a9f Stavros Sachtouris
class Shell(Cmd):
60 fd5db045 Stavros Sachtouris
    """Kamaki interactive shell"""
61 fd5db045 Stavros Sachtouris
    _prefix = '['
62 53254b46 Stavros Sachtouris
    _suffix = ']: '
63 fd5db045 Stavros Sachtouris
    cmd_tree = None
64 fd5db045 Stavros Sachtouris
    _history = None
65 834200da Stavros Sachtouris
    _context_stack = []
66 834200da Stavros Sachtouris
    _prompt_stack = []
67 074f5027 Stavros Sachtouris
    _parser = None
68 834200da Stavros Sachtouris
69 fd5db045 Stavros Sachtouris
    undoc_header = 'interactive shell commands:'
70 fd5db045 Stavros Sachtouris
71 77e7bef7 Stavros Sachtouris
    def postcmd(self, post, line):
72 77e7bef7 Stavros Sachtouris
        if self._context_stack:
73 77e7bef7 Stavros Sachtouris
            self._roll_command()
74 77e7bef7 Stavros Sachtouris
            self._restore(self._context_stack.pop())
75 53254b46 Stavros Sachtouris
            self.set_prompt(
76 53254b46 Stavros Sachtouris
                self._prompt_stack.pop()[len(self._prefix):-len(self._suffix)])
77 77e7bef7 Stavros Sachtouris
78 77e7bef7 Stavros Sachtouris
        return Cmd.postcmd(self, post, line)
79 77e7bef7 Stavros Sachtouris
80 42c739c0 Stavros Sachtouris
    def precmd(self, line):
81 834200da Stavros Sachtouris
        if line.startswith('/'):
82 de73876b Stavros Sachtouris
            start, end = len(self._prefix), -len(self._suffix)
83 de73876b Stavros Sachtouris
            cur_cmd_path = self.prompt.replace(' ', '_')[start:end]
84 524dc2f8 Stavros Sachtouris
            if cur_cmd_path != self.cmd_tree.name:
85 834200da Stavros Sachtouris
                cur_cmd = self.cmd_tree.get_command(cur_cmd_path)
86 834200da Stavros Sachtouris
                self._context_stack.append(self._backup())
87 834200da Stavros Sachtouris
                self._prompt_stack.append(self.prompt)
88 834200da Stavros Sachtouris
                new_context = self
89 a6ad7781 Stavros Sachtouris
                self._roll_command(cur_cmd.path)
90 834200da Stavros Sachtouris
                new_context.set_prompt(self.cmd_tree.name)
91 834200da Stavros Sachtouris
                for grp_cmd in self.cmd_tree.get_subcommands():
92 834200da Stavros Sachtouris
                    self._register_command(grp_cmd.path)
93 834200da Stavros Sachtouris
            return line[1:]
94 42c739c0 Stavros Sachtouris
        return line
95 42c739c0 Stavros Sachtouris
96 fd5db045 Stavros Sachtouris
    def greet(self, version):
97 de73876b Stavros Sachtouris
        print('kamaki v%s - Interactive Shell\n' % version)
98 a6aced18 Stavros Sachtouris
        print('\t/exit     \tterminate kamaki')
99 de73876b Stavros Sachtouris
        print('\texit or ^D\texit context')
100 de73876b Stavros Sachtouris
        print('\t? or help \tavailable commands')
101 de73876b Stavros Sachtouris
        print('\t?command  \thelp on command')
102 de73876b Stavros Sachtouris
        print('\t!<command>\texecute OS shell command')
103 de73876b Stavros Sachtouris
        print('')
104 fd5db045 Stavros Sachtouris
105 fd5db045 Stavros Sachtouris
    def set_prompt(self, new_prompt):
106 53254b46 Stavros Sachtouris
        self.prompt = '%s%s%s' % (self._prefix, new_prompt, self._suffix)
107 fd5db045 Stavros Sachtouris
108 af569ab9 Stavros Sachtouris
    def cmdloop(self):
109 af569ab9 Stavros Sachtouris
        while True:
110 af569ab9 Stavros Sachtouris
            try:
111 af569ab9 Stavros Sachtouris
                Cmd.cmdloop(self)
112 af569ab9 Stavros Sachtouris
            except KeyboardInterrupt:
113 af569ab9 Stavros Sachtouris
                print(' - interrupted')
114 af569ab9 Stavros Sachtouris
                continue
115 af569ab9 Stavros Sachtouris
            break
116 af569ab9 Stavros Sachtouris
117 fd5db045 Stavros Sachtouris
    def do_exit(self, line):
118 fd5db045 Stavros Sachtouris
        print('')
119 de73876b Stavros Sachtouris
        start, end = len(self._prefix), -len(self._suffix)
120 de73876b Stavros Sachtouris
        if self.prompt[start:end] == self.cmd_tree.name:
121 a6ad7781 Stavros Sachtouris
            exit(0)
122 fd5db045 Stavros Sachtouris
        return True
123 fd5db045 Stavros Sachtouris
124 fd5db045 Stavros Sachtouris
    def do_shell(self, line):
125 fd5db045 Stavros Sachtouris
        output = popen(line).read()
126 fd5db045 Stavros Sachtouris
        print(output)
127 fd5db045 Stavros Sachtouris
128 fd5db045 Stavros Sachtouris
    @property
129 fd5db045 Stavros Sachtouris
    def path(self):
130 fd5db045 Stavros Sachtouris
        if self._cmd:
131 3dabe5d2 Stavros Sachtouris
            return self._cmd.path
132 fd5db045 Stavros Sachtouris
        return ''
133 fd5db045 Stavros Sachtouris
134 fd5db045 Stavros Sachtouris
    @classmethod
135 fd5db045 Stavros Sachtouris
    def _register_method(self, method, name):
136 fd5db045 Stavros Sachtouris
        self.__dict__[name] = method
137 fd5db045 Stavros Sachtouris
138 fd5db045 Stavros Sachtouris
    @classmethod
139 fd5db045 Stavros Sachtouris
    def _unregister_method(self, name):
140 fd5db045 Stavros Sachtouris
        try:
141 fd5db045 Stavros Sachtouris
            self.__dict__.pop(name)
142 fd5db045 Stavros Sachtouris
        except KeyError:
143 fd5db045 Stavros Sachtouris
            pass
144 fd5db045 Stavros Sachtouris
145 77e7bef7 Stavros Sachtouris
    def _roll_command(self, cmd_path=None):
146 fd5db045 Stavros Sachtouris
        for subname in self.cmd_tree.get_subnames(cmd_path):
147 fd5db045 Stavros Sachtouris
            self._unregister_method('do_%s' % subname)
148 fd5db045 Stavros Sachtouris
            self._unregister_method('complete_%s' % subname)
149 fd5db045 Stavros Sachtouris
            self._unregister_method('help_%s' % subname)
150 fd5db045 Stavros Sachtouris
151 fd5db045 Stavros Sachtouris
    @classmethod
152 fd5db045 Stavros Sachtouris
    def _backup(self):
153 fd5db045 Stavros Sachtouris
        return dict(self.__dict__)
154 fd5db045 Stavros Sachtouris
155 fd5db045 Stavros Sachtouris
    @classmethod
156 fd5db045 Stavros Sachtouris
    def _restore(self, oldcontext):
157 fd5db045 Stavros Sachtouris
        self.__dict__ = oldcontext
158 fd5db045 Stavros Sachtouris
159 d53062bd Stavros Sachtouris
    def _register_command(self, cmd_path):
160 fd5db045 Stavros Sachtouris
        cmd = self.cmd_tree.get_command(cmd_path)
161 074f5027 Stavros Sachtouris
        arguments = self._parser.arguments
162 fd5db045 Stavros Sachtouris
163 834200da Stavros Sachtouris
        def do_method(new_context, line):
164 fd5db045 Stavros Sachtouris
            """ Template for all cmd.Cmd methods of the form do_<cmd name>
165 fd5db045 Stavros Sachtouris
                Parse cmd + args and decide to execute or change context
166 fd5db045 Stavros Sachtouris
                <cmd> <term> <term> <args> is always parsed to most specific
167 fd5db045 Stavros Sachtouris
                even if cmd_term_term is not a terminal path
168 fd5db045 Stavros Sachtouris
            """
169 efbcdc41 Stavros Sachtouris
            subcmd, cmd_args = cmd.parse_out(split_input(line))
170 074f5027 Stavros Sachtouris
            self._history.add(' '.join([cmd.path.replace('_', ' '), line]))
171 4e01956e Stavros Sachtouris
            tmp_args = dict(self._parser.arguments)
172 4e01956e Stavros Sachtouris
            tmp_args.pop('options', None)
173 4e01956e Stavros Sachtouris
            tmp_args.pop('debug', None)
174 4e01956e Stavros Sachtouris
            tmp_args.pop('verbose', None)
175 4e01956e Stavros Sachtouris
            tmp_args.pop('include', None)
176 4e01956e Stavros Sachtouris
            tmp_args.pop('silent', None)
177 4e01956e Stavros Sachtouris
            cmd_parser = ArgumentParseManager(cmd.name, dict(tmp_args))
178 074f5027 Stavros Sachtouris
179 074f5027 Stavros Sachtouris
            cmd_parser.parser.description = subcmd.help
180 fd5db045 Stavros Sachtouris
181 fd5db045 Stavros Sachtouris
            # exec command or change context
182 fd5db045 Stavros Sachtouris
            if subcmd.is_command:  # exec command
183 db950b10 Stavros Sachtouris
                try:
184 c8e17a67 Stavros Sachtouris
                    cls = subcmd.get_class()
185 c8e17a67 Stavros Sachtouris
                    ldescr = getattr(cls, 'long_description', '')
186 c8e17a67 Stavros Sachtouris
                    if subcmd.path == 'history_run':
187 53e1f8d5 Stavros Sachtouris
                        instance = cls(
188 53e1f8d5 Stavros Sachtouris
                            dict(cmd_parser.arguments),
189 c8e17a67 Stavros Sachtouris
                            self.cmd_tree)
190 c8e17a67 Stavros Sachtouris
                    else:
191 c8e17a67 Stavros Sachtouris
                        instance = cls(dict(cmd_parser.arguments))
192 c8e17a67 Stavros Sachtouris
                    cmd_parser.update_arguments(instance.arguments)
193 0d4a6d0a Stavros Sachtouris
                    #instance.arguments.pop('config')
194 c8e17a67 Stavros Sachtouris
                    cmd_parser.arguments = instance.arguments
195 c8e17a67 Stavros Sachtouris
                    cmd_parser.syntax = '%s %s' % (
196 c8e17a67 Stavros Sachtouris
                        subcmd.path.replace('_', ' '), cls.syntax)
197 c8e17a67 Stavros Sachtouris
                    if '-h' in cmd_args or '--help' in cmd_args:
198 c8e17a67 Stavros Sachtouris
                        cmd_parser.parser.print_help()
199 c8e17a67 Stavros Sachtouris
                        if ldescr.strip():
200 c8e17a67 Stavros Sachtouris
                            print('\nDetails:')
201 c8e17a67 Stavros Sachtouris
                            print('%s' % ldescr)
202 c8e17a67 Stavros Sachtouris
                        return
203 c8e17a67 Stavros Sachtouris
                    cmd_parser.parse(cmd_args)
204 c8e17a67 Stavros Sachtouris
205 c8e17a67 Stavros Sachtouris
                    for name, arg in instance.arguments.items():
206 de73876b Stavros Sachtouris
                        arg.value = getattr(
207 de73876b Stavros Sachtouris
                            cmd_parser.parsed,
208 de73876b Stavros Sachtouris
                            name,
209 c8e17a67 Stavros Sachtouris
                            arg.default)
210 c8e17a67 Stavros Sachtouris
211 53e1f8d5 Stavros Sachtouris
                    exec_cmd(
212 53e1f8d5 Stavros Sachtouris
                        instance,
213 53e1f8d5 Stavros Sachtouris
                        cmd_parser.unparsed,
214 074f5027 Stavros Sachtouris
                        cmd_parser.parser.print_help)
215 53e1f8d5 Stavros Sachtouris
                        #[term for term in cmd_parser.unparsed\
216 53e1f8d5 Stavros Sachtouris
                        #    if not term.startswith('-')],
217 fce93ff6 Stavros Sachtouris
                except (ClientError, CLIError) as err:
218 b6a99832 Stavros Sachtouris
                    print_error_message(err)
219 de73876b Stavros Sachtouris
            elif ('-h' in cmd_args or '--help' in cmd_args) or len(cmd_args):
220 de73876b Stavros Sachtouris
                # print options
221 a71bb904 Stavros Sachtouris
                print('%s' % cmd.help)
222 b6a99832 Stavros Sachtouris
                print_subcommands_help(cmd)
223 fd5db045 Stavros Sachtouris
            else:  # change context
224 834200da Stavros Sachtouris
                #new_context = this
225 fd5db045 Stavros Sachtouris
                backup_context = self._backup()
226 fd5db045 Stavros Sachtouris
                old_prompt = self.prompt
227 fd5db045 Stavros Sachtouris
                new_context._roll_command(cmd.parent_path)
228 fd5db045 Stavros Sachtouris
                new_context.set_prompt(subcmd.path.replace('_', ' '))
229 fd5db045 Stavros Sachtouris
                newcmds = [subcmd for subcmd in subcmd.get_subcommands()]
230 fd5db045 Stavros Sachtouris
                for subcmd in newcmds:
231 d53062bd Stavros Sachtouris
                    new_context._register_command(subcmd.path)
232 fd5db045 Stavros Sachtouris
                new_context.cmdloop()
233 fd5db045 Stavros Sachtouris
                self.prompt = old_prompt
234 fd5db045 Stavros Sachtouris
                #when new context is over, roll back to the old one
235 fd5db045 Stavros Sachtouris
                self._restore(backup_context)
236 fd5db045 Stavros Sachtouris
        self._register_method(do_method, 'do_%s' % cmd.name)
237 fd5db045 Stavros Sachtouris
238 fd5db045 Stavros Sachtouris
        def help_method(self):
239 fd5db045 Stavros Sachtouris
            print('%s (%s -h for more options)' % (cmd.help, cmd.name))
240 54d800e8 Stavros Sachtouris
            if cmd.is_command:
241 54d800e8 Stavros Sachtouris
                cls = cmd.get_class()
242 2fbca093 Stavros Sachtouris
                ldescr = getattr(cls, 'long_description', '')
243 54d800e8 Stavros Sachtouris
                #_construct_command_syntax(cls)
244 54d800e8 Stavros Sachtouris
                plist = self.prompt[len(self._prefix):-len(self._suffix)]
245 54d800e8 Stavros Sachtouris
                plist = plist.split(' ')
246 54d800e8 Stavros Sachtouris
                clist = cmd.path.split('_')
247 54d800e8 Stavros Sachtouris
                upto = 0
248 2fbca093 Stavros Sachtouris
                if ldescr:
249 2fbca093 Stavros Sachtouris
                    print('%s' % ldescr)
250 54d800e8 Stavros Sachtouris
                for i, term in enumerate(plist):
251 54d800e8 Stavros Sachtouris
                    try:
252 54d800e8 Stavros Sachtouris
                        if clist[i] == term:
253 54d800e8 Stavros Sachtouris
                            upto += 1
254 54d800e8 Stavros Sachtouris
                    except IndexError:
255 54d800e8 Stavros Sachtouris
                        break
256 54d800e8 Stavros Sachtouris
                print('Syntax: %s %s' % (' '.join(clist[upto:]), cls.syntax))
257 8741c407 Stavros Sachtouris
            if cmd.subcommands:
258 b6a99832 Stavros Sachtouris
                print_subcommands_help(cmd)
259 54d800e8 Stavros Sachtouris
260 fd5db045 Stavros Sachtouris
        self._register_method(help_method, 'help_%s' % cmd.name)
261 fd5db045 Stavros Sachtouris
262 fd5db045 Stavros Sachtouris
        def complete_method(self, text, line, begidx, endidx):
263 efbcdc41 Stavros Sachtouris
            subcmd, cmd_args = cmd.parse_out(split_input(line)[1:])
264 fd5db045 Stavros Sachtouris
            if subcmd.is_command:
265 fd5db045 Stavros Sachtouris
                cls = subcmd.get_class()
266 834200da Stavros Sachtouris
                instance = cls(dict(arguments))
267 fd5db045 Stavros Sachtouris
                empty, sep, subname = subcmd.path.partition(cmd.path)
268 fd5db045 Stavros Sachtouris
                cmd_name = '%s %s' % (cmd.name, subname.replace('_', ' '))
269 de73876b Stavros Sachtouris
                print('\n%s\nSyntax:\t%s %s' % (
270 de73876b Stavros Sachtouris
                    cls.description,
271 de73876b Stavros Sachtouris
                    cmd_name,
272 de73876b Stavros Sachtouris
                    cls.syntax))
273 fd5db045 Stavros Sachtouris
                cmd_args = {}
274 fd5db045 Stavros Sachtouris
                for arg in instance.arguments.values():
275 fd5db045 Stavros Sachtouris
                    cmd_args[','.join(arg.parsed_name)] = arg.help
276 f551841a Stavros Sachtouris
                print_dict(cmd_args, ident=2)
277 fd5db045 Stavros Sachtouris
                stdout.write('%s %s' % (self.prompt, line))
278 fd5db045 Stavros Sachtouris
            return subcmd.get_subnames()
279 fd5db045 Stavros Sachtouris
        self._register_method(complete_method, 'complete_%s' % cmd.name)
280 fd5db045 Stavros Sachtouris
281 fd5db045 Stavros Sachtouris
    @property
282 fd5db045 Stavros Sachtouris
    def doc_header(self):
283 fd5db045 Stavros Sachtouris
        tmp_partition = self.prompt.partition(self._prefix)
284 fd5db045 Stavros Sachtouris
        tmp_partition = tmp_partition[2].partition(self._suffix)
285 fd5db045 Stavros Sachtouris
        hdr = tmp_partition[0].strip()
286 fd5db045 Stavros Sachtouris
        return '%s commands:' % hdr
287 fd5db045 Stavros Sachtouris
288 074f5027 Stavros Sachtouris
    def run(self, parser, path=''):
289 074f5027 Stavros Sachtouris
        self._parser = parser
290 074f5027 Stavros Sachtouris
        self._history = History(
291 074f5027 Stavros Sachtouris
            parser.arguments['config'].get('history', 'file'))
292 db950b10 Stavros Sachtouris
        if path:
293 fd5db045 Stavros Sachtouris
            cmd = self.cmd_tree.get_command(path)
294 fd5db045 Stavros Sachtouris
            intro = cmd.path.replace('_', ' ')
295 fd5db045 Stavros Sachtouris
        else:
296 fd5db045 Stavros Sachtouris
            intro = self.cmd_tree.name
297 fd5db045 Stavros Sachtouris
298 fd5db045 Stavros Sachtouris
        for subcmd in self.cmd_tree.get_subcommands(path):
299 d53062bd Stavros Sachtouris
            self._register_command(subcmd.path)
300 fd5db045 Stavros Sachtouris
301 fd5db045 Stavros Sachtouris
        self.set_prompt(intro)
302 db950b10 Stavros Sachtouris
303 f9af2848 Stavros Sachtouris
        try:
304 f9af2848 Stavros Sachtouris
            self.cmdloop()
305 af569ab9 Stavros Sachtouris
        except Exception as e:
306 af569ab9 Stavros Sachtouris
            print('(%s)' % e)
307 f9af2848 Stavros Sachtouris
            from traceback import print_stack
308 f9af2848 Stavros Sachtouris
            print_stack()