mirror of
https://github.com/rio-labs/rio.git
synced 2026-02-09 23:29:30 -06:00
tracebacks of event handlers are now colorful
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
import functools
|
||||
import html
|
||||
import itertools
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
import types
|
||||
import typing as t
|
||||
from pathlib import Path
|
||||
@@ -14,8 +11,7 @@ import rio
|
||||
import rio.app_server.fastapi_server
|
||||
import rio.global_state
|
||||
|
||||
from ... import icon_registry, project_config
|
||||
from .. import nice_traceback
|
||||
from ... import icon_registry, nice_traceback, project_config
|
||||
|
||||
|
||||
class AppLoadError(Exception):
|
||||
@@ -27,24 +23,6 @@ class AppLoadError(Exception):
|
||||
return self.args[0]
|
||||
|
||||
|
||||
def remove_rio_internals_from_traceback(
|
||||
tb: list[traceback.FrameSummary],
|
||||
) -> t.Sequence[traceback.FrameSummary]:
|
||||
# Skip frames which are internal to rio (or libraries used by rio) until we
|
||||
# hit the first non-rio frame. Then include everything.
|
||||
rio_root = rio.__file__
|
||||
assert rio_root.endswith(os.sep + "__init__.py")
|
||||
rio_root = rio_root.removesuffix(os.sep + "__init__.py")
|
||||
|
||||
def predicate(frame: traceback.FrameSummary) -> bool:
|
||||
return (
|
||||
frame.filename.startswith((rio_root, "<frozen importlib"))
|
||||
or frame.filename == path_imports.__file__
|
||||
)
|
||||
|
||||
return list(itertools.dropwhile(predicate, tb))
|
||||
|
||||
|
||||
def make_traceback_html(
|
||||
*,
|
||||
err: t.Union[str, BaseException],
|
||||
@@ -58,7 +36,6 @@ def make_traceback_html(
|
||||
traceback_html = nice_traceback.format_exception_html(
|
||||
err,
|
||||
relpath=project_directory,
|
||||
preprocess_traceback=remove_rio_internals_from_traceback,
|
||||
)
|
||||
|
||||
return f"""
|
||||
|
||||
@@ -19,9 +19,8 @@ import rio.arequests as arequests
|
||||
import rio.cli
|
||||
import rio.snippets
|
||||
|
||||
from ... import project_config, utils, version
|
||||
from ... import nice_traceback, project_config, utils, version
|
||||
from ...debug.monkeypatches import apply_monkeypatches
|
||||
from .. import nice_traceback
|
||||
from . import (
|
||||
app_loading,
|
||||
file_watcher_worker,
|
||||
@@ -306,12 +305,9 @@ class Arbiter:
|
||||
revel.error(f"The app could not be loaded: {err}")
|
||||
|
||||
if err.__cause__ is not None:
|
||||
revel.print(
|
||||
nice_traceback.format_exception_revel(
|
||||
err.__cause__,
|
||||
relpath=self.proj.project_directory,
|
||||
preprocess_traceback=app_loading.remove_rio_internals_from_traceback,
|
||||
)
|
||||
nice_traceback.print_exception(
|
||||
err.__cause__,
|
||||
relpath=self.proj.project_directory,
|
||||
)
|
||||
|
||||
# If running in release mode, no further attempts to load the app
|
||||
@@ -454,7 +450,7 @@ class Arbiter:
|
||||
revel.error("The arbiter has crashed.")
|
||||
revel.error("This is a bug in Rio - please report it")
|
||||
print()
|
||||
revel.print(nice_traceback.format_exception_revel(err))
|
||||
nice_traceback.print_exception(err)
|
||||
|
||||
rio.cli._logger.exception("The arbiter has crashed")
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@ import rio
|
||||
import rio.app_server.fastapi_server
|
||||
import rio.cli
|
||||
|
||||
from ... import utils
|
||||
from .. import nice_traceback
|
||||
from ... import nice_traceback, utils
|
||||
from . import run_models
|
||||
|
||||
|
||||
@@ -110,8 +109,7 @@ class UvicornWorker:
|
||||
rio.cli._logger.exception(f"Uvicorn has crashed")
|
||||
|
||||
revel.error(f"Uvicorn has crashed:")
|
||||
print()
|
||||
revel.print(nice_traceback.format_exception_revel(err))
|
||||
nice_traceback.print_exception(err)
|
||||
self.push_event(run_models.StopRequested())
|
||||
finally:
|
||||
rio.cli._logger.debug("Requesting uvicorn to exit")
|
||||
|
||||
@@ -6,14 +6,33 @@ but is colored and just tweaked in general.
|
||||
import dataclasses
|
||||
import html
|
||||
import io
|
||||
import itertools
|
||||
import linecache
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
import typing as t
|
||||
from pathlib import Path
|
||||
|
||||
import path_imports
|
||||
import revel
|
||||
|
||||
import rio
|
||||
|
||||
__all__ = [
|
||||
"format_exception_revel",
|
||||
"format_exception_html",
|
||||
"FormatStyle",
|
||||
"print_exception",
|
||||
]
|
||||
|
||||
|
||||
def print_exception(
|
||||
error: BaseException, *, relpath: Path | None = None
|
||||
) -> None:
|
||||
text = format_exception_revel(error, relpath=relpath)
|
||||
revel.print(text)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class FormatStyle:
|
||||
@@ -60,6 +79,24 @@ def _handle_syntax_error(err: SyntaxError) -> traceback.FrameSummary:
|
||||
)
|
||||
|
||||
|
||||
def remove_rio_internals_from_traceback(
|
||||
tb: list[traceback.FrameSummary],
|
||||
) -> t.Sequence[traceback.FrameSummary]:
|
||||
# Skip frames which are internal to rio (or libraries used by rio) until we
|
||||
# hit the first non-rio frame. Then include everything.
|
||||
rio_root = rio.__file__
|
||||
assert rio_root.endswith(os.sep + "__init__.py")
|
||||
rio_root = rio_root.removesuffix(os.sep + "__init__.py")
|
||||
|
||||
def predicate(frame: traceback.FrameSummary) -> bool:
|
||||
return (
|
||||
frame.filename.startswith((rio_root, "<frozen importlib"))
|
||||
or frame.filename == path_imports.__file__
|
||||
)
|
||||
|
||||
return list(itertools.dropwhile(predicate, tb))
|
||||
|
||||
|
||||
def _format_single_exception_raw(
|
||||
out: t.IO[str],
|
||||
err: BaseException,
|
||||
@@ -67,9 +104,6 @@ def _format_single_exception_raw(
|
||||
include_header: bool,
|
||||
style: FormatStyle,
|
||||
relpath: Path | None,
|
||||
preprocess_traceback: t.Callable[
|
||||
[list[traceback.FrameSummary]], t.Sequence[traceback.FrameSummary]
|
||||
],
|
||||
) -> None:
|
||||
"""
|
||||
Format a single exception and write it to the output stream.
|
||||
@@ -81,7 +115,7 @@ def _format_single_exception_raw(
|
||||
if isinstance(err, SyntaxError):
|
||||
tb_list.append(_handle_syntax_error(err))
|
||||
|
||||
tb_list = preprocess_traceback(tb_list)
|
||||
tb_list = remove_rio_internals_from_traceback(tb_list)
|
||||
|
||||
# TODO: Add special handling for recursion errors. Instead of printing the
|
||||
# same frame 1000 times, print a message like "Last 5 frames repeated 200
|
||||
@@ -164,9 +198,6 @@ def format_exception_raw(
|
||||
*,
|
||||
style: FormatStyle,
|
||||
relpath: Path | None = None,
|
||||
preprocess_traceback: t.Callable[
|
||||
[list[traceback.FrameSummary]], t.Sequence[traceback.FrameSummary]
|
||||
] = lambda tb: tb,
|
||||
) -> str:
|
||||
"""
|
||||
Format an exception into a pretty string with the given style.
|
||||
@@ -198,7 +229,6 @@ def format_exception_raw(
|
||||
include_header=include_header,
|
||||
style=style,
|
||||
relpath=relpath,
|
||||
preprocess_traceback=preprocess_traceback,
|
||||
)
|
||||
|
||||
# Format
|
||||
@@ -211,9 +241,6 @@ def format_exception_revel(
|
||||
err: BaseException,
|
||||
*,
|
||||
relpath: Path | None = None,
|
||||
preprocess_traceback: t.Callable[
|
||||
[list[traceback.FrameSummary]], t.Sequence[traceback.FrameSummary]
|
||||
] = lambda tb: tb,
|
||||
) -> str:
|
||||
"""
|
||||
Format an exception using revel's styling.
|
||||
@@ -237,7 +264,6 @@ def format_exception_revel(
|
||||
err,
|
||||
style=style,
|
||||
relpath=relpath,
|
||||
preprocess_traceback=preprocess_traceback,
|
||||
)
|
||||
|
||||
|
||||
@@ -245,9 +271,6 @@ def format_exception_html(
|
||||
err: BaseException,
|
||||
*,
|
||||
relpath: Path | None = None,
|
||||
preprocess_traceback: t.Callable[
|
||||
[list[traceback.FrameSummary]], t.Sequence[traceback.FrameSummary]
|
||||
] = lambda tb: tb,
|
||||
) -> str:
|
||||
"""
|
||||
Format an exception into HTML with appropriate styling.
|
||||
@@ -271,7 +294,6 @@ def format_exception_html(
|
||||
err,
|
||||
style=style,
|
||||
relpath=relpath,
|
||||
preprocess_traceback=preprocess_traceback,
|
||||
)
|
||||
|
||||
# HTML-ify newlines
|
||||
@@ -16,7 +16,9 @@ import typing as t
|
||||
import weakref
|
||||
from datetime import tzinfo
|
||||
|
||||
import introspection
|
||||
import ordered_set
|
||||
import revel
|
||||
import starlette.datastructures
|
||||
import unicall
|
||||
import uniserde
|
||||
@@ -33,6 +35,7 @@ from . import (
|
||||
fills,
|
||||
global_state,
|
||||
inspection,
|
||||
nice_traceback,
|
||||
routing,
|
||||
serialization,
|
||||
session_attachments,
|
||||
@@ -864,9 +867,9 @@ window.resizeTo(screen.availWidth, screen.availHeight);
|
||||
await result
|
||||
|
||||
# Display and discard exceptions
|
||||
except Exception:
|
||||
print("Exception in event handler:")
|
||||
traceback.print_exc()
|
||||
except Exception as error:
|
||||
revel.error("Exception in event handler:")
|
||||
nice_traceback.print_exception(error)
|
||||
|
||||
if refresh:
|
||||
await self._refresh()
|
||||
@@ -2318,6 +2321,7 @@ window.history.{method}(null, "", {json.dumps(relative_url)})
|
||||
new_name="file_types",
|
||||
)
|
||||
@deprecations.deprecated(since="0.10", replacement=pick_file)
|
||||
@introspection.set_signature(pick_file)
|
||||
async def file_chooser(
|
||||
self,
|
||||
*args,
|
||||
|
||||
Reference in New Issue
Block a user