Skip to content

Instantly share code, notes, and snippets.

@rduplain
Last active December 6, 2023 15:08
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save rduplain/899f6a5e583a85668822 to your computer and use it in GitHub Desktop.
Save rduplain/899f6a5e583a85668822 to your computer and use it in GitHub Desktop.
Demo of the Python `code` module, for interaction and DSLs.

import code

This is a demonstration of the Python code module, which allows for an interactive interpreter to be embedded into a Python program.

code.interact(banner=None, readfunc=None, local=None)

Convenience function to run a read-eval-print loop. This creates a new instance of InteractiveConsole and sets readfunc to be used as the InteractiveConsole.raw_input() method, if provided. If local is provided, it is passed to the InteractiveConsole constructor for use as the default namespace for the interpreter loop. The interact() method of the instance is then run with banner passed as the banner to use, if provided. The console object is discarded after use.

python interact.py

code.interact is useful for:

  1. Pre-loading python interactive interpreter. (flask-script does this.)
  2. Embedding an interactive interpreter in a program, with an internal DSL.
  3. Quick interactive debugging (you probably want pdb.set_trace()).

Example:

$ python interact.py

>>> foo
'this is foo'
>>> bar
'this is bar'
>>> 6 * 7
42
>>>
$
class code.InteractiveConsole(locals=None, filename="<console>")

Closely emulate the behavior of the interactive Python interpreter. This class builds on InteractiveInterpreter and adds prompting using the familiar sys.ps1 and sys.ps2, and input buffering.

python console.py

code.InteractiveConsole is useful when you want to customize the REPL:

  1. Changing the REPL behavior of the python interactive interpreter.
  2. Embedding an interactive interpreter in a program, with an external DSL.
  3. Provide a scripting interface for an external DSL.

Example:

$ python console.py
> room
No one is in the room.
> enter John Mary Joseph
John enters the room.
Mary enters the room.
Joseph enters the room.
> exit Joseph
Joseph leaves the room.
> room
In the room: John, Mary
>
$

Non-interactive usage:

$ echo 'room' | python console.py
No one is in the room.
$

Non-interactive scripting (could update code to take files on sys.argv):

python console.py <<EOF
enter John Mary Joseph
exit Joseph
room
EOF

... produces output:

John enters the room.
Mary enters the room.
Joseph enters the room.
Joseph leaves the room.
In the room: John, Mary

Selected real-world use cases of code.InteractiveConsole:

  1. interactive step interpreter for Cucumber-style steps, with behave
  2. send bytes interactively to test a network protocol driver
  3. command REPL for an instrument controller
import code
import shlex
import sys
from sys import stderr
class CommandRunner(object):
"Simple demo."
def __init__(self):
self.commands = {}
def command(self, name, fn):
self.commands[name] = fn
def run(self, line):
tokens = shlex.split(line, comments=True)
command, args = tokens[0], tokens[1:]
if command not in self.commands:
print('{}: no such command'.format(command), file=stderr)
return
result = self.commands[command](*args)
if result is not None:
print(result)
class Console(object):
ps1 = '> '
ps2 = '. '
def __init__(self, runner):
self.runner = runner
def run(self, fd):
for line in fd:
self.runner.run(line)
def interact(self, locals=None):
class LambdaConsole(code.InteractiveConsole):
def runsource(code_console, source, filename=None, symbol=None):
# Return True if more input needed, else False.
try:
self.runner.run(source)
except SystemExit:
raise
except:
code_console.showtraceback()
return False
# import readline to support line editing within console session.
try:
import readline; readline
except ImportError:
pass
# Patch ps1 & ps2 for interaction. Note sys.psX may be unset.
ps1, ps2 = getattr(sys, 'ps1', None), getattr(sys, 'ps2', None)
try:
sys.ps1, sys.ps2 = self.ps1, self.ps2
LambdaConsole(locals=locals, filename="<demo>").interact(banner='')
finally:
sys.ps1, sys.ps2 = ps1, ps2
def run_in_main(self, fd=None, interact=False):
if fd is None:
fd = sys.stdin
if fd.isatty():
self.interact()
else:
try:
self.run(fd=fd)
except Exception as err:
print(err, file=stderr)
return 1
return 0
class Room(object):
"Simple demo."
def __init__(self):
self.people = set()
def enter(self, *people):
for person in people:
if person in self.people:
print('{} is already in the room.'.format(person), file=stderr)
else:
print('{} enters the room.'.format(person))
self.people.add(person)
def exit(self, *people):
for person in people:
if person in self.people:
print('{} leaves the room.'.format(person))
self.people.remove(person)
else:
print('{} is not in the room.'.format(person), file=stderr)
def room(self):
if self.people:
print('In the room: {}'.format(', '.join(self.people)))
else:
print('No one is in the room.')
def main(fd=None):
room = Room()
runner = CommandRunner()
runner.command('enter', room.enter)
runner.command('exit', room.exit)
runner.command('room', room.room)
return Console(runner).run_in_main(fd)
if __name__ == '__main__':
sys.exit(main())
import code
def interact():
foo = 'this is foo'
bar = 'this is bar'
code.interact(banner='', local=locals())
if __name__ == '__main__':
interact()
@ReddyKilowatt
Copy link

Have you tried running console.py?
You should

@rduplain
Copy link
Author

Are you using Python 3? This won't run under Python 2. Then, know that there isn't much error handling in the code, so if you deviate from the demo, you may run into cases that I didn't expect when I wrote it.

I just ran it again exactly as described with Python 3.4 and it works as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment