Skip to content

Commit

Permalink
Merge pull request #1566 from Sefaria/fix-pytest-error-log
Browse files Browse the repository at this point in the history
fix(pytest): fix broken pytest after changing index error to log
  • Loading branch information
akiva10b authored Aug 7, 2023
2 parents 1e061f9 + 7b2f2c5 commit 4dc194a
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ data:
structlog = None
import sefaria.system.logging as sefaria_logging
import os
import sys
import re
import json
Expand Down Expand Up @@ -328,3 +329,10 @@ data:
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
# Fail gracefully when decorator conditional_graceful_exception on function. This should be set to True on production
# Example: If a text or ref cannot be properly loaded, fail gracefully and let the server continue to run
FAIL_GRACEFULLY = True
if "pytest" in sys.modules:
FAIL_GRACEFULLY = False
9 changes: 8 additions & 1 deletion sefaria/local_settings_example.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# An example of settings needed in a local_settings.py file.
# copy this file to sefaria/local_settings.py and provide local info to run.
from datetime import timedelta
import sys
import structlog
import sefaria.system.logging as sefaria_logging
import os
Expand Down Expand Up @@ -308,4 +309,10 @@
)

SENTRY_DSN = None
CLIENT_SENTRY_DSN = None
CLIENT_SENTRY_DSN = None

# Fail gracefully when decorator conditional_graceful_exception on function. This should be set to True on production
# Example: If a text or ref cannot be properly loaded, fail gracefully and let the server continue to run
FAIL_GRACEFULLY = False
if "pytest" in sys.modules:
FAIL_GRACEFULLY = False
7 changes: 6 additions & 1 deletion sefaria/model/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

import structlog
from functools import reduce

from sefaria.system.decorators import conditional_graceful_exception


logger = structlog.get_logger(__name__)

try:
Expand Down Expand Up @@ -210,6 +214,7 @@ def _load_title_group(self):

self._process_terms()

@conditional_graceful_exception()
def _process_terms(self):
# To be called after raw data load
from sefaria.model import library
Expand All @@ -219,7 +224,7 @@ def _process_terms(self):
try:
self.title_group = term.title_group
except AttributeError:
logger.error(f"Term {self.sharedTitle} has no title_group")
raise IndexError("Failed to load term named {}.".format(self.sharedTitle))

def add_shared_term(self, term):
self.sharedTitle = term
Expand Down
30 changes: 30 additions & 0 deletions sefaria/system/decorators.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from functools import wraps, partial
from typing import Any

from django.http import HttpResponse, Http404
from django.template import RequestContext
Expand All @@ -13,6 +14,8 @@
import structlog
logger = structlog.get_logger(__name__)

from sefaria.settings import FAIL_GRACEFULLY


# TODO: we really need to fix the way we are using json responses. Django 1.7 introduced a baked in JsonResponse.
def json_response_decorator(func):
Expand Down Expand Up @@ -95,6 +98,33 @@ def wrapper(request, *args, **kwargs):
return wrapper


def conditional_graceful_exception(logLevel: str = "exception", exception_type: Exception = Exception) -> Any:
"""
Decorator that catches exceptions and logs them if FAIL_GRACEFULLY is True.
For instance, when loading the server on prod, we want to fail gracefully if a text or ref cannot be properly loaded.
However, when running text creation functions, we want to fail loudly so that we can fix the problem.
:param logLevel: "exception" or "warning"
:param return_value: function return value if exception is caught
:param exception_type: type of exception to catch
:return: return_value no error, if FAIL_GRACEFULLY is True log the error, otherwise raise exception
"""
def argumented_decorator(func):
@wraps(func)
def decorated_function(*args, **kwargs):
try:
return func(*args, **kwargs)
except exception_type as e:
if FAIL_GRACEFULLY:
if logger:
logger.exception(str(e)) if logLevel == "exception" else logger.warning(str(e))
else:
raise e
return decorated_function
return argumented_decorator


class memoized(object):
"""Decorator. Caches a function's return value each time it is called.
If called later with the same arguments, the cached value is returned
Expand Down

0 comments on commit 4dc194a

Please sign in to comment.