123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- from __future__ import print_function # PY2
- import __main__
- import argparse
- import sys
- import traceback
- import tokenize
- from ctypes import pythonapi, POINTER, c_long, cast
- from types import CodeType as Code
- from . import console, enable, disable
- from .info import PY2
- inspect_flag = cast(pythonapi.Py_InspectFlag, POINTER(c_long)).contents
- def set_inspect_flag(value):
- inspect_flag.value = int(value)
- CODE_FIELDS = ["argcount", "kwonlyargcount", "nlocals", "stacksize",
- "flags", "code", "consts", "names", "varnames", "filename",
- "name", "firstlineno", "lnotab", "freevars", "cellvars"]
- if PY2:
- CODE_FIELDS.remove("kwonlyargcount")
- def update_code(codeobj, **kwargs):
- def field_values():
- for field in CODE_FIELDS:
- original_value = getattr(codeobj, "co_{}".format(field))
- value = kwargs.get(field, original_value)
- yield value
-
- return Code(*field_values())
- def update_code_recursively(codeobj, **kwargs):
- updated = {}
-
- def update(codeobj, **kwargs):
- result = updated.get(codeobj, None)
- if result is not None:
- return result
-
- if any(isinstance(c, Code) for c in codeobj.co_consts):
- consts = tuple(update(c, **kwargs) if isinstance(c, Code) else c
- for c in codeobj.co_consts)
- else:
- consts = codeobj.co_consts
-
- result = update_code(codeobj, consts=consts, **kwargs)
- updated[codeobj] = result
- return result
-
- return update(codeobj, **kwargs)
- def get_code(path):
- if PY2:
- from .tokenize_open import read_source_lines
- source = u"".join(read_source_lines(path))
- else:
- with tokenize.open(path) as f: # opens with detected source encoding
- source = f.read()
-
- try:
- code = compile(source, path, "exec", dont_inherit=True)
- except UnicodeEncodeError:
- code = compile(source, "<encoding error>", "exec", dont_inherit=True)
- if PY2:
- path = path.encode("utf-8")
- code = update_code_recursively(code, filename=path)
- # so code constains correct filename (even if it contains Unicode)
- # and tracebacks show contents of code lines
-
- return code
- def print_exception_without_first_line(etype, value, tb, limit=None, file=None, chain=True):
- if file is None:
- file = sys.stderr
-
- lines = iter(traceback.TracebackException(
- type(value), value, tb, limit=limit).format(chain=chain))
-
- next(lines)
- for line in lines:
- print(line, file=file, end="")
- def run_script(args):
- sys.argv = [args.script] + args.script_arguments
- path = args.script
- __main__.__file__ = path
-
- try:
- code = get_code(path)
- except Exception as e:
- traceback.print_exception(e.__class__, e, None, file=sys.stderr)
- else:
- try:
- exec(code, __main__.__dict__)
- except BaseException as e:
- if not sys.flags.inspect and isinstance(e, SystemExit):
- raise
-
- elif PY2: # Python 2 produces tracebacks in mixed encoding (!)
- etype, e, tb = sys.exc_info()
- for line in traceback.format_exception(etype, e, tb.tb_next):
- line = line.decode("utf-8", "replace")
- try:
- sys.stderr.write(line)
- except UnicodeEncodeError:
- line = line.encode(sys.stderr.encoding, "backslashreplace")
- sys.stderr.write(line)
-
- sys.stderr.flush() # is this needed?
-
- else: # PY3
- traceback.print_exception(e.__class__, e, e.__traceback__.tb_next, file=sys.stderr)
- def run_init(args):
- if args.init == "enable":
- enable()
- elif args.init == "disable":
- disable()
- elif args.init == "module":
- __import__(args.module)
- elif args.init == "none":
- pass
- else:
- raise ValueError("unknown runner init mode {}".format(repr(args.init)))
- def run_with_custom_repl(args):
- run_init(args)
-
- if args.script:
- run_script(args)
-
- if sys.flags.interactive or not args.script:
- if sys.flags.interactive and not args.script:
- console.print_banner()
- try:
- console.enable()
- finally:
- set_inspect_flag(0)
- def run_with_standard_repl(args):
- run_init(args)
-
- if args.script:
- run_script(args)
-
- if sys.flags.interactive and not args.script:
- console.print_banner()
- def run_arguments():
- parser = argparse.ArgumentParser(description="Runs a script after customizable initialization. By default, win_unicode_console is enabled.")
-
- init_group = parser.add_mutually_exclusive_group()
- init_group.add_argument(
- "-e", "--init-enable", dest="init", action="store_const", const="enable",
- help="enable win_unicode_console on init (default)")
- init_group.add_argument(
- "-d", "--init-disable", dest="init", action="store_const", const="disable",
- help="disable win_unicode_console on init")
- init_group.add_argument(
- "-m", "--init-module", dest="module",
- help="import the given module on init")
- init_group.add_argument(
- "-n", "--no-init", dest="init", action="store_const", const="none",
- help="do nothing special on init")
- parser.set_defaults(init="enable")
-
- repl_group = parser.add_mutually_exclusive_group()
- repl_group.add_argument(
- "-s", "--standard-repl", dest="use_repl", action="store_false",
- help="use the standard Python REPL (default)")
- repl_group.add_argument(
- "-c", "--custom-repl", dest="use_repl", action="store_true",
- help="use win_unicode_console.console REPL")
- parser.set_defaults(use_repl=False)
-
- parser.add_argument("script", nargs="?")
- parser.add_argument("script_arguments", nargs=argparse.REMAINDER, metavar="script-arguments")
-
- try:
- args = parser.parse_args(sys.argv[1:])
- except SystemExit:
- set_inspect_flag(0) # don't go interactive after printing help
- raise
-
- if args.module:
- args.init = "module"
-
- if args.use_repl:
- run_with_custom_repl(args)
- else:
- run_with_standard_repl(args)
|