|
1 | 1 | import argparse |
2 | 2 | import os |
3 | 3 | import sys |
| 4 | +import traceback |
| 5 | +import pathlib |
4 | 6 |
|
5 | 7 | from cf_remote import log |
6 | 8 | from cfengine_cli.version import cfengine_cli_version_string |
7 | 9 | from cfengine_cli import commands |
8 | 10 | from cfengine_cli.utils import UserError |
| 11 | +from cfbs.utils import CFBSProgrammerError |
9 | 12 |
|
10 | 13 |
|
11 | 14 | def _get_arg_parser(): |
@@ -105,23 +108,61 @@ def validate_args(args): |
105 | 108 | if args.command == "dev" and args.dev_command is None: |
106 | 109 | raise UserError("Missing subcommand - cfengine dev <subcommand>") |
107 | 110 |
|
| 111 | +def _main(): |
| 112 | + args = get_args() |
| 113 | + if args.log_level: |
| 114 | + log.set_level(args.log_level) |
| 115 | + validate_args(args) |
| 116 | + return run_command_with_args(args) |
108 | 117 |
|
109 | 118 | def main(): |
| 119 | + if os.getenv("CFBACKTRACE") == "1": |
| 120 | + r = _main() |
| 121 | + return r |
110 | 122 | try: |
111 | | - args = get_args() |
112 | | - if args.log_level: |
113 | | - log.set_level(args.log_level) |
114 | | - validate_args(args) |
115 | | - |
116 | | - exit_code = run_command_with_args(args) |
| 123 | + exit_code = _main() |
117 | 124 | assert type(exit_code) is int |
118 | 125 | sys.exit(exit_code) |
119 | | - except AssertionError as e: |
120 | | - print(f"Error: {str(e)} (programmer error, please file a bug)") |
121 | | - sys.exit(-1) |
122 | 126 | except UserError as e: |
123 | 127 | print(str(e)) |
124 | 128 | sys.exit(-1) |
| 129 | + # AssertionError and CFBSProgrammerError are not expected, print extra info: |
| 130 | + except AssertionError as e: |
| 131 | + tb = traceback.extract_tb(e.__traceback__) |
| 132 | + frame = tb[-1] |
| 133 | + this_file = pathlib.Path(__file__) |
| 134 | + cfbs_prefix = os.path.abspath(this_file.parent.parent.resolve()) |
| 135 | + filename = os.path.abspath(frame.filename) |
| 136 | + # Opportunistically cut off beginning of path if possible: |
| 137 | + if filename.startswith(cfbs_prefix): |
| 138 | + filename = filename[len(cfbs_prefix) :] |
| 139 | + if filename.startswith("/"): |
| 140 | + filename = filename[1:] |
| 141 | + line = frame.lineno |
| 142 | + # Avoid using frame.colno - it was not available in python 3.5, |
| 143 | + # and even in the latest version, it is not declared in the |
| 144 | + # docstring, so you will get linting warnings; |
| 145 | + # https://github.com/python/cpython/blob/v3.13.5/Lib/traceback.py#L276-L288 |
| 146 | + # column = frame.colno |
| 147 | + assertion = frame.line |
| 148 | + explanation = str(e) |
| 149 | + message = "Assertion failed - %s%s (%s:%s)" % ( |
| 150 | + assertion, |
| 151 | + (" - " + explanation) if explanation else "", |
| 152 | + filename, |
| 153 | + line, |
| 154 | + ) |
| 155 | + print("Error: " + message) |
| 156 | + except CFBSProgrammerError as e: |
| 157 | + print("Error: " + str(e)) |
| 158 | + print(" This is an unexpected error indicating a bug, please create a ticket at:") |
| 159 | + print(" https://northerntech.atlassian.net/") |
| 160 | + print( |
| 161 | + " (Rerun with CFBACKTRACE=1 in front of your command to show the full backtrace)" |
| 162 | + ) |
| 163 | + |
| 164 | + # TODO: Handle other exceptions |
| 165 | + return 1 |
125 | 166 |
|
126 | 167 |
|
127 | 168 | if __name__ == "__main__": |
|
0 commit comments