frappe/env/lib/python3.12/site-packages/traceback_with_variables/main.py

131 lines
3.8 KiB
Python

import argparse
import os
import sys
from distutils.spawn import find_executable
from importlib.util import find_spec
from pathlib import Path
from typing import List, Optional, NoReturn, Tuple
from traceback_with_variables.print import printing_exc, Format
def run_script(
path: Path,
argv: List[str],
fmt: Format,
) -> int:
sys.path[0] = str(path.parent)
sys.argv = [str(path)] + argv
globals_ = {k: v for k, v in globals().items() if k in {
'__builtins__', '__loader__',
}}
globals_.update({k: None for k in globals().keys() if k in {
'__cached__', '__package__', '__annotations__', '__spec__',
}})
globals_.update({
'__name__': '__main__',
'__file__': str(path),
})
with printing_exc(
reraise=False,
skip_cur_frame=True,
fmt=fmt,
):
exec(compile(path.read_text(), str(path), 'exec'), globals_, globals_)
return 0
return 1 # noqa # actually 'this code is unreachable' is wrong
# - parse args - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class ParseError(RuntimeError):
pass
def raising_error_func(message: str) -> NoReturn:
raise ParseError(message)
def split_argv_to_own_and_script(
raising_noabbrev_parser: argparse.ArgumentParser, # with raising .error, no abbrev
argv: Optional[List[str]] = None,
) -> Tuple[List[str], List[str]]:
_argv = (argv or sys.argv)[1:]
for num_args in range(1, len(_argv) + 1):
try:
raising_noabbrev_parser.parse_args(_argv[:num_args])
except ParseError as e:
if e.args[0].startswith("unrecognized arguments"):
return _argv[: num_args - 1], _argv[num_args - 1:]
return _argv, []
def parse_args_and_script_cmd(
raising_nohelp_noabbrev_parser: argparse.ArgumentParser, # with raising .error, no help, no abbrev
) -> Tuple[argparse.Namespace, Path, List[str]]:
# public_parser is only for help messages and complaints, it is always called with wrong arguments or '--help'
# so it terminates the program and exits with status 1 (or 0 if called with '--help')
public_parser = argparse.ArgumentParser(parents=[raising_nohelp_noabbrev_parser])
public_parser.add_argument("script")
public_parser.add_argument("script-arg", nargs="*")
own_argv, script_argv = split_argv_to_own_and_script(
raising_nohelp_noabbrev_parser, sys.argv
)
args = argparse.Namespace() # make linter happy
try:
args = raising_nohelp_noabbrev_parser.parse_args(own_argv)
except ParseError:
public_parser.parse_args(own_argv)
if not script_argv:
public_parser.parse_args(own_argv)
if script_argv[0].startswith('-'):
public_parser.parse_args(own_argv + script_argv[:1] + ['some_cmd'])
if os.path.isfile(script_argv[0]):
script_path_str = script_argv[0]
else:
script_path_str = find_executable(script_argv[0])
if not script_path_str:
module_spec = find_spec(script_argv[0])
if not module_spec:
public_parser.error(f"No such file or command or module: {script_argv[0]}")
script_path_str = module_spec.origin
return args, Path(script_path_str), script_argv[1:]
def parse_args() -> Tuple[argparse.Namespace, Path, List[str]]:
parser = argparse.ArgumentParser(add_help=False, allow_abbrev=False)
parser.error = raising_error_func
Format.add_arguments(parser)
return parse_args_and_script_cmd(parser)
def main():
ns, script_path, script_argv = parse_args()
return run_script(
path=script_path,
argv=script_argv,
fmt=Format.parse(ns)
)
if __name__ == "__main__":
sys.exit(main())