You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by br...@apache.org on 2012/01/30 22:54:42 UTC
git commit: cqlsh: update syntax for tab completions Patch by Paul
Cannon, reviewed by brandonwilliams for CASSANDRA-3757
Updated Branches:
refs/heads/trunk f0c0224da -> d4f205157
cqlsh: update syntax for tab completions
Patch by Paul Cannon, reviewed by brandonwilliams for CASSANDRA-3757
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/d4f20515
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/d4f20515
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/d4f20515
Branch: refs/heads/trunk
Commit: d4f2051578948448b35fa59d85bd70ae4d3673b0
Parents: f0c0224
Author: paul cannon <pa...@datastax.com>
Authored: Mon Jan 30 15:43:53 2012 -0600
Committer: Brandon Williams <br...@apache.org>
Committed: Mon Jan 30 15:43:53 2012 -0600
----------------------------------------------------------------------
bin/cqlsh | 35 ++++---
pylib/cqlshlib/cqlhandling.py | 206 ++++++++++++++++++++++++++++++------
pylib/cqlshlib/pylexotron.py | 39 +++++---
3 files changed, 219 insertions(+), 61 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d4f20515/bin/cqlsh
----------------------------------------------------------------------
diff --git a/bin/cqlsh b/bin/cqlsh
index 74d62f2..4e67fda 100755
--- a/bin/cqlsh
+++ b/bin/cqlsh
@@ -619,22 +619,22 @@ class Shell(cmd.Cmd):
self.reset_statement()
print
- def onecmd(self, statement):
+ def onecmd(self, statementtext):
"""
Returns true if the statement is complete and was handled (meaning it
can be reset).
"""
try:
- statements, in_batch = cqlhandling.cql_split_statements(statement)
+ statements, in_batch = cqlhandling.cql_split_statements(statementtext)
except pylexotron.LexingError, e:
if self.show_line_nums:
self.printerr('Invalid syntax at char %d' % (e.charnum,))
else:
self.printerr('Invalid syntax at line %d, char %d'
% (e.linenum, e.charnum))
- statement = statement.split('\n')[e.linenum - 1]
- self.printerr(' %s' % statement)
+ statementline = statementtext.split('\n')[e.linenum - 1]
+ self.printerr(' %s' % statementline)
self.printerr(' %s^' % (' ' * e.charnum))
return True
@@ -647,7 +647,7 @@ class Shell(cmd.Cmd):
return
for st in statements:
try:
- self.handle_statement(st)
+ self.handle_statement(st, statementtext)
except Exception, e:
if self.debug:
import traceback
@@ -665,25 +665,26 @@ class Shell(cmd.Cmd):
self.printerr('Incomplete statement at end of file')
self.do_exit()
- def handle_statement(self, tokens):
+ def handle_statement(self, tokens, srcstr):
cmdword = tokens[0][1]
if cmdword == '?':
cmdword = 'help'
custom_handler = getattr(self, 'do_' + cmdword.lower(), None)
if custom_handler:
- parsed = cqlhandling.cql_whole_parse_tokens(tokens, startsymbol='cqlshCommand')
+ parsed = cqlhandling.cql_whole_parse_tokens(tokens, srcstr=srcstr,
+ startsymbol='cqlshCommand')
if parsed and not parsed.remainder:
# successful complete parse
return custom_handler(parsed)
else:
- return self.handle_parse_error(cmdword, tokens, parsed)
- return self.perform_statement_as_tokens(tokens)
+ return self.handle_parse_error(cmdword, tokens, parsed, srcstr)
+ return self.perform_statement(cqlhandling.cql_extract_orig(tokens, srcstr))
- def handle_parse_error(self, cmdword, tokens, parsed):
+ def handle_parse_error(self, cmdword, tokens, parsed, srcstr):
if cmdword == 'select':
# hey, maybe they know about some new syntax we don't. type
# assumptions won't work, but maybe the query will.
- return self.perform_statement_as_tokens(tokens)
+ return self.perform_statement(cqlhandling.cql_extract_orig(tokens, srcstr))
if parsed:
self.printerr('Improper %s command (problem at %r).' % (cmdword, parsed.remainder[0]))
else:
@@ -703,7 +704,7 @@ class Shell(cmd.Cmd):
number, it can be enclosed in quotes and expressed as a string literal.
"""
ksname = parsed.get_binding('ksname')
- if self.perform_statement_as_tokens(parsed.matched):
+ if self.perform_statement(parsed.extract_orig()):
self.current_keyspace = cql_dequote(ksname)
def do_select(self, parsed):
@@ -731,10 +732,7 @@ class Shell(cmd.Cmd):
ksname = cql_dequote(ksname)
cfname = cql_dequote(parsed.get_binding('selectsource'))
decoder = self.determine_decoder_for(cfname, ksname=ksname)
- self.perform_statement_as_tokens(parsed.matched, decoder=decoder)
-
- def perform_statement_as_tokens(self, tokens, decoder=None):
- return self.perform_statement(cqlhandling.cql_detokenize(tokens), decoder=decoder)
+ self.perform_statement(parsed.extract_orig(), decoder=decoder)
def perform_statement(self, statement, decoder=None):
if not statement:
@@ -804,6 +802,7 @@ class Shell(cmd.Cmd):
formatted_data = [map(self.myformat_value, row, coltypes) for row in self.cursor]
# determine column widths
+ colnames = map(str, colnames)
widths = map(len, colnames)
for fmtrow in formatted_data:
for num, col in enumerate(fmtrow):
@@ -902,6 +901,10 @@ class Shell(cmd.Cmd):
else:
optval = cql_escape(optval)
notable_columns.append((option, optval))
+ for option, thriftname, _ in cqlhandling.columnfamily_map_options:
+ optmap = getattr(cfdef, thriftname or option)
+ for k, v in optmap.items():
+ notable_columns.append(('%s:%s' % (option, k), cql_escape(v)))
out.write('\n)')
if notable_columns:
joiner = 'WITH'
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d4f20515/pylib/cqlshlib/cqlhandling.py
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/cqlhandling.py b/pylib/cqlshlib/cqlhandling.py
index 340d471..e45fb45 100644
--- a/pylib/cqlshlib/cqlhandling.py
+++ b/pylib/cqlshlib/cqlhandling.py
@@ -27,17 +27,43 @@ columnfamily_options = (
# (CQL option name, Thrift option name (or None if same))
('comment', None),
('comparator', 'comparator_type'),
- ('row_cache_provider', None),
- ('key_cache_size', None),
- ('row_cache_size', None),
('read_repair_chance', None),
('gc_grace_seconds', None),
('default_validation', 'default_validation_class'),
('min_compaction_threshold', None),
('max_compaction_threshold', None),
+ ('replicate_on_write', None),
+ ('compaction_strategy_class', 'compaction_strategy'),
+)
+
+obsolete_cf_options = (
+ ('key_cache_size', None),
+ ('row_cache_size', None),
('row_cache_save_period_in_seconds', None),
('key_cache_save_period_in_seconds', None),
- ('replicate_on_write', None)
+ ('memtable_throughput_in_mb', None),
+ ('memtable_operations_in_millions', None),
+ ('memtable_flush_after_mins', None),
+ ('row_cache_provider', None),
+)
+
+all_columnfamily_options = columnfamily_options + obsolete_cf_options
+
+columnfamily_map_options = (
+ ('compaction_strategy_options', None,
+ ()),
+ ('compression_parameters', 'compression_options',
+ ('sstable_compression', 'chunk_length_kb', 'crc_check_chance')),
+)
+
+available_compression_classes = (
+ 'DeflateCompressor',
+ 'SnappyCompressor',
+)
+
+available_compaction_classes = (
+ 'LeveledCompactionStrategy',
+ 'SizeTieredCompactionStrategy'
)
cql_type_to_apache_class = {
@@ -88,8 +114,9 @@ def is_valid_cql_word(s):
def tokenize_cql(cql_text):
return CqlLexotron.scan(cql_text)[0]
-def cql_detokenize(toklist):
- return ' '.join([t[1] for t in toklist])
+def cql_extract_orig(toklist, srcstr):
+ # low end of span for first token, to high end of span for last token
+ return srcstr[toklist[0][2][0]:toklist[-1][2][1]]
# note: commands_end_with_newline may be extended by an importing module.
commands_end_with_newline = set()
@@ -185,7 +212,8 @@ JUNK ::= /([ \t\r\f\v]+|(--|[/][/])[^\n\r]*([\n\r]|$)|[/][*].*?[*][/])/ ;
<float> ::= /-?[0-9]+\.[0-9]+/ ;
<integer> ::= /-?[0-9]+/ ;
<uuid> ::= /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/ ;
-<identifier> ::= /[a-z][a-z0-9_:]*/ ;
+<identifier> ::= /[a-z][a-z0-9_]*/ ;
+<colon> ::= ":" ;
<star> ::= "*" ;
<range> ::= ".." ;
<endtoken> ::= ";" ;
@@ -319,7 +347,7 @@ explain_completion('whatToSelect', 'rangestart', '<range_start>')
explain_completion('whatToSelect', 'rangeend', '<range_end>')
syntax_rules += r'''
-<insertStatement> ::= "INSERT" "INTO" insertcf=<name>
+<insertStatement> ::= "INSERT" "INTO" ( insertks=<name> "." )? insertcf=<name>
"(" keyname=<colname> ","
[colname]=<colname> ( "," [colname]=<colname> )* ")"
"VALUES" "(" <term> "," <term> ( "," <term> )* ")"
@@ -332,9 +360,22 @@ syntax_rules += r'''
;
'''
+@completer_for('insertStatement', 'insertks')
+def insert_ks_completer(ctxt, cass):
+ return [maybe_cql_escape(ks) + '.' for ks in cass.get_keyspace_names()]
+
@completer_for('insertStatement', 'insertcf')
def insert_cf_completer(ctxt, cass):
- return map(maybe_cql_escape, cass.get_columnfamily_names())
+ ks = ctxt.get_binding('insertks', None)
+ if ks is not None:
+ ks = cql_dequote(ks)
+ try:
+ cfnames = cass.get_columnfamily_names(ks)
+ except Exception:
+ if ks is None:
+ return ()
+ raise
+ return map(maybe_cql_escape, cfnames)
@completer_for('insertStatement', 'keyname')
def insert_keyname_completer(ctxt, cass):
@@ -352,7 +393,7 @@ def insert_option_completer(ctxt, cass):
return opts
syntax_rules += r'''
-<updateStatement> ::= "UPDATE" cf=<name>
+<updateStatement> ::= "UPDATE" ( updateks=<name> "." )? updatecf=<name>
( "USING" [updateopt]=<usingOption>
( "AND" [updateopt]=<usingOption> )* )?
"SET" <assignment> ( "," <assignment> )*
@@ -366,9 +407,22 @@ syntax_rules += r'''
;
'''
-@completer_for('updateStatement', 'cf')
+@completer_for('updateStatement', 'updateks')
def update_cf_completer(ctxt, cass):
- return map(maybe_cql_escape, cass.get_columnfamily_names())
+ return [maybe_cql_escape(ks) + '.' for ks in cass.get_keyspace_names()]
+
+@completer_for('updateStatement', 'updatecf')
+def update_cf_completer(ctxt, cass):
+ ks = ctxt.get_binding('updateks', None)
+ if ks is not None:
+ ks = cql_dequote(ks)
+ try:
+ cfnames = cass.get_columnfamily_names(ks)
+ except Exception:
+ if ks is None:
+ return ()
+ raise
+ return map(maybe_cql_escape, cfnames)
@completer_for('updateStatement', 'updateopt')
def insert_option_completer(ctxt, cass):
@@ -413,7 +467,7 @@ def update_filter_in_completer(ctxt, cass):
syntax_rules += r'''
<deleteStatement> ::= "DELETE" ( [delcol]=<colname> ( "," [delcol]=<colname> )* )?
- "FROM" cf=<name>
+ "FROM" ( deleteks=<name> "." )? deletecf=<name>
( "USING" [delopt]=<deleteOption> ( "AND" [delopt]=<deleteOption> )* )?
"WHERE" <updateWhereClause>
;
@@ -422,9 +476,22 @@ syntax_rules += r'''
;
'''
-@completer_for('deleteStatement', 'cf')
+@completer_for('deleteStatement', 'deleteks')
+def update_cf_completer(ctxt, cass):
+ return [maybe_cql_escape(ks) + '.' for ks in cass.get_keyspace_names()]
+
+@completer_for('deleteStatement', 'deletecf')
def delete_cf_completer(ctxt, cass):
- return map(maybe_cql_escape, cass.get_columnfamily_names())
+ ks = ctxt.get_binding('deleteks', None)
+ if ks is not None:
+ ks = cql_dequote(ks)
+ try:
+ cfnames = cass.get_columnfamily_names(ks)
+ except Exception:
+ if ks is None:
+ return ()
+ raise
+ return map(maybe_cql_escape, cfnames)
@completer_for('deleteStatement', 'delopt')
def delete_opt_completer(ctxt, cass):
@@ -457,13 +524,26 @@ def batch_opt_completer(ctxt, cass):
return opts
syntax_rules += r'''
-<truncateStatement> ::= "TRUNCATE" cf=<name>
+<truncateStatement> ::= "TRUNCATE" ( truncateks=<name> "." )? truncatecf=<name>
;
'''
-@completer_for('truncateStatement', 'cf')
+@completer_for('truncateStatement', 'truncateks')
+def update_cf_completer(ctxt, cass):
+ return [maybe_cql_escape(ks) + '.' for ks in cass.get_keyspace_names()]
+
+@completer_for('truncateStatement', 'truncatecf')
def truncate_cf_completer(ctxt, cass):
- return map(maybe_cql_escape, cass.get_columnfamily_names())
+ ks = ctxt.get_binding('truncateks', None)
+ if ks is not None:
+ ks = cql_dequote(ks)
+ try:
+ cfnames = cass.get_columnfamily_names(ks)
+ except Exception:
+ if ks is None:
+ return ()
+ raise
+ return map(maybe_cql_escape, cfnames)
syntax_rules += r'''
<createKeyspaceStatement> ::= "CREATE" "KEYSPACE" ksname=<name>
@@ -504,11 +584,14 @@ syntax_rules += r'''
<createColumnFamilyStatement> ::= "CREATE" "COLUMNFAMILY" cf=<name>
"(" keyalias=<colname> <storageType> "PRIMARY" "KEY"
( "," colname=<colname> <storageType> )* ")"
- ( "WITH" [cfopt]=<identifier> "=" [optval]=<cfOptionVal>
- ( "AND" [cfopt]=<identifier> "=" [optval]=<cfOptionVal> )* )?
+ ( "WITH" [cfopt]=<cfOptionName> "=" [optval]=<cfOptionVal>
+ ( "AND" [cfopt]=<cfOptionName> "=" [optval]=<cfOptionVal> )* )?
;
-<cfOptionVal> ::= <storageType>
- | <identifier>
+
+<cfOptionName> ::= cfoptname=<identifier> ( cfoptsep=":" cfsubopt=( <identifier> | <integer> ) )?
+ ;
+
+<cfOptionVal> ::= <identifier>
| <stringLiteral>
| <integer>
| <float>
@@ -518,11 +601,67 @@ syntax_rules += r'''
explain_completion('createColumnFamilyStatement', 'keyalias', '<new_key_alias>')
explain_completion('createColumnFamilyStatement', 'cf', '<new_columnfamily_name>')
explain_completion('createColumnFamilyStatement', 'colname', '<new_column_name>')
-explain_completion('createColumnFamilyStatement', 'optval', '<option_value>')
-@completer_for('createColumnFamilyStatement', 'cfopt')
+@completer_for('cfOptionName', 'cfoptname')
def create_cf_option_completer(ctxt, cass):
- return [c[0] for c in columnfamily_options]
+ return [c[0] for c in columnfamily_options] + \
+ [c[0] + ':' for c in columnfamily_map_options]
+
+@completer_for('cfOptionName', 'cfoptsep')
+def create_cf_suboption_separator(ctxt, cass):
+ opt = ctxt.get_binding('cfoptname')
+ if any(opt == c[0] for c in columnfamily_map_options):
+ return [':']
+ return ()
+
+@completer_for('cfOptionName', 'cfsubopt')
+def create_cf_suboption_completer(ctxt, cass):
+ opt = ctxt.get_binding('cfoptname')
+ if opt == 'compaction_strategy_options':
+ # try to determine the strategy class in use
+ prevopts = ctxt.get_binding('cfopt', ())
+ prevvals = ctxt.get_binding('optval', ())
+ for prevopt, prevval in zip(prevopts, prevvals):
+ if prevopt == 'compaction_strategy_class':
+ csc = cql_dequote(prevval)
+ break
+ else:
+ cf = ctxt.get_binding('cf')
+ try:
+ csc = cass.get_columnfamily(cf).compaction_strategy
+ except Exception:
+ csc = ''
+ csc = csc.split('.')[-1]
+ if csc == 'SizeTieredCompactionStrategy':
+ return ['min_sstable_size']
+ elif csc == 'LeveledCompactionStrategy':
+ return ['sstable_size_in_mb']
+ for optname, _, subopts in columnfamily_map_options:
+ if opt == optname:
+ return subopts
+ return ()
+
+def create_cf_option_val_completer(ctxt, cass):
+ exist_opts = ctxt.get_binding('cfopt')
+ this_opt = exist_opts[-1]
+ if this_opt == 'compression_parameters:sstable_compression':
+ return map(cql_escape, available_compression_classes)
+ if this_opt == 'compaction_strategy_class':
+ return map(cql_escape, available_compaction_classes)
+ if any(this_opt == opt[0] for opt in obsolete_cf_options):
+ return ["'<obsolete_option>'"]
+ if this_opt in ('comparator', 'default_validation'):
+ return cql_types
+ if this_opt == 'read_repair_chance':
+ return [Hint('<float_between_0_and_1>')]
+ if this_opt == 'replicate_on_write':
+ return [Hint('<yes_or_no>')]
+ if this_opt in ('min_compaction_threshold', 'max_compaction_threshold', 'gc_grace_seconds'):
+ return [Hint('<integer>')]
+ return [Hint('<option_value>')]
+
+completer_for('createColumnFamilyStatement', 'optval') \
+ (create_cf_option_val_completer)
syntax_rules += r'''
<createIndexStatement> ::= "CREATE" "INDEX" indexname=<identifier>? "ON"
@@ -575,6 +714,8 @@ syntax_rules += r'''
<alterInstructions> ::= "ALTER" existcol=<name> "TYPE" <storageType>
| "ADD" newcol=<name> <storageType>
| "DROP" existcol=<name>
+ | "WITH" [cfopt]=<cfOptionName> "=" [optval]=<cfOptionVal>
+ ( "AND" [cfopt]=<cfOptionName> "=" [optval]=<cfOptionVal> )*
;
'''
@@ -589,6 +730,9 @@ def alter_table_col_completer(ctxt, cass):
explain_completion('alterInstructions', 'newcol', '<new_column_name>')
+completer_for('alterInstructions', 'optval') \
+ (create_cf_option_val_completer)
+
# END SYNTAX/COMPLETION RULE DEFINITIONS
@@ -608,13 +752,10 @@ def cql_add_completer(rulename, symname):
def cql_parse(text, startsymbol='Start'):
tokens = CqlRuleSet.lex(text)
tokens = cql_massage_tokens(tokens)
- return cql_parse_tokens(tokens, startsymbol)
-
-def cql_parse_tokens(toklist, startsymbol='Start'):
- return CqlRuleSet.parse(startsymbol, toklist)
+ return CqlRuleSet.parse(startsymbol, tokens, init_bindings={'*SRC*': text})
-def cql_whole_parse_tokens(toklist, startsymbol='Start'):
- return CqlRuleSet.whole_match(startsymbol, toklist)
+def cql_whole_parse_tokens(toklist, srcstr=None, startsymbol='Start'):
+ return CqlRuleSet.whole_match(startsymbol, toklist, srcstr=srcstr)
def cql_massage_tokens(toklist):
curstmt = []
@@ -625,7 +766,7 @@ def cql_massage_tokens(toklist):
for t in toklist:
if t[0] == 'endline':
if term_on_nl:
- t = ('endtoken', '\n')
+ t = ('endtoken',) + t[1:]
else:
# don't put any 'endline' tokens in output
continue
@@ -716,6 +857,7 @@ def cql_complete_single(text, partial, init_bindings={}, ignore_case=True, start
if tokens and tokens[-1][0] == 'unclosedComment':
return []
bindings['partial'] = partial
+ bindings['*SRC*'] = text
# find completions for the position
completions = CqlRuleSet.complete(startsymbol, tokens, bindings)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d4f20515/pylib/cqlshlib/pylexotron.py
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/pylexotron.py b/pylib/cqlshlib/pylexotron.py
index ff099ff..7482aba 100644
--- a/pylib/cqlshlib/pylexotron.py
+++ b/pylib/cqlshlib/pylexotron.py
@@ -88,6 +88,18 @@ class ParseContext:
return self.__class__(self.ruleset, self.bindings, self.matched,
self.remainder, newname)
+ def extract_orig(self, tokens=None):
+ if tokens is None:
+ tokens = self.matched
+ if not tokens:
+ return ''
+ orig = self.bindings.get('*SRC*', None)
+ if orig is None:
+ # pretty much just guess
+ return ' '.join([t[1] for t in tokens])
+ # low end of span for first token, to high end of span for last token
+ return orig[tokens[0][2][0]:tokens[-1][2][1]]
+
def __repr__(self):
return '<%s matched=%r remainder=%r prodname=%r bindings=%r>' \
% (self.__class__.__name__, self.matched, self.remainder, self.productionname, self.bindings)
@@ -183,7 +195,7 @@ class named_symbol(matcher):
# don't collect other completions under this; use a dummy
pass_in_compls = set()
results = self.arg.match_with_results(ctxt, pass_in_compls)
- return [c.with_binding(self.name, tokens_to_text(matchtoks)) for (c, matchtoks) in results]
+ return [c.with_binding(self.name, ctxt.extract_orig(matchtoks)) for (c, matchtoks) in results]
def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.arg)
@@ -197,7 +209,7 @@ class named_collector(named_symbol):
output = []
for ctxt, matchtoks in self.arg.match_with_results(ctxt, pass_in_compls):
oldval = ctxt.get_binding(self.name, ())
- output.append(ctxt.with_binding(self.name, oldval + (tokens_to_text(matchtoks),)))
+ output.append(ctxt.with_binding(self.name, oldval + (ctxt.extract_orig(matchtoks),)))
return output
class terminal_matcher(matcher):
@@ -257,9 +269,6 @@ class case_match(text_match):
def pattern(self):
return re.escape(self.arg)
-def tokens_to_text(toks):
- return ' '.join([t[1] for t in toks])
-
class ParsingRuleSet:
RuleSpecScanner = SaferScanner([
(r'::=', lambda s,t: t),
@@ -381,7 +390,7 @@ class ParsingRuleSet:
def make_handler(name):
if name == 'JUNK':
return None
- return lambda s, t: (name, t)
+ return lambda s, t: (name, t, s.match.span())
regexes = [(p.pattern(), make_handler(name)) for (name, p) in self.terminals]
return SaferScanner(regexes, re.I | re.S).scan
@@ -400,13 +409,16 @@ class ParsingRuleSet:
pattern = self.ruleset[startsymbol]
return pattern.match(ctxt, None)
- def whole_match(self, startsymbol, tokens):
- newctxts = [c for c in self.parse(startsymbol, tokens) if not c.remainder]
- if newctxts:
- return newctxts[0]
+ def whole_match(self, startsymbol, tokens, srcstr=None):
+ bindings = {}
+ if srcstr is not None:
+ bindings['*SRC*'] = srcstr
+ for c in self.parse(startsymbol, tokens, init_bindings=bindings):
+ if not c.remainder:
+ return c
def lex_and_parse(self, text, startsymbol='Start'):
- return self.parse(startsymbol, self.lex(text))
+ return self.parse(startsymbol, self.lex(text), init_bindings={'*SRC*': text})
def complete(self, startsymbol, tokens, init_bindings=None):
if init_bindings is None:
@@ -442,11 +454,12 @@ class Debugotron(set):
lineno = frame.f_lineno
if 'self' in frame.f_locals:
clsobj = frame.f_locals['self']
- cls = clsobj.__class__
line = '%s.%s() (%s:%d)' % (clsobj, name, filename, lineno)
else:
line = '%s (%s:%d)' % (name, filename, lineno)
- self.stream.write(' %s\n' % (line,))
+ self.stream.write(' - %s\n' % (line,))
+ if i == 0 and 'ctxt' in frame.f_locals:
+ self.stream.write(' - %s\n' % (frame.f_locals['ctxt'],))
frame = frame.f_back
def update(self, items):