prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Error handler should not capture exceptions in DEBUG mode
  ## Description
  Based on the [conclusion from the discussion](https://github.com/centerofci/mathesar/discussions/1150).
  
  Improve backend's debugability via two changes:
  1. when backend is in debug-mode, add a pretty-printed stacktrace alongside the current error JSON;
  2. stop wrapping unhandled exceptions in 4999 errors.
  
  For some context, we're catching backend exceptions and wrapping them in custom errors so as to provide a uniform error interface to API clients. As it is now it has detrimental effects on debugging, because the resulting error messages don't include stacktraces. Above changes aim to address that.
  
  </issue>
  <code>
  [start of mathesar/exception_handlers.py]
  1 import warnings
  2 
  3 from django.conf import settings
  4 from django.db import IntegrityError as DjangoIntegrityError
  5 from django.utils.encoding import force_str
  6 from rest_framework.views import exception_handler
  7 from rest_framework_friendly_errors.settings import FRIENDLY_EXCEPTION_DICT
  8 from sqlalchemy.exc import IntegrityError, ProgrammingError
  9 
  10 from db.types.exceptions import UnsupportedTypeException
  11 from mathesar.api.exceptions.database_exceptions import (
  12     base_exceptions as base_api_exceptions,
  13     exceptions as database_api_exceptions,
  14 )
  15 from mathesar.api.exceptions.data_import_exceptions import exceptions as data_import_api_exceptions
  16 from mathesar.api.exceptions.error_codes import ErrorCodes
  17 from mathesar.api.exceptions.exception_mappers import integrity_error_mapper
  18 from mathesar.api.exceptions.generic_exceptions.base_exceptions import get_default_api_exception
  19 from mathesar.errors import URLDownloadError, URLNotReachable, URLInvalidContentTypeError
  20 
  21 exception_map = {
  22     IntegrityError: integrity_error_mapper,
  23     DjangoIntegrityError: integrity_error_mapper,
  24     UnsupportedTypeException: lambda exc: database_api_exceptions.UnsupportedTypeAPIException(exc),
  25     ProgrammingError: lambda exc: base_api_exceptions.ProgrammingAPIException(exc),
  26     URLDownloadError: lambda exc: data_import_api_exceptions.URLDownloadErrorAPIException(exc),
  27     URLNotReachable: lambda exc: data_import_api_exceptions.URLNotReachableAPIException(exc),
  28     URLInvalidContentTypeError: lambda exc: data_import_api_exceptions.URLInvalidContentTypeAPIException(exc)
  29 }
  30 
  31 
  32 def standardize_error_response(data):
  33     for index, error in enumerate(data):
  34         if 'code' in error:
  35             if error['code'] is not None and str(error['code']) != 'None':
  36                 data[index]['code'] = int(error['code'])
  37             else:
  38                 data[index]['code'] = ErrorCodes.UnknownError.value
  39         if 'detail' not in error:
  40             data[index]['detail'] = error.pop('details', {})
  41     return data
  42 
  43 
  44 def mathesar_exception_handler(exc, context):
  45     response = exception_handler(exc, context)
  46     # DRF default exception handler does not handle non API errors,
  47     # So we convert it to proper API response
  48     if not response:
  49         # Check if we have an equivalent API exception that is able to convert the exception to proper error
  50         mapped_exception_class = exception_map.get(exc.__class__)
  51         if mapped_exception_class is None:
  52             if getattr(settings, 'MATHESAR_CAPTURE_UNHANDLED_EXCEPTION', False):
  53                 mapped_exception_class = get_default_api_exception
  54             else:
  55                 raise exc
  56         api_exception = mapped_exception_class(exc)
  57         response = exception_handler(api_exception, context)
  58 
  59     if response is not None:
  60         # Check if conforms to the api spec
  61         if is_pretty(response.data):
  62             # Validation exception converts error_codes from integer to string, we need to convert it back into
  63             response.data = standardize_error_response(response.data)
  64             return response
  65         # Certain error raised by drf automatically don't follow the api error spec,
  66         # so we convert those into proper format
  67         else:
  68             warnings.warn("Error Response does not conform to the api spec. Please handle the exception properly")
  69             error_code = FRIENDLY_EXCEPTION_DICT.get(
  70                 exc.__class__.__name__, None
  71             )
  72             if error_code is None and settings.MATHESAR_MODE != "PRODUCTION":
  73                 raise Exception("Error Response does not conform to the api spec. Please handle the exception properly")
  74 
  75             # Errors thrown by drf automatically
  76             if isinstance(response.data, dict):
  77                 error_message = response.data.pop('detail', '')
  78 
  79                 response_data = {}
  80                 response_data['code'] = error_code
  81                 response_data['message'] = error_message
  82                 response_data['details'] = {'exception': force_str(exc)}
  83                 response.data = [response_data]
  84     return response
  85 
  86 
  87 def is_pretty(data):
  88     if not isinstance(data, list):
  89         return False
  90     else:
  91         for error_details in data:
  92             if (
  93                     not isinstance(error_details, dict)
  94                     or 'code' not in error_details
  95                     or 'message' not in error_details
  96             ):
  97                 return False
  98         return True
  99
  [end of mathesar/exception_handlers.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/mathesar/exception_handlers.py b/mathesar/exception_handlers.py
  --- a/mathesar/exception_handlers.py
  +++ b/mathesar/exception_handlers.py
  @@ -1,4 +1,5 @@
   import warnings
  +import traceback
   
   from django.conf import settings
   from django.db import IntegrityError as DjangoIntegrityError
  @@ -38,6 +39,9 @@
                   data[index]['code'] = ErrorCodes.UnknownError.value
           if 'detail' not in error:
               data[index]['detail'] = error.pop('details', {})
  +            # Adds a Stack-trace of the error for better debugging
  +        if settings.MATHESAR_MODE == 'DEVELOPMENT':
  +            data[index]['stacktrace'] = reformat_stacktrace(traceback.format_exc())
       return data
   
   
  @@ -80,6 +84,9 @@
                   response_data['code'] = error_code
                   response_data['message'] = error_message
                   response_data['details'] = {'exception': force_str(exc)}
  +                # Stacktrace should only be returned if MATHESAR_MODE is set to DEVELOPMENT
  +                if settings.MATHESAR_MODE == 'DEVELOPMENT':
  +                    response_data['stacktrace'] = reformat_stacktrace(traceback.format_exc())
                   response.data = [response_data]
       return response
   
  @@ -96,3 +103,8 @@
               ):
                   return False
           return True
  +
  +
  +def reformat_stacktrace(stacktrace):
  +    stacktrace_list = stacktrace.splitlines()[1:]
  +    return [f'{i + 1}. {line.strip()}' for i, line in enumerate(stacktrace_list)]
  
verification_info: {"golden_diff": "diff --git a/mathesar/exception_handlers.py b/mathesar/exception_handlers.py\n--- a/mathesar/exception_handlers.py\n+++ b/mathesar/exception_handlers.py\n@@ -1,4 +1,5 @@\n import warnings\n+import traceback\n \n from django.conf import settings\n from django.db import IntegrityError as DjangoIntegrityError\n@@ -38,6 +39,9 @@\n                 data[index]['code'] = ErrorCodes.UnknownError.value\n         if 'detail' not in error:\n             data[index]['detail'] = error.pop('details', {})\n+            # Adds a Stack-trace of the error for better debugging\n+        if settings.MATHESAR_MODE == 'DEVELOPMENT':\n+            data[index]['stacktrace'] = reformat_stacktrace(traceback.format_exc())\n     return data\n \n \n@@ -80,6 +84,9 @@\n                 response_data['code'] = error_code\n                 response_data['message'] = error_message\n                 response_data['details'] = {'exception': force_str(exc)}\n+                # Stacktrace should only be returned if MATHESAR_MODE is set to DEVELOPMENT\n+                if settings.MATHESAR_MODE == 'DEVELOPMENT':\n+                    response_data['stacktrace'] = reformat_stacktrace(traceback.format_exc())\n                 response.data = [response_data]\n     return response\n \n@@ -96,3 +103,8 @@\n             ):\n                 return False\n         return True\n+\n+\n+def reformat_stacktrace(stacktrace):\n+    stacktrace_list = stacktrace.splitlines()[1:]\n+    return [f'{i + 1}. {line.strip()}' for i, line in enumerate(stacktrace_list)]\n", "issue": "Error handler should not capture exceptions in DEBUG mode\n## Description\r\nBased on the [conclusion from the discussion](https://github.com/centerofci/mathesar/discussions/1150).\r\n\r\nImprove backend's debugability via two changes:\r\n1. when backend is in debug-mode, add a pretty-printed stacktrace alongside the current error JSON;\r\n2. stop wrapping unhandled exceptions in 4999 errors.\r\n\r\nFor some context, we're catching backend exceptions and wrapping them in custom errors so as to provide a uniform error interface to API clients. As it is now it has detrimental effects on debugging, because the resulting error messages don't include stacktraces. Above changes aim to address that.\n", "before_files": [{"content": "import warnings\n\nfrom django.conf import settings\nfrom django.db import IntegrityError as DjangoIntegrityError\nfrom django.utils.encoding import force_str\nfrom rest_framework.views import exception_handler\nfrom rest_framework_friendly_errors.settings import FRIENDLY_EXCEPTION_DICT\nfrom sqlalchemy.exc import IntegrityError, ProgrammingError\n\nfrom db.types.exceptions import UnsupportedTypeException\nfrom mathesar.api.exceptions.database_exceptions import (\n    base_exceptions as base_api_exceptions,\n    exceptions as database_api_exceptions,\n)\nfrom mathesar.api.exceptions.data_import_exceptions import exceptions as data_import_api_exceptions\nfrom mathesar.api.exceptions.error_codes import ErrorCodes\nfrom mathesar.api.exceptions.exception_mappers import integrity_error_mapper\nfrom mathesar.api.exceptions.generic_exceptions.base_exceptions import get_default_api_exception\nfrom mathesar.errors import URLDownloadError, URLNotReachable, URLInvalidContentTypeError\n\nexception_map = {\n    IntegrityError: integrity_error_mapper,\n    DjangoIntegrityError: integrity_error_mapper,\n    UnsupportedTypeException: lambda exc: database_api_exceptions.UnsupportedTypeAPIException(exc),\n    ProgrammingError: lambda exc: base_api_exceptions.ProgrammingAPIException(exc),\n    URLDownloadError: lambda exc: data_import_api_exceptions.URLDownloadErrorAPIException(exc),\n    URLNotReachable: lambda exc: data_import_api_exceptions.URLNotReachableAPIException(exc),\n    URLInvalidContentTypeError: lambda exc: data_import_api_exceptions.URLInvalidContentTypeAPIException(exc)\n}\n\n\ndef standardize_error_response(data):\n    for index, error in enumerate(data):\n        if 'code' in error:\n            if error['code'] is not None and str(error['code']) != 'None':\n                data[index]['code'] = int(error['code'])\n            else:\n                data[index]['code'] = ErrorCodes.UnknownError.value\n        if 'detail' not in error:\n            data[index]['detail'] = error.pop('details', {})\n    return data\n\n\ndef mathesar_exception_handler(exc, context):\n    response = exception_handler(exc, context)\n    # DRF default exception handler does not handle non API errors,\n    # So we convert it to proper API response\n    if not response:\n        # Check if we have an equivalent API exception that is able to convert the exception to proper error\n        mapped_exception_class = exception_map.get(exc.__class__)\n        if mapped_exception_class is None:\n            if getattr(settings, 'MATHESAR_CAPTURE_UNHANDLED_EXCEPTION', False):\n                mapped_exception_class = get_default_api_exception\n            else:\n                raise exc\n        api_exception = mapped_exception_class(exc)\n        response = exception_handler(api_exception, context)\n\n    if response is not None:\n        # Check if conforms to the api spec\n        if is_pretty(response.data):\n            # Validation exception converts error_codes from integer to string, we need to convert it back into\n            response.data = standardize_error_response(response.data)\n            return response\n        # Certain error raised by drf automatically don't follow the api error spec,\n        # so we convert those into proper format\n        else:\n            warnings.warn(\"Error Response does not conform to the api spec. Please handle the exception properly\")\n            error_code = FRIENDLY_EXCEPTION_DICT.get(\n                exc.__class__.__name__, None\n            )\n            if error_code is None and settings.MATHESAR_MODE != \"PRODUCTION\":\n                raise Exception(\"Error Response does not conform to the api spec. Please handle the exception properly\")\n\n            # Errors thrown by drf automatically\n            if isinstance(response.data, dict):\n                error_message = response.data.pop('detail', '')\n\n                response_data = {}\n                response_data['code'] = error_code\n                response_data['message'] = error_message\n                response_data['details'] = {'exception': force_str(exc)}\n                response.data = [response_data]\n    return response\n\n\ndef is_pretty(data):\n    if not isinstance(data, list):\n        return False\n    else:\n        for error_details in data:\n            if (\n                    not isinstance(error_details, dict)\n                    or 'code' not in error_details\n                    or 'message' not in error_details\n            ):\n                return False\n        return True\n", "path": "mathesar/exception_handlers.py"}]}