예제 #1
0
    def test_send_message(self):

        def test1(subject, message):
            self.assertRegexpMatches(subject,
                                     r"Soft Assert: \[\w+\] This should fail")
            things_that_should_show_up_in_message = [
                r"Message: This should fail",
                r"Traceback:",
                r'File ".*corehq/util/tests/test_soft_assert.py", line \d+, in test_send_message',
                r"self\.soft_assert\(False, 'This should fail'\)",
                r"Occurrences to date: 1",
            ]
            for thing in things_that_should_show_up_in_message:
                self.assertRegexpMatches(
                    message,
                    thing,
                    '{!r}\ndoes not match\n---\n{}---\n'.format(thing, message)
                )

        def test2(subject, message):
            self.assertRegexpMatches(
                subject,
                r'Soft Assert: \[\w+\] None',
            )
            things_that_should_show_up_in_message = [
                r"Message: None",
                r"Traceback:",
                r'File ".*corehq/util/tests/test_soft_assert.py", line \d+, in test_send_message',
                r"self\.soft_assert\(False\)",
                r"Occurrences to date: 1",
            ]
            for thing in things_that_should_show_up_in_message:
                self.assertRegexpMatches(
                    message,
                    thing,
                    '{!r}\ndoes not match\n---\n{}---\n'.format(thing, message)
                )

        tests = iter([test1, test2])

        def backend(subject, message):
            test_ = next(tests)
            test_(subject, message)

        self.soft_assert = SoftAssert(
            send=lambda info: _send_message(info, backend=backend),
        )
        # sent to test1
        self.soft_assert(False, 'This should fail')
        # sent to test2
        self.soft_assert(False)
예제 #2
0
def soft_assert(to=None,
                notify_admins=False,
                fail_if_debug=False,
                exponential_backoff=True,
                skip_frames=0,
                send_to_ops=True,
                log_to_file=False,
                include_breadcrumbs=False):
    """
    send an email with stack trace if assertion is not True

    Parameters:
    - to: Email address or list of email addresses that should receive the email
    - notify_admins: Send to all admins (using mail_admins) as well
    - fail_if_debug: if True, will fail hard (like a normal assert)
      if called in a developer environment (settings.DEBUG = True).
      If False, behavior will not depend on DEBUG setting.
    - exponential_backoff: if True, will only email every time an assert has
      failed 2**n times (1, 2, 4, 8, 16 times, etc.). If False, it will email
      every time.
    - skip_frames: number of frames of the traceback (from the bottom)
      to ignore. Useful if you're calling this from within a helper function.
      In that case if you want the call _to_ the helper function to be the
      last frame in the stack trace, then pass in skip_frames=1.
      This affects both the traceback in the email as the "key" by which
      errors are grouped.
    - log_to_file: write the message to a log file

    For the purposes of grouping errors into email threads,
    counting occurrences (sent in the email), and implementing exponential
    backoff, errors are always grouped by a key that varies on the
    last two frames (that aren't skipped by skip_frames) of the stack.

    Returns assertion. This makes it easy to do something like the following:

        if not soft_assert(to=['*****@*****.**']).call(
                isinstance(n, float), 'myfunction should be passed a float'):
            n = float(n)

    etc.

    """

    assert not isinstance(to, bytes)
    if isinstance(to, six.text_type):
        to = [to]

    if to is None:
        to = []

    if to is not None and send_to_ops and settings.SOFT_ASSERT_EMAIL:
        to = to + [settings.SOFT_ASSERT_EMAIL]

    def send_to_recipients(subject, message):
        from corehq.apps.hqwebapp.tasks import send_mail_async

        send_mail_async.delay(
            # this prefix is automatically added in mail_admins
            # but not send mail
            subject=settings.EMAIL_SUBJECT_PREFIX + subject,
            message=message,
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=to,
        )

    def send_to_admins(subject, message):
        from corehq.apps.hqwebapp.tasks import mail_admins_async

        if settings.DEBUG:
            return

        mail_admins_async.delay(
            subject=subject,
            message=message,
        )

    def write_to_log_file(subject, message):
        logger.debug(message)

    if not (notify_admins or to or log_to_file):
        raise ValueError('You must call soft assert with either a '
                         'list of recipients or notify_admins=True')

    def send(info):
        send_email = settings.ENABLE_SOFT_ASSERT_EMAILS
        if send_email and to:
            _send_message(info, backend=send_to_recipients)
        if send_email and notify_admins:
            _send_message(info, backend=send_to_admins)
        if log_to_file:
            _send_message(info, backend=write_to_log_file)

    if fail_if_debug:
        debug = settings.DEBUG
    else:
        debug = False

    return SoftAssert(debug=debug,
                      send=send,
                      use_exponential_backoff=exponential_backoff,
                      skip_frames=skip_frames,
                      include_breadcrumbs=include_breadcrumbs)
예제 #3
0
 def setUp(self):
     self.infos = []
     self.soft_assert = SoftAssert(send=self.send,
                                   use_exponential_backoff=False)
예제 #4
0
def soft_assert(to=None,
                notify_admins=False,
                fail_if_debug=False,
                exponential_backoff=True,
                skip_frames=0):
    """
    send an email with stack trace if assertion is not True

    Parameters:
    - to: Email address or list of email addresses that should receive the email
    - notify_admins: Send to all admins (using mail_admins) as well
    - fail_if_debug: if True, will fail hard (like a normal assert)
      if called in a developer environment (settings.DEBUG = True).
      If False, behavior will not depend on DEBUG setting.
    - exponential_backoff: if True, will only email every time an assert has
      failed 2**n times (1, 2, 4, 8, 16 times, etc.). If False, it will email
      every time.
    - skip_frames: number of frames of the traceback (from the bottom)
      to ignore. Useful if you're calling this from within a helper function.
      In that case if you want the call _to_ the helper function to be the
      last frame in the stack trace, then pass in skip_frames=1.
      This affects both the traceback in the email as the "key" by which
      errors are grouped.

    For the purposes of grouping errors into email threads,
    counting occurrences (sent in the email), and implementing exponential
    backoff, errors are always grouped by a key that varies on the
    last two frames (that aren't skipped by skip_frames) of the stack.

    Returns assertion. This makes it easy to do something like the following:

        if not soft_assert(to=['*****@*****.**']).call(
                isinstance(n, float), 'myfunction should be passed a float'):
            n = float(n)

    etc.

    """

    if isinstance(to, basestring):
        to = [to]

    def send_to_recipients(subject, message):
        send_mail(
            # this prefix is automatically added in mail_admins
            # but not send mail
            subject=settings.EMAIL_SUBJECT_PREFIX + subject,
            message=message,
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=to,
        )

    if to and notify_admins:

        def send(info):
            _send_message(info, backend=mail_admins)
            _send_message(info, backend=send_to_recipients)
    elif to:

        def send(info):
            _send_message(info, backend=send_to_recipients)
    elif notify_admins:

        def send(info):
            _send_message(info, backend=mail_admins)
    else:
        raise ValueError('You must call soft assert with either a '
                         'list of recipients or notify_admins=True')

    if fail_if_debug:
        debug = settings.DEBUG
    else:
        debug = False

    return SoftAssert(
        debug=debug,
        send=send,
        use_exponential_backoff=exponential_backoff,
        skip_frames=skip_frames,
    )