Skip to content

Commit 956899f

Browse files
committed
Make bpython embeddable.
This will close issue #51.
1 parent c0f29ce commit 956899f

File tree

2 files changed

+52
-27
lines changed

2 files changed

+52
-27
lines changed

bpython/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,8 @@
2222

2323

2424
__version__ = '0.9.4'
25+
26+
27+
def embed(locals_=None, args=['-i', '-q']):
28+
from bpython.cli import main
29+
return main(args, locals_)

bpython/cli.py

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ def log(x):
7878
f.write('%s\n' % (x,))
7979

8080
py3 = sys.version_info[0] == 3
81-
orig_stdout = sys.__stdout__
8281
stdscr = None
8382

8483

@@ -2097,7 +2096,7 @@ def gethw():
20972096
20982097
"""
20992098
h, w = struct.unpack(
2100-
"hhhh", fcntl.ioctl(orig_stdout, termios.TIOCGWINSZ, "\000"*8))[0:2]
2099+
"hhhh", fcntl.ioctl(sys.__stdout__, termios.TIOCGWINSZ, "\000"*8))[0:2]
21012100
return h, w
21022101

21032102

@@ -2156,7 +2155,30 @@ def newwin(*args):
21562155
return win
21572156

21582157

2159-
def main_curses(scr, args, interactive=True):
2158+
def curses_wrapper(func, *args, **kwargs):
2159+
"""Like curses.wrapper(), but reuses stdscr when called again."""
2160+
global stdscr
2161+
if stdscr is None:
2162+
stdscr = curses.initscr()
2163+
try:
2164+
curses.noecho()
2165+
curses.cbreak()
2166+
stdscr.keypad(1)
2167+
2168+
try:
2169+
curses.start_color()
2170+
except curses.error:
2171+
pass
2172+
2173+
return func(stdscr, *args, **kwargs)
2174+
finally:
2175+
stdscr.keypad(0)
2176+
curses.echo()
2177+
curses.nocbreak()
2178+
curses.endwin()
2179+
2180+
2181+
def main_curses(scr, args, interactive=True, locals_=None):
21602182
"""main function for the curses convenience wrapper
21612183
21622184
Initialise the two main objects: the interpreter
@@ -2189,10 +2211,9 @@ def main_curses(scr, args, interactive=True):
21892211
curses.raw(True)
21902212
main_win, statusbar = init_wins(scr, cols)
21912213

2192-
curses.raw(True)
2193-
2194-
interpreter = Interpreter(dict(__name__='__main__', __doc__=None),
2195-
getpreferredencoding())
2214+
if locals_ is None:
2215+
locals_ = dict(__name__='__main__', __doc__=None)
2216+
interpreter = Interpreter(locals_, getpreferredencoding())
21962217

21972218
repl = Repl(main_win, interpreter, statusbar, idle)
21982219
interpreter.syntaxerror_callback = repl.clear_current_line
@@ -2209,6 +2230,7 @@ def main_curses(scr, args, interactive=True):
22092230
interpreter.runcode(code_obj)
22102231
sys.argv = old_argv
22112232
if not interactive:
2233+
curses.raw(False)
22122234
return repl.getstdout()
22132235

22142236
repl.repl()
@@ -2222,10 +2244,13 @@ def main_curses(scr, args, interactive=True):
22222244
main_win.refresh()
22232245
statusbar.win.clear()
22242246
statusbar.win.refresh()
2247+
curses.raw(False)
22252248
return repl.getstdout()
22262249

22272250

2228-
def main(args=None):
2251+
def main(args=None, locals_=None):
2252+
global stdscr
2253+
22292254
if args is None:
22302255
args = sys.argv[1:]
22312256

@@ -2238,6 +2263,8 @@ def main(args=None):
22382263
parser.add_option('--interactive', '-i', action='store_true',
22392264
help='Drop to bpython shell after running file '
22402265
'instead of exiting')
2266+
parser.add_option('--quiet', '-q', action='store_true',
2267+
help="Don't flush the output to stdout.")
22412268
parser.add_option('--version', '-V', action='store_true',
22422269
help='print version and exit')
22432270

@@ -2265,33 +2292,26 @@ def main(args=None):
22652292

22662293
setlocale(LC_ALL, '')
22672294

2268-
tb = None
2269-
22702295
path = os.path.expanduser('~/.bpythonrc') # migrating old configuration file
22712296
if os.path.isfile(path):
22722297
migrate_rc(path)
22732298
loadini(OPTS, options.config)
22742299

2300+
# Save stdin, stdout and stderr for later restoration
2301+
orig_stdin = sys.stdin
2302+
orig_stdout = sys.stdout
2303+
orig_stderr = sys.stderr
2304+
22752305
try:
2276-
o = curses.wrapper(main_curses, exec_args, options.interactive)
2277-
except:
2278-
tb = traceback.format_exc()
2279-
# I don't know why this is necessary; without it the wrapper doesn't always do
2280-
# its job.
2281-
if stdscr is not None:
2282-
stdscr.clear()
2283-
stdscr.keypad(0)
2284-
curses.echo()
2285-
curses.nocbreak()
2286-
curses.endwin()
2287-
2288-
sys.stdout = orig_stdout
2289-
if tb:
2290-
print tb
2291-
sys.exit(1)
2306+
o = curses_wrapper(main_curses, exec_args, options.interactive,
2307+
locals_)
2308+
finally:
2309+
sys.stdin = orig_stdin
2310+
sys.stderr = orig_stderr
2311+
sys.stdout = orig_stdout
22922312

22932313
# Fake stdout data so everything's still visible after exiting
2294-
if OPTS.flush_output:
2314+
if OPTS.flush_output and not options.quiet:
22952315
sys.stdout.write(o)
22962316
sys.stdout.flush()
22972317

0 commit comments

Comments
 (0)