benedikt-bartscher / reflex

🕸 Web apps in pure Python 🐍
https://reflex.dev
Apache License 2.0
0 stars 1 forks source link

reflex exception handling #4

Open benedikt-bartscher opened 4 months ago

benedikt-bartscher commented 4 months ago

reflex exception handling

https://github.com/orgs/reflex-dev/discussions/2509 Currently reflex catches all exceptions during the execution of event handlers, and just uses print() to dump them to the console. Could we maybe add a config option to register a simple function which handles exceptions? Users could them f.e. ignore some exceptions, reraise others, or pass everything to a tracing system like sentry.

Please ensure everything is typed as much as possible and add some tests to verify everything works as expected.

Backend Exception handler

Frontend Exception handler

maximvlah commented 4 months ago

Hey Benedikt,

rx.Config now takes optional frontend_exception_handler and backend_exception_handler arguments. If not provided, all unhandled event exceptions will be printed to the console.

Both should obey 2 rules:

  1. Be a named function (no lambdas allowed) or a class method
  2. Take message and stack arguments as input and they both are of type str

All the other logic within the handlers can be arbitrary.

Backend Exception handler

Frontend Exception handler

I purposefully haven't enabled registering multiple handlers since the approach would have been too opinionated. I think it is better to give the user full freedom to write the exception handling logic which can be done from a single function.

Frontend exception handling works by calling addEvents with the exception message and stack trace within the window.onerror and window.onunhandledrejection

Problems

I have followed the contirbuting guide at https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md and did not encounter any problems except when running poetry run pyright reflex tests which caused:

WARNING: there is a new pyright version available (v1.1.334 -> v1.1.360).
Please install the new version or set PYRIGHT_PYTHON_FORCE_VERSION to `latest`

/Users/max/projects/reflex/reflex/__init__.py
  /Users/max/projects/reflex/reflex/__init__.py:202:1 - warning: Operation on "__all__" is not supported, so exported symbol list may be incorrect (reportUnsupportedDunderAll)
/Users/max/projects/reflex/reflex/state.py
  /Users/max/projects/reflex/reflex/state.py:1548:23 - error: Cannot access member "backend_exception_handler" for type "Config"
    Member "backend_exception_handler" is unknown (reportGeneralTypeIssues)
  /Users/max/projects/reflex/reflex/state.py:1549:24 - error: Cannot access member "backend_exception_handler" for type "Config"
    Member "backend_exception_handler" is unknown (reportGeneralTypeIssues)
  /Users/max/projects/reflex/reflex/state.py:1839:19 - error: Cannot access member "frontend_exception_handler" for type "Config"
    Member "frontend_exception_handler" is unknown (reportGeneralTypeIssues)
  /Users/max/projects/reflex/reflex/state.py:1840:20 - error: Cannot access member "frontend_exception_handler" for type "Config"
    Member "frontend_exception_handler" is unknown (reportGeneralTypeIssues)
/Users/max/projects/reflex/tests/test_config.py
  /Users/max/projects/reflex/tests/test_config.py:234:19 - error: Cannot access member "frontend_exception_handler" for type "Config"
    Member "frontend_exception_handler" is unknown (reportGeneralTypeIssues)
  /Users/max/projects/reflex/tests/test_config.py:245:19 - error: Cannot access member "backend_exception_handler" for type "Config"
    Member "backend_exception_handler" is unknown (reportGeneralTypeIssues)
6 errors, 1 warning, 0 informations 

I tried multipled things but failed to fix it. I would be very greatful if you could help me with that.

Looking forward to hearing your feedback!

Example

Here's an example app that you can use to play around with:

##rxconfig.py
import reflex as rx

def custom_frontend_exception_handler(
        message:str,
        stack:str,
        *args,
        **kwargs,
):
    print("Custom Frontend Error")
    print(stack)

def custom_backend_exception_handler(
        message:str,
        stack:str
):
    print("Custom Backend Error:")
    print(stack)

config = rx.Config(
    app_name="examples",
    frontend_exception_handler = custom_frontend_exception_handler,
    backend_exception_handler = custom_backend_exception_handler,
)
##examples.py
import reflex as rx

class State(rx.State):
    """The app state."""

def index() -> rx.Component:
    return rx.center(
        rx.button(
            "Induce Frontend Error",
            color_scheme="red",
            on_click=lambda: rx.event.call_script('error'),
        ),
        height="100vh",
    )

app = rx.App()
app.add_page(index)
benedikt-bartscher commented 4 months ago

Hey Maxim, thanks for adding the new tests. If you have time for it, I think you could already raise a Draft PR to reflex main. This way we can collect some feedback from the maintainers as well.

Just a small hint regarding your pyright issues: Take a look at reflex/config.pyi.

maximvlah commented 4 months ago

@benedikt-bartscher good morning, thanks a lot for the feedback. It has been a lot of fun tweaking with the library! I have just made some final changes. Please review them, and then I will submit the draft pr to reflex main.