From b05b3197d81dc78950a2f8ec1148fd85ebc101f7 Mon Sep 17 00:00:00 2001 From: Fantix King Date: Thu, 17 Nov 2022 10:02:01 -0500 Subject: [PATCH 1/5] Output pretty error if possible Error position and hint are now included by default if present, colored if the stderr refers to a terminal, overridden by EDGEDB_PRETTY_ERROR. --- edgedb/errors/_base.py | 123 +++++++++++++++++++++++++++++++++-- edgedb/protocol/protocol.pyx | 2 + 2 files changed, 121 insertions(+), 4 deletions(-) diff --git a/edgedb/errors/_base.py b/edgedb/errors/_base.py index 9dec14f6c..d8f13045a 100644 --- a/edgedb/errors/_base.py +++ b/edgedb/errors/_base.py @@ -17,6 +17,11 @@ # +import io +import os +import sys +import unicodedata + __all__ = ( 'EdgeDBError', 'EdgeDBMessage', ) @@ -79,6 +84,7 @@ class EdgeDBErrorMeta(Meta): class EdgeDBError(Exception, metaclass=EdgeDBErrorMeta): _code = None + _query = None tags = frozenset() def __init__(self, *args, **kwargs): @@ -93,15 +99,25 @@ def _position(self): # not a stable API method return int(self._read_str_field(FIELD_POSITION_START, -1)) + @property + def _position_start(self): + # not a stable API method + return int(self._read_str_field(FIELD_CHARACTER_START, -1)) + + @property + def _position_end(self): + # not a stable API method + return int(self._read_str_field(FIELD_CHARACTER_END, -1)) + @property def _line(self): # not a stable API method - return int(self._read_str_field(FIELD_LINE, -1)) + return int(self._read_str_field(FIELD_LINE_START, -1)) @property def _col(self): # not a stable API method - return int(self._read_str_field(FIELD_COLUMN, -1)) + return int(self._read_str_field(FIELD_COLUMN_START, -1)) @property def _hint(self): @@ -127,6 +143,21 @@ def _from_code(code, *args, **kwargs): exc._code = code return exc + def __str__(self): + msg = super().__str__() + if self._query and self._position_start >= 0: + return _format_error( + msg, + self._query, + self._position_start, + max(1, self._position_end - self._position_start), + self._line if self._line > 0 else "?", + self._col if self._col > 0 else "?", + self._hint or "error", + ) + else: + return msg + def _lookup_cls(code: int, *, meta: type, default: type): try: @@ -180,6 +211,67 @@ def _severity_name(severity): return 'PANIC' +def _format_error(msg, query, start, offset, line, col, hint): + rv = io.StringIO() + rv.write(f"{BOLD}{msg}{ENDC}\n") + lines = query.splitlines(keepends=True) + num_len = len(str(len(lines))) + rv.write(f"{OKBLUE}{'':>{num_len}} ┌─{ENDC} query:{line}:{col}\n") + rv.write(f"{OKBLUE}{'':>{num_len}} │ {ENDC} \n") + for num, line in enumerate(lines): + length = len(line) + line = line.rstrip() # we'll use our own newline + if start >= length: + # skip lines before the error + start -= length + continue + + if start >= 0: + # Error starts in current line, write the line before the error + first_half = repr(line[:start])[1:-1] + line = line[start:] + length -= start + rv.write(f"{OKBLUE}{num + 1:>{num_len}} │ {ENDC}{first_half}") + start = _unicode_width(first_half) + else: + # Multi-line error continues + rv.write(f"{OKBLUE}{num + 1:>{num_len}} │ {FAIL}│ {ENDC}") + + if offset > length: + # Error is ending beyond current line + line = repr(line)[1:-1] + rv.write(f"{FAIL}{line}{ENDC}\n") + if start >= 0: + # Multi-line error starts + rv.write(f"{OKBLUE}{'':>{num_len}} │ " + f"{FAIL}╭─{'─' * start}^{ENDC} \n") + offset -= length + start = -1 # mark multi-line + else: + # Error is ending within current line + first_half = repr(line[:offset])[1:-1] + line = repr(line[offset:])[1:-1] + rv.write(f"{FAIL}{first_half}{ENDC}{line}\n") + size = _unicode_width(first_half) + if start >= 0: + # Mark single-line error + rv.write(f"{OKBLUE}{'':>{num_len}} │ {' ' * start}" + f"{FAIL}{'^' * size} {hint}{ENDC}") + else: + # End of multi-line error + rv.write(f"{OKBLUE}{'':>{num_len}} │ " + f"{FAIL}╰─{'─' * (size - 1)}^ {hint}{ENDC}") + break + return rv.getvalue() + + +def _unicode_width(text): + return sum( + 2 if unicodedata.east_asian_width(c) == "W" else 1 + for c in unicodedata.normalize("NFC", text) + ) + + FIELD_HINT = 0x_00_01 FIELD_DETAILS = 0x_00_02 FIELD_SERVER_TRACEBACK = 0x_01_01 @@ -187,8 +279,14 @@ def _severity_name(severity): # XXX: Subject to be changed/deprecated. FIELD_POSITION_START = 0x_FF_F1 FIELD_POSITION_END = 0x_FF_F2 -FIELD_LINE = 0x_FF_F3 -FIELD_COLUMN = 0x_FF_F4 +FIELD_LINE_START = 0x_FF_F3 +FIELD_COLUMN_START = 0x_FF_F4 +FIELD_UTF16_COLUMN_START = 0x_FF_F5 +FIELD_LINE_END = 0x_FF_F6 +FIELD_COLUMN_END = 0x_FF_F7 +FIELD_UTF16_COLUMN_END = 0x_FF_F8 +FIELD_CHARACTER_START = 0x_FF_F9 +FIELD_CHARACTER_END = 0x_FF_FA EDGE_SEVERITY_DEBUG = 20 @@ -198,3 +296,20 @@ def _severity_name(severity): EDGE_SEVERITY_ERROR = 120 EDGE_SEVERITY_FATAL = 200 EDGE_SEVERITY_PANIC = 255 + + +if os.getenv( + "EDGEDB_PRETTY_ERROR", "1" if sys.stderr.isatty() else "0" +).lower() in {"1", "yes", "y", "true", "t", "on"}: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKCYAN = '\033[96m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' +else: + HEADER = OKBLUE = OKCYAN = OKGREEN = WARNING = "" + FAIL = ENDC = BOLD = UNDERLINE = "" diff --git a/edgedb/protocol/protocol.pyx b/edgedb/protocol/protocol.pyx index 6bc5cd75f..770dcdd03 100644 --- a/edgedb/protocol/protocol.pyx +++ b/edgedb/protocol/protocol.pyx @@ -305,6 +305,7 @@ cdef class SansIOProtocol: elif mtype == ERROR_RESPONSE_MSG: exc = self.parse_error_message() + exc._query = query exc = self._amend_parse_error( exc, output_format, expect_one, required_one) @@ -435,6 +436,7 @@ cdef class SansIOProtocol: elif mtype == ERROR_RESPONSE_MSG: exc = self.parse_error_message() + exc._query = query if exc.get_code() == parameter_type_mismatch_code: if not isinstance(in_dc, NullCodec): buf = WriteBuffer.new() From 17c6997a7e96dc649c59fa8079f0dab511246af9 Mon Sep 17 00:00:00 2001 From: Fantix King Date: Thu, 17 Nov 2022 10:18:54 -0500 Subject: [PATCH 2/5] OS-specific line separator --- edgedb/errors/_base.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/edgedb/errors/_base.py b/edgedb/errors/_base.py index d8f13045a..5946e4288 100644 --- a/edgedb/errors/_base.py +++ b/edgedb/errors/_base.py @@ -213,14 +213,14 @@ def _severity_name(severity): def _format_error(msg, query, start, offset, line, col, hint): rv = io.StringIO() - rv.write(f"{BOLD}{msg}{ENDC}\n") + rv.write(f"{BOLD}{msg}{ENDC}{LINESEP}") lines = query.splitlines(keepends=True) num_len = len(str(len(lines))) - rv.write(f"{OKBLUE}{'':>{num_len}} ┌─{ENDC} query:{line}:{col}\n") - rv.write(f"{OKBLUE}{'':>{num_len}} │ {ENDC} \n") + rv.write(f"{OKBLUE}{'':>{num_len}} ┌─{ENDC} query:{line}:{col}{LINESEP}") + rv.write(f"{OKBLUE}{'':>{num_len}} │ {ENDC}{LINESEP}") for num, line in enumerate(lines): length = len(line) - line = line.rstrip() # we'll use our own newline + line = line.rstrip() # we'll use our own line separator if start >= length: # skip lines before the error start -= length @@ -240,18 +240,18 @@ def _format_error(msg, query, start, offset, line, col, hint): if offset > length: # Error is ending beyond current line line = repr(line)[1:-1] - rv.write(f"{FAIL}{line}{ENDC}\n") + rv.write(f"{FAIL}{line}{ENDC}{LINESEP}") if start >= 0: # Multi-line error starts rv.write(f"{OKBLUE}{'':>{num_len}} │ " - f"{FAIL}╭─{'─' * start}^{ENDC} \n") + f"{FAIL}╭─{'─' * start}^{ENDC}{LINESEP}") offset -= length start = -1 # mark multi-line else: # Error is ending within current line first_half = repr(line[:offset])[1:-1] line = repr(line[offset:])[1:-1] - rv.write(f"{FAIL}{first_half}{ENDC}{line}\n") + rv.write(f"{FAIL}{first_half}{ENDC}{line}{LINESEP}") size = _unicode_width(first_half) if start >= 0: # Mark single-line error @@ -313,3 +313,4 @@ def _unicode_width(text): else: HEADER = OKBLUE = OKCYAN = OKGREEN = WARNING = "" FAIL = ENDC = BOLD = UNDERLINE = "" +LINESEP = os.linesep From 0595db9a795293a89ed40da266730eb6059f0279 Mon Sep 17 00:00:00 2001 From: Fantix King Date: Thu, 17 Nov 2022 10:27:18 -0500 Subject: [PATCH 3/5] Safe lazy color initialization --- edgedb/errors/_base.py | 58 ++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/edgedb/errors/_base.py b/edgedb/errors/_base.py index 5946e4288..d10563981 100644 --- a/edgedb/errors/_base.py +++ b/edgedb/errors/_base.py @@ -212,12 +212,13 @@ def _severity_name(severity): def _format_error(msg, query, start, offset, line, col, hint): + _init_colors() rv = io.StringIO() rv.write(f"{BOLD}{msg}{ENDC}{LINESEP}") lines = query.splitlines(keepends=True) num_len = len(str(len(lines))) - rv.write(f"{OKBLUE}{'':>{num_len}} ┌─{ENDC} query:{line}:{col}{LINESEP}") - rv.write(f"{OKBLUE}{'':>{num_len}} │ {ENDC}{LINESEP}") + rv.write(f"{BLUE}{'':>{num_len}} ┌─{ENDC} query:{line}:{col}{LINESEP}") + rv.write(f"{BLUE}{'':>{num_len}} │ {ENDC}{LINESEP}") for num, line in enumerate(lines): length = len(line) line = line.rstrip() # we'll use our own line separator @@ -231,11 +232,11 @@ def _format_error(msg, query, start, offset, line, col, hint): first_half = repr(line[:start])[1:-1] line = line[start:] length -= start - rv.write(f"{OKBLUE}{num + 1:>{num_len}} │ {ENDC}{first_half}") + rv.write(f"{BLUE}{num + 1:>{num_len}} │ {ENDC}{first_half}") start = _unicode_width(first_half) else: # Multi-line error continues - rv.write(f"{OKBLUE}{num + 1:>{num_len}} │ {FAIL}│ {ENDC}") + rv.write(f"{BLUE}{num + 1:>{num_len}} │ {FAIL}│ {ENDC}") if offset > length: # Error is ending beyond current line @@ -243,7 +244,7 @@ def _format_error(msg, query, start, offset, line, col, hint): rv.write(f"{FAIL}{line}{ENDC}{LINESEP}") if start >= 0: # Multi-line error starts - rv.write(f"{OKBLUE}{'':>{num_len}} │ " + rv.write(f"{BLUE}{'':>{num_len}} │ " f"{FAIL}╭─{'─' * start}^{ENDC}{LINESEP}") offset -= length start = -1 # mark multi-line @@ -255,11 +256,11 @@ def _format_error(msg, query, start, offset, line, col, hint): size = _unicode_width(first_half) if start >= 0: # Mark single-line error - rv.write(f"{OKBLUE}{'':>{num_len}} │ {' ' * start}" + rv.write(f"{BLUE}{'':>{num_len}} │ {' ' * start}" f"{FAIL}{'^' * size} {hint}{ENDC}") else: # End of multi-line error - rv.write(f"{OKBLUE}{'':>{num_len}} │ " + rv.write(f"{BLUE}{'':>{num_len}} │ " f"{FAIL}╰─{'─' * (size - 1)}^ {hint}{ENDC}") break return rv.getvalue() @@ -272,6 +273,33 @@ def _unicode_width(text): ) +def _init_colors(): + global HEADER, BLUE, CYAN, GREEN, WARNING, FAIL, ENDC, BOLD, UNDERLINE + global COLOR_INITIALIZED + if COLOR_INITIALIZED: + return + COLOR_INITIALIZED = True + try: + use_color = os.getenv( + "EDGEDB_PRETTY_ERROR", "1" if sys.stderr.isatty() else "0" + ).lower() in {"1", "yes", "y", "true", "t", "on"} + except Exception: + use_color = False + if use_color: + HEADER = '\033[95m' + BLUE = '\033[94m' + CYAN = '\033[96m' + GREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + else: + HEADER = BLUE = CYAN = GREEN = WARNING = "" + FAIL = ENDC = BOLD = UNDERLINE = "" + + FIELD_HINT = 0x_00_01 FIELD_DETAILS = 0x_00_02 FIELD_SERVER_TRACEBACK = 0x_01_01 @@ -298,19 +326,5 @@ def _unicode_width(text): EDGE_SEVERITY_PANIC = 255 -if os.getenv( - "EDGEDB_PRETTY_ERROR", "1" if sys.stderr.isatty() else "0" -).lower() in {"1", "yes", "y", "true", "t", "on"}: - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKCYAN = '\033[96m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' - UNDERLINE = '\033[4m' -else: - HEADER = OKBLUE = OKCYAN = OKGREEN = WARNING = "" - FAIL = ENDC = BOLD = UNDERLINE = "" LINESEP = os.linesep +COLOR_INITIALIZED = False From 89399c1e982f6aa51ae618e08b4366d2da781064 Mon Sep 17 00:00:00 2001 From: Fantix King Date: Thu, 17 Nov 2022 10:33:14 -0500 Subject: [PATCH 4/5] Able to turn off error hint by EDGEDB_ERROR_HINT=off --- edgedb/errors/_base.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/edgedb/errors/_base.py b/edgedb/errors/_base.py index d10563981..c029e54c4 100644 --- a/edgedb/errors/_base.py +++ b/edgedb/errors/_base.py @@ -145,7 +145,7 @@ def _from_code(code, *args, **kwargs): def __str__(self): msg = super().__str__() - if self._query and self._position_start >= 0: + if SHOW_HINT and self._query and self._position_start >= 0: return _format_error( msg, self._query, @@ -281,8 +281,8 @@ def _init_colors(): COLOR_INITIALIZED = True try: use_color = os.getenv( - "EDGEDB_PRETTY_ERROR", "1" if sys.stderr.isatty() else "0" - ).lower() in {"1", "yes", "y", "true", "t", "on"} + "EDGEDB_PRETTY_ERROR", str(sys.stderr.isatty()) + ).lower() in ENV_ON_FLAGS except Exception: use_color = False if use_color: @@ -327,4 +327,6 @@ def _init_colors(): LINESEP = os.linesep +ENV_ON_FLAGS = {"1", "yes", "y", "true", "t", "on"} +SHOW_HINT = os.getenv("EDGEDB_ERROR_HINT", "1").lower() in ENV_ON_FLAGS COLOR_INITIALIZED = False From 0a5acae6ee634b1c299ba731bcef074fdd40b4f5 Mon Sep 17 00:00:00 2001 From: Fantix King Date: Fri, 18 Nov 2022 11:46:30 -0500 Subject: [PATCH 5/5] CRF: use consistent env var values Also: * Extracted color impl * Recover from error formatting failure --- edgedb/color.py | 60 +++++++++++++++++++++++ edgedb/errors/_base.py | 106 ++++++++++++++++++++--------------------- 2 files changed, 112 insertions(+), 54 deletions(-) create mode 100644 edgedb/color.py diff --git a/edgedb/color.py b/edgedb/color.py new file mode 100644 index 000000000..1e95c7bf1 --- /dev/null +++ b/edgedb/color.py @@ -0,0 +1,60 @@ +import os +import sys +import warnings + +COLOR = None + + +class Color: + HEADER = "" + BLUE = "" + CYAN = "" + GREEN = "" + WARNING = "" + FAIL = "" + ENDC = "" + BOLD = "" + UNDERLINE = "" + + +def get_color() -> Color: + global COLOR + + if COLOR is None: + COLOR = Color() + if type(USE_COLOR) is bool: + use_color = USE_COLOR + else: + try: + use_color = USE_COLOR() + except Exception: + use_color = False + if use_color: + COLOR.HEADER = '\033[95m' + COLOR.BLUE = '\033[94m' + COLOR.CYAN = '\033[96m' + COLOR.GREEN = '\033[92m' + COLOR.WARNING = '\033[93m' + COLOR.FAIL = '\033[91m' + COLOR.ENDC = '\033[0m' + COLOR.BOLD = '\033[1m' + COLOR.UNDERLINE = '\033[4m' + + return COLOR + + +try: + USE_COLOR = { + "default": lambda: sys.stderr.isatty(), + "auto": lambda: sys.stderr.isatty(), + "enabled": True, + "disabled": False, + }[ + os.getenv("EDGEDB_COLOR_OUTPUT", "default") + ] +except KeyError: + warnings.warn( + "EDGEDB_COLOR_OUTPUT can only be one of: " + "default, auto, enabled or disabled" + ) + USE_COLOR = False diff --git a/edgedb/errors/_base.py b/edgedb/errors/_base.py index c029e54c4..5756f264f 100644 --- a/edgedb/errors/_base.py +++ b/edgedb/errors/_base.py @@ -19,8 +19,9 @@ import io import os -import sys +import traceback import unicodedata +import warnings __all__ = ( 'EdgeDBError', 'EdgeDBMessage', @@ -146,15 +147,29 @@ def _from_code(code, *args, **kwargs): def __str__(self): msg = super().__str__() if SHOW_HINT and self._query and self._position_start >= 0: - return _format_error( - msg, - self._query, - self._position_start, - max(1, self._position_end - self._position_start), - self._line if self._line > 0 else "?", - self._col if self._col > 0 else "?", - self._hint or "error", - ) + try: + return _format_error( + msg, + self._query, + self._position_start, + max(1, self._position_end - self._position_start), + self._line if self._line > 0 else "?", + self._col if self._col > 0 else "?", + self._hint or "error", + ) + except Exception: + return "".join( + ( + msg, + LINESEP, + LINESEP, + "During formatting of the above exception, " + "another exception occurred:", + LINESEP, + LINESEP, + traceback.format_exc(), + ) + ) else: return msg @@ -212,13 +227,13 @@ def _severity_name(severity): def _format_error(msg, query, start, offset, line, col, hint): - _init_colors() + c = get_color() rv = io.StringIO() - rv.write(f"{BOLD}{msg}{ENDC}{LINESEP}") + rv.write(f"{c.BOLD}{msg}{c.ENDC}{LINESEP}") lines = query.splitlines(keepends=True) num_len = len(str(len(lines))) - rv.write(f"{BLUE}{'':>{num_len}} ┌─{ENDC} query:{line}:{col}{LINESEP}") - rv.write(f"{BLUE}{'':>{num_len}} │ {ENDC}{LINESEP}") + rv.write(f"{c.BLUE}{'':>{num_len}} ┌─{c.ENDC} query:{line}:{col}{LINESEP}") + rv.write(f"{c.BLUE}{'':>{num_len}} │ {c.ENDC}{LINESEP}") for num, line in enumerate(lines): length = len(line) line = line.rstrip() # we'll use our own line separator @@ -232,36 +247,36 @@ def _format_error(msg, query, start, offset, line, col, hint): first_half = repr(line[:start])[1:-1] line = line[start:] length -= start - rv.write(f"{BLUE}{num + 1:>{num_len}} │ {ENDC}{first_half}") + rv.write(f"{c.BLUE}{num + 1:>{num_len}} │ {c.ENDC}{first_half}") start = _unicode_width(first_half) else: # Multi-line error continues - rv.write(f"{BLUE}{num + 1:>{num_len}} │ {FAIL}│ {ENDC}") + rv.write(f"{c.BLUE}{num + 1:>{num_len}} │ {c.FAIL}│ {c.ENDC}") if offset > length: # Error is ending beyond current line line = repr(line)[1:-1] - rv.write(f"{FAIL}{line}{ENDC}{LINESEP}") + rv.write(f"{c.FAIL}{line}{c.ENDC}{LINESEP}") if start >= 0: # Multi-line error starts - rv.write(f"{BLUE}{'':>{num_len}} │ " - f"{FAIL}╭─{'─' * start}^{ENDC}{LINESEP}") + rv.write(f"{c.BLUE}{'':>{num_len}} │ " + f"{c.FAIL}╭─{'─' * start}^{c.ENDC}{LINESEP}") offset -= length start = -1 # mark multi-line else: # Error is ending within current line first_half = repr(line[:offset])[1:-1] line = repr(line[offset:])[1:-1] - rv.write(f"{FAIL}{first_half}{ENDC}{line}{LINESEP}") + rv.write(f"{c.FAIL}{first_half}{c.ENDC}{line}{LINESEP}") size = _unicode_width(first_half) if start >= 0: # Mark single-line error - rv.write(f"{BLUE}{'':>{num_len}} │ {' ' * start}" - f"{FAIL}{'^' * size} {hint}{ENDC}") + rv.write(f"{c.BLUE}{'':>{num_len}} │ {' ' * start}" + f"{c.FAIL}{'^' * size} {hint}{c.ENDC}") else: # End of multi-line error - rv.write(f"{BLUE}{'':>{num_len}} │ " - f"{FAIL}╰─{'─' * (size - 1)}^ {hint}{ENDC}") + rv.write(f"{c.BLUE}{'':>{num_len}} │ " + f"{c.FAIL}╰─{'─' * (size - 1)}^ {hint}{c.ENDC}") break return rv.getvalue() @@ -273,33 +288,6 @@ def _unicode_width(text): ) -def _init_colors(): - global HEADER, BLUE, CYAN, GREEN, WARNING, FAIL, ENDC, BOLD, UNDERLINE - global COLOR_INITIALIZED - if COLOR_INITIALIZED: - return - COLOR_INITIALIZED = True - try: - use_color = os.getenv( - "EDGEDB_PRETTY_ERROR", str(sys.stderr.isatty()) - ).lower() in ENV_ON_FLAGS - except Exception: - use_color = False - if use_color: - HEADER = '\033[95m' - BLUE = '\033[94m' - CYAN = '\033[96m' - GREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' - UNDERLINE = '\033[4m' - else: - HEADER = BLUE = CYAN = GREEN = WARNING = "" - FAIL = ENDC = BOLD = UNDERLINE = "" - - FIELD_HINT = 0x_00_01 FIELD_DETAILS = 0x_00_02 FIELD_SERVER_TRACEBACK = 0x_01_01 @@ -327,6 +315,16 @@ def _init_colors(): LINESEP = os.linesep -ENV_ON_FLAGS = {"1", "yes", "y", "true", "t", "on"} -SHOW_HINT = os.getenv("EDGEDB_ERROR_HINT", "1").lower() in ENV_ON_FLAGS -COLOR_INITIALIZED = False + +try: + SHOW_HINT = {"default": True, "enabled": True, "disabled": False}[ + os.getenv("EDGEDB_ERROR_HINT", "default") + ] +except KeyError: + warnings.warn( + "EDGEDB_ERROR_HINT can only be one of: default, enabled or disabled" + ) + SHOW_HINT = False + + +from edgedb.color import get_color