We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
c7n-mailer-replay : Python3 - TypeError: write() argument must be str, not bytes
Using c7n-mailer-replay under Python3 gives the following trace:
```
Traceback (most recent call last):
  File "HOME/.pyenv/versions/cloud-custodian-3.6/bin/c7n-mailer-replay", line 11, in <module>
    load_entry_point('c7n-mailer', 'console_scripts', 'c7n-mailer-replay')()
  File "HOME/CLOUD_CUSTODIAN/SRC/tools/c7n_mailer/c7n_mailer/replay.py", line 134, in main
    json_dump_file=options.json_dump_file
  File "HOME/CLOUD_CUSTODIAN/SRC/tools/c7n_mailer/c7n_mailer/replay.py", line 46, in __init__
    fh.write(raw)
TypeError: write() argument must be str, not bytes
```

I had success with the following change:
```diff
diff --git a/tools/c7n_mailer/c7n_mailer/replay.py b/tools/c7n_mailer/c7n_mailer/replay.py
index b3f5456be..72f63332f 100644
--- a/tools/c7n_mailer/c7n_mailer/replay.py
+++ b/tools/c7n_mailer/c7n_mailer/replay.py
@@ -42,7 +42,7 @@ class MailerTester(object):
             logger.debug('base64-decoding and zlib decompressing message')
             raw = zlib.decompress(base64.b64decode(raw))
             if json_dump_file is not None:
-                with open(json_dump_file, 'w') as fh:
+                with open(json_dump_file, 'wb') as fh:
                     fh.write(raw)
         self.data = json.loads(raw)
         logger.debug('Loaded message JSON')
```

I believe it could be compatible with Python2 also, but it needs some testing.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `tools/c7n_mailer/c7n_mailer/replay.py`
Content:
```
1 """
2 Allow local testing of mailer and templates by replaying an SQS message.
3 
4 MAILER_FILE input is a file containing the exact base64-encoded, gzipped
5 data that's enqueued to SQS via :py:meth:`c7n.actions.Notify.send_sqs`.
6 
7 Alternatively, with -p|--plain specified, the file will be assumed to be
8 JSON data that can be loaded directly.
9 """
10 from __future__ import absolute_import, division, print_function, unicode_literals
11 
12 import argparse
13 import boto3
14 import os
15 import logging
16 import zlib
17 import base64
18 import json
19 
20 import jsonschema
21 from ruamel import yaml
22 
23 from c7n_mailer.utils import setup_defaults
24 from c7n_mailer.cli import CONFIG_SCHEMA
25 from .email_delivery import EmailDelivery
26 
27 logger = logging.getLogger(__name__)
28 
29 
30 class MailerTester(object):
31 
32     def __init__(self, msg_file, config, msg_plain=False, json_dump_file=None):
33         if not os.path.exists(msg_file):
34             raise RuntimeError("File does not exist: %s" % msg_file)
35         logger.debug('Reading message from: %s', msg_file)
36         with open(msg_file, 'r') as fh:
37             raw = fh.read()
38         logger.debug('Read %d byte message', len(raw))
39         if msg_plain:
40             raw = raw.strip()
41         else:
42             logger.debug('base64-decoding and zlib decompressing message')
43             raw = zlib.decompress(base64.b64decode(raw))
44             if json_dump_file is not None:
45                 with open(json_dump_file, 'w') as fh:
46                     fh.write(raw)
47         self.data = json.loads(raw)
48         logger.debug('Loaded message JSON')
49         self.config = config
50         self.session = boto3.Session()
51 
52     def run(self, dry_run=False, print_only=False):
53         emd = EmailDelivery(self.config, self.session, logger)
54         addrs_to_msgs = emd.get_to_addrs_email_messages_map(self.data)
55         logger.info('Would send email to: %s', addrs_to_msgs.keys())
56         if print_only:
57             mime = emd.get_mimetext_message(
58                 self.data, self.data['resources'], ['foo@example.com']
59             )
60             logger.info('Send mail with subject: "%s"', mime['Subject'])
61             print(mime.get_payload(None, True))
62             return
63         if dry_run:
64             for to_addrs, mimetext_msg in addrs_to_msgs.items():
65                 print('-> SEND MESSAGE TO: %s' % '; '.join(to_addrs))
66                 print(mimetext_msg.get_payload(None, True))
67             return
68         # else actually send the message...
69         for to_addrs, mimetext_msg in addrs_to_msgs.items():
70             logger.info('Actually sending mail to: %s', to_addrs)
71             emd.send_c7n_email(self.data, list(to_addrs), mimetext_msg)
72 
73 
74 def setup_parser():
75     parser = argparse.ArgumentParser('Test c7n-mailer templates and mail')
76     parser.add_argument('-c', '--config', required=True)
77     parser.add_argument('-d', '--dry-run', dest='dry_run', action='store_true',
78                         default=False,
79                         help='Log messages that would be sent, but do not send')
80     parser.add_argument('-T', '--template-print', dest='print_only',
81                         action='store_true', default=False,
82                         help='Just print rendered templates')
83     parser.add_argument('-t', '--templates', default=None, type=str,
84                         help='message templates folder location')
85     parser.add_argument('-p', '--plain', dest='plain', action='store_true',
86                         default=False,
87                         help='Expect MESSAGE_FILE to be a plain string, '
88                              'rather than the base64-encoded, gzipped SQS '
89                              'message format')
90     parser.add_argument('-j', '--json-dump-file', dest='json_dump_file',
91                         type=str, action='store', default=None,
92                         help='If dump JSON of MESSAGE_FILE to this path; '
93                              'useful to base64-decode and gunzip a message')
94     parser.add_argument('MESSAGE_FILE', type=str,
95                         help='Path to SQS message dump/content file')
96     return parser
97 
98 
99 def session_factory(config):
100     return boto3.Session(
101         region_name=config['region'],
102         profile_name=config.get('profile'))
103 
104 
105 def main():
106     parser = setup_parser()
107     options = parser.parse_args()
108 
109     module_dir = os.path.dirname(os.path.abspath(__file__))
110     default_templates = [
111         os.path.abspath(os.path.join(module_dir, 'msg-templates')),
112         os.path.abspath(os.path.join(module_dir, '..', 'msg-templates')),
113         os.path.abspath('.')
114     ]
115     templates = options.templates
116     if templates:
117         default_templates.append(
118             os.path.abspath(os.path.expanduser(os.path.expandvars(templates)))
119         )
120 
121     log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
122     logging.basicConfig(level=logging.DEBUG, format=log_format)
123     logging.getLogger('botocore').setLevel(logging.WARNING)
124 
125     with open(options.config) as fh:
126         config = yaml.load(fh.read(), Loader=yaml.SafeLoader)
127 
128     jsonschema.validate(config, CONFIG_SCHEMA)
129     setup_defaults(config)
130     config['templates_folders'] = default_templates
131 
132     tester = MailerTester(
133         options.MESSAGE_FILE, config, msg_plain=options.plain,
134         json_dump_file=options.json_dump_file
135     )
136     tester.run(options.dry_run, options.print_only)
137 
138 
139 if __name__ == '__main__':
140     main()
141
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/tools/c7n_mailer/c7n_mailer/replay.py b/tools/c7n_mailer/c7n_mailer/replay.py
--- a/tools/c7n_mailer/c7n_mailer/replay.py
+++ b/tools/c7n_mailer/c7n_mailer/replay.py
@@ -42,7 +42,7 @@
             logger.debug('base64-decoding and zlib decompressing message')
             raw = zlib.decompress(base64.b64decode(raw))
             if json_dump_file is not None:
-                with open(json_dump_file, 'w') as fh:
+                with open(json_dump_file, 'wb') as fh:  # pragma: no cover
                     fh.write(raw)
         self.data = json.loads(raw)
         logger.debug('Loaded message JSON')

{"golden_diff": "diff --git a/tools/c7n_mailer/c7n_mailer/replay.py b/tools/c7n_mailer/c7n_mailer/replay.py\n--- a/tools/c7n_mailer/c7n_mailer/replay.py\n+++ b/tools/c7n_mailer/c7n_mailer/replay.py\n@@ -42,7 +42,7 @@\n             logger.debug('base64-decoding and zlib decompressing message')\n             raw = zlib.decompress(base64.b64decode(raw))\n             if json_dump_file is not None:\n-                with open(json_dump_file, 'w') as fh:\n+                with open(json_dump_file, 'wb') as fh:  # pragma: no cover\n                     fh.write(raw)\n         self.data = json.loads(raw)\n         logger.debug('Loaded message JSON')\n", "issue": "c7n-mailer-replay : Python3 - TypeError: write() argument must be str, not bytes\nUsing c7n-mailer-replay under Python3 gives the following trace:\r\n```\r\nTraceback (most recent call last):\r\n  File \"HOME/.pyenv/versions/cloud-custodian-3.6/bin/c7n-mailer-replay\", line 11, in <module>\r\n    load_entry_point('c7n-mailer', 'console_scripts', 'c7n-mailer-replay')()\r\n  File \"HOME/CLOUD_CUSTODIAN/SRC/tools/c7n_mailer/c7n_mailer/replay.py\", line 134, in main\r\n    json_dump_file=options.json_dump_file\r\n  File \"HOME/CLOUD_CUSTODIAN/SRC/tools/c7n_mailer/c7n_mailer/replay.py\", line 46, in __init__\r\n    fh.write(raw)\r\nTypeError: write() argument must be str, not bytes\r\n```\r\n\r\nI had success with the following change:\r\n```diff\r\ndiff --git a/tools/c7n_mailer/c7n_mailer/replay.py b/tools/c7n_mailer/c7n_mailer/replay.py\r\nindex b3f5456be..72f63332f 100644\r\n--- a/tools/c7n_mailer/c7n_mailer/replay.py\r\n+++ b/tools/c7n_mailer/c7n_mailer/replay.py\r\n@@ -42,7 +42,7 @@ class MailerTester(object):\r\n             logger.debug('base64-decoding and zlib decompressing message')\r\n             raw = zlib.decompress(base64.b64decode(raw))\r\n             if json_dump_file is not None:\r\n-                with open(json_dump_file, 'w') as fh:\r\n+                with open(json_dump_file, 'wb') as fh:\r\n                     fh.write(raw)\r\n         self.data = json.loads(raw)\r\n         logger.debug('Loaded message JSON')\r\n```\r\n\r\nI believe it could be compatible with Python2 also, but it needs some testing.\n", "before_files": [{"content": "\"\"\"\nAllow local testing of mailer and templates by replaying an SQS message.\n\nMAILER_FILE input is a file containing the exact base64-encoded, gzipped\ndata that's enqueued to SQS via :py:meth:`c7n.actions.Notify.send_sqs`.\n\nAlternatively, with -p|--plain specified, the file will be assumed to be\nJSON data that can be loaded directly.\n\"\"\"\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport argparse\nimport boto3\nimport os\nimport logging\nimport zlib\nimport base64\nimport json\n\nimport jsonschema\nfrom ruamel import yaml\n\nfrom c7n_mailer.utils import setup_defaults\nfrom c7n_mailer.cli import CONFIG_SCHEMA\nfrom .email_delivery import EmailDelivery\n\nlogger = logging.getLogger(__name__)\n\n\nclass MailerTester(object):\n\n    def __init__(self, msg_file, config, msg_plain=False, json_dump_file=None):\n        if not os.path.exists(msg_file):\n            raise RuntimeError(\"File does not exist: %s\" % msg_file)\n        logger.debug('Reading message from: %s', msg_file)\n        with open(msg_file, 'r') as fh:\n            raw = fh.read()\n        logger.debug('Read %d byte message', len(raw))\n        if msg_plain:\n            raw = raw.strip()\n        else:\n            logger.debug('base64-decoding and zlib decompressing message')\n            raw = zlib.decompress(base64.b64decode(raw))\n            if json_dump_file is not None:\n                with open(json_dump_file, 'w') as fh:\n                    fh.write(raw)\n        self.data = json.loads(raw)\n        logger.debug('Loaded message JSON')\n        self.config = config\n        self.session = boto3.Session()\n\n    def run(self, dry_run=False, print_only=False):\n        emd = EmailDelivery(self.config, self.session, logger)\n        addrs_to_msgs = emd.get_to_addrs_email_messages_map(self.data)\n        logger.info('Would send email to: %s', addrs_to_msgs.keys())\n        if print_only:\n            mime = emd.get_mimetext_message(\n                self.data, self.data['resources'], ['foo@example.com']\n            )\n            logger.info('Send mail with subject: \"%s\"', mime['Subject'])\n            print(mime.get_payload(None, True))\n            return\n        if dry_run:\n            for to_addrs, mimetext_msg in addrs_to_msgs.items():\n                print('-> SEND MESSAGE TO: %s' % '; '.join(to_addrs))\n                print(mimetext_msg.get_payload(None, True))\n            return\n        # else actually send the message...\n        for to_addrs, mimetext_msg in addrs_to_msgs.items():\n            logger.info('Actually sending mail to: %s', to_addrs)\n            emd.send_c7n_email(self.data, list(to_addrs), mimetext_msg)\n\n\ndef setup_parser():\n    parser = argparse.ArgumentParser('Test c7n-mailer templates and mail')\n    parser.add_argument('-c', '--config', required=True)\n    parser.add_argument('-d', '--dry-run', dest='dry_run', action='store_true',\n                        default=False,\n                        help='Log messages that would be sent, but do not send')\n    parser.add_argument('-T', '--template-print', dest='print_only',\n                        action='store_true', default=False,\n                        help='Just print rendered templates')\n    parser.add_argument('-t', '--templates', default=None, type=str,\n                        help='message templates folder location')\n    parser.add_argument('-p', '--plain', dest='plain', action='store_true',\n                        default=False,\n                        help='Expect MESSAGE_FILE to be a plain string, '\n                             'rather than the base64-encoded, gzipped SQS '\n                             'message format')\n    parser.add_argument('-j', '--json-dump-file', dest='json_dump_file',\n                        type=str, action='store', default=None,\n                        help='If dump JSON of MESSAGE_FILE to this path; '\n                             'useful to base64-decode and gunzip a message')\n    parser.add_argument('MESSAGE_FILE', type=str,\n                        help='Path to SQS message dump/content file')\n    return parser\n\n\ndef session_factory(config):\n    return boto3.Session(\n        region_name=config['region'],\n        profile_name=config.get('profile'))\n\n\ndef main():\n    parser = setup_parser()\n    options = parser.parse_args()\n\n    module_dir = os.path.dirname(os.path.abspath(__file__))\n    default_templates = [\n        os.path.abspath(os.path.join(module_dir, 'msg-templates')),\n        os.path.abspath(os.path.join(module_dir, '..', 'msg-templates')),\n        os.path.abspath('.')\n    ]\n    templates = options.templates\n    if templates:\n        default_templates.append(\n            os.path.abspath(os.path.expanduser(os.path.expandvars(templates)))\n        )\n\n    log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'\n    logging.basicConfig(level=logging.DEBUG, format=log_format)\n    logging.getLogger('botocore').setLevel(logging.WARNING)\n\n    with open(options.config) as fh:\n        config = yaml.load(fh.read(), Loader=yaml.SafeLoader)\n\n    jsonschema.validate(config, CONFIG_SCHEMA)\n    setup_defaults(config)\n    config['templates_folders'] = default_templates\n\n    tester = MailerTester(\n        options.MESSAGE_FILE, config, msg_plain=options.plain,\n        json_dump_file=options.json_dump_file\n    )\n    tester.run(options.dry_run, options.print_only)\n\n\nif __name__ == '__main__':\n    main()\n", "path": "tools/c7n_mailer/c7n_mailer/replay.py"}], "after_files": [{"content": "\"\"\"\nAllow local testing of mailer and templates by replaying an SQS message.\n\nMAILER_FILE input is a file containing the exact base64-encoded, gzipped\ndata that's enqueued to SQS via :py:meth:`c7n.actions.Notify.send_sqs`.\n\nAlternatively, with -p|--plain specified, the file will be assumed to be\nJSON data that can be loaded directly.\n\"\"\"\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport argparse\nimport boto3\nimport os\nimport logging\nimport zlib\nimport base64\nimport json\n\nimport jsonschema\nfrom ruamel import yaml\n\nfrom c7n_mailer.utils import setup_defaults\nfrom c7n_mailer.cli import CONFIG_SCHEMA\nfrom .email_delivery import EmailDelivery\n\nlogger = logging.getLogger(__name__)\n\n\nclass MailerTester(object):\n\n    def __init__(self, msg_file, config, msg_plain=False, json_dump_file=None):\n        if not os.path.exists(msg_file):\n            raise RuntimeError(\"File does not exist: %s\" % msg_file)\n        logger.debug('Reading message from: %s', msg_file)\n        with open(msg_file, 'r') as fh:\n            raw = fh.read()\n        logger.debug('Read %d byte message', len(raw))\n        if msg_plain:\n            raw = raw.strip()\n        else:\n            logger.debug('base64-decoding and zlib decompressing message')\n            raw = zlib.decompress(base64.b64decode(raw))\n            if json_dump_file is not None:\n                with open(json_dump_file, 'wb') as fh:  # pragma: no cover\n                    fh.write(raw)\n        self.data = json.loads(raw)\n        logger.debug('Loaded message JSON')\n        self.config = config\n        self.session = boto3.Session()\n\n    def run(self, dry_run=False, print_only=False):\n        emd = EmailDelivery(self.config, self.session, logger)\n        addrs_to_msgs = emd.get_to_addrs_email_messages_map(self.data)\n        logger.info('Would send email to: %s', addrs_to_msgs.keys())\n        if print_only:\n            mime = emd.get_mimetext_message(\n                self.data, self.data['resources'], ['foo@example.com']\n            )\n            logger.info('Send mail with subject: \"%s\"', mime['Subject'])\n            print(mime.get_payload(None, True))\n            return\n        if dry_run:\n            for to_addrs, mimetext_msg in addrs_to_msgs.items():\n                print('-> SEND MESSAGE TO: %s' % '; '.join(to_addrs))\n                print(mimetext_msg.get_payload(None, True))\n            return\n        # else actually send the message...\n        for to_addrs, mimetext_msg in addrs_to_msgs.items():\n            logger.info('Actually sending mail to: %s', to_addrs)\n            emd.send_c7n_email(self.data, list(to_addrs), mimetext_msg)\n\n\ndef setup_parser():\n    parser = argparse.ArgumentParser('Test c7n-mailer templates and mail')\n    parser.add_argument('-c', '--config', required=True)\n    parser.add_argument('-d', '--dry-run', dest='dry_run', action='store_true',\n                        default=False,\n                        help='Log messages that would be sent, but do not send')\n    parser.add_argument('-T', '--template-print', dest='print_only',\n                        action='store_true', default=False,\n                        help='Just print rendered templates')\n    parser.add_argument('-t', '--templates', default=None, type=str,\n                        help='message templates folder location')\n    parser.add_argument('-p', '--plain', dest='plain', action='store_true',\n                        default=False,\n                        help='Expect MESSAGE_FILE to be a plain string, '\n                             'rather than the base64-encoded, gzipped SQS '\n                             'message format')\n    parser.add_argument('-j', '--json-dump-file', dest='json_dump_file',\n                        type=str, action='store', default=None,\n                        help='If dump JSON of MESSAGE_FILE to this path; '\n                             'useful to base64-decode and gunzip a message')\n    parser.add_argument('MESSAGE_FILE', type=str,\n                        help='Path to SQS message dump/content file')\n    return parser\n\n\ndef session_factory(config):\n    return boto3.Session(\n        region_name=config['region'],\n        profile_name=config.get('profile'))\n\n\ndef main():\n    parser = setup_parser()\n    options = parser.parse_args()\n\n    module_dir = os.path.dirname(os.path.abspath(__file__))\n    default_templates = [\n        os.path.abspath(os.path.join(module_dir, 'msg-templates')),\n        os.path.abspath(os.path.join(module_dir, '..', 'msg-templates')),\n        os.path.abspath('.')\n    ]\n    templates = options.templates\n    if templates:\n        default_templates.append(\n            os.path.abspath(os.path.expanduser(os.path.expandvars(templates)))\n        )\n\n    log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'\n    logging.basicConfig(level=logging.DEBUG, format=log_format)\n    logging.getLogger('botocore').setLevel(logging.WARNING)\n\n    with open(options.config) as fh:\n        config = yaml.load(fh.read(), Loader=yaml.SafeLoader)\n\n    jsonschema.validate(config, CONFIG_SCHEMA)\n    setup_defaults(config)\n    config['templates_folders'] = default_templates\n\n    tester = MailerTester(\n        options.MESSAGE_FILE, config, msg_plain=options.plain,\n        json_dump_file=options.json_dump_file\n    )\n    tester.run(options.dry_run, options.print_only)\n\n\nif __name__ == '__main__':\n    main()\n", "path": "tools/c7n_mailer/c7n_mailer/replay.py"}]}