<commit_before>from email.mime.text import MIMEText
from functools import wraps
from itertools import chain
import smtplib
import argparse
import os
import socket
import subprocess
import sys
import time
import traceback
import errno

# TODO: refactor everything
# TODO: write docstrings

class donemail(object):
    def __init__(self, to, sender='', subject='', body=''):
        self._to = to
        self._subject = subject
        self._body = body
        self._sender = sender or '<EMAIL>(socket.<EMAIL>())

    def __call__(self, function):
        @wraps(function)
        def donemail_function(*args, **kwargs):
            call_str = '{function}({args})'.format(
                function=function.__name__, args=format_call_args(args, kwargs))
            raised = False
            try:
                result = function(*args, **kwargs)
                subject = '{} returned {!r}'.format(call_str, result)
                body = ''
            except Exception as exc:
                raised = True
                exc_type, exc_value, tb = sys.exc_info()
                subject = '{} raised {}'.format(call_str, exc_type.__name__)
                # tb_next is to hide the fact that we're inside of the decorator
                body = '\n'.join(traceback.format_exception(exc_type, exc_value, tb.tb_next))
            self.send_email(subject, body)
            if raised:
                # tb_next is to hide the fact that we're inside of the decorator
                raise exc_type, exc_value, tb.tb_next
            return result

        return donemail_function

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_val is not None:
            subject = 'block raised {}'.format(exc_type.__name__)
            body = '\n'.join(traceback.format_exception(exc_type, exc_val, exc_tb))
        else:
            subject = 'done'
            body = ''
        self.send_email(subject, body)

    def send_email(self, subject='', body=''):
        msg = MIMEText(self._body or body)
        # TODO: do we need both msg['To'] and sendmail(..., [self._to], ...)?
        msg['To'] = self._to
        msg['From'] = self._sender
        msg['Subject'] = self._subject or subject
        # TODO: handle bad connection gracefully
        s = smtplib.SMTP('localhost')
        try:
            s.sendmail(self._sender, [self._to], msg.as_string())
        finally:
            s.quit()


def main(cmd_args=None):
    parser = argparse.ArgumentParser(prog='donemail')
    parser.add_argument('email', type=email, help='address to send email to')
    parser.add_argument('--subject', help='subject of the email')
    parser.add_argument('--body', help='body of the email')
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('--pid', type=int, help='pid to wait for')
    group.add_argument('command', nargs='?', help='command to execute')
    parser.add_argument('command_args', nargs=argparse.REMAINDER,
                        help='command arguments')
    args = parser.parse_args(cmd_args)
<commit_msg>Move happy path of the decorator to try-else<commit_after>from email.mime.text import MIMEText
from functools import wraps
from itertools import chain
import smtplib
import argparse
import os
import socket
import subprocess
import sys
import time
import traceback
import errno

# TODO: refactor everything
# TODO: write docstrings

class donemail(object):
    def __init__(self, to, sender='', subject='', body=''):
        self._to = to
        self._subject = subject
        self._body = body
        self._sender = sender or '<EMAIL>())

    def __call__(self, function):
        @wraps(function)
        def donemail_function(*args, **kwargs):
            call_str = '{function}({args})'.format(
                function=function.__name__, args=format_call_args(args, kwargs))
            try:
                result = function(*args, **kwargs)
            except Exception as exc:
                exc_type, exc_value, tb = sys.exc_info()
                subject = '{} raised {}'.format(call_str, exc_type.__name__)
                # tb_next is to hide the fact that we're inside of the decorator
                body = '\n'.join(traceback.format_exception(exc_type, exc_value, tb.tb_next))
                self.send_email(subject, body)
                raise exc_type, exc_value, tb.tb_next
            else:
                self.send_email('{} returned {!r}'.format(call_str, result))
                return result
        return donemail_function

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_val is not None:
            subject = 'block raised {}'.format(exc_type.__name__)
            body = '\n'.join(traceback.format_exception(exc_type, exc_val, exc_tb))
            self.send_email(subject, body)
        else:
            self.send_email(subject='done')

    def send_email(self, subject='', body=''):
        msg = MIMEText(self._body or body)
        # TODO: do we need both msg['To'] and sendmail(..., [self._to], ...)?
        msg['To'] = self._to
        msg['From'] = self._sender
        msg['Subject'] = self._subject or subject
        # TODO: handle bad connection gracefully
        s = smtplib.SMTP('localhost')
        try:
            s.sendmail(self._sender, [self._to], msg.as_string())
        finally:
            s.quit()


def main(cmd_args=None):
    parser = argparse.ArgumentParser(prog='donemail')
    parser.add_argument('email', type=email, help='address to send email to')
    parser.add_argument('--subject', help='subject of the email')
    parser.add_argument('--body', help='body of the email')
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('--pid', type=int, help='pid to wait for')
    group.add_argument('command', nargs='?', help='command to execute')
    parser.add_argument('command_args', nargs=argparse.REMAINDER,
                        help='command arguments')
    args = parser.parse_args(cmd_args)

{'fraction_non_alphanumeric': 0.11729818780889621, 'fraction_numerical': 0.0, 'mean_word_length': 2.483075157773953, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 6, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '768377', 'n_tokens_mistral': 1761, 'n_tokens_neox': 1727, 'n_words': 461}