コード例 #1
0
ファイル: draftchanges.py プロジェクト: yanlimin9/critic
    def process(self, db, user, review_id, remark=None):
        cursor = db.cursor()
        profiler = profiling.Profiler()

        profiler.check("start")

        review = dbutils.Review.fromId(db, review_id)

        profiler.check("create review")

        was_accepted = review.state == "open" and review.accepted(db)

        profiler.check("accepted before")

        if remark and remark.strip():
            chain_id = createCommentChain(db, user, review, 'note')
            createComment(db, user, chain_id, remark, first=True)
        else:
            chain_id = None

        # Create a batch that groups all submitted changes together.
        cursor.execute(
            "INSERT INTO batches (review, uid, comment) VALUES (%s, %s, %s) RETURNING id",
            (review.id, user.id, chain_id))
        batch_id = cursor.fetchone()[0]

        profiler.check("batches++")

        # Reject all draft file approvals where the affected review file isn't in
        # the state it was in when the change was drafted.
        cursor.execute(
            """UPDATE reviewfilechanges
                             SET state='rejected',
                                 time=now()
                           WHERE uid=%s
                             AND state='draft'
                             AND file IN (SELECT id
                                            FROM reviewfiles
                                            JOIN reviewfilechanges ON (reviewfilechanges.file=reviewfiles.id)
                                           WHERE reviewfiles.review=%s
                                             AND reviewfilechanges.uid=%s
                                             AND reviewfilechanges.state='draft'
                                             AND reviewfilechanges.from_state!=reviewfiles.state)""",
            (user.id, review.id, user.id))

        profiler.check("reviewfilechanges reject state changes")

        # Then perform the remaining draft file approvals by updating the state of
        # the corresponding review file.
        cursor.execute(
            """UPDATE reviewfiles
                             SET state='reviewed',
                                 reviewer=%s,
                                 time=now()
                           WHERE review=%s
                             AND id IN (SELECT file
                                          FROM reviewfilechanges
                                         WHERE uid=%s
                                           AND state='draft'
                                           AND to_state='reviewed')""",
            (user.id, review.id, user.id))

        profiler.check("reviewfiles pending=>reviewed")

        # Then perform the remaining draft file disapprovals by updating the state
        # of the corresponding review file.
        cursor.execute(
            """UPDATE reviewfiles
                             SET state='pending',
                                 reviewer=NULL,
                                 time=now()
                           WHERE review=%s
                             AND id IN (SELECT file
                                          FROM reviewfilechanges
                                         WHERE uid=%s
                                           AND state='draft'
                                           AND to_state='pending')""",
            (review.id, user.id))

        profiler.check("reviewfiles reviewed=>pending")

        # Finally change the state of just performed approvals from draft to
        # 'performed'.
        cursor.execute(
            """UPDATE reviewfilechanges
                             SET batch=%s,
                                 state='performed',
                                 time=now()
                           WHERE uid=%s
                             AND state='draft'
                             AND file IN (SELECT id
                                            FROM reviewfiles
                                           WHERE reviewfiles.review=%s)""",
            (batch_id, user.id, review.id))

        profiler.check("reviewfilechanges draft=>performed")

        # Find all chains with draft comments being submitted that the current user
        # isn't associated with via the commentchainusers table, and associate the
        # user with them.
        cursor.execute(
            """SELECT DISTINCT commentchains.id, commentchainusers.uid IS NULL
                            FROM commentchains
                            JOIN comments ON (comments.chain=commentchains.id)
                 LEFT OUTER JOIN commentchainusers ON (commentchainusers.chain=commentchains.id
                                                   AND commentchainusers.uid=comments.uid)
                           WHERE commentchains.review=%s
                             AND comments.uid=%s
                             AND comments.state='draft'""",
            (review.id, user.id))

        for chain_id, need_associate in cursor.fetchall():
            if need_associate:
                cursor.execute(
                    "INSERT INTO commentchainusers (chain, uid) VALUES (%s, %s)",
                    (chain_id, user.id))

        profiler.check("commentchainusers++")

        # Find all chains with draft comments being submitted and add a record for
        # every user associated with the chain to read the comment.
        cursor.execute(
            """INSERT
                            INTO commentstoread (uid, comment)
                          SELECT commentchainusers.uid, comments.id
                            FROM commentchains, commentchainusers, comments
                           WHERE commentchains.review=%s
                             AND commentchainusers.chain=commentchains.id
                             AND commentchainusers.uid!=comments.uid
                             AND comments.chain=commentchains.id
                             AND comments.uid=%s
                             AND comments.state='draft'""",
            (review.id, user.id))

        profiler.check("commentstoread++")

        # Associate all users associated with a draft comment chain to
        # the review (if they weren't already.)
        cursor.execute(
            """SELECT DISTINCT commentchainusers.uid
                            FROM commentchains
                            JOIN commentchainusers ON (commentchainusers.chain=commentchains.id)
                 LEFT OUTER JOIN reviewusers ON (reviewusers.review=commentchains.review AND reviewusers.uid=commentchainusers.uid)
                           WHERE commentchains.review=%s
                             AND commentchains.uid=%s
                             AND commentchains.state='draft'
                             AND reviewusers.uid IS NULL""",
            (review.id, user.id))

        for (user_id, ) in cursor.fetchall():
            cursor.execute(
                "INSERT INTO reviewusers (review, uid) VALUES (%s, %s)",
                (review.id, user_id))

        # Change state on all draft commentchains by the user in the review to 'open'.
        cursor.execute(
            """UPDATE commentchains
                             SET batch=%s,
                                 state='open',
                                 time=now()
                           WHERE commentchains.review=%s
                             AND commentchains.uid=%s
                             AND commentchains.state='draft'""",
            (batch_id, review.id, user.id))

        profiler.check("commentchains draft=>open")

        # Reject all draft comment chain changes where the affected comment
        # chain isn't in the state it was in when the change was drafted, or has
        # been morphed into a note since the change was drafted.
        cursor.execute(
            """UPDATE commentchainchanges
                             SET state='rejected',
                                 time=now()
                           WHERE uid=%s
                             AND state='draft'
                             AND from_state IS NOT NULL
                             AND chain IN (SELECT id
                                             FROM commentchains
                                             JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id
                                                                      AND (commentchainchanges.from_state!=commentchains.state
                                                                        OR commentchainchanges.from_last_commit!=commentchains.last_commit
                                                                        OR commentchains.type!='issue'))
                                            WHERE commentchains.review=%s
                                              AND commentchainchanges.uid=%s
                                              AND commentchainchanges.state='draft')""",
            (user.id, review.id, user.id))

        profiler.check("commentchainchanges reject state changes")

        # Reject all draft comment chain changes where the affected comment chain
        # type isn't what it was in when the change was drafted.
        cursor.execute(
            """UPDATE commentchainchanges
                             SET state='rejected',
                                 time=now()
                           WHERE uid=%s
                             AND state='draft'
                             AND from_type IS NOT NULL
                             AND chain IN (SELECT id
                                             FROM commentchains
                                             JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id
                                                                      AND commentchainchanges.from_type!=commentchains.type)
                                            WHERE commentchains.review=%s
                                              AND commentchainchanges.uid=%s
                                              AND commentchainchanges.state='draft')""",
            (user.id, review.id, user.id))

        profiler.check("commentchainchanges reject type changes")

        # Reject all draft comment chain changes where the affected comment chain
        # addressed_by isn't what it was in when the change was drafted.
        cursor.execute(
            """UPDATE commentchainchanges
                             SET state='rejected',
                                 time=now()
                           WHERE uid=%s
                             AND state='draft'
                             AND from_addressed_by IS NOT NULL
                             AND chain IN (SELECT id
                                             FROM commentchains
                                             JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id
                                                                      AND commentchainchanges.from_addressed_by!=commentchains.addressed_by)
                                            WHERE commentchains.review=%s
                                              AND commentchainchanges.uid=%s
                                              AND commentchainchanges.state='draft')""",
            (user.id, review.id, user.id))

        profiler.check("commentchainchanges reject addressed_by changes")

        # Then perform the remaining draft comment chain changes by updating the
        # state of the corresponding comment chain.

        # Perform open->closed changes, including setting 'closed_by'.
        cursor.execute(
            """UPDATE commentchains
                             SET state='closed',
                                 closed_by=%s
                           WHERE review=%s
                             AND id IN (SELECT chain
                                          FROM commentchainchanges
                                         WHERE uid=%s
                                           AND state='draft'
                                           AND to_state='closed')""",
            (user.id, review.id, user.id))

        profiler.check("commentchains closed")

        # Perform (closed|addressed)->open changes, including resetting 'closed_by' and
        # 'addressed_by' to NULL.
        cursor.execute(
            """SELECT commentchainchanges.to_last_commit, commentchains.id
                            FROM commentchains
                            JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id)
                           WHERE commentchains.review=%s
                             AND commentchainchanges.uid=%s
                             AND commentchainchanges.state='draft'
                             AND commentchainchanges.to_state='open'""",
            (review.id, user.id))
        cursor.executemany(
            """UPDATE commentchains
                                 SET state='open',
                                     last_commit=%s,
                                     closed_by=NULL,
                                     addressed_by=NULL
                               WHERE id=%s""", cursor.fetchall())

        profiler.check("commentchains reopen")

        # Perform addressed->addressed changes, i.e. updating 'addressed_by'.
        cursor.execute(
            """SELECT commentchainchanges.to_addressed_by, commentchains.id
                            FROM commentchains
                            JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id)
                           WHERE commentchains.review=%s
                             AND commentchainchanges.uid=%s
                             AND commentchainchanges.state='draft'
                             AND commentchainchanges.to_addressed_by IS NOT NULL""",
            (review.id, user.id))
        cursor.executemany(
            """UPDATE commentchains
                                 SET addressed_by=%s
                               WHERE id=%s""", cursor.fetchall())

        profiler.check("commentchains reopen (partial)")

        # Perform type changes.
        cursor.execute(
            """SELECT commentchainchanges.to_type, commentchains.id
                            FROM commentchains
                            JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id)
                           WHERE commentchains.review=%s
                             AND commentchainchanges.uid=%s
                             AND commentchainchanges.state='draft'
                             AND commentchainchanges.to_type IS NOT NULL""",
            (review.id, user.id))
        cursor.executemany(
            """UPDATE commentchains
                                 SET type=%s
                               WHERE id=%s""", cursor.fetchall())

        profiler.check("commentchains type change")

        # Finally change the state of just performed changes from draft to
        # 'performed'.
        cursor.execute(
            """UPDATE commentchainchanges
                             SET batch=%s,
                                 state='performed',
                                 time=now()
                           WHERE uid=%s
                             AND state='draft'
                             AND chain IN (SELECT id
                                             FROM commentchains
                                            WHERE review=%s)""",
            (batch_id, user.id, review.id))

        profiler.check("commentchainchanges draft=>performed")

        # Change state on all draft commentchainlines by the user in the review to 'current'.
        cursor.execute(
            """UPDATE commentchainlines
                             SET state='current',
                                 time=now()
                           WHERE uid=%s
                             AND state='draft'
                             AND chain IN (SELECT id
                                             FROM commentchains
                                            WHERE review=%s)""",
            (user.id, review.id))

        profiler.check("commentchainlines draft=>current")

        # Change state on all draft comments by the user in the review to 'current'.
        cursor.execute(
            """UPDATE comments
                             SET batch=%s,
                                 state='current',
                                 time=now()
                           WHERE comments.uid=%s
                             AND comments.state='draft'
                             AND chain IN (SELECT id
                                             FROM commentchains
                                            WHERE review=%s)""",
            (batch_id, user.id, review.id))

        profiler.check("comments draft=>current")

        # Associate the submitting user with the review if he isn't already.
        cursor.execute("SELECT 1 FROM reviewusers WHERE review=%s AND uid=%s",
                       (review.id, user.id))
        if not cursor.fetchone():
            cursor.execute(
                "INSERT INTO reviewusers (review, uid) VALUES (%s, %s)",
                (review.id, user.id))

        generate_emails = profiler.start("generate emails")

        is_accepted = review.state == "open" and review.accepted(db)
        pending_mails = generateMailsForBatch(db,
                                              batch_id,
                                              was_accepted,
                                              is_accepted,
                                              profiler=profiler)

        generate_emails.stop()

        review.incrementSerial(db)
        db.commit()

        profiler.check("commit transaction")

        sendPendingMails(pending_mails)

        profiler.check("finished")

        if user.getPreference(db, "debug.profiling.submitChanges"):
            return OperationResult(batch_id=batch_id,
                                   serial=review.serial,
                                   profiling=profiler.output())
        else:
            return OperationResult(batch_id=batch_id, serial=review.serial)
コード例 #2
0
ファイル: draftchanges.py プロジェクト: zhanghaibo/critic
    def process(self, db, user, review_id, remark=None):
        cursor = db.cursor()
        profiler = profiling.Profiler()

        profiler.check("start")

        review = dbutils.Review.fromId(db, review_id, load_commits=False)

        profiler.check("create review")

        was_accepted = review.state == "open" and review.accepted(db)

        profiler.check("accepted before")

        if remark:
            chain_id = createCommentChain(db, user, review, 'note')
            createComment(db, user, chain_id, remark, first=True)
        else:
            chain_id = None

        # Create a batch that groups all submitted changes together.
        cursor.execute("INSERT INTO batches (review, uid, comment) VALUES (%s, %s, %s) RETURNING id", (review.id, user.id, chain_id))
        batch_id = cursor.fetchone()[0]

        profiler.check("batches++")

        # Reject all draft file approvals where the affected review file isn't in
        # the state it was in when the change was drafted.
        cursor.execute("""UPDATE reviewfilechanges
                             SET state='rejected',
                                 time=now()
                            FROM reviewfiles
                           WHERE reviewfiles.review=%s
                             AND reviewfiles.id=reviewfilechanges.file
                             AND reviewfilechanges.uid=%s
                             AND reviewfilechanges.state='draft'
                             AND reviewfilechanges.from!=reviewfiles.state""",
                       (review.id, user.id))

        profiler.check("reviewfilechanges reject state changes")

        # Then perform the remaining draft file approvals by updating the state of
        # the corresponding review file.
        cursor.execute("""UPDATE reviewfiles
                             SET state='reviewed',
                                 reviewer=reviewfilechanges.uid,
                                 time=now()
                            FROM reviewfilechanges
                           WHERE reviewfiles.review=%s
                             AND reviewfilechanges.uid=%s
                             AND reviewfilechanges.state='draft'
                             AND reviewfilechanges.file=reviewfiles.id
                             AND reviewfilechanges.from=reviewfiles.state
                             AND reviewfilechanges.to='reviewed'""",
                       (review.id, user.id))

        profiler.check("reviewfiles pending=>reviewed")

        # Then perform the remaining draft file disapprovals by updating the state
        # of the corresponding review file.
        cursor.execute("""UPDATE reviewfiles
                             SET state='pending',
                                 reviewer=NULL,
                                 time=now()
                            FROM reviewfilechanges
                           WHERE reviewfiles.review=%s
                             AND reviewfilechanges.uid=%s
                             AND reviewfilechanges.state='draft'
                             AND reviewfilechanges.file=reviewfiles.id
                             AND reviewfilechanges.from=reviewfiles.state
                             AND reviewfilechanges.to='pending'""",
                       (review.id, user.id))

        profiler.check("reviewfiles reviewed=>pending")

        # Finally change the state of just performed approvals from draft to
        # 'performed'.
        cursor.execute("""UPDATE reviewfilechanges
                             SET batch=%s,
                                 state='performed',
                                 time=now()
                            FROM reviewfiles
                           WHERE reviewfiles.review=%s
                             AND reviewfiles.id=reviewfilechanges.file
                             AND reviewfilechanges.uid=%s
                             AND reviewfilechanges.state='draft'
                             AND reviewfilechanges.to=reviewfiles.state""",
                       (batch_id, review.id, user.id))

        profiler.check("reviewfilechanges draft=>performed")

        # Find all chains with draft comments being submitted that the current user
        # isn't associated with via the commentchainusers table, and associate the
        # user with them.
        cursor.execute("""SELECT DISTINCT commentchains.id, commentchainusers.uid IS NULL
                            FROM commentchains
                            JOIN comments ON (comments.chain=commentchains.id)
                 LEFT OUTER JOIN commentchainusers ON (commentchainusers.chain=commentchains.id
                                                   AND commentchainusers.uid=comments.uid)
                           WHERE commentchains.review=%s
                             AND comments.uid=%s
                             AND comments.state='draft'""",
                       (review.id, user.id))

        for chain_id, need_associate in cursor.fetchall():
            if need_associate:
                cursor.execute("INSERT INTO commentchainusers (chain, uid) VALUES (%s, %s)", (chain_id, user.id))

        profiler.check("commentchainusers++")

        # Find all chains with draft comments being submitted and add a record for
        # every user associated with the chain to read the comment.
        cursor.execute("""INSERT
                            INTO commentstoread (uid, comment)
                          SELECT commentchainusers.uid, comments.id
                            FROM commentchains, commentchainusers, comments
                           WHERE commentchains.review=%s
                             AND commentchainusers.chain=commentchains.id
                             AND commentchainusers.uid!=comments.uid
                             AND comments.chain=commentchains.id
                             AND comments.uid=%s
                             AND comments.state='draft'""",
                       (review.id, user.id))

        profiler.check("commentstoread++")

        # Associate all users associated with a draft comment chain to
        # the review (if they weren't already.)
        cursor.execute("""SELECT DISTINCT commentchainusers.uid
                            FROM commentchains
                            JOIN commentchainusers ON (commentchainusers.chain=commentchains.id)
                 LEFT OUTER JOIN reviewusers ON (reviewusers.review=commentchains.review AND reviewusers.uid=commentchainusers.uid)
                           WHERE commentchains.review=%s
                             AND commentchains.uid=%s
                             AND commentchains.state='draft'
                             AND reviewusers.uid IS NULL""",
                       (review.id, user.id))

        for (user_id,) in cursor.fetchall():
            cursor.execute("INSERT INTO reviewusers (review, uid) VALUES (%s, %s)", (review.id, user_id))

        # Change state on all draft commentchains by the user in the review to 'open'.
        cursor.execute("""UPDATE commentchains
                             SET batch=%s,
                                 state='open',
                                 time=now()
                           WHERE commentchains.review=%s
                             AND commentchains.uid=%s
                             AND commentchains.state='draft'""",
                       (batch_id, review.id, user.id))

        profiler.check("commentchains draft=>open")

        # Reject all draft comment chain changes where the affected comment chain
        # isn't in the state it was in when the change was drafted.
        cursor.execute("""UPDATE commentchainchanges
                             SET state='rejected',
                                 time=now()
                            FROM commentchains
                           WHERE commentchains.review=%s
                             AND commentchainchanges.chain=commentchains.id
                             AND commentchainchanges.uid=%s
                             AND commentchainchanges.state='draft'
                             AND commentchainchanges.from_state IS NOT NULL
                             AND (commentchainchanges.from_state!=commentchains.state
                               OR commentchainchanges.from_last_commit!=commentchains.last_commit)""",
                       (review.id, user.id))

        profiler.check("commentchainchanges reject state changes")

        # Reject all draft comment chain changes where the affected comment chain
        # type isn't what it was in when the change was drafted.
        cursor.execute("""UPDATE commentchainchanges
                             SET state='rejected',
                                 time=now()
                            FROM commentchains
                           WHERE commentchains.review=%s
                             AND commentchainchanges.chain=commentchains.id
                             AND commentchainchanges.uid=%s
                             AND commentchainchanges.state='draft'
                             AND commentchainchanges.from_type IS NOT NULL
                             AND commentchainchanges.from_type!=commentchains.type""",
                       (review.id, user.id))

        profiler.check("commentchainchanges reject type changes")

        # Then perform the remaining draft comment chain changes by updating the
        # state of the corresponding comment chain.

        # Perform open->closed changes, including setting 'closed_by'.
        cursor.execute("""UPDATE commentchains
                             SET state='closed',
                                 closed_by=commentchainchanges.uid
                            FROM commentchainchanges
                           WHERE commentchains.review=%s
                             AND commentchainchanges.chain=commentchains.id
                             AND commentchainchanges.uid=%s
                             AND commentchainchanges.state='draft'
                             AND commentchainchanges.to_state='closed'""",
                       (review.id, user.id))

        profiler.check("commentchains closed")

        # Perform (closed|addressed)->open changes, including resetting 'closed_by' and
        # 'addressed_by' to NULL.
        cursor.execute("""UPDATE commentchains
                             SET state='open',
                                 last_commit=commentchainchanges.to_last_commit,
                                 closed_by=NULL,
                                 addressed_by=NULL
                            FROM commentchainchanges
                           WHERE commentchains.review=%s
                             AND commentchainchanges.chain=commentchains.id
                             AND commentchainchanges.uid=%s
                             AND commentchainchanges.state='draft'
                             AND commentchainchanges.to_state='open'""",
                       (review.id, user.id))

        profiler.check("commentchains reopen")

        # Perform type changes.
        cursor.execute("""UPDATE commentchains
                             SET type=commentchainchanges.to_type
                            FROM commentchainchanges
                           WHERE commentchains.review=%s
                             AND commentchainchanges.chain=commentchains.id
                             AND commentchainchanges.uid=%s
                             AND commentchainchanges.state='draft'
                             AND commentchainchanges.from_type=commentchains.type
                             AND commentchainchanges.to_type IS NOT NULL""",
                       (review.id, user.id))

        profiler.check("commentchains type change")

        # Finally change the state of just performed changes from draft to
        # 'performed'.
        cursor.execute("""UPDATE commentchainchanges
                             SET batch=%s,
                                 state='performed',
                                 time=now()
                            FROM commentchains
                           WHERE commentchains.review=%s
                             AND commentchainchanges.chain=commentchains.id
                             AND commentchainchanges.uid=%s
                             AND commentchainchanges.state='draft'""",
                       (batch_id, review.id, user.id))

        profiler.check("commentchainchanges draft=>performed")

        # Change state on all draft commentchainlines by the user in the review to 'current'.
        cursor.execute("""UPDATE commentchainlines
                             SET state='current',
                                 time=now()
                            FROM commentchains
                           WHERE commentchains.review=%s
                             AND commentchainlines.chain=commentchains.id
                             AND commentchainlines.uid=%s
                             AND commentchainlines.state='draft'""",
                       (review.id, user.id))

        profiler.check("commentchainlines draft=>current")

        # Change state on all draft comments by the user in the review to 'current'.
        cursor.execute("""UPDATE comments
                             SET batch=%s,
                                 state='current',
                                 time=now()
                            FROM commentchains
                           WHERE commentchains.review=%s
                             AND comments.chain=commentchains.id
                             AND comments.uid=%s
                             AND comments.state='draft'""",
                       (batch_id, review.id, user.id))

        profiler.check("comments draft=>current")

        # Associate the submitting user with the review if he isn't already.
        cursor.execute("SELECT 1 FROM reviewusers WHERE review=%s AND uid=%s", (review.id, user.id))
        if not cursor.fetchone():
            cursor.execute("INSERT INTO reviewusers (review, uid) VALUES (%s, %s)", (review.id, user.id))

        generate_emails = profiler.start("generate emails")

        is_accepted = review.state == "open" and review.accepted(db)
        pending_mails = generateMailsForBatch(db, batch_id, was_accepted, is_accepted, profiler=profiler)

        generate_emails.stop()

        review.incrementSerial(db)
        db.commit()

        profiler.check("commit transaction")

        sendPendingMails(pending_mails)

        profiler.check("finished")

        if user.getPreference(db, "debug.profiling.submitChanges"):
            return OperationResult(serial=review.serial, profiling=profiler.output())
        else:
            return OperationResult(serial=review.serial)
コード例 #3
0
ファイル: cli.py プロジェクト: Tigge/critic
        db.close()
        db = None

try:
    if len(sys.argv) > 1:
        init()

        for command in sys.argv[1:]:
            pending_mails = None

            if command == "generate-mails-for-batch":
                data = json_decode(sys.stdin.readline())
                batch_id = data["batch_id"]
                was_accepted = data["was_accepted"]
                is_accepted = data["is_accepted"]
                pending_mails = review_utils.generateMailsForBatch(db, batch_id, was_accepted, is_accepted)
            elif command == "generate-mails-for-assignments-transaction":
                data = json_decode(sys.stdin.readline())
                transaction_id = data["transaction_id"]
                pending_mails = review_utils.generateMailsForAssignmentsTransaction(db, transaction_id)
            else:
                print "unknown command: %s" % command
                sys.exit(1)

            if pending_mails is not None:
                sys.stdout.write(json_encode(pending_mails) + "\n")

        finish()
finally:
    abort()