{
    "file_diffs": [
        {
            "old_file_content": "#!/usr/bin/env python\n#\n# Copyright 2009 Facebook\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\n\"\"\"HTTP utility code shared by clients and servers.\"\"\"\n\nimport urllib\nimport re\n\nclass HTTPHeaders(dict):\n    \"\"\"A dictionary that maintains Http-Header-Case for all keys.\n\n    Supports multiple values per key via a pair of new methods,\n    add() and get_list().  The regular dictionary interface returns a single\n    value per key, with multiple values joined by a comma.\n\n    >>> h = HTTPHeaders({\"content-type\": \"text/html\"})\n    >>> h.keys()\n    ['Content-Type']\n    >>> h[\"Content-Type\"]\n    'text/html'\n\n    >>> h.add(\"Set-Cookie\", \"A=B\")\n    >>> h.add(\"Set-Cookie\", \"C=D\")\n    >>> h[\"set-cookie\"]\n    'A=B,C=D'\n    >>> h.get_list(\"set-cookie\")\n    ['A=B', 'C=D']\n\n    >>> for (k,v) in sorted(h.get_all()):\n    ...    print '%s: %s' % (k,v)\n    ...\n    Content-Type: text/html\n    Set-Cookie: A=B\n    Set-Cookie: C=D\n    \"\"\"\n    def __init__(self, *args, **kwargs):\n        # Don't pass args or kwargs to dict.__init__, as it will bypass\n        # our __setitem__\n        dict.__init__(self)\n        self._as_list = {}\n        self.update(*args, **kwargs)\n\n    # new public methods\n\n    def add(self, name, value):\n        \"\"\"Adds a new value for the given key.\"\"\"\n        norm_name = HTTPHeaders._normalize_name(name)\n        if norm_name in self:\n            # bypass our override of __setitem__ since it modifies _as_list\n            dict.__setitem__(self, norm_name, self[norm_name] + ',' + value)\n            self._as_list[norm_name].append(value)\n        else:\n            self[norm_name] = value\n\n    def get_list(self, name):\n        \"\"\"Returns all values for the given header as a list.\"\"\"\n        norm_name = HTTPHeaders._normalize_name(name)\n        return self._as_list.get(norm_name, [])\n\n    def get_all(self):\n        \"\"\"Returns an iterable of all (name, value) pairs.\n\n        If a header has multiple values, multiple pairs will be\n        returned with the same name.\n        \"\"\"\n        for name, list in self._as_list.iteritems():\n            for value in list:\n                yield (name, value)\n\n    def parse_line(self, line):\n        \"\"\"Updates the dictionary with a single header line.\n\n        >>> h = HTTPHeaders()\n        >>> h.parse_line(\"Content-Type: text/html\")\n        >>> h.get('content-type')\n        'text/html'\n        \"\"\"\n        name, value = line.split(\":\", 1)\n        self.add(name, value.strip())\n\n    @classmethod\n    def parse(cls, headers):\n        \"\"\"Returns a dictionary from HTTP header text.\n\n        >>> h = HTTPHeaders.parse(\"Content-Type: text/html\\\\r\\\\nContent-Length: 42\\\\r\\\\n\")\n        >>> sorted(h.iteritems())\n        [('Content-Length', '42'), ('Content-Type', 'text/html')]\n        \"\"\"\n        h = cls()\n        for line in headers.splitlines():\n            if line:\n                h.parse_line(line)\n        return h\n\n    # dict implementation overrides\n\n    def __setitem__(self, name, value):\n        norm_name = HTTPHeaders._normalize_name(name)\n        dict.__setitem__(self, norm_name, value)\n        self._as_list[norm_name] = [value]\n\n    def __getitem__(self, name):\n        return dict.__getitem__(self, HTTPHeaders._normalize_name(name))\n\n    def __delitem__(self, name):\n        norm_name = HTTPHeaders._normalize_name(name)\n        dict.__delitem__(self, norm_name)\n        del self._as_list[norm_name]\n\n    def get(self, name, default=None):\n        return dict.get(self, HTTPHeaders._normalize_name(name), default)\n\n    def update(self, *args, **kwargs):\n        # dict.update bypasses our __setitem__\n        for k, v in dict(*args, **kwargs).iteritems():\n            self[k] = v\n\n    _NORMALIZED_HEADER_RE = re.compile(r'^[A-Z0-9][a-z0-9]*(-[A-Z0-9][a-z0-9]*)*$')\n    _normalized_headers = {}\n\n    @staticmethod\n    def _normalize_name(name):\n        \"\"\"Converts a name to Http-Header-Case.\n\n        >>> HTTPHeaders._normalize_name(\"coNtent-TYPE\")\n        'Content-Type'\n        \"\"\"\n        try:\n            return HTTPHeaders._normalized_headers[name]\n        except KeyError:\n            if HTTPHeaders._NORMALIZED_HEADER_RE.match(name):\n                normalized = name\n            else:\n                normalized = \"-\".join([w.capitalize() for w in name.split(\"-\")])\n            HTTPHeaders._normalized_headers[name] = normalized\n            return normalized\n\n\ndef url_concat(url, args):\n    \"\"\"Concatenate url and argument dictionary regardless of whether\n    url has existing query parameters.\n\n    >>> url_concat(\"http://example.com/foo?a=b\", dict(c=\"d\"))\n    'http://example.com/foo?a=b&c=d'\n    \"\"\"\n    if url[-1] not in ('?', '&'):\n        url += '&' if ('?' in url) else '?'\n    return url + urllib.urlencode(args)\n\n\ndef doctests():\n    import doctest\n    return doctest.DocTestSuite()\n\nif __name__ == \"__main__\":\n    import doctest\n    doctest.testmod()\n",
            "new_file_content": "#!/usr/bin/env python\n#\n# Copyright 2009 Facebook\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\n\"\"\"HTTP utility code shared by clients and servers.\"\"\"\n\nimport urllib\nimport re\n\nclass HTTPHeaders(dict):\n    \"\"\"A dictionary that maintains Http-Header-Case for all keys.\n\n    Supports multiple values per key via a pair of new methods,\n    add() and get_list().  The regular dictionary interface returns a single\n    value per key, with multiple values joined by a comma.\n\n    >>> h = HTTPHeaders({\"content-type\": \"text/html\"})\n    >>> h.keys()\n    ['Content-Type']\n    >>> h[\"Content-Type\"]\n    'text/html'\n\n    >>> h.add(\"Set-Cookie\", \"A=B\")\n    >>> h.add(\"Set-Cookie\", \"C=D\")\n    >>> h[\"set-cookie\"]\n    'A=B,C=D'\n    >>> h.get_list(\"set-cookie\")\n    ['A=B', 'C=D']\n\n    >>> for (k,v) in sorted(h.get_all()):\n    ...    print '%s: %s' % (k,v)\n    ...\n    Content-Type: text/html\n    Set-Cookie: A=B\n    Set-Cookie: C=D\n    \"\"\"\n    def __init__(self, *args, **kwargs):\n        # Don't pass args or kwargs to dict.__init__, as it will bypass\n        # our __setitem__\n        dict.__init__(self)\n        self._as_list = {}\n        self.update(*args, **kwargs)\n\n    # new public methods\n\n    def add(self, name, value):\n        \"\"\"Adds a new value for the given key.\"\"\"\n        norm_name = HTTPHeaders._normalize_name(name)\n        if norm_name in self:\n            # bypass our override of __setitem__ since it modifies _as_list\n            dict.__setitem__(self, norm_name, self[norm_name] + ',' + value)\n            self._as_list[norm_name].append(value)\n        else:\n            self[norm_name] = value\n\n    def get_list(self, name):\n        \"\"\"Returns all values for the given header as a list.\"\"\"\n        norm_name = HTTPHeaders._normalize_name(name)\n        return self._as_list.get(norm_name, [])\n\n    def get_all(self):\n        \"\"\"Returns an iterable of all (name, value) pairs.\n\n        If a header has multiple values, multiple pairs will be\n        returned with the same name.\n        \"\"\"\n        for name, list in self._as_list.iteritems():\n            for value in list:\n                yield (name, value)\n\n    def parse_line(self, line):\n        \"\"\"Updates the dictionary with a single header line.\n\n        >>> h = HTTPHeaders()\n        >>> h.parse_line(\"Content-Type: text/html\")\n        >>> h.get('content-type')\n        'text/html'\n        \"\"\"\n        name, value = line.split(\":\", 1)\n        self.add(name, value.strip())\n\n    @classmethod\n    def parse(cls, headers):\n        \"\"\"Returns a dictionary from HTTP header text.\n\n        >>> h = HTTPHeaders.parse(\"Content-Type: text/html\\\\r\\\\nContent-Length: 42\\\\r\\\\n\")\n        >>> sorted(h.iteritems())\n        [('Content-Length', '42'), ('Content-Type', 'text/html')]\n        \"\"\"\n        h = cls()\n        for line in headers.splitlines():\n            if line:\n                h.parse_line(line)\n        return h\n\n    # dict implementation overrides\n\n    def __setitem__(self, name, value):\n        norm_name = HTTPHeaders._normalize_name(name)\n        dict.__setitem__(self, norm_name, value)\n        self._as_list[norm_name] = [value]\n\n    def __getitem__(self, name):\n        return dict.__getitem__(self, HTTPHeaders._normalize_name(name))\n\n    def __delitem__(self, name):\n        norm_name = HTTPHeaders._normalize_name(name)\n        dict.__delitem__(self, norm_name)\n        del self._as_list[norm_name]\n\n    def get(self, name, default=None):\n        return dict.get(self, HTTPHeaders._normalize_name(name), default)\n\n    def update(self, *args, **kwargs):\n        # dict.update bypasses our __setitem__\n        for k, v in dict(*args, **kwargs).iteritems():\n            self[k] = v\n\n    _NORMALIZED_HEADER_RE = re.compile(r'^[A-Z0-9][a-z0-9]*(-[A-Z0-9][a-z0-9]*)*$')\n    _normalized_headers = {}\n\n    @staticmethod\n    def _normalize_name(name):\n        \"\"\"Converts a name to Http-Header-Case.\n\n        >>> HTTPHeaders._normalize_name(\"coNtent-TYPE\")\n        'Content-Type'\n        \"\"\"\n        try:\n            return HTTPHeaders._normalized_headers[name]\n        except KeyError:\n            if HTTPHeaders._NORMALIZED_HEADER_RE.match(name):\n                normalized = name\n            else:\n                normalized = \"-\".join([w.capitalize() for w in name.split(\"-\")])\n            HTTPHeaders._normalized_headers[name] = normalized\n            return normalized\n\n\ndef url_concat(url, args):\n    \"\"\"Concatenate url and argument dictionary regardless of whether\n    url has existing query parameters.\n\n    >>> url_concat(\"http://example.com/foo?a=b\", dict(c=\"d\"))\n    'http://example.com/foo?a=b&c=d'\n    \"\"\"\n    if not args: return url\n    if url[-1] not in ('?', '&'):\n        url += '&' if ('?' in url) else '?'\n    return url + urllib.urlencode(args)\n\n\ndef doctests():\n    import doctest\n    return doctest.DocTestSuite()\n\nif __name__ == \"__main__\":\n    import doctest\n    doctest.testmod()\n",
            "header": {
                "file": {
                    "path": "tornado/httputil.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "b33ea33e",
                "new_commit_hash": "04a21fbc",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tornado/httputil.py"
            },
            "plus_file": {
                "path": "b/tornado/httputil.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 156,
                            "length": 6
                        },
                        "new_range": {
                            "start": 156,
                            "length": 7
                        },
                        "section": "def url_concat(url, args):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    >>> url_concat(\"http://example.com/foo?a=b\", dict(c=\"d\"))",
                                "type": "context"
                            },
                            {
                                "content": "    'http://example.com/foo?a=b&c=d'",
                                "type": "context"
                            },
                            {
                                "content": "    \"\"\"",
                                "type": "context"
                            },
                            {
                                "content": "    if not args: return url",
                                "type": "added"
                            },
                            {
                                "content": "    if url[-1] not in ('?', '&'):",
                                "type": "context"
                            },
                            {
                                "content": "        url += '&' if ('?' in url) else '?'",
                                "type": "context"
                            },
                            {
                                "content": "    return url + urllib.urlencode(args)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tornado/httputil.py",
                            "type": "function",
                            "name": "url_concat",
                            "content": "def url_concat(url, args):\n    \"\"\"Concatenate url and argument dictionary regardless of whether\n    url has existing query parameters.\n\n    >>> url_concat(\"http://example.com/foo?a=b\", dict(c=\"d\"))\n    'http://example.com/foo?a=b&c=d'\n    \"\"\"\n    if not args: return url\n    if url[-1] not in ('?', '&'):\n        url += '&' if ('?' in url) else '?'\n    return url + urllib.urlencode(args)",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 152,
                            "end_lineno": 162,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "#!/usr/bin/env python\n\nfrom tornado.httputil import url_concat\nimport unittest\n\n\nclass TestUrlConcat(unittest.TestCase):\n\n    def test_url_concat_no_query_params(self):\n        url = url_concat(\n                \"https://localhost/path\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=y&z=z\")\n\n    def test_url_concat_encode_args(self):\n        url = url_concat(\n                \"https://localhost/path\",\n                {'y':'/y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=%2Fy&z=z\")\n\n    def test_url_concat_trailing_q(self):\n        url = url_concat(\n                \"https://localhost/path?\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=y&z=z\")\n\n    def test_url_concat_q_with_no_trailing_amp(self):\n        url = url_concat(\n                \"https://localhost/path?x\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?x&y=y&z=z\")\n\n    def test_url_concat_trailing_amp(self):\n        url = url_concat(\n                \"https://localhost/path?x&\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?x&y=y&z=z\")\n\n    def test_url_concat_mult_params(self):\n        url = url_concat(\n                \"https://localhost/path?a=1&b=2\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?a=1&b=2&y=y&z=z\")\n",
            "new_file_content": "#!/usr/bin/env python\n\nfrom tornado.httputil import url_concat\nimport unittest\n\n\nclass TestUrlConcat(unittest.TestCase):\n\n    def test_url_concat_no_query_params(self):\n        url = url_concat(\n                \"https://localhost/path\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=y&z=z\")\n\n    def test_url_concat_encode_args(self):\n        url = url_concat(\n                \"https://localhost/path\",\n                {'y':'/y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=%2Fy&z=z\")\n\n    def test_url_concat_trailing_q(self):\n        url = url_concat(\n                \"https://localhost/path?\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=y&z=z\")\n\n    def test_url_concat_q_with_no_trailing_amp(self):\n        url = url_concat(\n                \"https://localhost/path?x\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?x&y=y&z=z\")\n\n    def test_url_concat_trailing_amp(self):\n        url = url_concat(\n                \"https://localhost/path?x&\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?x&y=y&z=z\")\n\n    def test_url_concat_mult_params(self):\n        url = url_concat(\n                \"https://localhost/path?a=1&b=2\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?a=1&b=2&y=y&z=z\")\n\n    def test_url_concat_no_params(self):\n        url = url_concat(\n            \"https://localhost/path?r=1&t=2\",\n            {},\n            )\n        self.assertEqual(url, \"https://localhost/path?r=1&t=2\")\n",
            "header": {
                "file": {
                    "path": "tornado/test/httputil_test.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "abad065e",
                "new_commit_hash": "d7329595",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tornado/test/httputil_test.py"
            },
            "plus_file": {
                "path": "b/tornado/test/httputil_test.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 47,
                            "length": 3
                        },
                        "new_range": {
                            "start": 47,
                            "length": 10
                        },
                        "section": "class TestUrlConcat(unittest.TestCase):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "                {'y':'y', 'z':'z'},",
                                "type": "context"
                            },
                            {
                                "content": "                )",
                                "type": "context"
                            },
                            {
                                "content": "        self.assertEqual(url, \"https://localhost/path?a=1&b=2&y=y&z=z\")",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def test_url_concat_no_params(self):",
                                "type": "added"
                            },
                            {
                                "content": "        url = url_concat(",
                                "type": "added"
                            },
                            {
                                "content": "            \"https://localhost/path?r=1&t=2\",",
                                "type": "added"
                            },
                            {
                                "content": "            {},",
                                "type": "added"
                            },
                            {
                                "content": "            )",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertEqual(url, \"https://localhost/path?r=1&t=2\")",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tornado/test/httputil_test.py",
                            "type": "class",
                            "name": "TestUrlConcat",
                            "content": "class TestUrlConcat(unittest.TestCase):\n\n    def test_url_concat_no_query_params(self):\n        url = url_concat(\n                \"https://localhost/path\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=y&z=z\")\n\n    def test_url_concat_encode_args(self):\n        url = url_concat(\n                \"https://localhost/path\",\n                {'y':'/y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=%2Fy&z=z\")\n\n    def test_url_concat_trailing_q(self):\n        url = url_concat(\n                \"https://localhost/path?\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=y&z=z\")\n\n    def test_url_concat_q_with_no_trailing_amp(self):\n        url = url_concat(\n                \"https://localhost/path?x\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?x&y=y&z=z\")\n\n    def test_url_concat_trailing_amp(self):\n        url = url_concat(\n                \"https://localhost/path?x&\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?x&y=y&z=z\")\n\n    def test_url_concat_mult_params(self):\n        url = url_concat(\n                \"https://localhost/path?a=1&b=2\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?a=1&b=2&y=y&z=z\")\n\n    def test_url_concat_no_params(self):\n        url = url_concat(\n            \"https://localhost/path?r=1&t=2\",\n            {},\n            )\n        self.assertEqual(url, \"https://localhost/path?r=1&t=2\")",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 7,
                            "end_lineno": 56,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "tornado/test/httputil_test.py",
                            "type": "method",
                            "name": "TestUrlConcat.test_url_concat_no_params",
                            "content": "class TestUrlConcat(unittest.TestCase):\n\n    def test_url_concat_no_query_params(self):\n        url = url_concat(\n                \"https://localhost/path\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=y&z=z\")\n\n    def test_url_concat_encode_args(self):\n        url = url_concat(\n                \"https://localhost/path\",\n                {'y':'/y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=%2Fy&z=z\")\n\n    def test_url_concat_trailing_q(self):\n        url = url_concat(\n                \"https://localhost/path?\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=y&z=z\")\n\n    def test_url_concat_q_with_no_trailing_amp(self):\n        url = url_concat(\n                \"https://localhost/path?x\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?x&y=y&z=z\")\n\n    def test_url_concat_trailing_amp(self):\n        url = url_concat(\n                \"https://localhost/path?x&\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?x&y=y&z=z\")\n\n    def test_url_concat_mult_params(self):\n        url = url_concat(\n                \"https://localhost/path?a=1&b=2\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?a=1&b=2&y=y&z=z\")\n\n    def test_url_concat_no_params(self):\n        url = url_concat(\n            \"https://localhost/path?r=1&t=2\",\n            {},\n            )\n        self.assertEqual(url, \"https://localhost/path?r=1&t=2\")",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 51,
                            "end_lineno": 56,
                            "parent": {
                                "file_name": "tornado/test/httputil_test.py",
                                "type": "class",
                                "name": "TestUrlConcat",
                                "content": "class TestUrlConcat(unittest.TestCase):\n\n    def test_url_concat_no_query_params(self):\n        url = url_concat(\n                \"https://localhost/path\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=y&z=z\")\n\n    def test_url_concat_encode_args(self):\n        url = url_concat(\n                \"https://localhost/path\",\n                {'y':'/y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=%2Fy&z=z\")\n\n    def test_url_concat_trailing_q(self):\n        url = url_concat(\n                \"https://localhost/path?\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=y&z=z\")\n\n    def test_url_concat_q_with_no_trailing_amp(self):\n        url = url_concat(\n                \"https://localhost/path?x\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?x&y=y&z=z\")\n\n    def test_url_concat_trailing_amp(self):\n        url = url_concat(\n                \"https://localhost/path?x&\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?x&y=y&z=z\")\n\n    def test_url_concat_mult_params(self):\n        url = url_concat(\n                \"https://localhost/path?a=1&b=2\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?a=1&b=2&y=y&z=z\")\n\n    def test_url_concat_no_params(self):\n        url = url_concat(\n            \"https://localhost/path?r=1&t=2\",\n            {},\n            )\n        self.assertEqual(url, \"https://localhost/path?r=1&t=2\")",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 7,
                                "end_lineno": 56,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "155c20fe890e1a71323ab8ac966f69027a512b0f^",
    "new_commit_hash": "155c20fe890e1a71323ab8ac966f69027a512b0f",
    "commit_message": "Don't add an extra delimter when no arguments are given",
    "commit_date": "2011-05-28T18:24:07-07:00",
    "metadata": {}
}
{
    "repo_name": "tornado",
    "new_commit_hash": "155c20fe890e1a71323ab8ac966f69027a512b0f",
    "test_file_codes": [
        "#!/usr/bin/env python\n\nfrom tornado.httputil import url_concat\nimport unittest\n\n\nclass TestUrlConcat(unittest.TestCase):\n\n    def test_url_concat_no_query_params(self):\n        url = url_concat(\n                \"https://localhost/path\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=y&z=z\")\n\n    def test_url_concat_encode_args(self):\n        url = url_concat(\n                \"https://localhost/path\",\n                {'y':'/y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=%2Fy&z=z\")\n\n    def test_url_concat_trailing_q(self):\n        url = url_concat(\n                \"https://localhost/path?\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?y=y&z=z\")\n\n    def test_url_concat_q_with_no_trailing_amp(self):\n        url = url_concat(\n                \"https://localhost/path?x\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?x&y=y&z=z\")\n\n    def test_url_concat_trailing_amp(self):\n        url = url_concat(\n                \"https://localhost/path?x&\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?x&y=y&z=z\")\n\n    def test_url_concat_mult_params(self):\n        url = url_concat(\n                \"https://localhost/path?a=1&b=2\",\n                {'y':'y', 'z':'z'},\n                )\n        self.assertEqual(url, \"https://localhost/path?a=1&b=2&y=y&z=z\")\n\n    def test_url_concat_no_params(self):\n        url = url_concat(\n            \"https://localhost/path?r=1&t=2\",\n            {},\n            )\n        self.assertEqual(url, \"https://localhost/path?r=1&t=2\")\n",
        "#!/usr/bin/env python\nimport unittest\nimport sys\nimport time\nimport traceback\nimport platform\nimport os\n\n\nclass PytestLikeResult(unittest.TestResult):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.start_time = None\n        # Store (test, outcome, info) for each test:\n        #   outcome ∈ { \"passed\", \"failed\", \"error\", \"skipped\" }\n        #   info can hold the exception or skip reason, etc.\n        self.results = []\n\n    def startTestRun(self):\n        \"\"\"Called once before all tests.\"\"\"\n        self.start_time = time.time()\n        # Print a header similar to pytest\n        print(\"=\" * 29 + \" test session starts \" + \"=\" * 29)\n        # You can customize the platform/versions/paths as you like:\n        print(\n            f\"platform {platform.system().lower()} -- Python {platform.python_version()}\"\n        )\n        print(f\"rootdir: {os.path.abspath(os.curdir)}\")\n\n    def stopTestRun(self):\n        \"\"\"Called once after all tests are done.\"\"\"\n        elapsed = time.time() - self.start_time\n        print()  # blank line after final dot\n\n        # Summarize\n        ntests = len(self.results)\n        print(f\"collected {ntests} items\")\n\n        # Show details of failures/errors\n        failures = [r for r in self.results if r[1] == \"failed\"]\n        errors = [r for r in self.results if r[1] == \"error\"]\n\n        if failures:\n            print(\"\\n\" + \"=\" * 35 + \" FAILURES \" + \"=\" * 35)\n            for test, outcome, err_info in failures:\n                test = test.split(\"::\")\n                if len(test) == 3:\n                    test = f\"{test[1]}.{test[2]}\"\n                else:\n                    test = f\"{test[1]}\"\n                print(f\"__________ {test} __________\")\n                self._print_traceback(err_info)\n        if errors:\n            print(\"\\n\" + \"=\" * 35 + \" ERRORS \" + \"=\" * 37)\n            for test, outcome, err_info in errors:\n                test = test.split(\"::\")\n                if len(test) == 3:\n                    test = f\"{test[1]}.{test[2]}\"\n                else:\n                    test = f\"{test[1]}\"\n                print(f\"__________ {test} __________\")\n                self._print_traceback(err_info)\n\n        # Optional short summary lines for passes/fails/skips\n        print(\"\\n==================== short test summary info ====================\")\n        for test, outcome, info in self.results:\n            if outcome in (\"passed\", \"failed\", \"error\", \"skipped\"):\n                print(f\"{outcome.upper()} {test}\")\n\n        # Final one-line summary: “1 failed, 1 errors, 10 passed in 2.04s”\n        passed_count = sum(1 for r in self.results if r[1] == \"passed\")\n        failed_count = len(failures)\n        error_count = len(errors)\n        skipped_count = sum(1 for r in self.results if r[1] == \"skipped\")\n\n        summary_bits = []\n        if failed_count:\n            summary_bits.append(f\"{failed_count} failed\")\n        if error_count:\n            summary_bits.append(f\"{error_count} error{'s' if error_count != 1 else ''}\")\n        if passed_count:\n            summary_bits.append(f\"{passed_count} passed\")\n        if skipped_count:\n            summary_bits.append(f\"{skipped_count} skipped\")\n\n        summary_str = \", \".join(summary_bits) if summary_bits else \"no tests run\"\n        print(\n            f\"=================== {summary_str} in {elapsed:.2f}s ===================\"\n        )\n\n    def startTest(self, test):\n        \"\"\"Called right before each test method.\"\"\"\n        super().startTest(test)\n        # Print a dot or some indicator\n        print(\".\", end=\"\", flush=True)\n\n    def addSuccess(self, test):\n        super().addSuccess(test)\n        self.results.append((self._test_id(test), \"passed\", None))\n\n    def addFailure(self, test, err):\n        super().addFailure(test, err)\n        self.results.append((self._test_id(test), \"failed\", err))\n\n    def addError(self, test, err):\n        super().addError(test, err)\n        self.results.append((self._test_id(test), \"error\", err))\n\n    def addSkip(self, test, reason):\n        super().addSkip(test, reason)\n        self.results.append((self._test_id(test), \"skipped\", reason))\n\n    def _print_traceback(self, err_info):\n        \"\"\"Helper to print the traceback like pytest does.\"\"\"\n        if isinstance(err_info, tuple) and len(err_info) == 3:\n            exc_type, exc_value, tb = err_info\n            traceback.print_exception(exc_type, exc_value, tb, file=sys.stdout)\n        else:\n            print(str(err_info))\n\n    def _test_id(self, test):\n        try:\n            return f\"{test.__class__.__module__}::{test.__class__.__name__}::{test._testMethodName}\"\n        except AttributeError:\n            # Fallback for _ErrorHolder objects\n            return f\"{test.__class__.__module__}::{test.__class__.__name__}\"\n\n\nclass PytestLikeRunner(unittest.TextTestRunner):\n    \"\"\"A custom TextTestRunner that uses the PytestLikeResult.\"\"\"\n\n    resultclass = PytestLikeResult\n\n    def run(self, test):\n        result = self._makeResult()\n        # Overriding run to ensure we call startTestRun/stopTestRun\n        result.startTestRun()\n        test(result)\n        result.stopTestRun()\n        return result\n\n    def tearDown(self):\n        # Reset signal handlers\n        from twisted.internet import reactor\n\n        reactor.removeAll()\n        reactor.stop()\n        super().tearDown()\n\n\ndef main():\n    loader = unittest.TestLoader()\n    suite = loader.discover(\".\", pattern=\"test_*.py\")  # discover tests in r2e_tests/\n    runner = PytestLikeRunner(verbosity=0)\n    result = runner.run(suite)\n    # Exit with code 0 if all tests passed/skipped, 1 if there were fails/errors\n    sys.exit(not result.wasSuccessful())\n\n\nif __name__ == \"__main__\":\n    main()\n"
    ],
    "test_file_names": [
        "test_1.py",
        "tornado_unittest_runner.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nerror: The build backend returned an error\n  Caused by: Call to `setuptools.build_meta:__legacy__.build_editable` failed (exit status: 1)\n\n[stderr]\nerror in tornado setup command: use_2to3 is invalid.\n\nhint: This usually indicates a problem with the package or the build environment.\nResolved 29 packages in 16ms\nInstalled 29 packages in 31ms\n + attrs==24.3.0\n + automat==24.8.1\n + cachetools==5.5.0\n + cffi==1.17.1\n + chardet==5.2.0\n + colorama==0.4.6\n + constantly==23.10.4\n + distlib==0.3.9\n + exceptiongroup==1.2.2\n + filelock==3.16.1\n + hyperlink==21.0.0\n + idna==3.10\n + incremental==24.7.2\n + iniconfig==2.0.0\n + packaging==24.2\n + platformdirs==4.3.6\n + pluggy==1.5.0\n + pycares==4.5.0\n + pycparser==2.22\n + pycurl==7.45.4\n + pyproject-api==1.8.0\n + pytest==8.3.4\n + setuptools==75.6.0\n + tomli==2.2.1\n + tox==4.23.2\n + twisted==24.11.0\n + typing-extensions==4.12.2\n + virtualenv==20.28.0\n + zope-interface==7.2\n",
    "new_commit_res_code": 1,
    "new_commit_res_stdout": "============================= test session starts =============================\nplatform linux -- Python 3.9.21\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f\n.......\ncollected 7 items\n\n=================================== ERRORS =====================================\n__________ TestUrlConcat.test_url_concat_encode_args __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/r2e_tests/test_1.py\", line 17, in test_url_concat_encode_args\n    url = url_concat(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/tornado/httputil.py\", line 162, in url_concat\n    return url + urllib.urlencode(args)\nAttributeError: module 'urllib' has no attribute 'urlencode'\n__________ TestUrlConcat.test_url_concat_mult_params __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/r2e_tests/test_1.py\", line 45, in test_url_concat_mult_params\n    url = url_concat(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/tornado/httputil.py\", line 162, in url_concat\n    return url + urllib.urlencode(args)\nAttributeError: module 'urllib' has no attribute 'urlencode'\n__________ TestUrlConcat.test_url_concat_no_query_params __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/r2e_tests/test_1.py\", line 10, in test_url_concat_no_query_params\n    url = url_concat(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/tornado/httputil.py\", line 162, in url_concat\n    return url + urllib.urlencode(args)\nAttributeError: module 'urllib' has no attribute 'urlencode'\n__________ TestUrlConcat.test_url_concat_q_with_no_trailing_amp __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/r2e_tests/test_1.py\", line 31, in test_url_concat_q_with_no_trailing_amp\n    url = url_concat(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/tornado/httputil.py\", line 162, in url_concat\n    return url + urllib.urlencode(args)\nAttributeError: module 'urllib' has no attribute 'urlencode'\n__________ TestUrlConcat.test_url_concat_trailing_amp __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/r2e_tests/test_1.py\", line 38, in test_url_concat_trailing_amp\n    url = url_concat(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/tornado/httputil.py\", line 162, in url_concat\n    return url + urllib.urlencode(args)\nAttributeError: module 'urllib' has no attribute 'urlencode'\n__________ TestUrlConcat.test_url_concat_trailing_q __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/r2e_tests/test_1.py\", line 24, in test_url_concat_trailing_q\n    url = url_concat(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/tornado/httputil.py\", line 162, in url_concat\n    return url + urllib.urlencode(args)\nAttributeError: module 'urllib' has no attribute 'urlencode'\n\n==================== short test summary info ====================\nERROR r2e_tests.test_1::TestUrlConcat::test_url_concat_encode_args\nERROR r2e_tests.test_1::TestUrlConcat::test_url_concat_mult_params\nPASSED r2e_tests.test_1::TestUrlConcat::test_url_concat_no_params\nERROR r2e_tests.test_1::TestUrlConcat::test_url_concat_no_query_params\nERROR r2e_tests.test_1::TestUrlConcat::test_url_concat_q_with_no_trailing_amp\nERROR r2e_tests.test_1::TestUrlConcat::test_url_concat_trailing_amp\nERROR r2e_tests.test_1::TestUrlConcat::test_url_concat_trailing_q\n=================== 6 errors, 1 passed in 0.00s ===================\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts =============================\nplatform linux -- Python 3.9.21\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f\n.......\ncollected 7 items\n\n=================================== ERRORS =====================================\n__________ TestUrlConcat.test_url_concat_encode_args __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/r2e_tests/test_1.py\", line 17, in test_url_concat_encode_args\n    url = url_concat(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/tornado/httputil.py\", line 161, in url_concat\n    return url + urllib.urlencode(args)\nAttributeError: module 'urllib' has no attribute 'urlencode'\n__________ TestUrlConcat.test_url_concat_mult_params __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/r2e_tests/test_1.py\", line 45, in test_url_concat_mult_params\n    url = url_concat(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/tornado/httputil.py\", line 161, in url_concat\n    return url + urllib.urlencode(args)\nAttributeError: module 'urllib' has no attribute 'urlencode'\n__________ TestUrlConcat.test_url_concat_no_params __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/r2e_tests/test_1.py\", line 52, in test_url_concat_no_params\n    url = url_concat(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/tornado/httputil.py\", line 161, in url_concat\n    return url + urllib.urlencode(args)\nAttributeError: module 'urllib' has no attribute 'urlencode'\n__________ TestUrlConcat.test_url_concat_no_query_params __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/r2e_tests/test_1.py\", line 10, in test_url_concat_no_query_params\n    url = url_concat(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/tornado/httputil.py\", line 161, in url_concat\n    return url + urllib.urlencode(args)\nAttributeError: module 'urllib' has no attribute 'urlencode'\n__________ TestUrlConcat.test_url_concat_q_with_no_trailing_amp __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/r2e_tests/test_1.py\", line 31, in test_url_concat_q_with_no_trailing_amp\n    url = url_concat(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/tornado/httputil.py\", line 161, in url_concat\n    return url + urllib.urlencode(args)\nAttributeError: module 'urllib' has no attribute 'urlencode'\n__________ TestUrlConcat.test_url_concat_trailing_amp __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/r2e_tests/test_1.py\", line 38, in test_url_concat_trailing_amp\n    url = url_concat(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/tornado/httputil.py\", line 161, in url_concat\n    return url + urllib.urlencode(args)\nAttributeError: module 'urllib' has no attribute 'urlencode'\n__________ TestUrlConcat.test_url_concat_trailing_q __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/r2e_tests/test_1.py\", line 24, in test_url_concat_trailing_q\n    url = url_concat(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_155c20fe890e1a71323ab8ac966f69027a512b0f/tornado/httputil.py\", line 161, in url_concat\n    return url + urllib.urlencode(args)\nAttributeError: module 'urllib' has no attribute 'urlencode'\n\n==================== short test summary info ====================\nERROR r2e_tests.test_1::TestUrlConcat::test_url_concat_encode_args\nERROR r2e_tests.test_1::TestUrlConcat::test_url_concat_mult_params\nERROR r2e_tests.test_1::TestUrlConcat::test_url_concat_no_params\nERROR r2e_tests.test_1::TestUrlConcat::test_url_concat_no_query_params\nERROR r2e_tests.test_1::TestUrlConcat::test_url_concat_q_with_no_trailing_amp\nERROR r2e_tests.test_1::TestUrlConcat::test_url_concat_trailing_amp\nERROR r2e_tests.test_1::TestUrlConcat::test_url_concat_trailing_q\n=================== 7 errors in 0.00s ===================\n",
    "old_commit_res_stderr": ""
}
tornado/httputil.py tornado/test/httputil_test.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]

**Title:** AttributeError Raised When `url_concat` Called Without Arguments

**Description:**

When using the `url_concat` function without providing any query parameters, an `AttributeError` is raised. The function attempts to call `urllib.urlencode(args)`, but the `urlencode` method does not exist in the `urllib` module in Python 3, leading to the failure.

**Example Code:**

```python
url = url_concat("https://localhost/path?r=1&t=2", {})
# Expected URL: "https://localhost/path?r=1&t=2"
# Instead, an AttributeError is raised
```

**Expected Behavior:**

Calling `url_concat` with an empty dictionary for arguments should return the original URL unchanged, without adding any extra delimiters.

**Actual Behavior:**

An error occurs:
```
AttributeError: module 'urllib' has no attribute 'urlencode'
```

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### tornado/httputil.py
#!/usr/bin/env python
#
# Copyright 2009 Facebook
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

"""HTTP utility code shared by clients and servers."""

import urllib
import re

class HTTPHeaders(dict):
    """A dictionary that maintains Http-Header-Case for all keys.

    Supports multiple values per key via a pair of new methods,
    add() and get_list().  The regular dictionary interface returns a single
    value per key, with multiple values joined by a comma.

    >>> h = HTTPHeaders({"content-type": "text/html"})
    >>> h.keys()
    ['Content-Type']
    >>> h["Content-Type"]
    'text/html'

    >>> h.add("Set-Cookie", "A=B")
    >>> h.add("Set-Cookie", "C=D")
    >>> h["set-cookie"]
    'A=B,C=D'
    >>> h.get_list("set-cookie")
    ['A=B', 'C=D']

    >>> for (k,v) in sorted(h.get_all()):
    ...    print '%s: %s' % (k,v)
    ...
    Content-Type: text/html
    Set-Cookie: A=B
    Set-Cookie: C=D
    """
    def __init__(self, *args, **kwargs):
        # Don't pass args or kwargs to dict.__init__, as it will bypass
        # our __setitem__
        dict.__init__(self)
        self._as_list = {}
        self.update(*args, **kwargs)

    # new public methods

    def add(self, name, value):
        """Adds a new value for the given key."""
        norm_name = HTTPHeaders._normalize_name(name)
        if norm_name in self:
            # bypass our override of __setitem__ since it modifies _as_list
            dict.__setitem__(self, norm_name, self[norm_name] + ',' + value)
            self._as_list[norm_name].append(value)
        else:
            self[norm_name] = value

    def get_list(self, name):
        """Returns all values for the given header as a list."""
        norm_name = HTTPHeaders._normalize_name(name)
        return self._as_list.get(norm_name, [])

    def get_all(self):
        """Returns an iterable of all (name, value) pairs.

        If a header has multiple values, multiple pairs will be
        returned with the same name.
        """
        for name, list in self._as_list.iteritems():
            for value in list:
                yield (name, value)

    def parse_line(self, line):
        """Updates the dictionary with a single header line.

        >>> h = HTTPHeaders()
        >>> h.parse_line("Content-Type: text/html")
        >>> h.get('content-type')
        'text/html'
        """
        name, value = line.split(":", 1)
        self.add(name, value.strip())

    @classmethod
    def parse(cls, headers):
        """Returns a dictionary from HTTP header text.

        >>> h = HTTPHeaders.parse("Content-Type: text/html\\r\\nContent-Length: 42\\r\\n")
        >>> sorted(h.iteritems())
        [('Content-Length', '42'), ('Content-Type', 'text/html')]
        """
        h = cls()
        for line in headers.splitlines():
            if line:
                h.parse_line(line)
        return h

    # dict implementation overrides

    def __setitem__(self, name, value):
        norm_name = HTTPHeaders._normalize_name(name)
        dict.__setitem__(self, norm_name, value)
        self._as_list[norm_name] = [value]

    def __getitem__(self, name):
        return dict.__getitem__(self, HTTPHeaders._normalize_name(name))

    def __delitem__(self, name):
        norm_name = HTTPHeaders._normalize_name(name)
        dict.__delitem__(self, norm_name)
        del self._as_list[norm_name]

    def get(self, name, default=None):
        return dict.get(self, HTTPHeaders._normalize_name(name), default)

    def update(self, *args, **kwargs):
        # dict.update bypasses our __setitem__
        for k, v in dict(*args, **kwargs).iteritems():
            self[k] = v

    _NORMALIZED_HEADER_RE = re.compile(r'^[A-Z0-9][a-z0-9]*(-[A-Z0-9][a-z0-9]*)*$')
    _normalized_headers = {}

    @staticmethod
    def _normalize_name(name):
        """Converts a name to Http-Header-Case.

        >>> HTTPHeaders._normalize_name("coNtent-TYPE")
        'Content-Type'
        """
        try:
            return HTTPHeaders._normalized_headers[name]
        except KeyError:
            if HTTPHeaders._NORMALIZED_HEADER_RE.match(name):
                normalized = name
            else:
                normalized = "-".join([w.capitalize() for w in name.split("-")])
            HTTPHeaders._normalized_headers[name] = normalized
            return normalized


def url_concat(url, args):
    """Concatenate url and argument dictionary regardless of whether
    url has existing query parameters.

    >>> url_concat("http://example.com/foo?a=b", dict(c="d"))
    'http://example.com/foo?a=b&c=d'
    """
    if url[-1] not in ('?', '&'):
        url += '&' if ('?' in url) else '?'
    return url + urllib.urlencode(args)


def doctests():
    import doctest
    return doctest.DocTestSuite()

if __name__ == "__main__":
    import doctest
    doctest.testmod()

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]

**Title:** AttributeError Raised When `url_concat` Called Without Arguments

**Description:**

When using the `url_concat` function without providing any query parameters, an `AttributeError` is raised. The function attempts to call `urllib.urlencode(args)`, but the `urlencode` method does not exist in the `urllib` module in Python 3, leading to the failure.

**Example Code:**

```python
url = url_concat("https://localhost/path?r=1&t=2", {})
# Expected URL: "https://localhost/path?r=1&t=2"
# Instead, an AttributeError is raised
```

**Expected Behavior:**

Calling `url_concat` with an empty dictionary for arguments should return the original URL unchanged, without adding any extra delimiters.

**Actual Behavior:**

An error occurs:
```
AttributeError: module 'urllib' has no attribute 'urlencode'
```

[/ISSUE]
{
    "TestUrlConcat.test_url_concat_encode_args": "ERROR",
    "TestUrlConcat.test_url_concat_mult_params": "ERROR",
    "TestUrlConcat.test_url_concat_no_params": "PASSED",
    "TestUrlConcat.test_url_concat_no_query_params": "ERROR",
    "TestUrlConcat.test_url_concat_q_with_no_trailing_amp": "ERROR",
    "TestUrlConcat.test_url_concat_trailing_amp": "ERROR",
    "TestUrlConcat.test_url_concat_trailing_q": "ERROR"
}
diff --git a/tornado/httputil.py b/tornado/httputil.py
index b33ea33e..04a21fbc 100644
--- a/tornado/httputil.py
+++ b/tornado/httputil.py
@@ -156,6 +156,7 @@ def url_concat(url, args):
     >>> url_concat("http://example.com/foo?a=b", dict(c="d"))
     'http://example.com/foo?a=b&c=d'
     """
+    if not args: return url
     if url[-1] not in ('?', '&'):
         url += '&' if ('?' in url) else '?'
     return url + urllib.urlencode(args)

{"tornado/httputil.py": "#!/usr/bin/env python\n#\n# Copyright 2009 Facebook\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\n\"\"\"HTTP utility code shared by clients and servers.\"\"\"\n\nimport urllib\nimport re\n\nclass HTTPHeaders(dict):\n    \"\"\"A dictionary that maintains Http-Header-Case for all keys.\n\n    Supports multiple values per key via a pair of new methods,\n    add() and get_list().  The regular dictionary interface returns a single\n    value per key, with multiple values joined by a comma.\n\n    >>> h = HTTPHeaders({\"content-type\": \"text/html\"})\n    >>> h.keys()\n    ['Content-Type']\n    >>> h[\"Content-Type\"]\n    'text/html'\n\n    >>> h.add(\"Set-Cookie\", \"A=B\")\n    >>> h.add(\"Set-Cookie\", \"C=D\")\n    >>> h[\"set-cookie\"]\n    'A=B,C=D'\n    >>> h.get_list(\"set-cookie\")\n    ['A=B', 'C=D']\n\n    >>> for (k,v) in sorted(h.get_all()):\n    ...    print '%s: %s' % (k,v)\n    ...\n    Content-Type: text/html\n    Set-Cookie: A=B\n    Set-Cookie: C=D\n    \"\"\"\n    def __init__(self, *args, **kwargs):\n        # Don't pass args or kwargs to dict.__init__, as it will bypass\n        # our __setitem__\n        dict.__init__(self)\n        self._as_list = {}\n        self.update(*args, **kwargs)\n\n    # new public methods\n\n    def add(self, name, value):\n        \"\"\"Adds a new value for the given key.\"\"\"\n        norm_name = HTTPHeaders._normalize_name(name)\n        if norm_name in self:\n            # bypass our override of __setitem__ since it modifies _as_list\n            dict.__setitem__(self, norm_name, self[norm_name] + ',' + value)\n            self._as_list[norm_name].append(value)\n        else:\n            self[norm_name] = value\n\n    def get_list(self, name):\n        \"\"\"Returns all values for the given header as a list.\"\"\"\n        norm_name = HTTPHeaders._normalize_name(name)\n        return self._as_list.get(norm_name, [])\n\n    def get_all(self):\n        \"\"\"Returns an iterable of all (name, value) pairs.\n\n        If a header has multiple values, multiple pairs will be\n        returned with the same name.\n        \"\"\"\n        for name, list in self._as_list.iteritems():\n            for value in list:\n                yield (name, value)\n\n    def parse_line(self, line):\n        \"\"\"Updates the dictionary with a single header line.\n\n        >>> h = HTTPHeaders()\n        >>> h.parse_line(\"Content-Type: text/html\")\n        >>> h.get('content-type')\n        'text/html'\n        \"\"\"\n        name, value = line.split(\":\", 1)\n        self.add(name, value.strip())\n\n    @classmethod\n    def parse(cls, headers):\n        \"\"\"Returns a dictionary from HTTP header text.\n\n        >>> h = HTTPHeaders.parse(\"Content-Type: text/html\\\\r\\\\nContent-Length: 42\\\\r\\\\n\")\n        >>> sorted(h.iteritems())\n        [('Content-Length', '42'), ('Content-Type', 'text/html')]\n        \"\"\"\n        h = cls()\n        for line in headers.splitlines():\n            if line:\n                h.parse_line(line)\n        return h\n\n    # dict implementation overrides\n\n    def __setitem__(self, name, value):\n        norm_name = HTTPHeaders._normalize_name(name)\n        dict.__setitem__(self, norm_name, value)\n        self._as_list[norm_name] = [value]\n\n    def __getitem__(self, name):\n        return dict.__getitem__(self, HTTPHeaders._normalize_name(name))\n\n    def __delitem__(self, name):\n        norm_name = HTTPHeaders._normalize_name(name)\n        dict.__delitem__(self, norm_name)\n        del self._as_list[norm_name]\n\n    def get(self, name, default=None):\n        return dict.get(self, HTTPHeaders._normalize_name(name), default)\n\n    def update(self, *args, **kwargs):\n        # dict.update bypasses our __setitem__\n        for k, v in dict(*args, **kwargs).iteritems():\n            self[k] = v\n\n    _NORMALIZED_HEADER_RE = re.compile(r'^[A-Z0-9][a-z0-9]*(-[A-Z0-9][a-z0-9]*)*$')\n    _normalized_headers = {}\n\n    @staticmethod\n    def _normalize_name(name):\n        \"\"\"Converts a name to Http-Header-Case.\n\n        >>> HTTPHeaders._normalize_name(\"coNtent-TYPE\")\n        'Content-Type'\n        \"\"\"\n        try:\n            return HTTPHeaders._normalized_headers[name]\n        except KeyError:\n            if HTTPHeaders._NORMALIZED_HEADER_RE.match(name):\n                normalized = name\n            else:\n                normalized = \"-\".join([w.capitalize() for w in name.split(\"-\")])\n            HTTPHeaders._normalized_headers[name] = normalized\n            return normalized\n\n\ndef url_concat(url, args):\n    \"\"\"Concatenate url and argument dictionary regardless of whether\n    url has existing query parameters.\n\n    >>> url_concat(\"http://example.com/foo?a=b\", dict(c=\"d\"))\n    'http://example.com/foo?a=b&c=d'\n    \"\"\"\n    if url[-1] not in ('?', '&'):\n        url += '&' if ('?' in url) else '?'\n    return url + urllib.urlencode(args)\n\n\ndef doctests():\n    import doctest\n    return doctest.DocTestSuite()\n\nif __name__ == \"__main__\":\n    import doctest\n    doctest.testmod()\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]

**Title:** AttributeError Raised When `url_concat` Called Without Arguments

**Description:**

When using the `url_concat` function without providing any query parameters, an `AttributeError` is raised. The function attempts to call `urllib.urlencode(args)`, but the `urlencode` method does not exist in the `urllib` module in Python 3, leading to the failure.

**Example Code:**

```python
url = url_concat("https://localhost/path?r=1&t=2", {})
# Expected URL: "https://localhost/path?r=1&t=2"
# Instead, an AttributeError is raised
```

**Expected Behavior:**

Calling `url_concat` with an empty dictionary for arguments should return the original URL unchanged, without adding any extra delimiters.

**Actual Behavior:**

An error occurs:
```
AttributeError: module 'urllib' has no attribute 'urlencode'
```

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### tornado/httputil.py
#!/usr/bin/env python
#
# Copyright 2009 Facebook
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

"""HTTP utility code shared by clients and servers."""

import urllib
import re

class HTTPHeaders(dict):
    """A dictionary that maintains Http-Header-Case for all keys.

    Supports multiple values per key via a pair of new methods,
    add() and get_list().  The regular dictionary interface returns a single
    value per key, with multiple values joined by a comma.

    >>> h = HTTPHeaders({"content-type": "text/html"})
    >>> h.keys()
    ['Content-Type']
    >>> h["Content-Type"]
    'text/html'

    >>> h.add("Set-Cookie", "A=B")
    >>> h.add("Set-Cookie", "C=D")
    >>> h["set-cookie"]
    'A=B,C=D'
    >>> h.get_list("set-cookie")
    ['A=B', 'C=D']

    >>> for (k,v) in sorted(h.get_all()):
    ...    print '%s: %s' % (k,v)
    ...
    Content-Type: text/html
    Set-Cookie: A=B
    Set-Cookie: C=D
    """
    def __init__(self, *args, **kwargs):
        # Don't pass args or kwargs to dict.__init__, as it will bypass
        # our __setitem__
        dict.__init__(self)
        self._as_list = {}
        self.update(*args, **kwargs)

    # new public methods

    def add(self, name, value):
        """Adds a new value for the given key."""
        norm_name = HTTPHeaders._normalize_name(name)
        if norm_name in self:
            # bypass our override of __setitem__ since it modifies _as_list
            dict.__setitem__(self, norm_name, self[norm_name] + ',' + value)
            self._as_list[norm_name].append(value)
        else:
            self[norm_name] = value

    def get_list(self, name):
        """Returns all values for the given header as a list."""
        norm_name = HTTPHeaders._normalize_name(name)
        return self._as_list.get(norm_name, [])

    def get_all(self):
        """Returns an iterable of all (name, value) pairs.

        If a header has multiple values, multiple pairs will be
        returned with the same name.
        """
        for name, list in self._as_list.iteritems():
            for value in list:
                yield (name, value)

    def parse_line(self, line):
        """Updates the dictionary with a single header line.

        >>> h = HTTPHeaders()
        >>> h.parse_line("Content-Type: text/html")
        >>> h.get('content-type')
        'text/html'
        """
        name, value = line.split(":", 1)
        self.add(name, value.strip())

    @classmethod
    def parse(cls, headers):
        """Returns a dictionary from HTTP header text.

        >>> h = HTTPHeaders.parse("Content-Type: text/html\\r\\nContent-Length: 42\\r\\n")
        >>> sorted(h.iteritems())
        [('Content-Length', '42'), ('Content-Type', 'text/html')]
        """
        h = cls()
        for line in headers.splitlines():
            if line:
                h.parse_line(line)
        return h

    # dict implementation overrides

    def __setitem__(self, name, value):
        norm_name = HTTPHeaders._normalize_name(name)
        dict.__setitem__(self, norm_name, value)
        self._as_list[norm_name] = [value]

    def __getitem__(self, name):
        return dict.__getitem__(self, HTTPHeaders._normalize_name(name))

    def __delitem__(self, name):
        norm_name = HTTPHeaders._normalize_name(name)
        dict.__delitem__(self, norm_name)
        del self._as_list[norm_name]

    def get(self, name, default=None):
        return dict.get(self, HTTPHeaders._normalize_name(name), default)

    def update(self, *args, **kwargs):
        # dict.update bypasses our __setitem__
        for k, v in dict(*args, **kwargs).iteritems():
            self[k] = v

    _NORMALIZED_HEADER_RE = re.compile(r'^[A-Z0-9][a-z0-9]*(-[A-Z0-9][a-z0-9]*)*$')
    _normalized_headers = {}

    @staticmethod
    def _normalize_name(name):
        """Converts a name to Http-Header-Case.

        >>> HTTPHeaders._normalize_name("coNtent-TYPE")
        'Content-Type'
        """
        try:
            return HTTPHeaders._normalized_headers[name]
        except KeyError:
            if HTTPHeaders._NORMALIZED_HEADER_RE.match(name):
                normalized = name
            else:
                normalized = "-".join([w.capitalize() for w in name.split("-")])
            HTTPHeaders._normalized_headers[name] = normalized
            return normalized


def url_concat(url, args):
    """Concatenate url and argument dictionary regardless of whether
    url has existing query parameters.

    >>> url_concat("http://example.com/foo?a=b", dict(c="d"))
    'http://example.com/foo?a=b&c=d'
    """
    if url[-1] not in ('?', '&'):
        url += '&' if ('?' in url) else '?'
    return url + urllib.urlencode(args)


def doctests():
    import doctest
    return doctest.DocTestSuite()

if __name__ == "__main__":
    import doctest
    doctest.testmod()

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user