J
Jack Carter
I have been delegated to produce a tool that has
python embedded in it. The desire is to have a
command line interface that inherits all the python
scripting functionality so people can use the tool
either with a flat commandline entry like you would
a debugger or with a script using scoping, loops and
conditional.
I am going from knowing nothing about python to
almost nothing so the learning curve is rather
steep at this point.
I have a dummy program that inherits code.InteractiveConsole
functionality. I can grab each input line and scan it for
commands that match my tool's syntax ("attach") and if it
does, I change the line to reflect a python legal function
call associated with that commond (DoAttach) and from there
do what I want with the line. The actual input lines are
executed with a self.push() call.
The problem I have is in timely symbol resolution. I really
want to run eval() on each of the arguments at the time
the DoAttach() function is executed, but don't know how
and where from does one get the dictionary with the proper
name space. If I run eval at the time of initial reading
of the input it works for flat first level input, but not
for nested scoping:
cli.py
bosco=5
if 1:
attach bosco
bosco=7
attach bosco
attach bosco
This will result in:
... attach bosco
... bosco=7
... attach bosco
...
DoAttach: ['5']
DoAttach: ['5'] <--- WRONG (at least, not what I want) DoAttach: ['7']
How does one export the correct dictionary?
Attached is the toy program that creates this output.
Keep in mind that I am probably aproaching this all
wrong. Any relevant suggestions would be most appreciated,
Jack
*****************
File cli.py:
*****************
#!/usr/bin/env python
import myparse
cli = myparse.CLI(globals())
cli.interact()
*****************
File myparse.py:
*****************
import code
import re
import string
import sys
def DoAttach(args):
print "DoAttach:", args
pass
class CLI(code.InteractiveConsole):
"""Simple test of a Python interpreter augmented with custom
commands."""
commands = { \
"attach" : "DoAttach"
}
def __init__(self, locals = None):
# Call super-class initializer
code.InteractiveConsole.__init__(self, locals, "<console>")
# Compile regular expression for finding commmands
self.regexp = re.compile('[a-z]*')
def interact(self):
my_dictionary = self.locals
# Set the primary and secondary prompts
sys.ps1 = ">>> "
sys.ps2 = "... "
# Input Loop
is_more = 0
bosco = 0
while 1:
try :
# Display the appropriate prompt
if not sys.stdin.isatty():
prompt = ""
elif is_more:
prompt = sys.ps2
else:
prompt = sys.ps1
# Read the next line of input
#self.write("interact 1\n")
line = self.raw_input(prompt)
# TODO: add logging of input line here...
# Process complete lines
if 1:
line = self.process(line)
# Push incomplete lines onto input stack
if line or is_more:
is_more = self.push(line)
# Handle CTRL-C
except KeyboardInterrupt:
self.write("\nKeyboardInterrupt\n")
is_more = 0
self.resetbuffer()
# Handle CTRL-D
except EOFError:
self.write("\n")
is_more = 0
self.resetbuffer()
raise SystemExit
def process(parent, line):
# Attempt to match line against our command regular expression
temp_line = string.lstrip(line)
len_1 = len(line)
len_2 = len(temp_line)
white_spaces = len_1-len_2
if white_spaces is not 0:
front_padding = line[0:white_spaces]
match = parent.regexp.match(temp_line)
if match is not None:
# Extract the command and argument strings
cmd_string = match.group()
arg_string = string.lstrip(temp_line[match.end():])
# Find the function for this command in the command
dictionary
function = parent.commands.get(cmd_string)
if function is not None:
# Split argument string into individual arguments
args = string.split(arg_string)
# Recursively evaluate all arguments
i = 0
while i < len(args):
try:
# Grab value and position of this argument
unevaluated, pos, i = args, i, i + 1
# Have Python attempt evaluation of the argument
evaluated = eval(unevaluated, parent.locals)
# Did the evaluation produce a "new" result?
if str(evaluated) != str(unevaluated):
# Place the evaluation in the argument list
args = args[os] + \
string.split(str(evaluated)) + \
args[pos + 1:]
# Go back to the beginning of the argument
list
i = 0
except (SyntaxError, NameError):
pass
# Convert to Python function-call syntax for this
command
line = "myparse." + function + "(" + str(args) + ")"
if white_spaces is not 0:
line = front_padding + line
# Return the line to be processed by Python
return line
python embedded in it. The desire is to have a
command line interface that inherits all the python
scripting functionality so people can use the tool
either with a flat commandline entry like you would
a debugger or with a script using scoping, loops and
conditional.
I am going from knowing nothing about python to
almost nothing so the learning curve is rather
steep at this point.
I have a dummy program that inherits code.InteractiveConsole
functionality. I can grab each input line and scan it for
commands that match my tool's syntax ("attach") and if it
does, I change the line to reflect a python legal function
call associated with that commond (DoAttach) and from there
do what I want with the line. The actual input lines are
executed with a self.push() call.
The problem I have is in timely symbol resolution. I really
want to run eval() on each of the arguments at the time
the DoAttach() function is executed, but don't know how
and where from does one get the dictionary with the proper
name space. If I run eval at the time of initial reading
of the input it works for flat first level input, but not
for nested scoping:
cli.py
bosco=5
if 1:
attach bosco
bosco=7
attach bosco
attach bosco
This will result in:
... attach bosco
... bosco=7
... attach bosco
...
DoAttach: ['5']
DoAttach: ['5'] <--- WRONG (at least, not what I want) DoAttach: ['7']
How does one export the correct dictionary?
Attached is the toy program that creates this output.
Keep in mind that I am probably aproaching this all
wrong. Any relevant suggestions would be most appreciated,
Jack
*****************
File cli.py:
*****************
#!/usr/bin/env python
import myparse
cli = myparse.CLI(globals())
cli.interact()
*****************
File myparse.py:
*****************
import code
import re
import string
import sys
def DoAttach(args):
print "DoAttach:", args
pass
class CLI(code.InteractiveConsole):
"""Simple test of a Python interpreter augmented with custom
commands."""
commands = { \
"attach" : "DoAttach"
}
def __init__(self, locals = None):
# Call super-class initializer
code.InteractiveConsole.__init__(self, locals, "<console>")
# Compile regular expression for finding commmands
self.regexp = re.compile('[a-z]*')
def interact(self):
my_dictionary = self.locals
# Set the primary and secondary prompts
sys.ps1 = ">>> "
sys.ps2 = "... "
# Input Loop
is_more = 0
bosco = 0
while 1:
try :
# Display the appropriate prompt
if not sys.stdin.isatty():
prompt = ""
elif is_more:
prompt = sys.ps2
else:
prompt = sys.ps1
# Read the next line of input
#self.write("interact 1\n")
line = self.raw_input(prompt)
# TODO: add logging of input line here...
# Process complete lines
if 1:
line = self.process(line)
# Push incomplete lines onto input stack
if line or is_more:
is_more = self.push(line)
# Handle CTRL-C
except KeyboardInterrupt:
self.write("\nKeyboardInterrupt\n")
is_more = 0
self.resetbuffer()
# Handle CTRL-D
except EOFError:
self.write("\n")
is_more = 0
self.resetbuffer()
raise SystemExit
def process(parent, line):
# Attempt to match line against our command regular expression
temp_line = string.lstrip(line)
len_1 = len(line)
len_2 = len(temp_line)
white_spaces = len_1-len_2
if white_spaces is not 0:
front_padding = line[0:white_spaces]
match = parent.regexp.match(temp_line)
if match is not None:
# Extract the command and argument strings
cmd_string = match.group()
arg_string = string.lstrip(temp_line[match.end():])
# Find the function for this command in the command
dictionary
function = parent.commands.get(cmd_string)
if function is not None:
# Split argument string into individual arguments
args = string.split(arg_string)
# Recursively evaluate all arguments
i = 0
while i < len(args):
try:
# Grab value and position of this argument
unevaluated, pos, i = args, i, i + 1
# Have Python attempt evaluation of the argument
evaluated = eval(unevaluated, parent.locals)
# Did the evaluation produce a "new" result?
if str(evaluated) != str(unevaluated):
# Place the evaluation in the argument list
args = args[os] + \
string.split(str(evaluated)) + \
args[pos + 1:]
# Go back to the beginning of the argument
list
i = 0
except (SyntaxError, NameError):
pass
# Convert to Python function-call syntax for this
command
line = "myparse." + function + "(" + str(args) + ")"
if white_spaces is not 0:
line = front_padding + line
# Return the line to be processed by Python
return line