# encoding: utf-8
"""
Module to handle command line argument parsing, for all front-ends.
"""
from __future__ import print_function, absolute_import
import code
import imp
import os
import sys
from optparse import OptionParser, OptionGroup
from . import __version__
from .config import default_config_path, loadini, Struct
from .translations import _
class OptionParserFailed(ValueError):
"""Raised by the RaisingOptionParser for a bogus commandline."""
class RaisingOptionParser(OptionParser):
def error(self, msg):
raise OptionParserFailed()
def version_banner():
return "bpython version %s on top of Python %s %s" % (
__version__,
sys.version.split()[0],
sys.executable,
)
def parse(args, extras=None, ignore_stdin=False):
"""Receive an argument list - if None, use sys.argv - parse all args and
take appropriate action. Also receive optional extra options: this should
be a tuple of (title, description, options)
title: The title for the option group
description: A full description of the option group
options: A list of optparse.Option objects to be added to the
group
e.g.:
parse(
['-i', '-m', 'foo.py'],
('Front end-specific options',
'A full description of what these options are for',
[optparse.Option('-f', action='store_true', dest='f', help='Explode'),
optparse.Option('-l', action='store_true', dest='l', help='Love')]))
Return a tuple of (config, options, exec_args) wherein "config" is the
config object either parsed from a default/specified config file or default
config options, "options" is the parsed options from
OptionParser.parse_args, and "exec_args" are the args (if any) to be parsed
to the executed file (if any).
"""
if args is None:
args = sys.argv[1:]
parser = RaisingOptionParser(
usage=_(
"Usage: %prog [options] [file [args]]\n"
"NOTE: If bpython sees an argument it does "
"not know, execution falls back to the "
"regular Python interpreter."
)
)
# This is not sufficient if bpython gains its own -m support
# (instead of falling back to Python itself for that).
# That's probably fixable though, for example by having that
# option swallow all remaining arguments in a callback.
parser.disable_interspersed_args()
parser.add_option(
"--config",
default=default_config_path(),
help=_("Use CONFIG instead of default config file."),
)
parser.add_option(
"--interactive",
"-i",
action="store_true",
help=_(
"Drop to bpython shell after running file " "instead of exiting."
),
)
parser.add_option(
"--quiet",
"-q",
action="store_true",
help=_("Don't flush the output to stdout."),
)
parser.add_option(
"--version",
"-V",
action="store_true",
help=_("Print version and exit."),
)
if extras is not None:
extras_group = OptionGroup(parser, extras[0], extras[1])
for option in extras[2]:
extras_group.add_option(option)
parser.add_option_group(extras_group)
try:
options, args = parser.parse_args(args)
except OptionParserFailed:
# Just let Python handle this
os.execv(sys.executable, [sys.executable] + args)
if options.version:
print(version_banner())
print(
"(C) 2008-2016 Bob Farrell, Andreas Stuehrk, Sebastian Ramacher, Thomas Ballinger, et al. "
"See AUTHORS for detail."
)
raise SystemExit
if not ignore_stdin and not (sys.stdin.isatty() and sys.stdout.isatty()):
interpreter = code.InteractiveInterpreter()
interpreter.runsource(sys.stdin.read())
raise SystemExit
config = Struct()
loadini(config, options.config)
return config, options, args
def exec_code(interpreter, args):
"""
Helper to execute code in a given interpreter. args should be a [faked]
sys.argv
"""
with open(args[0], "r") as sourcefile:
source = sourcefile.read()
old_argv, sys.argv = sys.argv, args
sys.path.insert(0, os.path.abspath(os.path.dirname(args[0])))
mod = imp.new_module("__console__")
sys.modules["__console__"] = mod
interpreter.locals = mod.__dict__
interpreter.locals["__file__"] = args[0]
interpreter.runsource(source, args[0], "exec")
sys.argv = old_argv